C人脸识别

 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;         // ###图片颜色的位数,一般为32unsigned int    biCompression;      // 说明图象数据压缩的类型,0为不压缩unsigned int    biSizeImage;        // 像素数据所占大小,因为使用BI_RGB,所以设置为0unsigned int    biXPelsPerMeter;    // 说明水平分辨率,缺省为0unsigned int    biYPelsPerMeter;    // 说明垂直分辨率,缺省为0unsigned 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;                // ###图片颜色的位数,一般为32unsigned int    biCompression = 0;              // 说明图象数据压缩的类型,0为不压缩unsigned int    biSizeImage = 0;                // 像素数据所占大小,因为使用BI_RGB,所以设置为0unsigned int    biXPelsPerMeter = 0;            // 说明水平分辨率,缺省为0unsigned int    biYPelsPerMeter = 0;            // 说明垂直分辨率,缺省为0unsigned 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;elsebgra[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;elsereturn (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.hqwc.cn/news/20870.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态内存管理

目录 动态内存分配存在的原因 动态内存函数的介绍 malloc和free calloc realloc 常见的动态内存错误 对NULL指针的解引用操作 对动态开辟空间的越界访问 对非动态开辟内存使用free释放 使用free释放一块动态开辟内存的一部分 对同一块动态内存多次释放 动态开辟…

Vue+elementUI实现下拉框多选和反选

Vue代码如下&#xff1a; <el-form-item label"下拉框名称&#xff1a;"><el-select size"mini" v-model"testModelName" focus"getSelectInfo" :disabled"SelectStyle" filterable clearable placeholder"&…

基于 FPGA 的 HDMI/DVI 显示

文章目录 前言一、HDMI 与 DVI 的区别与联系1.1 DVI 接口含义1.2 HDMI 接口含义1.3 HDMI 与 DVI 的区别1.4 HDMI 与 DVI 的兼容性1.5 HDMI 与 DVI 接口对比 二、DVI 数据链路介绍2.1 输入接口层2.2 TMDS 发送器2.3 TMDS 接收器2.4 输出接口层 三、传输原理与实现3.1 TMDS原理3.…

express框架使用express-generator工具

1.全局安装 npm install -g express-generator 2.检测是否安装成功 express -h 3. 快速创建Express应用程序的工具 express -e express-generator 说明&#xff1a;express-e和express-generator都是用于快速创建Express应用程序的工具。express-e是一个命令行工具&#xff0…

基于Javaweb实现ATM机系统开发实战(九)存款功能实现

先看前端界面确定后端需要处理的参数&#xff0c;把一些参数进行修改&#xff1a; <% page language"java" contentType"text/html; charsetUTF-8" pageEncoding"UTF-8"%> <% taglib prefix"c" uri"http://java.sun.com…

ubuntu使用WHEELTE N100并用rviz显示

写在最开头&#xff0c;如果wheeltec n100被自己改动过参数导致无法读取数据&#xff0c;建议在window的上位机中恢复出厂设置并重新上电&#xff0c;在转入ubuntu。因为我就是这个问题&#xff0c;客服远程操控才帮我解决的。 所有官方资料共享&#xff0c;侵删&#xff1a; …

bug:file name too long文件名超出系统最大限制

各操作系统支持最长的文件和目录名称长度&#xff08;Linux、Win、Mac&#xff09; 今天开发需求的时候发现无法新建文件&#xff0c;提示file name too lang&#xff0c;于是翻阅和查询了一些资料&#xff0c;发现不同操作系统下文件名和目录名最长的长度不同。 操作系统文件名…

Live800在线客服系统:工单系统如何提升企业服务效率?

随着企业规模的扩大和客户需求的增加&#xff0c;如何有效地管理客户服务日益成为企业发展过程中重要的一环。作为客户服务的重要支撑系统之一&#xff0c;工单系统被越来越多的企业所采用。那么工单系统究竟是如何帮助企业提升服务效率的呢&#xff1f;本文将从工单的分配、追…

LeetCode[470]用Rand7()实现Rand10()

难度&#xff1a;Medium 题目&#xff1a; 给定方法 rand7 可生成 [1,7] 范围内的均匀随机整数&#xff0c;试写一个方法 rand10 生成 [1,10] 范围内的均匀随机整数。 你只能调用 rand7() 且不能调用其他方法。请不要使用系统的 Math.random() 方法。 每个测试用例将有一个内部…

Android Java代码与JNI交互 JNI访问Java构造方法(九)

🔥 Android Studio 版本 🔥 🔥 创建包含JNI相关函数类 JNIConstructorClass.java 🔥 package com.cmake.ndk1.jni;import com.cmake.ndk1.model.Animal;public class JNIConstructorClass {static {System.loadLibrary("constructor-class-lib");}public …

Django_获取api接口的传参

目录 当参数为form-data 或者x-www-form-urlencoded类型时&#xff0c;使用request.POST获取到参数 当参数为raw类型时&#xff0c;使用request.body获取到参数&#xff0c;获取的参数需要经过处理才能使用 源码等资料获取方法 当参数为form-data 或者x-www-form-urlencoded…

连接区块链节点的 JavaScript 库 web3.js

文章目录 前言web3.js 介绍web3.js安装web3.js库模块介绍连接区块链节点向区块链网络发送数据查询区块链网络数据 前言 通过前面的文章我们可以知道基于区块链开发一个DApp&#xff0c;而DApp结合了智能合约和用户界面&#xff08;客户端&#xff09;&#xff0c;那客户端是如…

ubuntu20离线安装nodejs、GO、go.rice及yarn

虽然是离线安装&#xff0c;但该有的安装包还是需要的… 目录 1、安装nodejs1.1查看本地是否存在nodejs1.2创建nodejs文件夹1.3下载nodejs二进制文件1.4解压并改名1.5添加软连接 2安装GO2.1创建go文件夹2.2下载go二进制文件2.3解压文件2.4添加环境变量2.5设置sudo可执行go命令…

stm32(SPI读写W25Q18)

SPI 是什么&#xff1f; SPI是串行外设接口&#xff08;Serial Peripheral Interface&#xff09;的缩写&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总 线&#xff0c;并且在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&#xff0c;同时为PC…

移动互联网应用程序(app)个人信息安全测试能力验证-流程介绍

ILONGYU 产品简介 为规范检验检测市场&#xff0c;提升检验检测机构技术能力&#xff0c;根据《检验检测机构资质认定管理办法》《实验室能力验证实施办法》等有关规定&#xff0c;市场监管总局决定在社会重点关注的部分检验检测领域&#xff0c;组织开展2020年国家级检验检测…

Bootstrap编写一个兼容主流浏览器的受众巨幕式风格页面

Bootstrap编写一个兼容主流浏览器的受众巨幕式风格页面 虽然说IE6除了部分要求苛刻的需求以外已经被可以不考虑了&#xff0c;但是WIN7自带的浏览器IE8还是需要支持的。 本文这个方法主要的优点&#xff0c;个人觉得就是准备少&#xff0c;不需要上网寻找大量的图片做素材&…

MySQL的MVCC

目录 1.MVCC基本概念 1.当前读&#xff1a; 2.快照读&#xff1a; 3.MVCC&#xff1a;Multi-Version Concurrency Control&#xff0c;多版本并发控制&#xff1a; 2.MVCC实现原理-三个隐藏字段 3.MVCC实现原理-undo log日志 1.undo log&#xff1a;回滚日志 2.undo log…

samba服务

samba服务 1. samba诞生背景2. samba简介3. samba配置文件详解3.1 配置文件中参数的作用3.1.1 security参数的四种验证方式3.1.2 passdb backend定义用户后台的类型 4. samba服务部署与配置windos共享linux挂载共享 1. samba诞生背景 1987 年&#xff0c;微软公司和英特尔公司…

vue中elementUI表单循环验证

进行验证的步骤 1、表单el-form 添加 :model"form" ref"form" :rules"rules"&#xff0c;注意是 :model"form"不是v-model&#xff0c;而后每个el-form-item绑定prop 2、不循环的示例在官网可看 3、循环表单的验证&#xff1a;3-1、e…

顺序表 --- C语言实现

目录 1.线性表 2.顺序表 2.1 概念和结构 2.2 接口实现 2.3 数组相关面试题 2.4 顺序表的问题及思考 1.线性表 什么是线性表 &#xff1a; 线性表&#xff08;linear list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常…