5.4.1 人脸肤色相似度比较

编程实现

[1] 添加继承自ImageDib类的FaceDetect类进行有关人脸检测的算法编程与实现。在文件FaceDetect.h中定义该类如代码5-1所示。

代码5-1 FaceDetect类

        class FaceDetect : public ImageDib
        {
            public:
                int m_nBitCountOut;                   //输出图像每像素位数
                unsigned char * m_pImgDataOut;       //输出图像位图数据指针
                LPRGBQUAD m_lpColorTableOut;     //输出图像颜色表
                unsigned char * m_pImgDataIn;    //输入图像位图数据指针
                double** m_pSimArray;            //相似度矩阵
                double Cb_Mean;
                double Cr_Mean;
                double Cov00;
                double Cov01;
                double Cov10;
                double Cov11;
        private:
                int m_imgWidthOut;                //输出图像的宽,像素为单位
                int m_imgHeightOut;               //输出图像的高,像素为单位
                int m_nColorTableLengthOut;      //输出图像颜色表长度
        public:
                void CalSBound(int top,int bottom,int left,int right);
                FaceDetect();                     //不带参数的构造函数
                FaceDetect(CSize size, int nBitCount, LPRGBQUAD lpColorTable,
                    unsigned char *pImgData1,unsigned char *pImgData2);
                                                  //带参数的构造函数
                ~FaceDetect();                    //析构函数
                bool CalBin();
                void CalMedFl(double **s,int w,int h,int n);
                void CalSim();
                CSize GetDimensions();           //以像素为单位返回输出图像的宽和高
        };

[2] 在视图类里添加消息映射函数Onsimilardegree(),以进行相似度计算。Onsimilardegree ()函数代码如代码5-2所示。

代码5-2 Onsimilardegree()函数

        void CDemo1View::Onsimilardegree()
        {
                //获取文档类指针
            CDemo1Doc *pDoc=GetDocument();
            //获取ImgCenterDib类对象m_dib的指针,访问当前DIB数据
            ImageDib *pDib=pDoc->GetPDib();
            //只处理彩色图像
            if(pDib->m_nBitCount!=24){
                ::MessageBox(0, "只处理彩色图像", MB_OK,0);
                return ;
            }
            FaceDetect simcalculation(pDib->GetDimensions(),pDib->m_nBitCount,
                pDib->m_lpColorTable, pDib->m_pImgData,NULL);
            simcalculation.CalSim();
            //建立一个新视图,显示结果
            CMainFrame* pFrame = (CMainFrame *)(AfxGetApp()->m_pMainWnd);
            //发送新建文件的消息,创建一个新的文档-视图
            pFrame->SendMessage(WM_COMMAND, ID_FILE_NEW);
            //获取新建视图指针
            CDemo1View* pView=(CDemo1View*)pFrame->MDIGetActive()->GetActiveView();
            //获取相关联的新的文档类指针
            CDemo1Doc* pDocNew=pView->GetDocument();
            //获取新文档中的ImgCenterDib类对象指针
            ImageDib *dibNew=pDocNew->GetPDib();
            //将变换后的输出图像作为新建文档的DIB进行显示
            dibNew->ReplaceDib(simcalculation.GetDimensions(),simcalculation.m_nBitCou ntOut,simca lculation.m_lpColorTableOut, simcalculation.m_pImgDataOut);
            //设置滚动窗口
            pView->OnInitialUpdate();
            //文档数据置脏,提示存盘信息
            pDocNew->SetModifiedFlag(TRUE);
            //各视图刷新显示
            pDocNew->UpdateAllViews(pView);
        }

[3] 上述代码中,调用了FaceDetect类中的CalSim()函数,以进行肤色相似度计算,其具体代码如代码5-3所示。

代码5-3 CalSim()函数

        void FaceDetect::CalSim()
        {
            //若为灰度图像,则返回
            if(m_nBitCount==8) return;
            //释放旧的输出图像数据及颜色表缓冲区
            if(m_pImgDataOut!=NULL){
                delete []m_pImgDataOut;
                m_pImgDataOut=NULL;
            }
            if(m_lpColorTableOut!=NULL){
                delete []m_lpColorTableOut;
                m_lpColorTableOut=NULL;
            }
            //灰值化后,每像素位数为8比特
            m_nBitCountOut=8;
            //颜色表长度
            m_nColorTableLengthOut=ComputeColorTabalLength(m_nBitCountOut);
            //申请颜色表缓冲区,生成灰度图像的颜色表
            if(m_nColorTableLengthOut!=0){
            m_lpColorTableOut=new RGBQUAD[m_nColorTableLengthOut];
                for(int i=0; i<m_nColorTableLengthOut;i++){
                    m_lpColorTableOut[i].rgbBlue=i;
                    m_lpColorTableOut[i].rgbGreen=i;
                    m_lpColorTableOut[i].rgbRed=i;
                    m_lpColorTableOut[i].rgbReserved=0;
                }
            }
            //输入图像每像素字节数,彩色图像为3字节/像素
            int pixelByteIn=3;
            //输入图像每行像素所占字节数,必须是4的倍数
            int lineByteIn=(m_imgWidth*pixelByteIn+3)/4*4;
            //输出图像的宽高,与输入图像相等
            m_imgWidthOut=m_imgWidth;
            m_imgHeightOut=m_imgHeight;
            //输出图像每行像素所占字节数,必须是4的倍数
            int lineByteOut=(m_imgWidth*m_nBitCountOut/8+3)/4*4;
            //申请输出图像位图数据缓冲区
            m_pImgDataOut=new unsigned char[lineByteOut*m_imgHeight];
            //循环变量,图像的坐标
            int i,j,k,j1,j2,flag;
            char c_t1[4];
            for(i=0;i<m_imgHeight;i++){
                for(j=0;j<m_imgWidth;j++){
                    *(m_pImgDataOut+i*lineByteOut+j)=255;
                }
            }
            m_pSimArray = new double*[m_imgHeight];
            for(i=0;i<m_imgHeight;i++)
                m_pSimArray[i] = new double[m_imgWidth];
            //RGB色彩空间变换到YCgCr色彩空间,然后计算该点属于皮肤区域的概率
            for(i=0;i<m_imgHeight;i++)
                for (j=0;j<m_imgWidth;j++){
                    for(k=0;k<3;k++){
                        c_t1[k]=*(m_pImgDataIn1+i*lineByteIn+j*pixelByteIn+k);
                    }
                    int C_b=(int)c_t1[0]&255;
                    int C_g=(int)c_t1[1]&255;
                    int C_r=(int)c_t1[2]&255;
                    double Cb=(128-37.797*C_r/255-74.203*C_g/255+112*C_b/255);
                    double Cr=(128+112*C_r/255-93.786*C_g/255-18.214*C_b/255);
                    double tt =(Cb-Cb_Mean)*((Cb-Cb_Mean)*Cov11-(Cr-Cr_Mean)*Cov10)+
                        (Cr-Cr_Mean)*(-(Cb-Cb_Mean)*Cov01+(Cr-Cr_Mean)*Cov00);
                    tt =(-0.5*tt)/(Cov00*Cov11-Cov01*Cov10);
                    m_pSimArray[i][j]=exp(tt);
                }
                //调用中值滤波函数
                CalMedFl(m_pSimArray,m_imgWidth, m_imgHeight,9);
                //统计出该幅图像上所有像素点的最大肤色相似度
                double max=0.0;
                for(i=0;i<m_imgHeight;i++)
                    for (j=0;j<m_imgWidth;j++){
                        if(m_pSimArray[i][j]>max)
                            max=m_pSimArray[i][j];
                    }
                //将各个像素的肤色相似度值归一化
                for( i=0;i<m_imgHeight;i++)
                    for (j=0;j<m_imgWidth;j++){
                        m_pSimArray[i][j]= m_pSimArray[i][j]/max;
                    }
                //将各像素的肤色相似度值变换到[0,255]上,以进行灰度图像的显示
                for(i=0;i<m_imgHeight;i++)
                    for(j=0;j<m_imgWidth;j++){
                        *(m_pImgDataOut+i*lineByteOut+j)=(int)(m_pSimArray[i][j]*255);
                    }
}

[4] CalSim()函数调用了FaceDetect类中的CalMedFl()函数,以进行中值滤波,其具体代码如代码5-4所示。

代码5-4 CalMedFl()函数

        void FaceDetect::CalMedFl(double **s, int w, int h, int n)
        {
            int i,j;
            double **temp;
            temp = new double*[h+2*(int)(n/2)];
            for(i=0;i<h+2*(int)(n/2);i++)    temp[i] = new double[w+2*(int)(n/2)];
            for(i=0;i<w+2*(int)(n/2);i++)
                for(j=0;j<h+2*(int)(n/2);j++)    temp[j][i] = 0.0;
            for(i=0;i<h;i++)
                for(j=0;j<w;j++)
                    temp[i+(int)(n/2)][j+(int)(n/2)]=s[i][j];
            for(i=0;i<h;i++)
                for(j=0;j<w;j++) {
                    s[i][j]=0.0;
                    for(int r=0;r<n;r++)
                        for(int c=0;c<n;c++)
                              s[i][j]+=temp[i+r][j+c];
                        s[i][j]/=n*n;
            }
            if(temp!=NULL)   {
                for(i=0;i<h+2;i++)
                    if(temp[i]!=NULL)
                        delete temp[i];
                    delete temp;
            }
        }