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;         // ###图片颜色的位数,一般为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.mfbz.cn/a/37283.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

介绍AI绘画课,让智能工具助力创作 释放无限想象力 助你成为绘画大师

演示地址&#xff1a; www.runruncode.com/portal/article/index/id/19458/cid/81.html 画画是一项有趣的活动&#xff0c;它让人充满无限可能。对许多人来说&#xff0c;画画既是一种放松的方式&#xff0c;也是一种与创意、文化和艺术联系的途径。如果你是一个初学者&#x…

云原生——Docker容器化实战

❄️作者介绍&#xff1a;奇妙的大歪❄️ &#x1f380;个人名言&#xff1a;但行前路&#xff0c;不负韶华&#xff01;&#x1f380; &#x1f43d;个人简介&#xff1a;云计算网络运维专业人员&#x1f43d; 前言 "Docker"一词指代了多个概念&#xff0c;包括开源…

uniapp实现聊天消息触,vue3和vue2实现聊天消息触底 scrollTop ,scrollHeight Pc端H5端都适用

uniapp触底SDN链接如下(本人的另一篇博客) uniapp聊天时时触底链接 Pc端 模拟手机端H5 vue3写法 <template><div><!-- 聊天窗体 --><div class"test" id"gundong"><div class"text" v-for"p in chat"&…

2023年上半年总结

2023年上半年总结 引言问答CSDN 竞赛技能树博客原力值粉丝数贡献墙个人能力图新星计划总结 引言 老顾是一个懒癌晚期患者&#xff0c;通常情况下&#xff0c;是一条不折不扣的咸鱼&#xff0c;在工作中&#xff0c;也大多数时间都用来摸鱼了。 摸鱼时间太长&#xff0c;也就有…

RabbitMQ的集群

新建一个虚拟机,重新安装一个RabbitMQ,不会安装的可以看下面的连接: 在Linux中安装RabbitMQ_流殇꧂的博客-CSDN博客 1.修改/etc/hosts映射文件,两台虚拟机都需要修改 vim /etc/hosts 127.0.0.1 node1 localhost.localdomain localhost4 localhost4.localdomain4 ::1 node1 loca…

SpringBoot + Kotlin 中使用 GRPC 进行服务通信

示例项目见&#xff1a;kotlin-grpc 一、导入依赖&#xff1a; import com.google.protobuf.gradle.* plugins { id("org.springframework.boot") version "2.3.1.RELEASE" id("io.spring.dependency-management") version "1.0.9.REL…

idea连接远程MySQL数据库

填写URL&#xff0c;以mysql为例 格式 jdbc:mysql://ip地址:端口号/数据库名 jdbc:mysql://127.0.0.1:3306/ldentification _Information

软件测试基础知识

软件测试的生命周期 软件测试的生命周期和软件的生命周期是不一样的&#xff0c;软件包括需求分析和规划&#xff0c;设计和编码&#xff0c;测试和验证&#xff0c;部署和维护&#xff0c;退役和回收等等&#xff0c;而软件测试的生命周期则是需求分析-测试计划-测试设计&…

对 Jenkins+ANT+Jmeter 接口测试的实践

目录 1、前言 2、框架与数据准备 3、脚本设计 4、整理测试报告 1、前言 JenkinsANTJMeter是一种常见的接口测试实践方案&#xff0c;可以实现自动化的接口测试和持续集成。Jenkins是一个流行的持续集成工具&#xff0c;ANT是一个构建工具&#xff0c;而JMeter是一个功能强大…

maven配置java outofmemory选项

在maven之中选择Add VM options&#xff0c;这样命令就多出来一个关于VM options配置的属性&#xff0c;此时就可以输入对于VM的设置

Ubuntu 放弃了战斗向微软投降

导读这几天看到 Ubuntu 放弃 Unity 和 Mir 开发&#xff0c;转向 Gnome 作为默认桌面环境的新闻&#xff0c;作为一个Linux十几年的老兵和Linux桌面的开发者&#xff0c;内心颇感良多。Ubuntu 做为全世界Linux界的桌面先驱者和创新者&#xff0c;突然宣布放弃自己多年开发的Uni…

jenkins实现easyswoole 持续集成/持续部署

jenkins环境jenkins需要使用root用户启动可通过修改 vim /etc/sysconfig/jenkins改为root,也可直接命令行root启动新增流水线项目安装远程构建插件Generic Webhook Trigger勾选触发远程构建保存之后,访问 /generic-webhook-trigger/invoke?tokeneasyswoole-test,即可自动bui…

【网络】协议的定制与Json序列化和反序列化

文章目录 应用层初识TCP协议通讯流程定制协议再谈协议网络版本计算器Protocal.hppCalServerCalClient Json的安装 应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 初识TCP协议通讯流程 建立链接和断开链接 基于TCP协议&#xff0c…

nginx相关

1、nginx无默认配置文件 参考文章&#xff1a;nginx配置失败&#xff0c;卸载后重装无 nginx.conf文件_haojuntu的博客-CSDN博客 2、nginx更改服务器的端口号 参考文章&#xff1a;https://www.cnblogs.com/chaosfe/p/16123585.html#:~:text%E6%88%91%E4%BB%AC%E6%9F%A5%E7%…

Java----使用eureka进行注册连接(微服务简单实现)

当采用微服务架构时&#xff0c;各个业务流程被逐一分解&#xff0c;虽说是分解&#xff0c;但还是要进行连接的&#xff0c;最简单的就是使用http请求&#xff0c;将他们联系起来&#xff0c;通过给容器注入restTemplate&#xff0c;然后使用内置的方法进行请求&#xff0c;但…

Java虚拟机(JVM)介绍

JVM是什么 JVM是Java Virtual Machine的缩写。它是一种基于计算设备的规范&#xff0c;是一台虚拟机&#xff0c;即虚构的计算机。 JVM屏蔽了具体操作系统平台的信息&#xff08;显然&#xff0c;就像是我们在电脑上开了个虚拟机一样&#xff09;&#xff0c;当然&#xff0c;J…

支付宝接入

支付宝接入 python-alipay-sdk pycryptodome一、电脑网站支付 1.1 获取支付宝密钥 沙箱网址 1.APPID 2.应用私钥 3.支付宝公钥1.2 存放密钥 在与 settings.py 的同级目录下创建 pem 文件夹pem 文件夹下创建 app_private_key.pem 和 alipay_public_key.pem app_private_key…

pdf文档加水印怎么弄?用这款软件很方便

在工作中&#xff0c;我们经常需要将PDF文件发送给他人&#xff0c;但无法保证文件内容不被窃取&#xff0c;因此需要添加水印来保证文件的安全性。如果你不知道如何给PDF文件添加水印&#xff0c;以下两款软件可以帮助你轻松实现&#xff0c;一起来看看吧&#xff01; 方法一&…

爬虫之Scrapy

一 介绍 Scrapy一个开源和协作的框架&#xff0c;其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的&#xff0c;使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。但目前Scrapy的用途十分广泛&#xff0c;可用于如数据挖掘、监测和自动化测试等领域&#x…

Python实现Excel文件拷贝图片到另一个的Excel文件(保持原有图片比例)

Python实现Excel文件拷贝图片到另一个的Excel文件&#xff08;保持原有图片比例&#xff09; 1、前言1.1 成功拷贝但是比例错误1.2 直接报错 2、解决办法3、号外 1、前言 今天朋友给我一个需求&#xff0c;需要把xlsx文件中的图片拷贝到另一个xlsx中&#xff0c;但是试过网上比…