C人脸识别

news2024/12/25 15:41:39

 1、原始图片:

 2、灰度化下:

3、均值滤波: 

4、 二值图加边缘检测

 

 5、生成积分图

6、把待检测的人脸区域划分为25个,因为是一个数组,这样分别统计每个区域的像素个数:

x0: 60, y0: 100, x1: 157, y1: 200    width: 228, height: 228
IGmap.data a4: 7979, a3: 4423, a2: 2130, a1: 1407 result: 2833(外层红色方框内像素个数)

x0: 60, y0: 100, x1: 79, y1: 120    width: 228, height: 228
IGmap.data a4: 2345, a3: 2002, a2: 1624, a1: 1407 result: 126(第一排左边第一个小格子)

x0: 79, y0: 100, x1: 98, y1: 120    width: 228, height: 228
IGmap.data a4: 3132, a3: 2598, a2: 2345, a1: 2002 result: 191(第一排左边第二个小格子)

x0: 98, y0: 100, x1: 117, y1: 120    width: 228, height: 228
IGmap.data a4: 3790, a3: 3137, a2: 3132, a1: 2598 result: 119

x0: 117, y0: 100, x1: 136, y1: 120    width: 228, height: 228
IGmap.data a4: 4551, a3: 3703, a2: 3790, a1: 3137 result: 195

x0: 136, y0: 100, x1: 155, y1: 120    width: 228, height: 228
IGmap.data a4: 5319, a3: 4350, a2: 4551, a1: 3703 result: 121

x0: 60, y0: 120, x1: 79, y1: 140    width: 228, height: 228
IGmap.data a4: 2549, a3: 2345, a2: 1800, a1: 1624 result: 28

x0: 79, y0: 120, x1: 98, y1: 140    width: 228, height: 228
IGmap.data a4: 3419, a3: 3132, a2: 2549, a1: 2345 result: 83

x0: 98, y0: 120, x1: 117, y1: 140    width: 228, height: 228
IGmap.data a4: 4106, a3: 3790, a2: 3419, a1: 3132 result: 29

x0: 117, y0: 120, x1: 136, y1: 140    width: 228, height: 228
IGmap.data a4: 4972, a3: 4551, a2: 4106, a1: 3790 result: 105

x0: 136, y0: 120, x1: 155, y1: 140    width: 228, height: 228
IGmap.data a4: 5757, a3: 5319, a2: 4972, a1: 4551 result: 17

x0: 60, y0: 140, x1: 79, y1: 160    width: 228, height: 228
IGmap.data a4: 2722, a3: 2549, a2: 1913, a1: 1800 result: 60

x0: 79, y0: 140, x1: 98, y1: 160    width: 228, height: 228
IGmap.data a4: 3688, a3: 3419, a2: 2722, a1: 2549 result: 96

x0: 98, y0: 140, x1: 117, y1: 160    width: 228, height: 228
IGmap.data a4: 4533, a3: 4106, a2: 3688, a1: 3419 result: 158

x0: 117, y0: 140, x1: 136, y1: 160    width: 228, height: 228
IGmap.data a4: 5482, a3: 4972, a2: 4533, a1: 4106 result: 83

x0: 136, y0: 140, x1: 155, y1: 160    width: 228, height: 228
IGmap.data a4: 6291, a3: 5757, a2: 5482, a1: 4972 result: 24

x0: 60, y0: 160, x1: 79, y1: 180    width: 228, height: 228
IGmap.data a4: 2874, a3: 2722, a2: 2008, a1: 1913 result: 57

x0: 79, y0: 160, x1: 98, y1: 180    width: 228, height: 228
IGmap.data a4: 4043, a3: 3688, a2: 2874, a1: 2722 result: 203

x0: 98, y0: 160, x1: 117, y1: 180    width: 228, height: 228
IGmap.data a4: 5052, a3: 4533, a2: 4043, a1: 3688 result: 164

x0: 117, y0: 160, x1: 136, y1: 180    width: 228, height: 228
IGmap.data a4: 6190, a3: 5482, a2: 5052, a1: 4533 result: 189

x0: 136, y0: 160, x1: 155, y1: 180    width: 228, height: 228
IGmap.data a4: 7121, a3: 6291, a2: 6190, a1: 5482 result: 122

x0: 60, y0: 180, x1: 79, y1: 200    width: 228, height: 228
IGmap.data a4: 3047, a3: 2874, a2: 2130, a1: 2008 result: 51

x0: 79, y0: 180, x1: 98, y1: 200    width: 228, height: 228
IGmap.data a4: 4405, a3: 4043, a2: 3047, a1: 2874 result: 189

x0: 98, y0: 180, x1: 117, y1: 200    width: 228, height: 228
IGmap.data a4: 5580, a3: 5052, a2: 4405, a1: 4043 result: 166

x0: 117, y0: 180, x1: 136, y1: 200    width: 228, height: 228
IGmap.data a4: 6851, a3: 6190, a2: 5580, a1: 5052 result: 133

x0: 136, y0: 180, x1: 155, y1: 200    width: 228, height: 228
IGmap.data a4: 7863, a3: 7121, a2: 6851, a1: 6190 result: 81

运行效果图:

 

比如,这里是左边眼睛跟人中的比值(d1/d2),和左边眼睛跟人中像素的比值(d3/d2),正常人脸,眼睛区域比人中像素会多些 (如上图所示)

	//2、两只眼睛,与人中的比例,正常来说,眼睛比人中的像素点多些
	double d1 = (double)(Areas[0] + Areas[1] + Areas[5] + Areas[6]);        
	double d2 = (double)(Areas[2] + Areas[7]);  
	double d3 = (double)(Areas[3] + Areas[4] + Areas[8] + Areas[9]);
    DEBUG_PRINT_WITH_TIME("if: Step 2: %f,  %f", d1 / d2 ,  d3 / d2);
    if (d1 / d2 < 2.6 || d3 / d2 < 2.6)
    {
    	DEBUG_PRINT_WITH_TIME("1");
		return 1;
	}

当前还不能在图片中滑动窗口,检测窗口不断变化,这个其实也是需要测试参数的,因为图片中人脸大小是不固定的。

代码(从这里借鉴了不少,但发现原博客有些小问题,测试了一两天):

#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<windef.h>
#include<math.h>
#include<string.h>

#include <graphics.h>
#include "Global.h"

// 获取文件的后缀名
char* GetFlieExta(char* filename)
{
    int fileLen = strlen(filename);
    int exLen = 0;
    char *fileExta = (char *)malloc(255);
    memset(fileExta, 0, 255);

    for (int i = fileLen-1; i > 0; i--)
        if (filename[i] == '.')
		{
            exLen = fileLen - i;
            break;
        }
    strncpy(fileExta, filename + fileLen - exLen, exLen);
    return fileExta;
}


// BGRA颜色结构体
typedef struct tagBGRA
{
	unsigned char blue;          // 该颜色的蓝色分量  (值范围为0-255)
	unsigned char green;         // 该颜色的绿色分量  (值范围为0-255)
	unsigned char red;           // 该颜色的红色分量  (值范围为0-255)
	unsigned char transparency;  // 透明度,在bmp中是保留值,无实际效果
}BGRA, * PBGRA;

// 图像结构体
typedef struct tagIMAGE_SELF
{
	unsigned int width;
	unsigned int height;
    BGRA* data;
}IMAGE_SELF, * PIMAGE_SELF;


// BMP文件的处理

// BMP文件头结构体
typedef struct tagBITMAP_HEAD_INFO
{
    /* bmp文件头的信息,有#的是重点!!*/
    // bmp文件头
    unsigned short  bfType;             // 0x424D,即BM字符串,表明是bmp格式文件
    unsigned int    bfSize;             // ###总的bmp文件大小 以字节为单位     
    unsigned short  bfReserved1;        // 保留,必须设置为0                     
    unsigned short  bfReserved2;        // 保留,必须设置为0 
    unsigned int    bfOffBits;          // ###总的bmp头部的大小(包括位图信息头),即到像素数据的偏移  
    // 位图信息头
    unsigned int    biSize;             // 位图信息头的大小
    unsigned int    biWidth;            // ###图像的宽  
    unsigned int    biHeight;           // ###图像的高  
    unsigned short  biPlanes;           // 颜色平面数,即调色盘数,恒等于1 
    unsigned short  biBitCount;         // ###图片颜色的位数,一般为32
    unsigned int    biCompression;      // 说明图象数据压缩的类型,0为不压缩
    unsigned int    biSizeImage;        // 像素数据所占大小,因为使用BI_RGB,所以设置为0
    unsigned int    biXPelsPerMeter;    // 说明水平分辨率,缺省为0
    unsigned int    biYPelsPerMeter;    // 说明垂直分辨率,缺省为0
    unsigned int    biClrUsed;          // 说明本位图实际使用调色盘的颜色索引数,0表示全部
    unsigned int    biClrImportant;     // 说明本位图重要调色盘的颜色索引数,0表示全都重要
}BITMAP_HEAD_INFO,*PBITMAP_HEAD_INFO;

// 加载BMP图片
IMAGE_SELF Image_bmp_load(char* filename)
{
    IMAGE_SELF imageOld;
    BITMAP_HEAD_INFO bmpHeadInfo;
    FILE* fp;
    
	DEBUG_PRINT_WITH_TIME("filename: %s", filename);
    if ((fp = fopen(filename, "rb")) == NULL)
    {
		printf("打开%s文件失败!\n", filename);
		exit(0);
	}    
    
    // 读取bmp头部
    // bmp文件头
    fread(&bmpHeadInfo.bfType, 1, sizeof(bmpHeadInfo.bfType), fp);
    fread(&bmpHeadInfo.bfSize, 1, sizeof(bmpHeadInfo.bfSize), fp);
    fread(&bmpHeadInfo.bfReserved1, 1, sizeof(bmpHeadInfo.bfReserved1), fp);
    fread(&bmpHeadInfo.bfReserved2, 1, sizeof(bmpHeadInfo.bfReserved2), fp);
    fread(&bmpHeadInfo.bfOffBits, 1, sizeof(bmpHeadInfo.bfOffBits), fp);
    // 位图信息头
    fread(&bmpHeadInfo.biSize, 1, sizeof(bmpHeadInfo.biSize), fp);
    fread(&bmpHeadInfo.biWidth, 1, sizeof(bmpHeadInfo.biWidth), fp);
    fread(&bmpHeadInfo.biHeight, 1, sizeof(bmpHeadInfo.biHeight), fp);
    fread(&bmpHeadInfo.biPlanes, 1, sizeof(bmpHeadInfo.biPlanes), fp);
    fread(&bmpHeadInfo.biBitCount, 1, sizeof(bmpHeadInfo.biBitCount), fp);
    fread(&bmpHeadInfo.biCompression, 1, sizeof(bmpHeadInfo.biCompression), fp);
    fread(&bmpHeadInfo.biSizeImage, 1, sizeof(bmpHeadInfo.biSizeImage), fp);
    fread(&bmpHeadInfo.biXPelsPerMeter, 1, sizeof(bmpHeadInfo.biXPelsPerMeter), fp);
    fread(&bmpHeadInfo.biYPelsPerMeter, 1, sizeof(bmpHeadInfo.biYPelsPerMeter), fp);
    fread(&bmpHeadInfo.biClrUsed, 1, sizeof(bmpHeadInfo.biClrUsed), fp);
    fread(&bmpHeadInfo.biClrImportant, 1, sizeof(bmpHeadInfo.biClrImportant), fp);


    // 读取bmp位图数据
    BGRA* bgra = (BGRA*)malloc(sizeof(BGRA) * (bmpHeadInfo.biWidth * bmpHeadInfo.biHeight));
    fseek(fp, bmpHeadInfo.bfOffBits, SEEK_SET);

    if (bmpHeadInfo.biBitCount == 32)
    {
       for (unsigned int i = 0; i < bmpHeadInfo.biWidth * bmpHeadInfo.biHeight; i++)
            fread(&bgra[i], 1, sizeof(BGRA), fp);
    }
    else if (bmpHeadInfo.biBitCount == 24)
    {
		// 计算每行补几个字节零
        int k = 4 * (3 * bmpHeadInfo.biWidth / 4 + 1) - 3 * bmpHeadInfo.biWidth;
        for (unsigned int i = 0; i < bmpHeadInfo.biWidth * bmpHeadInfo.biHeight; i++)
        {
            if (k != 4 && (ftell(fp)- 54 + k ) % (3 * bmpHeadInfo.biWidth + k)==0)
                fseek(fp, ftell(fp) + k, SEEK_SET);

            fread(&bgra[i].blue, 1, sizeof(unsigned char), fp);
            fread(&bgra[i].green, 1, sizeof(unsigned char), fp);
            fread(&bgra[i].red, 1, sizeof(unsigned char), fp);
            bgra[i].transparency = (unsigned char)0xFF;
        }
    }

    imageOld.data = bgra;
    imageOld.width = bmpHeadInfo.biWidth;
    imageOld.height = bmpHeadInfo.biHeight;

    fclose(fp);
    return imageOld;
}

// 保存BMP图片
void Image_bmp_save(char* filename,IMAGE_SELF imageOld)
{
    FILE* fp = fopen(filename, "wb");

    unsigned short  bfType = 0x4D42;                // 0x424D,即BM字符串,表明是bmp格式文件
    unsigned int    bfSize = imageOld.width * imageOld.height * 4 + 54;  // ###总的bmp文件大小 以字节为单位     
    unsigned short  bfReserved1 = 0;                // 保留,必须设置为0                     
    unsigned short  bfReserved2 = 0;                // 保留,必须设置为0 
    unsigned int    bfOffBits = 54;                 // ###总的bmp头部的大小(包括位图信息头),即到像素数据的偏移  
    unsigned int    biSize = 40;                    // 位图信息头的大小
    unsigned int    biWidth = imageOld.width;                 // ###图像的宽  
    unsigned int    biHeight = imageOld.height;                // ###图像的高  
    unsigned short  biPlanes = 1;                   // 颜色平面数,即调色盘数,恒等于1 
    unsigned short  biBitCount = 32;                // ###图片颜色的位数,一般为32
    unsigned int    biCompression = 0;              // 说明图象数据压缩的类型,0为不压缩
    unsigned int    biSizeImage = 0;                // 像素数据所占大小,因为使用BI_RGB,所以设置为0
    unsigned int    biXPelsPerMeter = 0;            // 说明水平分辨率,缺省为0
    unsigned int    biYPelsPerMeter = 0;            // 说明垂直分辨率,缺省为0
    unsigned int    biClrUsed = 0;                  // 说明本位图实际使用调色盘的颜色索引数,0表示全部
    unsigned int    biClrImportant = 0;             // 说明本位图重要调色盘的颜色索引数,0表示全都重要

    fwrite(&bfType, 2, 1, fp);
    fwrite(&bfSize, 4, 1, fp);
    fwrite(&bfReserved1, 2, 1, fp);
    fwrite(&bfReserved2, 2, 1, fp);
    fwrite(&bfOffBits, 4, 1, fp);
    fwrite(&biSize, 4, 1, fp);
    fwrite(&biWidth, 4, 1, fp);
    fwrite(&biHeight, 4, 1, fp);
    fwrite(&biPlanes, 2, 1, fp);
    fwrite(&biBitCount, 2, 1, fp);
    fwrite(&biCompression, 4, 1, fp);
    fwrite(&biSizeImage, 4, 1, fp);
    fwrite(&biXPelsPerMeter, 4, 1, fp);
    fwrite(&biYPelsPerMeter, 4, 1, fp);
    fwrite(&biClrUsed, 4, 1, fp);
    fwrite(&biClrImportant, 4, 1, fp);

    fwrite(imageOld.data, sizeof(BGRA) * imageOld.width * imageOld.height, 1, fp);

    fclose(fp);
}


// 加载图片
IMAGE_SELF Image_load(char* filename)
{
    IMAGE_SELF im;
    //char* fileEx= GetFlieExta(filename);
	//DEBUG_PRINT_WITH_TIME("fileEx: %s", fileEx);
    //if (strcmp(fileEx, ".bmp") == 0)
        im = Image_bmp_load(filename);

    return im;
}

// 保存图片
void Image_save(char* filename, IMAGE_SELF imageOld)
{
    char* fileEx = GetFlieExta(filename);

    if (strcmp(fileEx, ".bmp") == 0)
        Image_bmp_save(filename, imageOld);
}

// 释放图像结构体
void Image_free(IMAGE_SELF imageOld)
{
    free(imageOld.data);
}

#define UPTURN_MODE_HORIZONTAL 0    // 水平翻转
#define UPTURN_MODE_VERTICAL 1      // 垂直翻转

#define GRAY_MODE_WEIGHT 1           // 加权法(推荐使用)
#define GRAY_MODE_BEST 2             // 最值法
#define GRAY_MODE_AVERAGE 3          // 均值法
#define GRAY_MODE_PART_RED 4         // 分量法_RED
#define GRAY_MODE_PART_GREEN 5       // 分量法_GREEN
#define GRAY_MODE_PART_BLUE 6        // 分量法_BLUE

// 彩色图转灰度图
IMAGE_SELF Transform_color_grayscale(IMAGE_SELF imageOld, int grayscale_mode)
{
    int color = 0;
    
    IMAGE_SELF imageNew;
    imageNew.width = imageOld.width;
    imageNew.height = imageOld.height;
	imageNew.data = (BGRA*)malloc(sizeof(BGRA) * imageOld.width * imageOld.height);
	
    switch (grayscale_mode)
    {
	    case GRAY_MODE_WEIGHT:
	    {
	        for (unsigned int i = 0; i < imageOld.width * imageOld.height; i++)
	        {
	            color = (imageOld.data[i].blue * 114 + imageOld.data[i].green * 587 + imageOld.data[i].red * 299) / 1000;
	            imageNew.data[i].blue = color;
	            imageNew.data[i].green = color;
	            imageNew.data[i].red = color;
	        }
	        break;
	    }

	    default: DEBUG_PRINT_WITH_TIME("error:  switch default branch....");
    }
	return imageNew;
}

// 二值图(自适应阈值法,areaSize=25较合适,当图片线条多且密时,不推荐使用)
IMAGE_SELF Transform_color_BW_Adaptive(IMAGE_SELF imageOld, int areaSize)
{
	IMAGE_SELF imageNew;
    imageNew.width = imageOld.width;
    imageNew.height = imageOld.height;
	
    // areaSize为区域的大小,区域越大,效果图的细节越好,areaSize=25较合适
    BGRA* bgra = (BGRA*)malloc(sizeof(BGRA) * imageOld.width * imageOld.height);
    int* p = (int*)malloc(sizeof(int) * areaSize); // p->position 位置坐标
    int k = (int)(sqrt((double)areaSize)) / 2;  // 重合区域边长的一半

    for (unsigned int i = 0; i < imageOld.width * imageOld.height; i++)
    {
        // 计算与卷积和对应重合区域的坐标
        int t = 0; // 记录p的下标
        for (int n = k; n >= -k; n--)
            for (int m = -k; m <= k; m++)
            {
                p[t] = ((i % imageOld.width) + m) + (i / imageOld.width + n) * imageOld.width;
                t++;
            }

        // 判断是否越界
        for (int j = 0; j < areaSize; j++)
            if (p[j] < 0 || p[j] >= imageOld.width * imageOld.height)
                p[j] = i;

        unsigned int color = 0;
        for (int j = 0; j < areaSize; j++)
            color += imageOld.data[p[j]].blue;
        color /= areaSize;

        if (imageOld.data[i].blue >= color)
            bgra[i].blue = 255;
        else
            bgra[i].blue = 0;

        bgra[i].green = bgra[i].blue;
        bgra[i].red = bgra[i].blue;
    }

    free(p);
    imageNew.data = bgra;
    return imageNew;
}

// 判断像素值的范围
unsigned char Tool_RBG(int BRRA)
{
    if (BRRA > 255)
        return (unsigned char)255;
    else if (BRRA < 0)
        return (unsigned char)0;
    else
        return (unsigned char)BRRA;
}

// 卷积操作(自定义)
IMAGE_SELF Kernels_use_DIY(IMAGE_SELF imageOld, double* kernels, int areaSize, double modulus)
{
	IMAGE_SELF imageNew;
    imageNew.width = imageOld.width;
    imageNew.height = imageOld.height;
	imageNew.data = (BGRA*)malloc(sizeof(BGRA) * imageOld.width * imageOld.height);	
	memcpy(imageNew.data, imageOld.data, sizeof(BGRA) * imageOld.width * imageOld.height);
	
	// kernels卷积核
	// areaSize区域的大小
	// modulus最后乘的系数
	
    BGRA* bgra = (BGRA*)malloc(sizeof(BGRA) * imageOld.width * imageOld.height);
    int* p = (int*)malloc(sizeof(int) * areaSize); // p->position 位置坐标
    int k = (int)(sqrt((double)areaSize)) / 2;  // 重合区域边长的一半

    for (unsigned int i = 0; i < imageOld.width * imageOld.height; i++)
    {
        // 计算与卷积和对应重合区域的坐标
        int t = 0; // 记录p的下标
        for(int n = k; n >= -k; n--)
            for (int m = -k; m <= k; m++)
                p[t] = ((i % imageOld.width) + m) + (i / imageOld.width + n) * imageOld.width, t++;
                
        // 判断是否越界
        for (int j = 0; j < areaSize; j++) 
            if (p[j] < 0 || p[j] >= imageOld.width * imageOld.height)
                p[j] = i;

		// 相乘相加
        int blue = 0, green = 0, red = 0;
        for (int j = 0; j < areaSize; j++)
        {
            blue += imageOld.data[p[j]].blue * kernels[j];
            green += imageOld.data[p[j]].green * kernels[j];
            red += imageOld.data[p[j]].red * kernels[j];
        }
        
        bgra[i].blue = Tool_RBG(blue * modulus);
        bgra[i].green = Tool_RBG(green * modulus);
        bgra[i].red = Tool_RBG(red * modulus);
    }

    free(p);
    imageNew.data = bgra;
    return imageNew;
}

// 均值滤波卷积核
double KERNELS_Wave_Average[25] =
{
  1, 1, 1, 1, 1,
  1, 1, 1, 1, 1,
  1, 1, 1, 1, 1,
  1, 1, 1, 1, 1,
  1, 1, 1, 1, 1
};

// 均值滤波
IMAGE_SELF Wavefiltering_Average(IMAGE_SELF imageOld)
{
    return Kernels_use_DIY(imageOld, KERNELS_Wave_Average, 25, 1.0 / 25);
}


// 积分图结构体
typedef struct tagIGIMAGE_SELF
{
    unsigned int width;
    unsigned int height;
    unsigned int *data;
}IGIMAGE_SELF, *PIGIMAGE_SELF;

// 获得积分图(在此之前要保证图片是“白底黑字”)
IGIMAGE_SELF IntegralImage_get(IMAGE_SELF imageOld)
{
    IGIMAGE_SELF IGmap;
    unsigned int* array = (unsigned int *)malloc(sizeof(unsigned int) * imageOld.width * imageOld.height);
    memset(array, 0, sizeof(int) * imageOld.width * imageOld.height);

    int k = 0; // 用于统计每一行的像素个数
    
    for(int height = imageOld.height; height > 0; height--)
	{
		k = 0;
		for(int width = 0; width < imageOld.width; width++)
		{
			int temp = imageOld.data[(height - 1) * imageOld.width + width].blue;
			if(temp == 0)
			{
				k++;
			}
			if (temp == 0)
			{
				//printf("y:");
			}
			else
			{
				//printf("n:");
			}
			
			int heightTemp = imageOld.height - height;
			if (height == imageOld.height)
			{
				array[width] = k;
			}
			else
			{
				array[heightTemp * imageOld.width + width] =  array[(heightTemp - 1) * imageOld.width + width] + k;
				//printf("%3d ", array[heightTemp * imageOld.width + width]);
			}
		}
		//printf("\n");
		//Sleep(1000);
		//pause();
		//DEBUG_PRINT_WITH_TIME("height: %d, k: %d", height, k);
	}

    IGmap.data = array;
    IGmap.width = imageOld.width;
    IGmap.height = imageOld.height;
    return IGmap;
}

// 计算积分区域像素个数
//int IntegralImage_count(IGIMAGE_SELF IGmap, int x0, int y0, int x1, int y1)
long IntegralImage_count(IGIMAGE_SELF IGmap, int x0, int y0, int x1, int y1)
{
    long a1, a2, a3, a4;
    //int leftBottom = x0 + y1 *IGmap.width;
    //int rightTop = x1 + y0 *IGmap.width;
    
    if(x0 > IGmap.width || x1 > IGmap.width || y1 > IGmap.height || y0 > IGmap.height)
	{
		DEBUG_PRINT_WITH_TIME("x0: %ld, y0: %ld, x1: %ld, y1: %ld    width: %d, height: %d", x0, y0, x1, y1, IGmap.width, IGmap.height)
		return -1;
	}
    
	//DEBUG_PRINT_WITH_TIME("x0: %ld, y0: %ld, x1: %ld, y1: %ld    width: %d, height: %d", x0, y0, x1, y1, IGmap.width, IGmap.height)
    
    a1 = y0 * IGmap.width + x0;
    a2 = y1 * IGmap.width + x0;
    a3 = y0 * IGmap.width + x1;
    a4 = y1 * IGmap.width + x1;

	//DEBUG_PRINT_WITH_TIME("a4: %u, a3: %u, a2: %u, a1: %u", a4, a3, a2, a1)
    //  判断是否越界
    if (a1 < 0)
        a1 = 0;
    if (a2 < 0)
        a2 = 0;
    if (a3 < 0)
        a3 = 0;
    if (a3 > IGmap.width * IGmap.height - 1)
        a3 = a4;

	long result = IGmap.data[a4] + IGmap.data[a1] - IGmap.data[a3]  - IGmap.data[a2];
	//DEBUG_PRINT_WITH_TIME("IGmap.data a4: %u, a3: %u, a2: %u, a1: %u result: %u\n", IGmap.data[a4], IGmap.data[a3], IGmap.data[a2], IGmap.data[a1], result)
		
    // 计算区域中的像素数
    return  result;
}

// 释放积分图结构体
void IntegralImage_free(IGIMAGE_SELF IGimage)
{
    free(IGimage.data);
}

// 单分支决策树分类器
double Classifier_decisionStump(IGIMAGE_SELF IGmap, int x0, int y0, int x1, int y1)
{   
    int areaW = abs(x0-x1);
    int areaH = abs(y0-y1);

    // 计算25个区域的像素个数
    int w_all = IntegralImage_count(IGmap, x0, y0, x1, y1);

	int xStep = abs(x0-x1)/5;
	int yStep = abs(y0-y1)/5;
	
	long Areas[25] = {0};
	
	for(int j = 0; j < 5; j++)
	{
		for(int i = 0; i < 5; i++)
		{
			Areas[j * 5 + i] = IntegralImage_count(IGmap, x0 + i * xStep, y0 + yStep * j, x0 + (i + 1) * xStep, y0 + yStep * (j + 1));
			//DEBUG_PRINT_WITH_TIME("j: %d, i: %d, num: %d\n", j, i, num);
		}
	}
	DEBUG_PRINT_WITH_TIME("if: Step 1: %f", (double)w_all / (areaW * areaH));

	for(int i=0;i<25;i++)
	{
		//printf("%d: %d, ", i, Areas[i]);
	}

    // 1、判断是否为人脸,整个区域有的像素个数占整个区域面积的比例
    if ((double)w_all / (areaW * areaH) < 0.19)
    {
    	DEBUG_PRINT_WITH_TIME("1");
		return 0;
	}

	//2、两只眼睛,与人中的比例,正常来说,眼睛比人中的像素点多些
	double d1 = (double)(Areas[0] + Areas[1] + Areas[5] + Areas[6]);        
	double d2 = (double)(Areas[2] + Areas[7]);  
	double d3 = (double)(Areas[3] + Areas[4] + Areas[8] + Areas[9]);
    DEBUG_PRINT_WITH_TIME("if: Step 2: %f,  %f", d1 / d2 ,  d3 / d2);
    if (d1 / d2 < 2.6 || d3 / d2 < 2.6)
    {
    	DEBUG_PRINT_WITH_TIME("1");
		return 0;
	}
	
	//3、鼻子的像素正常比鼻子两边像素多些
	d1 = (double)(Areas[12] + Areas[17]);
	d2 = (double)(Areas[11] + Areas[16]);
	double d4 = (double)(Areas[13] + Areas[18]);
    DEBUG_PRINT_WITH_TIME("if: Step 3: %f,  %f",  d1 / d2 , d3 / d4);
	if (d1 / d2 < 1 || d1 / d4 < 1)
    {
    	DEBUG_PRINT_WITH_TIME("1");
		return 0;
	}
	
	//4、眼睛的像素比眼睛下边的脸像素少些,两边都是,因为考虑到还有胡子
	d1 = (double)(Areas[0] + Areas[1] + Areas[5] + Areas[6]);
	d2 = (double)(Areas[10] + Areas[11] + Areas[15]);
	d3 = (double)(Areas[3] + Areas[4] + Areas[8] + Areas[9]);
	d4 = (double)(Areas[13] + Areas[14] + Areas[19]);   
    DEBUG_PRINT_WITH_TIME("if: Step 4: %f,  %f",  d1 / d2 , d3 / d4);
    if (d1 / d2 < 1.3 || d3 / d4 < 1.3)
    {
    	DEBUG_PRINT_WITH_TIME("1");
		return 0;
	}

	//5、上边脸跟嘴和下巴像素的比值
	d1 = (double)(Areas[0] + Areas[1] + Areas[2] + Areas[3] + Areas[4] + Areas[5] + Areas[6] + Areas[7] + Areas[8] + Areas[9]);
	d2 = (double)(Areas[15] + Areas[16] + Areas[17] + Areas[18] + Areas[19] + Areas[20] + Areas[21] + Areas[22] + Areas[23] + Areas[24]);
    DEBUG_PRINT_WITH_TIME("if: Step 5: %f", d1 / d2);
    if ( d1 / d2 > 2)
    {
    	DEBUG_PRINT_WITH_TIME("1");
		return 0;
	}

	//6、眼睛和嘴巴所占的像素比值不能太小
	d1 = (double)(Areas[0] + Areas[1] + Areas[3] + Areas[4] + Areas[5] + Areas[6] + Areas[8] + Areas[9] 
				+ Areas[12] + Areas[16] + Areas[17] + Areas[18] + Areas[22]);
    DEBUG_PRINT_WITH_TIME("if: Step 6: %f", d1 / w_all);
    if (d1/ w_all < 0.6)
    {
    	DEBUG_PRINT_WITH_TIME("1");
		return 0;
	}
	
	//7、脸左边跟脸右边的比值不能差太多
	d1 = (double)(Areas[0] + Areas[1] + Areas[5] + Areas[6] + Areas[10] + Areas[11]  + Areas[15] + Areas[16]  + Areas[20] + Areas[21]);
	d2 = (double)(Areas[3] + Areas[4] + Areas[8] + Areas[9] + Areas[13] + Areas[14] + Areas[18] + Areas[19] + Areas[23] + Areas[24]);
    double PCT_1 = (double)min(d1, d2)/ max(d1, d2);
    PCT_1 = exp(-3.125 * (PCT_1 - 1) * (PCT_1 - 1)) * 100;

	//8、两只眼睛的比值不能差太多
	d1 = (double)(Areas[0] + Areas[1] + Areas[5] + Areas[6]);
	d2 = (double)(Areas[3] + Areas[4] + Areas[8] + Areas[9]);
    double PCT_2 = (double)min(d1, d2) / max(d1, d2);
    PCT_2 = exp(-3.125 * (PCT_2 - 1) * (PCT_2 - 1)) * 100;

	//9、两腮的比值不能差太多
	d1 = (double)(Areas[15] + Areas[20]);
	d2 = (double)(Areas[19] + Areas[24]);
    double PCT_3 = (double)min(d1, d2) / max(d1, d2);
    PCT_3 = exp(-3.125 * (PCT_3 - 1) * (PCT_3 - 1)) * 100;

    // 计算总的概率
    double PCT_all = (PCT_1 + PCT_2 + PCT_3) / 3;

	DEBUG_PRINT_WITH_TIME("PCT_all: %f, PCT_1: %f, PCT_2: %f, PCT_3: %f", PCT_all, PCT_1, PCT_2, PCT_3)

    if (PCT_all > 60)
        return PCT_all;
}

// 人脸数据结构体
typedef struct tagFACEDATE
{
    int x0;
    int y0;
    int x1;
    int y1;
    double confidence;
}FACEDATE;


//滑动窗口区域(训练用)
FACEDATE MoveWindowArea(IMAGE_SELF imageOld, IGIMAGE_SELF IGmap)
{
    FACEDATE maxFaceDate = { 0, 0, 0 };       // 保存概率最大的人脸区域
    double confidence = 0;              // 置信度
    int minSide = min(imageOld.width, imageOld.height) / 4;  // 最小区域
    int step = 5;                  // 区域每次的增加量
    
    DEBUG_PRINT_WITH_TIME("minSide: %d", minSide)
    
    
	setinitmode(0);
	initgraph(imageOld.width, imageOld.height);
	
	
	int heightStep = imageOld.height / 10;
	int widthStep = imageOld.width / 10;
	
	setcolor(RED);
	int miniStep = 3;
	
	setlinewidth(2);
	
	int FaceWidth = 97;
	int FaceHeight = 100;
	int MoveStepSize = 15;
	
	int x0 = 0;
	int y0 = 0;
	for(int i = 0; i * MoveStepSize < imageOld.width - FaceWidth; i++) //height
	{
	    x0 = i * MoveStepSize;
		for(int j = 0 ; j * MoveStepSize < imageOld.height - FaceHeight; j++)
		{
			y0 = j * MoveStepSize;
			for(int k = 0; k <= 20; k++)
			{
				int x1 = x0 + 97;
				int y1 = y0 + 100;
				
				if(x1 > imageOld.width || y1 > imageOld.height)
				{
					continue;	//TODO
				}
				

				for(unsigned int i = imageOld.height - 1; i > 0 ; i--)
				{
					for(unsigned int j = 0; j < imageOld.width; j++)
					{
						unsigned int point = (imageOld.height - i) * imageOld.width + j; 
						putpixel(j, i, EGERGB(imageOld.data[point].red, 
										       imageOld.data[point].green, 
											   imageOld.data[point].blue));
					}
				}
    
				
				rectangle(x0, y0, x1, y1);

				int xStep = abs(x0-x1)/5;
				int yStep = abs(y0-y1)/5;
				
				for(int j = 1; j < 5; j++)
				{
					line(x0 + xStep * j, y0, x0 + xStep * j, y1 );
					line(x0, y0 + yStep * j, x1, y0 + yStep * j );
				}
			
				
				double confidenceTemp = Classifier_decisionStump(IGmap, x0, y0, x1, y1);
				printf("confidenceTemp: %lf\n\n", confidenceTemp);
				/*DEBUG_PRINT_WITH_TIME("confidenceTemp: %lf", confidenceTemp);
				
				if ((confidence = confidenceTemp) > 1 && confidence > maxFaceDate.confidence)
	            {
	                maxFaceDate.confidence = confidence;
	                maxFaceDate.x0 = x0;
	                maxFaceDate.y0 = y0;
	                maxFaceDate.x1 = x1;
	                maxFaceDate.y1 = y1;
	            }
				*/
				Sleep(100);
			}		
		}
		
		Sleep(100);
	}
	
    // 窗口区域的取值范围
	getch();
	closegraph();
    return maxFaceDate;
 }

// 画出人框
void Image_draw(IMAGE_SELF imageOld ,FACEDATE faceDate)
{
    
}

int main()
{
	DEBUG_PRINT_WITH_TIME("main2 start....");
	char loadFilename[300] = "InputTest_01.bmp";
	char saveFilename[300] = "456.bmp";

	// 用于处理
	IMAGE_SELF image1 = Image_load(loadFilename);
	
	// 用于保存
	IMAGE_SELF image2 = Image_load(loadFilename);
	Image_save("test_image2.bmp", image2);
	
	// 灰度图
	IMAGE_SELF image3 = Transform_color_grayscale(image1, GRAY_MODE_WEIGHT);
	Image_save("test_image3.bmp", image3);
	
	// 均值滤波
	IMAGE_SELF image4 = Wavefiltering_Average(image3);
	Image_save("test_image4.bmp", image4);
	
	// 二值图加边缘检测
	IMAGE_SELF image5 = Transform_color_BW_Adaptive(image4, 25);
	Image_save("test_image5.bmp", image5);
	
	// 积分图
	IGIMAGE_SELF IGmap1 = IntegralImage_get(image5);
	//return 0;
	IMAGE_SELF image36;
	image36.width = IGmap1.width;
	image36.height = IGmap1.height;
	image36.data = (BGRA *)malloc(sizeof(BGRA) * IGmap1.width * IGmap1.height);
	
    for (unsigned int i = 0; i < IGmap1.width * IGmap1.height; i++)
    {
		image36.data[i].red = IGmap1.data[i] % 256;
		image36.data[i].green = IGmap1.data[i] / 256 % 256;
		image36.data[i].blue = IGmap1.data[i] / 256 / 256 % 256;
		image36.data[i].transparency = 0;
		//if (i %1000 == 0)
		//	DEBUG_PRINT_WITH_TIME("w: %d, h: %d, i: %d", image35.width, image35.h, i);
    }
	
	Image_save("test_image6.bmp", image36);	
	
	
	unsigned int *p = IGmap1.data;
	
	DEBUG_PRINT_WITH_TIME("1")
	for(unsigned int i = 0; i < IGmap1.width * IGmap1.height; i++)
	{
		//printf("%d ", p[i]);
		if((i + 1) % IGmap1.width == 0 )
		{
			//putchar('\n');
		}
	}
	
	// 滑动窗口
	FACEDATE faceDate1 = MoveWindowArea(image5, IGmap1);
	
	//DEBUG_PRINT_WITH_TIME("%d, %d, %f", faceDate1.leftBottom, faceDate1.rightTop, faceDate1.confidence);
	//return 0;
	// 画出人脸框
	//Image_draw(image2, faceDate1);
	
	// 保存图片
	Image_save(saveFilename, image2);
	
	// 释放积分图
	IntegralImage_free(IGmap1);
	
	// 释放图片资源 
	Image_free(image1);
	Image_free(image2);
	//Image_show(saveFilename);
	
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/741048.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

X86架构上的Linux操作系统安装与配置ARM架构(并进行示例测试/为DDS编译打基础)

1 查看Linux操作系统是运行在X86架构还是ARM架构平台上2 X86和ARM到底是什么关系&#xff1f;3 X86、ARM、Linux三者的关系到底是什么&#xff1f;&#xff08;大白话理解&#xff09;4、在X86架构的Linux操作系统上搭建ARM架构并进行测试&#xff08;看elf文件的类型是否属于a…

Spark---第 1 章 Spark 内核概述

Spark 内核泛指 Spark 的核心运行机制&#xff0c;包括 Spark 核心组件的运行机制、Spark 任务调度机制、Spark 内存管理机制、Spark 核心功能的运行原理等&#xff0c;熟练掌握 Spark 内核原理&#xff0c;能够帮助我们更好地完成 Spark 代码设计&#xff0c;并能够帮助我们准…

HTML5+CSS3小实例:带进度条的人物卡片切换效果

实例:带进度条的人物卡片切换效果 技术栈:HTML+CSS 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" conte…

XHcms 1.0代码审计

关于熊海CMS1.0的代码审计&#xff0c;一直想要提升自己的代码审计的能力&#xff0c;php没有继续去深入的学习这门语言&#xff0c;反而直接是审计CMS&#xff0c;就拿着熊海CMS来进行审计。 关于熊海cms的源码&#xff0c;附在下方&#xff1a; 链接&#xff1a;https://pan…

How to Write and Publish a Scientific Paper-Preparing the Tables and Figures

Preparing the Tables and Figures 目录 Preparing the Tables and FiguresWHEN TO USE TABLESHOW TO ARRANGE TABULAR MATERIALEXPONENTS IN TABLE HEADINGSFOLLOWING THE JOURNAL’S INSTRUCTIONSTITLES, FOOTNOTES, AND ABBREVIATIONSADDITIONAL TIPS ON TABLES WHEN TO USE…

algo算法实验平台代码分享

algo算法实验平台代码分享 1. 介绍 本实验平台包括前端及后台两个模块&#xff0c;前端由 vue开发而来&#xff0c;后端主要采用 python编写&#xff0c;主要用于算法效果测试测评及改进。 原平台算法已做脱敏&#xff0c;使用者可以根据自己需求进行改写或开发&#xff0c;此…

Echarts使用,大数据量时,折线图曲线Y轴取值刻度不正确

如下图&#xff0c;当数据量过大时&#xff0c;会出现数值对应Y轴刻度显示不正确问题。 ​​​​​​​ 可检查配置项是否存在sampling字段 option {series: [{data: [...],type: line,smooth: true,symbolSize: 0,sampling: "average", // 注意此行}] }; samplin…

android 中颜色的基础知识

一.简单介绍Android中的颜色 安卓支持的颜色模式&#xff1a; PS&#xff1a;其中字母表示通道类型&#xff0c;数值表示该类型用多少位二进制来描述。如ARGB8888则表示有四个通道(ARGB),每个对应的通道均用8位来描述。 注意&#xff1a;我们常用的是ARGB8888和ARGB4444&am…

java 网上二手车销售系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 网上二手车销售系统是一套完善的web设计系统&#xff0c;对理解JSP java SERLVET mvc编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0…

C#ConsoleKey类讲解

大家好&#xff0c;我是华山自控编程朱老师 前几天一个学员在学习C#与ConsoleKey类交互时,对ConsoleKey提取的键盘字符有些疑惑 。下面我们就详细讲讲C# 和ConsoleKey交互的相关知识。 C#和ConsoleKey是编程领域中重要的概念和工具之一。C#是一种通用的、面向对象的编程语言&…

【优选算法】—— 双指针问题

从今天开始&#xff0c;整个暑假期间。我将不定期给大家带来有关各种算法的题目&#xff0c;帮助大家攻克面试过程中可能会遇到的算法这一道难关。 目录 &#xff08;一&#xff09; 基本概念 &#xff08;二&#xff09;题目讲解 1、难度&#xff1a;easy 1️⃣移动零 2️…

数据结构——二叉树的实现

什么是二叉树&#xff1f; 二叉树&#xff08;binary tree&#xff09;是指树中节点的度不大于2的有序树&#xff0c;它是一种最简单且最重要的树。二叉树的递归定义为&#xff1a;二叉树是一棵空树&#xff0c;或者是一棵由一个根节点和两棵互不相交的&#xff0c;分别称作根…

【实验七】界面化和事件处理

1、完成书上239页实验题目1和题目2 题目1 package chap10.test; //awt是一个软件包&#xff0c;包含用于创建用户界面和绘制图形图像的所有分类。 import java.awt.BorderLayout;//边界管理器import java.awt.Color; import java.awt.FlowLayout;//流布局一般用来安排面板中的…

Django_自定义文件存储类并将图片上传到FastDFS

目录 将图片到FastDFS和浏览的流程图 使用自定义文件存储类 1、定义存储类 2、创建FastDFS的配置文件 3、修改settings.py配置 4、上传图片进行验证 4.1 定义一个包含ImageField字段的模型类 4.2 登录django的admin后台 4.3 上传图片 4.4 查看图片 源码等资料获取方…

What Is the Character Limit for ChatGPT? 聊天GPT的字符限制是多少?

The character limit for ChatGPT text prompts is 4096 characters, and there’s also a limit of 4096 tokens per conversation. If you aren’t sure how many tokens your prompt uses, you can calculate that with OpenAI’s Tokenizer tool. ChatGPT 文本提示的字符限…

AutoHand第三人称的AutoHandVRIK开启和关闭

第三人称结构 第三人称在AutoHandVRIK脚本初始化完毕后&#xff0c;会出把两只手提到玩家对象根部对象上&#xff0c;并且在原始的模型手的位置创建了新的对象&#xff08;leftHandFollowTarget&#xff0c;rightHandFollowTarget&#xff09;&#xff0c;副VRIK大概用途就是I…

从字母百分比看字符串处理的奥妙

本篇博客会讲解力扣“2278. 字母在字符串中的百分比”的解题思路&#xff0c;这是题目链接。 本题的思路是&#xff0c;统计字符letter在字符串s中出现的次数cnt&#xff0c;以及字符串s的长度n&#xff0c;其中n也可以使用strlen来求。最终要求计算的百分比是cnt*100/n。 int…

性能测试工具LoadRunne函数如何使用详解(上)

性能测试工具LoadRunner是软件测试行业主流测试工具&#xff0c;也是软件测试实验室在申请CNAS/CMA资质、测试质量体系建设一定会用到的一款软件测试工具。LoadRunner的函数是是组成测试脚本的最小单元&#xff0c;它真实模拟 一个个请求操作&#xff0c;并接收返回结果。学习L…

王道考研数据结构——顺序表

08 线性表的基本概念 对参数的修改结果需要“带回来”就使用引用类型的参数 顺序表的定义&#xff1a; 顺序存储、链式存储 sizeof(int)、sizeof(MyStruct) 顺序表的扩展 虽然大小改变&#xff0c;但是时间开销大 让代码健壮&#xff1a;对数据操作的合法性进行检查&#xf…

How to Write and Publish a Scientific Paper-How to Prepare Effective Graphs

How to Prepare Effective Graphs WHEN NOT TO USE GRAPHS 在上一章中&#xff0c;我们讨论了某些不应该制成表格的数据类型。它们也不应该被转换成图表。基本上&#xff0c;图表都是图形表格。 要点是这样的。某些类型的数据&#xff0c;特别是稀疏类型或单调重复的类型&am…