第七章 学习OpenCV——直方图与匹配

第七章 学习OpenCV——直方图与匹配

目录

  • 第七章 学习OpenCV直方图与匹配
    • 目录
      • 例7-1 直方图的计算与显示
      • 例7-2 从EMD直方图创建signature用EMD度量相似性
      • 例7-3 基于像素点的反向投影cvCalcBackProject
      • 例7-4 基于块的反向投影cvCalcArrBackProjectPatch
      • 例7-5 模板匹配
      • 例7-6 创建一个包含直方图1000个随机值
      • 例7-7 bin的个数对直方图匹配产生的影响
      • 例7-8 光照对直方图EMD匹配产生的影响
      • 例7-9 图像拍摄的光照条件判定
      • 例7-10 肤色场景检测器
      • 例7-11 RGB肤色区域检测器
      • 例7-12 手势梯度方向直方图
      • 例7-13 识别手势
      • 例7-14 识别手势EMD
      • 例7-15 模板匹配寻找手掌区域

例7-1 直方图的计算与显示

根据输入的图像计算出一个色相饱和度(hue-saturation)直方图,然后利用网格的方式将该直方图以网格形式显示出来,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

CvPoint Current_Point;              //值为255点当前点 全局变量才可通过普通成员引用变更其值

bool find_point(IplImage *img, char val);

int main(int argc, char* argv[])
{
    int threshold_type = CV_THRESH_BINARY;      //阈值类型
    int Last_Area = 0;                          //上一个区域面积       
    int Current_Area = 0;                       //当前区域面积
    double threshold = 65;                      //阈值

    CvPoint Last_Point;                         //值为255点的上一点
    CvConnectedComp comp;                       //被填充区域统计属性
    IplImage *src1, *hsv, *Igray, *Ithreshold, *Itemp, *Iopen, *Imask;      //源图像 HSV格式图像

    Last_Point = cvPoint(0, 0);         //初始化上一点
    Current_Point = cvPoint(0, 0);      //初始化当前点

    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template46_hue-saturation_Hist\\Debug\\handdd.jpg")))
        return -1;

    //此处调入图像掩码应为单通道
    //if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template46_hue-saturation_Hist\\Debug\\cup2.jpg", CV_LOAD_IMAGE_GRAYSCALE)))
    //  return -2;

    hsv=cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    Igray = cvCreateImage(cvGetSize(src1), src1->depth, 1);
    Ithreshold = cvCreateImage(cvGetSize(src1), src1->depth, 1);
    Itemp = cvCreateImage(cvGetSize(src1), src1->depth, 1);
    Iopen = cvCreateImage(cvGetSize(src1), src1->depth, 1);
    Imask = cvCreateImage(cvGetSize(src1), src1->depth, 1); //生成手掌掩码图像用

    cvCvtColor(src1, hsv, CV_BGR2HSV);      //源图像->HSV格式图像
    cvCvtColor(src1, Igray, CV_BGR2GRAY);   //源图像->灰度图像

    cvThreshold(Igray, Ithreshold, threshold, 255, threshold_type); //二值阈值化
    //开运算,去除小亮区域,其他联结 NULL:3*3参考点为中心的核
    cvMorphologyEx(Ithreshold, Iopen, Itemp, NULL, CV_MOP_OPEN, 1); 

    cvNamedWindow("src1", 1);
    cvNamedWindow("GRAY_Image", 1);
    cvNamedWindow("THRESHHOLD_Image", 1);
    cvNamedWindow("OPEN_Image", 1);
    cvNamedWindow("FLOOD_FILL", 1);

    cvShowImage("src1", src1);
    cvShowImage("GRAY_Image", Igray);
    cvShowImage("THRESHHOLD_Image", Ithreshold);
    cvShowImage("OPEN_Image", Iopen);
    cvShowImage("FLOOD_FILL", Imask);

    //漫水填充 获得手掌掩码
    cvNamedWindow("FLOOD_FILL", 1);
    cvCopy(Iopen, Imask);           //复制生成手掌掩码

    do
    {
        if (find_point(Imask, 255))    //找像素值为255的像素点
        {

            cout << " X: " << Current_Point.x << " Y: " << Current_Point.y << endl;

            cvFloodFill(Imask, Current_Point, cvScalar(100), cvScalar(0), cvScalar(0),
                &comp, 8 | CV_FLOODFILL_FIXED_RANGE);       //对值为255的点进行漫水填充,值100
            Current_Area = comp.area;                       //当前区域面积

            if (Last_Area//当前区域大于上一区域,上一区域清0
            {
                if (Last_Area>0)
                    cvFloodFill(Imask, Last_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //上一区域赋值0
                cvShowImage("FLOOD_FILL", Imask);
                cvWaitKey(500);

                Last_Area = Current_Area;                               //当前区域赋值给上一区域
                Last_Point = Current_Point;                             //当前点赋值给上一点
                //memcpy(&Last_Point, &Current_Point, sizeof(CvPoint)); //错误,此方法复制无法正常使用掩码
            }
            else                                            //当前区域小于等于上一区域,当前区域清0
            {
                if (Current_Area>0)
                    cvFloodFill(Imask, Current_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //当前区域赋值0
                cvShowImage("FLOOD_FILL", Imask);
                cvWaitKey(500);
            }
        }
        else                                                //最后剩余的最大区域赋值255
        {
            cvFloodFill(Imask, Last_Point, cvScalar(255), cvScalar(0), cvScalar(0), &comp, 8 | CV_FLOODFILL_FIXED_RANGE);
            cvShowImage("FLOOD_FILL", Imask);
            cvWaitKey(500);
            //上一区域赋值0
            break;
        }
    } while (true);

    cvSaveImage("Imask.jpg", Imask);

    //色调(hue) 饱和度(saturation) 明度(value)
    IplImage *h_plane = cvCreateImage(cvSize(hsv->width, hsv->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane = cvCreateImage(cvSize(hsv->width, hsv->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane = cvCreateImage(cvSize(hsv->width, hsv->height), IPL_DEPTH_8U, 1);

    IplImage *planes[] = {h_plane,s_plane};                     //色相饱和度数组

    cvCvtPixToPlane(hsv, h_plane, s_plane, v_plane, NULL);      //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    int h_bins = 30, s_bins = 32;                               

    //建立直方图
    CvHistogram *hist;

    int hist_size[] = { h_bins, s_bins };       //对应维数包含bins个数的数组
    float h_ranges[] = { 0, 180 };              //H通道划分范围 饱和度0-180
    float s_ranges[] = { 0, 255 };              //S通道划分范围
    float* ranges[] = { h_ranges, s_ranges };   //划分范围数对, ****均匀bin,range只要最大最小边界
    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);

    cvCalcHist(planes, hist, 0, Imask); //计算直方图(图像,直方图结构,不累加,掩码)
    cvNormalizeHist(hist, 1.0);         //直方图归一化

    //绘制可视化直方图
    int scale = 10;
    IplImage* hist_img = cvCreateImage(cvSize(h_bins*scale, s_bins*scale), 8, 3);   //300*320
    cvZero(hist_img);

    //以小灰度块填充图像
    float max_value = 0;
    cvGetMinMaxHistValue(hist, NULL, &max_value, NULL, NULL);       //获取直方图最大值

    for (int h = 0; h < h_bins; h++)
    {
        for (int s = 0; s < s_bins; s++)
        {
            float bin_val = cvQueryHistValue_2D(hist, h, s);    //获取直方图相应bin中的浮点数
            int intensity = cvRound(bin_val * 255 / max_value); //映射到255空间,归一后太小,难辨
            cvRectangle(hist_img, cvPoint(h*scale, s*scale),        
                cvPoint((h + 1)*scale - 1, (s + 1)*scale - 1), 
                CV_RGB(intensity, intensity, intensity), CV_FILLED);
        }
    }

    cvNamedWindow("HIST_Image", 1);
    cvShowImage("HIST_Image", hist_img);

    cvWaitKey();

    cvReleaseHist(&hist);

    cvReleaseImage(&src1);
    cvReleaseImage(&hsv);
    cvReleaseImage(&Igray);
    cvReleaseImage(&Ithreshold);
    cvReleaseImage(&Itemp);
    cvReleaseImage(&Iopen);
    cvReleaseImage(&Imask);
    cvReleaseImage(&h_plane);
    cvReleaseImage(&s_plane);
    cvReleaseImage(&v_plane);
    cvReleaseImage(&hist_img);

    cvDestroyWindow("src1");
    cvDestroyWindow("HIST_Image");
    cvDestroyWindow("GRAY_Image");
    cvDestroyWindow("THRESHHOLD_Image");
    cvDestroyWindow("OPEN_Image");
    cvDestroyWindow("FLOOD_FILL");
    cvDestroyWindow("HIST_Image");

}

/******************遍历图像,指针算法********************/
bool find_point(IplImage *img, char val)
{
    char* ptr = NULL;

    if (img->nChannels == 1)
    {
        ptr = img->imageData;
        if (ptr != NULL)
        {
            for (int i = 0; i < img->height; i++)       //矩阵指针行寻址
            {
                ptr = (img->imageData + i*(img->widthStep));   //i 行 j 列
                for (int j = 0; j < img->width; j++)    //矩阵指针列寻址
                {
                    if (ptr[j] == val)              //判断某点像素是否为255
                    {
                        Current_Point.x = j;        /********局部变量此方式 无法实现赋值********/
                        Current_Point.y = i;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第1张图片

例7-2 从EMD直方图创建signature,用EMD度量相似性

根据输入的图像计算色相饱和度(hue-saturation)直方图,然后利用该直方图创建EMD接口参数signature,最后利用EMD来度量两个分布之间的相似性,程序中src1与src2已经过处理,有40的亮度值偏移,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    IplImage* src1,*src2,*Imask,*hsv1,*hsv2;                //源图像 HSV格式图像

    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template47_hue-saturation_EMD\\Debug\\hand1.jpg")))
        return -1;
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template47_hue-saturation_EMD\\Debug\\hand3.jpg")))
        return -2;
    if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template47_hue-saturation_EMD\\Debug\\Imask.jpg", CV_LOAD_IMAGE_GRAYSCALE)))
        return -3;

    hsv1 = cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    hsv2 = cvCreateImage(cvGetSize(src2), src2->depth, src2->nChannels);

    cvCvtColor(src1, hsv1, CV_BGR2HSV); //源图像->HSV格式图像
    cvCvtColor(src2, hsv2, CV_BGR2HSV); //源图像->HSV格式图像

    //色调(hue) 饱和度(saturation) 明度(value)
    IplImage *h_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { h_plane_1, s_plane_1 };                     //色相饱和度数组
    IplImage *planes2[] = { h_plane_2, s_plane_2 };                     //色相饱和度数组

    cvCvtPixToPlane(hsv1, h_plane_1, s_plane_1, v_plane_1, NULL);       //图像分割
    cvCvtPixToPlane(hsv2, h_plane_2, s_plane_2, v_plane_2, NULL);       //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    int h_bins = 30, s_bins = 32;                               

    //建立直方图
    CvHistogram *hist1,*hist2;

    int hist_size[] = { h_bins, s_bins };       //对应维数包含bins个数的数组
    float h_ranges[] = { 0, 180 };              //H通道划分范围 饱和度0-180
    float s_ranges[] = { 0, 255 };              //S通道划分范围
    float* ranges[] = { h_ranges, s_ranges };   //划分范围数对, ****均匀bin,range只要最大最小边界
    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist1 = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist2 = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);

    cvCalcHist(planes1, hist1, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes2, hist2, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)

    //cvCalcHist(planes1, hist1, 0, Imask);     //计算直方图(图像,直方图结构,不累加,掩码)
    //cvCalcHist(planes2, hist2, 0, Imask);     //计算直方图(图像,直方图结构,不累加,掩码)

    cvNormalizeHist(hist1, 1.0);            //直方图归一化
    cvNormalizeHist(hist2, 1.0);            //直方图归一化

    CvMat *sig1, *sig2;
    int numrows = h_bins*s_bins;

    sig1 = cvCreateMat(numrows, 3, CV_32FC1);   //numrows行 3列 矩阵
    sig2 = cvCreateMat(numrows, 3, CV_32FC1);

    for (int h = 0; h < h_bins; h++)
    {
        for (int s = 0; s < s_bins; s++)
        {
            float bin_val = cvQueryHistValue_2D(hist1, h, s);
            //h:行数 s_bins:总列数(行长度)s:列数  h*s_bins+s 当前bin对应的sig行数   
            cvSet2D(sig1, h*s_bins + s, 0, cvScalar(bin_val));  
            cvSet2D(sig1, h*s_bins + s, 1, cvScalar(h));
            cvSet2D(sig1, h*s_bins + s, 2, cvScalar(s));

            bin_val = cvQueryHistValue_2D(hist2, h, s);
            cvSet2D(sig2, h*s_bins + s, 0, cvScalar(bin_val));
            cvSet2D(sig2, h*s_bins + s, 1, cvScalar(h));
            cvSet2D(sig2, h*s_bins + s, 2, cvScalar(s));
        }
    }

    float emd = cvCalcEMD2(sig1, sig2, CV_DIST_L2);
    printf("EMD距离:%f; ", emd);

    cvNamedWindow("SRC1",1);
    cvNamedWindow("SRC2",2);

    cvShowImage("SRC1", src1);
    cvShowImage("SRC2", src2);

    cvWaitKey(0);
    //system("pause");

    cvReleaseMat(&sig1);
    cvReleaseMat(&sig2);

    cvReleaseHist(&hist1);
    cvReleaseHist(&hist2);

    cvReleaseImage(&src1);
    cvReleaseImage(&src2);
    cvReleaseImage(&Imask);
    cvReleaseImage(&hsv1);
    cvReleaseImage(&hsv2);
    cvReleaseImage(&h_plane_1);
    cvReleaseImage(&s_plane_1);
    cvReleaseImage(&v_plane_1);
    cvReleaseImage(&h_plane_2);
    cvReleaseImage(&s_plane_2);
    cvReleaseImage(&v_plane_2);

    cvDestroyAllWindows();
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第2张图片

例7-3 基于像素点的反向投影(cvCalcBackProject)

根据输入的图像计算色相饱和度(hue-saturation)直方图,以网格形式显示,利用肤色模板直方图进行基于像素点的反向投影,在测试图像中找出该肤色模板直方图对应的区域,对应具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    IplImage* src1,*src2,*Imask,*hsv1,*hsv2;    //源图像 HSV格式图像

    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template48_hue-saturation_BackProjection\\Debug\\hand1.jpg")))
        return -1;
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template48_hue-saturation_BackProjection\\Debug\\hand3.jpg")))
        return -2;
    //此处调入图像掩码应为单通道
    if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template48_hue-saturation_BackProjection\\Debug\\Imask.jpg", CV_LOAD_IMAGE_GRAYSCALE)))
        return -3;

    cvXorS(Imask, cvScalar(255), Imask);        //掩码图像按位异或,求反生成新的掩码处理模板
    cvSet(src1, cvScalarAll(0), Imask);

    hsv1 = cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    hsv2 = cvCreateImage(cvGetSize(src2), src2->depth, src2->nChannels);

    cvCvtColor(src1, hsv1, CV_BGR2HSV); //源图像->HSV格式图像
    cvCvtColor(src2, hsv2, CV_BGR2HSV); //源图像->HSV格式图像

    //反向投影图像
    IplImage *back_projection = cvCreateImage(cvGetSize(src2), IPL_DEPTH_8U, 1);

    //色调(hue) 饱和度(saturation) 明度(value)
    IplImage *h_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { h_plane_1, s_plane_1 };                     //色相饱和度数组
    IplImage *planes2[] = { h_plane_2, s_plane_2 };                     //色相饱和度数组

    cvCvtPixToPlane(hsv1, h_plane_1, s_plane_1, v_plane_1, NULL);       //图像分割
    cvCvtPixToPlane(hsv2, h_plane_2, s_plane_2, v_plane_2, NULL);       //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    int h_bins = 30, s_bins = 32;                               

    //建立直方图
    CvHistogram *hist_model,*hist_test;

    int hist_size[] = { h_bins, s_bins };       //对应维数包含bins个数的数组
    float h_ranges[] = { 0, 180 };              //H通道划分范围 饱和度0-180
    float s_ranges[] = { 0, 255 };              //S通道划分范围
    float* ranges[] = { h_ranges, s_ranges };   //划分范围数对, ****均匀bin,range只要最大最小边界
    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist_model = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);


    cvCalcHist(planes1, hist_model, 0, 0);      //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes2, hist_test, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)

    //cvNormalizeHist(hist_model, 1.0);         //直方图归一化
    //cvNormalizeHist(hist_test, 1.0);          //直方图归一化

    //绘制可视化直方图
    int scale = 10;
    IplImage* hist_img_model = cvCreateImage(cvSize(h_bins*scale, s_bins*scale), 8, 3); //300*320
    IplImage* hist_img_test = cvCreateImage(cvSize(h_bins*scale, s_bins*scale), 8, 3);  //300*320

    cvZero(hist_img_model);
    cvZero(hist_img_test);

    //以小灰度块填充图像
    float max_value_model = 0;
    float max_value_test = 0;

    cvGetMinMaxHistValue(hist_model, NULL, &max_value_model, NULL, NULL);   //获取直方图最大值
    cvGetMinMaxHistValue(hist_test, NULL, &max_value_test, NULL, NULL);     //获取直方图最大值

    for (int h = 0; h < h_bins; h++)
    {
        for (int s = 0; s < s_bins; s++)
        {
            float bin_val_model = cvQueryHistValue_2D(hist_model, h, s);    //获取直方图相应bin中的浮点数
            float bin_val_test = cvQueryHistValue_2D(hist_test, h, s);      //获取直方图相应bin中的浮点数

            int intensity1 = cvRound(bin_val_model * 255 / max_value_model);//映射到255空间
            int intensity2 = cvRound(bin_val_test * 255 / max_value_test);  //归一后太小

            cvRectangle(hist_img_model, cvPoint(h*scale, s*scale),
                cvPoint((h + 1)*scale - 1, (s + 1)*scale - 1),
                CV_RGB(intensity1, intensity1, intensity1), CV_FILLED);
            cvRectangle(hist_img_test, cvPoint(h*scale, s*scale),
                cvPoint((h + 1)*scale - 1, (s + 1)*scale - 1),
                CV_RGB(intensity2, intensity2, intensity2), CV_FILLED);
        }
    }

    cvCalcBackProject(planes2, back_projection, hist_model);        //像素点的反射投影

    cvNamedWindow("Mask", 1);
    cvNamedWindow("Model", 1);
    cvNamedWindow("Test", 1);
    cvNamedWindow("HIST_Model", 1);
    cvNamedWindow("HIST_Test", 1);
    cvNamedWindow("BACK_Projection", 1);

    cvShowImage("Mask", Imask);
    cvShowImage("Model", src1);
    cvShowImage("Test", src2);
    cvShowImage("HIST_Model", hist_img_model);
    cvShowImage("HIST_Test", hist_img_test);
    cvShowImage("BACK_Projection", back_projection);

    cvWaitKey(0);
    //system("pause");

    cvReleaseHist(&hist_model);
    cvReleaseHist(&hist_test);

    cvReleaseImage(&Imask);
    cvReleaseImage(&src1);
    cvReleaseImage(&src2);
    cvReleaseImage(&hist_img_model);
    cvReleaseImage(&hist_img_test);
    cvReleaseImage(&h_plane_1);
    cvReleaseImage(&s_plane_1);
    cvReleaseImage(&v_plane_1);
    cvReleaseImage(&h_plane_2);
    cvReleaseImage(&s_plane_2);
    cvReleaseImage(&v_plane_2);
    cvReleaseImage(&back_projection);

    cvDestroyAllWindows();
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第3张图片

例7-4 基于块的反向投影(cvCalcArrBackProjectPatch)

根据输入的图像计算色相饱和度(hue-saturation)直方图,以网格形式显示,利用颜色模板直方图进行基于块的反向投影,在测试图像中找出该颜色模板直方图对应的区域,程序中,对于手的检测cvCalcArrBackProjectPatch()做区域检测器,对于杯子的检测cvCalcArrBackProjectPatch()做目标检测器,对应具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    //源图像 HSV格式图像
    IplImage* Ihand_model, *Ihand_test, *Ihand_mask, *hsv1, *hsv2, *hsv3, *hsv4, *Icup_model, *Icup_test, *Icup_mask;   

    //未处理的肤色模板图像
    if (!(Ihand_model = cvLoadImage("D:\\Template\\OpenCV\\Template49_hue-saturation_BackProjection_Patch\\Debug\\hand1.jpg")))
        return -1;
    //寻找手掌反向投影的测试图像
    if (!(Ihand_test = cvLoadImage("D:\\Template\\OpenCV\\Template49_hue-saturation_BackProjection_Patch\\Debug\\hand3.jpg")))
        return -2;
    //用于处理肤色模板图像的掩码,此处调入图像掩码应为单通道
    if (!(Ihand_mask = cvLoadImage("D:\\Template\\OpenCV\\Template49_hue-saturation_BackProjection_Patch\\Debug\\Imask.jpg",
        CV_LOAD_IMAGE_GRAYSCALE)))
        return -3;
    //未处理的杯子颜色模板图像
    if (!(Icup_model = cvLoadImage("D:\\Template\\OpenCV\\Template49_hue-saturation_BackProjection_Patch\\Debug\\cup2.jpg")))
        return -4;
    //寻找杯子反向投影的测试图像
    if (!(Icup_test = cvLoadImage("D:\\Template\\OpenCV\\Template49_hue-saturation_BackProjection_Patch\\Debug\\cup1.jpg")))
        return -5;
    //用于处理杯子颜色模板图像的掩码 可获取杯子反向投影块大小
    if (!(Icup_mask = cvLoadImage("D:\\Template\\OpenCV\\Template49_hue-saturation_BackProjection_Patch\\Debug\\cup3.jpg",
        CV_LOAD_IMAGE_GRAYSCALE)))
        return -6;

    int hand_patch_width = 5;                       //手掌反向投影块宽度
    int hand_patch_height = 5;                      //手掌反向投影块高度

    int cup_patch_width = Icup_model->width;        //杯子反向投影块宽度
    int cup_patch_height = Icup_model->height;      //杯子反向投影块高度

    cvXorS(Ihand_mask, cvScalar(255), Ihand_mask);          //掩码图像按位异或,求反,以手掌以外区域作为掩码
    cvXorS(Icup_mask, cvScalar(255), Icup_mask);            //掩码图像按位异或,求反,以杯子以外区域作为掩码

    cvSet(Ihand_model, cvScalarAll(0), Ihand_mask);         //将手掌以外区域变黑,生成肤色模板图像
    cvSet(Icup_model, cvScalarAll(0), Icup_mask);           //将手掌以外区域变黑,生成肤色模板图像

    hsv1 = cvCreateImage(cvGetSize(Ihand_model), Ihand_model->depth, Ihand_model->nChannels);   //HSV
    hsv2 = cvCreateImage(cvGetSize(Ihand_test), Ihand_test->depth, Ihand_test->nChannels);      //HSV
    hsv3 = cvCreateImage(cvGetSize(Icup_model), Icup_model->depth, Icup_model->nChannels);      //HSV
    hsv4 = cvCreateImage(cvGetSize(Icup_test), Icup_test->depth, Icup_test->nChannels);         //HSV

    cvCvtColor(Ihand_model, hsv1, CV_BGR2HSV);      //源图像->HSV格式图像
    cvCvtColor(Ihand_test, hsv2, CV_BGR2HSV);       //源图像->HSV格式图像
    cvCvtColor(Icup_model, hsv3, CV_BGR2HSV);       //源图像->HSV格式图像
    cvCvtColor(Icup_test, hsv4, CV_BGR2HSV);        //源图像->HSV格式图像

    //反向投影图像 大小:测试图像-块的大小 浮点型数组
    IplImage *Iback_projection_patch_hand = cvCreateImage(
        cvSize(Ihand_test->width - hand_patch_width + 1, Ihand_test->height - hand_patch_height + 1), 
        IPL_DEPTH_32F, 1);
    IplImage *Iback_projection_patch_cup = cvCreateImage(
        cvSize(Icup_test->width - cup_patch_width + 1, Icup_test->height - cup_patch_height + 1),
        IPL_DEPTH_32F, 1);

    //色调(hue) 饱和度(saturation) 明度(value) 创建三通道图像
    IplImage *h_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { h_plane_1, s_plane_1 };                     //色相饱和度数组
    IplImage *planes2[] = { h_plane_2, s_plane_2 };                     //色相饱和度数组
    IplImage *planes3[] = { h_plane_3, s_plane_3 };                     //色相饱和度数组
    IplImage *planes4[] = { h_plane_4, s_plane_4 };                     //色相饱和度数组

    cvCvtPixToPlane(hsv1, h_plane_1, s_plane_1, v_plane_1, NULL);       //图像分割
    cvCvtPixToPlane(hsv2, h_plane_2, s_plane_2, v_plane_2, NULL);       //图像分割
    cvCvtPixToPlane(hsv3, h_plane_3, s_plane_3, v_plane_3, NULL);
    cvCvtPixToPlane(hsv4, h_plane_4, s_plane_4, v_plane_4, NULL);
    //cvSplit(hsv4, h_plane_4, s_plane_4, v_plane_4, NULL);             //两函数效果相似

    int h_bins = 30, s_bins = 32;               //h维bins的个数,s维bins的个数               

    //建立模板和测试直方图
    CvHistogram *hist_model_hand, *hist_test_hand, *hist_model_cup, *hist_test_cup;

    int hist_size[] = { h_bins, s_bins };       //对应维数包含bins个数的数组
    float h_ranges[] = { 0, 180 };              //H通道划分范围 饱和度0-180
    float s_ranges[] = { 0, 255 };              //S通道划分范围
    float* ranges[] = { h_ranges, s_ranges };   //划分范围数对, ****均匀bin,range只要最大最小边界

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist_model_hand = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test_hand = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_model_cup= cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test_cup = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    //计算直方图
    cvCalcHist(planes1, hist_model_hand, 0, 0);     //计算直方图(图像,直方图结构,不累加,掩码)肤色直方图
    cvCalcHist(planes2, hist_test_hand, 0, 0);      //计算直方图(图像,直方图结构,不累加,掩码)测试直方图
    cvCalcHist(planes3, hist_model_cup, 0, 0);      //计算直方图(图像,直方图结构,不累加,掩码)杯色直方图
    cvCalcHist(planes4, hist_test_cup, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)测试直方图
    //直方图归一化
    cvNormalizeHist(hist_model_hand, 1.0);          //直方图归一化
    cvNormalizeHist(hist_test_hand, 1.0);           //直方图归一化
    cvNormalizeHist(hist_model_cup, 1.0);           //直方图归一化
    cvNormalizeHist(hist_test_cup, 1.0);            //直方图归一化

    //绘制可视化直方图
    int scale = 10;     //直方图颜色值图像显示倍数
    IplImage* Ihist_model_hand = cvCreateImage(cvSize(h_bins*scale, s_bins*scale), 8, 3);   //300*320
    IplImage* Ihist_test_hand = cvCreateImage(cvSize(h_bins*scale, s_bins*scale), 8, 3);    //300*320
    IplImage* Ihist_model_cup = cvCreateImage(cvSize(h_bins*scale, s_bins*scale), 8, 3);    //300*320
    IplImage* Ihist_test_cup = cvCreateImage(cvSize(h_bins*scale, s_bins*scale), 8, 3);     //300*320

    //直方图颜色值图像清零
    cvZero(Ihist_model_hand);
    cvZero(Ihist_test_hand);
    cvZero(Ihist_model_cup);
    cvZero(Ihist_test_cup);

    //以小灰度块填充图像
    float max_value_model_hand = 0; //直方图中最大值,为映射做准备
    float max_value_test_hand = 0;
    float max_value_model_cup = 0;  //直方图中最大值,为映射做准备
    float max_value_test_cup = 0;

    cvGetMinMaxHistValue(hist_model_hand, NULL, &max_value_model_hand, NULL, NULL); //直方图最大值
    cvGetMinMaxHistValue(hist_test_hand, NULL, &max_value_test_hand, NULL, NULL);   //直方图最大值
    cvGetMinMaxHistValue(hist_model_cup, NULL, &max_value_model_cup, NULL, NULL);   //直方图最大值
    cvGetMinMaxHistValue(hist_test_cup, NULL, &max_value_test_cup, NULL, NULL);     //直方图最大值

    for (int h = 0; h < h_bins; h++)
    {
        for (int s = 0; s < s_bins; s++)
        {
            float bin_val_model_hand = cvQueryHistValue_2D(hist_model_hand, h, s);  //bin中的浮点数
            float bin_val_test_hand = cvQueryHistValue_2D(hist_test_hand, h, s);    //bin中的浮点数
            float bin_val_model_cup = cvQueryHistValue_2D(hist_model_cup, h, s);    //bin中的浮点数
            float bin_val_test_cup = cvQueryHistValue_2D(hist_test_cup, h, s);      //bin中的浮点数

            int intensity1 = cvRound(bin_val_model_hand * 255 / max_value_model_hand);  //映射到255空间
            int intensity2 = cvRound(bin_val_test_hand * 255 / max_value_test_hand);    //归一后太小
            int intensity3 = cvRound(bin_val_model_cup * 255 / max_value_model_cup);    //映射到255空间
            int intensity4 = cvRound(bin_val_test_cup * 255 / max_value_test_cup);      //归一后太小

            cvRectangle(Ihist_model_hand, cvPoint(h*scale, s*scale),            //绘制小灰度块填充图像
                cvPoint((h + 1)*scale - 1, (s + 1)*scale - 1),
                CV_RGB(intensity1, intensity1, intensity1), CV_FILLED);
            cvRectangle(Ihist_test_hand, cvPoint(h*scale, s*scale),
                cvPoint((h + 1)*scale - 1, (s + 1)*scale - 1),
                CV_RGB(intensity2, intensity2, intensity2), CV_FILLED);
            cvRectangle(Ihist_model_cup, cvPoint(h*scale, s*scale),         //绘制小灰度块填充图像
                cvPoint((h + 1)*scale - 1, (s + 1)*scale - 1),
                CV_RGB(intensity3, intensity3, intensity3), CV_FILLED);
            cvRectangle(Ihist_test_cup, cvPoint(h*scale, s*scale),
                cvPoint((h + 1)*scale - 1, (s + 1)*scale - 1),
                CV_RGB(intensity4, intensity4, intensity4), CV_FILLED);
        }
    }

    CvSize hand_patch_size = cvSize(hand_patch_width, hand_patch_height);   //手掌反向投影块尺寸
    CvSize cup_patch_size = cvSize(cup_patch_width, cup_patch_height);      //杯子反向投影块尺寸

    //做区域检测器 采样窗口小于目标 测试图像数组 块的反向投影图像 块大小 模板直方图 比较方式(相关) 归一化水平
    cvCalcArrBackProjectPatch((CvArr **)planes2, Iback_projection_patch_hand, hand_patch_size, hist_model_hand, CV_COMP_CORREL, 1);
    //做目标检测器 采样窗口等于目标 测试图像数组 块的反向投影图像 块大小 模板直方图 比较方式(相关) 归一化水平
    cvCalcArrBackProjectPatch((CvArr **)planes4, Iback_projection_patch_cup, cup_patch_size, hist_model_cup, CV_COMP_CORREL, 1);

    cvNamedWindow("Mask_Hand", 1);
    cvNamedWindow("Model_Hand", 1);
    cvNamedWindow("Test_Hand", 1);
    cvNamedWindow("HIST_Model_Hand", 1);
    cvNamedWindow("HIST_Test_Hand", 1);
    cvNamedWindow("BACK_Projection_Hand", 1);

    cvNamedWindow("Mask_Cup", 1);
    cvNamedWindow("Model_Cup", 1);
    cvNamedWindow("Test_Cup", 1);
    cvNamedWindow("HIST_Model_Cup", 1);
    cvNamedWindow("HIST_Test_Cup", 1);
    cvNamedWindow("BACK_Projection_Cup", 1);

    cvShowImage("Mask_Hand", Ihand_mask);
    cvShowImage("Model_Hand", Ihand_model);
    cvShowImage("Test_Hand", Ihand_test);
    cvShowImage("HIST_Model_Hand", Ihist_model_hand);
    cvShowImage("HIST_Test_Hand", Ihist_test_hand);
    cvShowImage("BACK_Projection_Hand", Iback_projection_patch_hand);

    cvShowImage("Mask_Cup", Icup_mask);
    cvShowImage("Model_Cup", Icup_model);
    cvShowImage("Test_Cup", Icup_test);
    cvShowImage("HIST_Model_Cup", Ihist_model_cup);
    cvShowImage("HIST_Test_Cup", Ihist_test_cup);
    cvShowImage("BACK_Projection_Cup", Iback_projection_patch_cup);

    cvWaitKey(0);
    //system("pause");

    cvReleaseHist(&hist_model_hand);
    cvReleaseHist(&hist_test_hand);
    cvReleaseHist(&hist_model_cup);
    cvReleaseHist(&hist_test_cup);

    cvReleaseImage(&Ihand_model);
    cvReleaseImage(&Ihand_test);
    cvReleaseImage(&Ihand_mask);
    cvReleaseImage(&hsv1);
    cvReleaseImage(&hsv2);
    cvReleaseImage(&hsv3);
    cvReleaseImage(&hsv4);  
    cvReleaseImage(&Icup_model);
    cvReleaseImage(&Icup_test);
    cvReleaseImage(&Icup_mask);
    cvReleaseImage(&h_plane_1);
    cvReleaseImage(&s_plane_1);
    cvReleaseImage(&v_plane_1);
    cvReleaseImage(&h_plane_2);
    cvReleaseImage(&s_plane_2);
    cvReleaseImage(&v_plane_2);
    cvReleaseImage(&h_plane_3);
    cvReleaseImage(&s_plane_3);
    cvReleaseImage(&v_plane_3);
    cvReleaseImage(&h_plane_4);
    cvReleaseImage(&s_plane_4);
    cvReleaseImage(&v_plane_4);
    cvReleaseImage(&Ihist_model_hand);
    cvReleaseImage(&Ihist_test_hand);
    cvReleaseImage(&Ihist_model_cup);
    cvReleaseImage(&Ihist_test_cup);
    cvReleaseImage(&Iback_projection_patch_hand);
    cvReleaseImage(&Iback_projection_patch_cup);

    cvDestroyAllWindows();
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第4张图片

例7-5 模板匹配

读入一个模板和要匹配的图像,然后分别利用6种方法进行匹配,规范化后将匹配结果显示出来,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    //源图像 匹配模板 不同匹配方法结果
    IplImage* src, *temp1, *result[6];  

    //杯子源图像
    if (!(src = cvLoadImage("D:\\Template\\OpenCV\\Template50_Match_Template\\Debug\\cup1.jpg")))
        return -1;
    //用于匹配的杯子模板图像
    if (!(temp1 = cvLoadImage("D:\\Template\\OpenCV\\Template50_Match_Template\\Debug\\cup2.jpg")))
        return -2;
    //结果图像尺寸
    int result_width = src->width - temp1->width + 1;
    int result_height = src->height - temp1->height + 1;;
    CvSize result_size = cvSize(result_width, result_height);
    //创建结果图像
    for (int i = 0; i < 6; ++i)
    {
        printf("i=%d\n", i);
        result[i] = cvCreateImage(result_size, IPL_DEPTH_32F, 1);
    }
    //均衡化图像
    for (int i = 0; i < 6; i++)
    {
        printf("i=%d\n", i);
        cvMatchTemplate(src, temp1, result[i], i);              //模板匹配
        cvNormalize(result[i], result[i], 1, 0, CV_MINMAX);     //元素规范化 平移缩放返回值[0,1]
    }

    cvNamedWindow("Src", 1);
    cvNamedWindow("Template", 1);

    cvNamedWindow("SQDIFF", 1);
    cvNamedWindow("CCORR", 1);
    cvNamedWindow("CCOEFF", 1);
    cvNamedWindow("SQDIFF_NORMED", 1);
    cvNamedWindow("CCORR_NORMED", 1);
    cvNamedWindow("CCOEFF_NORMED", 1);

    cvShowImage("Src", src);
    cvShowImage("Template", temp1);

    cvShowImage("SQDIFF", result[0]);
    cvShowImage("SQDIFF_NORMED", result[1]);
    cvShowImage("CCORR", result[2]);
    cvShowImage("CCORR_NORMED", result[3]);
    cvShowImage("CCOEFF", result[4]);
    cvShowImage("CCOEFF_NORMED", result[5]);

    cvWaitKey(0);
    //system("pause");

    cvReleaseImage(&src);
    cvReleaseImage(&temp1);
    cvReleaseImage(&result[0]);
    cvReleaseImage(&result[1]);
    cvReleaseImage(&result[2]);
    cvReleaseImage(&result[3]);
    cvReleaseImage(&result[4]);
    cvReleaseImage(&result[5]);

    cvDestroyAllWindows();
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第5张图片
注意:本程序中,打印了两次i的值,分别对应for循环中的“++i”“i++”,打印结果相同,并不代表“++i”“i++”没有区别,而是因为for循环中表达式是作为一个语句来执行,因此此处i均是其最终的值。

例7-6 创建一个包含直方图1000个随机值

在0~1之间生成1000个随机值ri,定义一个bin的大小,并且建立一个直方图1/ri,,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    //产生1000个随机数
    CvRNG rng;
    IplImage *Img = cvCreateImage(cvSize(1000,1),32,1); //数据图像

    cvSetZero(Img);     //清零

    rng = cvRNG(cvGetTickCount());                  //64位长整数的时间数据作为种子
    for (int i = 0; i<1000; i++)
    {
        double value;   //获取的随机值
        cvSetReal1D(Img, i, cvRandReal(&rng));      //返回均匀分布,0~1之间的随机小数
        value = cvGetReal1D(Img, i);                //返回图像中小数值
        //printf("%d\n", cvRandInt(&rng) % 6);      //返回均匀分布32位的随机数,%6将会是0~255的正整数
        printf("%.2lf\n", value);                   //打印
    }

    printf("Tick Frequency= %f\n", cvGetTickFrequency());   //系统时钟频率
    system("pause");                    

    //建立直方图
    CvHistogram *hist;
    int dims = 1;       //维数
    int bins = 1000;    //bins个数        
    int hist_size[] = { bins };     //对应维数包含bins个数的数组
    float range[] = { 0, 1 };       //划分范围[0,1]
    float* ranges[] = { range };    //划分范围数对, ****均匀bin,range只要最大最小边界

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist = cvCreateHist(dims, hist_size, CV_HIST_ARRAY, ranges, 1);

    IplImage *img[] = { Img };      //计算直方图的图像数组
    cvCalcHist(img, hist, 0, 0);    //计算直方图

    for (int j = 0; j < bins; j++)
    {
        float bin_val = cvQueryHistValue_1D(hist,j);    //获取直方图相应bin中的浮点数
        cout << "the bins of " << j << ":" << bin_val << endl;
    }

    system("pause");
    cvWaitKey(0);

    cvReleaseHist(&hist);
    cvReleaseImage(&Img);
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第6张图片
第七章 学习OpenCV——直方图与匹配_第7张图片

例7-7 bin的个数对直方图匹配产生的影响

给定三幅在书中讨论的不同光照条件下的手图像,利用cvCalcHist()来获得室内拍照的手的肤色直方图。

 1. 依次尝试用少量的bin(如每维有2个),中等数目的bin(每维有16个)和很多bin(每维256个),然后对各种光线下的图像运行匹配程序(使用所有的直方图匹配方法);
 2. 现在加上每维为8个和32个bin,在各种光线条件下进行匹配;

程序中三幅图像已经过处理,依次比前一幅亮度增加40,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    IplImage* src1, *src2, *src3,*Imask, *hsv1, *hsv2,*hsv3;                //源图像 HSV格式图像
    //src1 src2 亮度较前一张增加了10 src2 src3 亮度较前一张增加了40
    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template52_hue-saturation_Compare\\Debug\\hand1.jpg")))
        return -1;
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template52_hue-saturation_Compare\\Debug\\hand3.jpg")))
        return -2;
    if (!(src3 = cvLoadImage("D:\\Template\\OpenCV\\Template52_hue-saturation_Compare\\Debug\\hand5.jpg")))
        return -3;
    //Mask为手掌掩码 过滤掉其他背景 只分析手掌颜色直方图 可略
    if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template52_hue-saturation_Compare\\Debug\\Imask.jpg", 
        CV_LOAD_IMAGE_GRAYSCALE)))
        return -4;

    hsv1 = cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    hsv2 = cvCreateImage(cvGetSize(src2), src2->depth, src2->nChannels);
    hsv3 = cvCreateImage(cvGetSize(src3), src3->depth, src3->nChannels);

    cvCvtColor(src1, hsv1, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src2, hsv2, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src3, hsv3, CV_BGR2HSV);     //源图像->HSV格式图像

    //色调(hue) 饱和度(saturation) 明度(value)
    IplImage *h_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { h_plane_1, s_plane_1 };                     //色相饱和度数组
    IplImage *planes2[] = { h_plane_2, s_plane_2 };                     //色相饱和度数组
    IplImage *planes3[] = { h_plane_3, s_plane_3 };                     //色相饱和度数组

    cvCvtPixToPlane(hsv1, h_plane_1, s_plane_1, v_plane_1, NULL);       //图像分割
    cvCvtPixToPlane(hsv2, h_plane_2, s_plane_2, v_plane_2, NULL);       //图像分割
    cvCvtPixToPlane(hsv3, h_plane_3, s_plane_3, v_plane_3, NULL);       //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    for (int i = 0; i < 5; i++)
    {
        //建立直方图
        CvHistogram *hist1, *hist2, *hist3;

        int bins=0;
        int h_bins_1 = 2, s_bins_1 = 2;
        int h_bins_2 = 8, s_bins_2 = 8;
        int h_bins_3 = 16, s_bins_3 = 16;
        int h_bins_4 = 32, s_bins_4 = 32;
        int h_bins_5 = 256, s_bins_5 = 256;

        int hist_size_1[] = { h_bins_1, s_bins_1 }; //对应维数包含bins个数的数组
        int hist_size_2[] = { h_bins_2, s_bins_2 }; //对应维数包含bins个数的数组
        int hist_size_3[] = { h_bins_3, s_bins_3 }; //对应维数包含bins个数的数组
        int hist_size_4[] = { h_bins_4, s_bins_4 }; //对应维数包含bins个数的数组
        int hist_size_5[] = { h_bins_5, s_bins_5 }; //对应维数包含bins个数的数组

        float h_ranges[] = { 0, 180 };              //H通道划分范围 饱和度0-180
        float s_ranges[] = { 0, 255 };              //S通道划分范围
        float* ranges[] = { h_ranges, s_ranges };   //划分范围数对, 均匀bin,range只要最大最小边界
        //bins 2*2
        if (i == 0)
        {
            hist1 = cvCreateHist(2, hist_size_1, CV_HIST_ARRAY, ranges, 1);
            hist2 = cvCreateHist(2, hist_size_1, CV_HIST_ARRAY, ranges, 1);
            hist3 = cvCreateHist(2, hist_size_1, CV_HIST_ARRAY, ranges, 1);
            bins = h_bins_1;
        }
        //bins 8*8
        if (i == 1)
        {
            hist1 = cvCreateHist(2, hist_size_2, CV_HIST_ARRAY, ranges, 1);
            hist2 = cvCreateHist(2, hist_size_2, CV_HIST_ARRAY, ranges, 1);
            hist3 = cvCreateHist(2, hist_size_2, CV_HIST_ARRAY, ranges, 1);
            bins = h_bins_2;
        }
        //bins 16*16
        if (i == 2)
        {
            hist1 = cvCreateHist(2, hist_size_3, CV_HIST_ARRAY, ranges, 1);
            hist2 = cvCreateHist(2, hist_size_3, CV_HIST_ARRAY, ranges, 1);
            hist3 = cvCreateHist(2, hist_size_3, CV_HIST_ARRAY, ranges, 1);
            bins = h_bins_3;
        }
        //bins 32*32
        if (i == 3)
        {
            hist1 = cvCreateHist(2, hist_size_4, CV_HIST_ARRAY, ranges, 1);
            hist2 = cvCreateHist(2, hist_size_4, CV_HIST_ARRAY, ranges, 1);
            hist3 = cvCreateHist(2, hist_size_4, CV_HIST_ARRAY, ranges, 1);
            bins = h_bins_4;
        }
        //bins 256*256
        if (i == 4)
        {
            hist1 = cvCreateHist(2, hist_size_5, CV_HIST_ARRAY, ranges, 1);
            hist2 = cvCreateHist(2, hist_size_5, CV_HIST_ARRAY, ranges, 1);
            hist3 = cvCreateHist(2, hist_size_5, CV_HIST_ARRAY, ranges, 1);
            bins = h_bins_5;
        }

        //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
        cvCalcHist(planes1, hist1, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)
        cvCalcHist(planes2, hist2, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)
        cvCalcHist(planes3, hist3, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)
        //cvCalcHist(planes1, hist1, 0, Imask);     //计算直方图(图像,直方图结构,不累加,掩码)
        //cvCalcHist(planes2, hist2, 0, Imask);     //计算直方图(图像,直方图结构,不累加,掩码)
        //cvCalcHist(planes3, hist3, 0, Imask);     //计算直方图(图像,直方图结构,不累加,掩码)


        cvNormalizeHist(hist1, 1.0);            //直方图归一化
        cvNormalizeHist(hist2, 1.0);            //直方图归一化
        cvNormalizeHist(hist3, 1.0);            //直方图归一化

        //比较直方图
        for (int j = 0; j < 4; j++)
        {
            double value1 = cvCompareHist(hist1, hist2, j);         //相关方式比较
            double value2 = cvCompareHist(hist1, hist3, j);         //相关方式比较
                if (j == 0)
                {
                    printf("Bins:%d*%d ,Hist1 & Hist2 ,CORREL: %lf;\n", bins, bins, value1);
                    printf("Bins:%d*%d ,Hist1 & Hist3 ,CORREL: %lf;\n", bins, bins, value2);
                }
                if (j == 1)
                {
                    printf("Bins:%d*%d ,Hist1 & Hist2 ,CHISQR: %lf;\n", bins, bins, value1);
                    printf("Bins:%d*%d ,Hist1 & Hist3 ,CHISQR: %lf;\n", bins, bins, value2);
                }
                if (j == 2)
                {
                    printf("Bins:%d*%d ,Hist1 & Hist2 ,INTERSECT: %lf;\n", bins, bins, value1);
                    printf("Bins:%d*%d ,Hist1 & Hist3 ,INTERSECT: %lf;\n", bins, bins, value2);
                }
                if (j == 3)
                {
                    printf("Bins:%d*%d ,Hist1 & Hist2 ,BHATTACHARYYA: %lf;\n", bins, bins, value1);
                    printf("Bins:%d*%d ,Hist1 & Hist3 ,BHATTACHARYYA: %lf;\n", bins, bins, value2);
                }   
        }
        cvReleaseHist(&hist1);
        cvReleaseHist(&hist2);
        cvReleaseHist(&hist3);

        cout << endl;
    }

    cvNamedWindow("SRC1", 1);
    cvNamedWindow("SRC2", 1);
    cvNamedWindow("SRC3", 1);
    cvNamedWindow("IMASK", 1);

    cvShowImage("SRC1", src1);
    cvShowImage("SRC2", src2);
    cvShowImage("SRC3", src3);
    cvShowImage("IMASK", Imask);

    cvWaitKey(0);
    system("pause");

    cvReleaseImage(&src1);
    cvReleaseImage(&src2);
    cvReleaseImage(&src3);
    cvReleaseImage(&Imask);
    cvReleaseImage(&hsv1);
    cvReleaseImage(&hsv2);
    cvReleaseImage(&hsv3);
    cvReleaseImage(&h_plane_1);
    cvReleaseImage(&s_plane_1);
    cvReleaseImage(&v_plane_1);
    cvReleaseImage(&h_plane_2);
    cvReleaseImage(&s_plane_2);
    cvReleaseImage(&v_plane_2);
    cvReleaseImage(&h_plane_3);
    cvReleaseImage(&s_plane_3);
    cvReleaseImage(&v_plane_3);

    cvDestroyAllWindows();
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第8张图片
第七章 学习OpenCV——直方图与匹配_第9张图片

例7-8 光照对直方图EMD匹配产生的影响

与上例一样,收集手的肤色直方图。以其中的一个室内直方图样本作为模型,并计算其与另一个室内直方图、一个室外阴影直方图、一个室外光照直方图的EMD距离,利用这些测量值设置一个距离阈值,再次比较EMD距离,程序中三幅图像已经过处理,依次比前一幅亮度增加25,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    IplImage* src1, *src2, *src3, *src4, *Imask, *hsv1, *hsv2, *hsv3, *hsv4;        //源图像 HSV格式图像
    //src1 src2 src3 每张亮度较前一张增加了10 src3 src4增加40
    //模板
    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template53_hue-saturation_Compare_EMD\\Debug\\handdd.jpg")))
        return -1;
    //室内
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template53_hue-saturation_Compare_EMD\\Debug\\handd.jpg")))
        return -2;
    //室外阴影
    if (!(src3 = cvLoadImage("D:\\Template\\OpenCV\\Template53_hue-saturation_Compare_EMD\\Debug\\handdd_out.jpg")))
        return -3;
    //室外光照
    if (!(src4 = cvLoadImage("D:\\Template\\OpenCV\\Template53_hue-saturation_Compare_EMD\\Debug\\handdd_out_sun.jpg")))
        return -4;
    //Mask为手掌掩码 过滤掉其他背景 只分析手掌颜色直方图 可略
    if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template53_hue-saturation_Compare_EMD\\Debug\\Imask1.jpg", 
        CV_LOAD_IMAGE_GRAYSCALE)))
        return -5;

    hsv1 = cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    hsv2 = cvCreateImage(cvGetSize(src2), src2->depth, src2->nChannels);
    hsv3 = cvCreateImage(cvGetSize(src3), src3->depth, src3->nChannels);
    hsv4 = cvCreateImage(cvGetSize(src4), src4->depth, src4->nChannels);

    cvCvtColor(src1, hsv1, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src2, hsv2, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src3, hsv3, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src4, hsv4, CV_BGR2HSV);     //源图像->HSV格式图像

    //色调(hue) 饱和度(saturation) 明度(value)
    IplImage *h_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { h_plane_1, s_plane_1 };                     //色相饱和度数组
    IplImage *planes2[] = { h_plane_2, s_plane_2 };                     //色相饱和度数组
    IplImage *planes3[] = { h_plane_3, s_plane_3 };                     //色相饱和度数组
    IplImage *planes4[] = { h_plane_4, s_plane_4 };                     //色相饱和度数组

    cvCvtPixToPlane(hsv1, h_plane_1, s_plane_1, v_plane_1, NULL);       //图像分割
    cvCvtPixToPlane(hsv2, h_plane_2, s_plane_2, v_plane_2, NULL);       //图像分割
    cvCvtPixToPlane(hsv3, h_plane_3, s_plane_3, v_plane_3, NULL);       //图像分割
    cvCvtPixToPlane(hsv4, h_plane_4, s_plane_4, v_plane_4, NULL);       //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    //建立直方图
    CvHistogram *hist1, *hist2, *hist3, *hist4;

    int h_bins = 30, s_bins = 32;

    int hist_size[] = { h_bins, s_bins };   //对应维数包含bins个数的数组

    float h_ranges[] = { 0, 180 };              //H通道划分范围 饱和度0-180
    float s_ranges[] = { 0, 255 };              //S通道划分范围
    float* ranges[] = { h_ranges, s_ranges };   //划分范围数对, ****均匀bin,range只要最大最小边界
    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist1 = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist2 = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist3 = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist4 = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    cvCalcHist(planes1, hist1, 0, Imask);   //计算直方图(图像,直方图结构,不累加,mask仅采集手掌)
    cvCalcHist(planes2, hist2, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes3, hist3, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes4, hist4, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)

    //cvNormalizeHist(hist1, 1.0);          //直方图归一化
    //cvNormalizeHist(hist2, 1.0);          // 归一化不可在此调用,后面要调用阈值化
    //cvNormalizeHist(hist3, 1.0);          
    //cvNormalizeHist(hist4, 1.0);          

    CvMat *sig1, *sig2, *sig3, *sig4;
    int numrows = h_bins*s_bins;

    sig1 = cvCreateMat(numrows, 3, CV_32FC1);   //numrows行 3列 矩阵
    sig2 = cvCreateMat(numrows, 3, CV_32FC1);
    sig3 = cvCreateMat(numrows, 3, CV_32FC1);   //numrows行 3列 矩阵
    sig4 = cvCreateMat(numrows, 3, CV_32FC1);

    for (int i = 0; i < 2; i++)
    {
        for (int h = 0; h < h_bins; h++)
        {
            for (int s = 0; s < s_bins; s++)
            {
                double bin_val = cvQueryHistValue_2D(hist1, h, s);
                //h:行数 s_bins:总列数(行长度)s:列数  h*s_bins+s 当前bin对应的sig行数   
                cvSet2D(sig1, h*s_bins + s, 0, cvScalar(bin_val));
                cvSet2D(sig1, h*s_bins + s, 1, cvScalar(h));
                cvSet2D(sig1, h*s_bins + s, 2, cvScalar(s));

                bin_val = cvQueryHistValue_2D(hist2, h, s);
                cvSet2D(sig2, h*s_bins + s, 0, cvScalar(bin_val));
                cvSet2D(sig2, h*s_bins + s, 1, cvScalar(h));
                cvSet2D(sig2, h*s_bins + s, 2, cvScalar(s));

                bin_val = cvQueryHistValue_2D(hist3, h, s);
                cvSet2D(sig3, h*s_bins + s, 0, cvScalar(bin_val));
                cvSet2D(sig3, h*s_bins + s, 1, cvScalar(h));
                cvSet2D(sig3, h*s_bins + s, 2, cvScalar(s));

                bin_val = cvQueryHistValue_2D(hist4, h, s);
                cvSet2D(sig4, h*s_bins + s, 0, cvScalar(bin_val));
                cvSet2D(sig4, h*s_bins + s, 1, cvScalar(h));
                cvSet2D(sig4, h*s_bins + s, 2, cvScalar(s));
            }
        }
        float emd1 = cvCalcEMD2(sig1, sig2, CV_DIST_L2);
        float emd2 = cvCalcEMD2(sig1, sig3, CV_DIST_L2);
        float emd3 = cvCalcEMD2(sig1, sig4, CV_DIST_L2);
        std::printf("Room EMD: %f; \n", emd1);
        std::printf("Outside EMD: %f; \n", emd2);
        std::printf("Outside_sun EMD: %f; \n", emd3);

        cvThreshHist(hist2, 87);        //距离阈值描述不明确,threshhold:87 EMD最小
        cvThreshHist(hist3, 87);
        cvThreshHist(hist4, 87);
        if (i==0)
            cout << endl << endl << "After Threshhold" << endl << endl;
    }

    cvNamedWindow("Model", 1);
    cvNamedWindow("Room", 1);
    cvNamedWindow("Outside", 1);
    cvNamedWindow("Outside_sun", 1);
    cvNamedWindow("IMASK", 1);

    cvShowImage("Model", src1);
    cvShowImage("Room", src2);
    cvShowImage("Outside", src3);
    cvShowImage("Outside_sun", src4);
    cvShowImage("IMASK", Imask);

    cvWaitKey(0);

    cvReleaseMat(&sig1);
    cvReleaseMat(&sig2);
    cvReleaseMat(&sig3);
    cvReleaseMat(&sig4);

    cvReleaseHist(&hist1);
    cvReleaseHist(&hist2);
    cvReleaseHist(&hist3);
    cvReleaseHist(&hist4);

    cvReleaseImage(&src1);
    cvReleaseImage(&src2);
    cvReleaseImage(&src3);
    cvReleaseImage(&Imask);
    cvReleaseImage(&hsv1);
    cvReleaseImage(&hsv2);
    cvReleaseImage(&hsv3);
    cvReleaseImage(&h_plane_1);
    cvReleaseImage(&s_plane_1);
    cvReleaseImage(&v_plane_1);
    cvReleaseImage(&h_plane_2);
    cvReleaseImage(&s_plane_2);
    cvReleaseImage(&v_plane_2);
    cvReleaseImage(&h_plane_3);
    cvReleaseImage(&s_plane_3);
    cvReleaseImage(&v_plane_3);

    cvDestroyAllWindows();
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第10张图片
第七章 学习OpenCV——直方图与匹配_第11张图片

例7-9 图像拍摄的光照条件判定

利用手机的手的图像,设计一个直方图,可以判断给定的图像是在哪种光线条件下被捕捉到的。然后,建立亮度值采样特征程序中三幅图像已经过处理,依次比前一幅亮度增加25,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    //源图像 HSV格式图像
    IplImage* src1, *src2, *src3, *src4, *src5, *src6, *Imask, *hsv1, *hsv2, *hsv3, *hsv4,*hsv5, *hsv6;     
    //src1 src2 亮度相同  src3 src4亮度相同 src5 src6亮度相同  每级依次增加20
    //模板 室内
    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template54_value_Compare\\Debug\\handdd.jpg")))
        return -1;
    //测试 室内
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template54_value_Compare\\Debug\\handd.jpg")))
        return -2;
    //模板 室外阴影
    if (!(src3 = cvLoadImage("D:\\Template\\OpenCV\\Template54_value_Compare\\Debug\\handdd_out.jpg")))
        return -3;
    //测试 室外阴影
    if (!(src4 = cvLoadImage("D:\\Template\\OpenCV\\Template54_value_Compare\\Debug\\handd_out.jpg")))
        return -4;
    //模板 室外光照
    if (!(src5 = cvLoadImage("D:\\Template\\OpenCV\\Template54_value_Compare\\Debug\\handdd_out_sun.jpg")))
        return -5;
    if (!(src6 = cvLoadImage("D:\\Template\\OpenCV\\Template54_value_Compare\\Debug\\handd_out_sun.jpg")))
        return -6;
    //Mask为手掌掩码 过滤掉其他背景 只分析手掌颜色直方图 可略
    if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template54_value_Compare\\Debug\\Imask.jpg", 
        CV_LOAD_IMAGE_GRAYSCALE)))
        return -7;

    hsv1 = cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    hsv2 = cvCreateImage(cvGetSize(src2), src2->depth, src2->nChannels);
    hsv3 = cvCreateImage(cvGetSize(src3), src3->depth, src3->nChannels);
    hsv4 = cvCreateImage(cvGetSize(src4), src4->depth, src4->nChannels);
    hsv5 = cvCreateImage(cvGetSize(src5), src5->depth, src5->nChannels);
    hsv6 = cvCreateImage(cvGetSize(src6), src6->depth, src6->nChannels);

    cvCvtColor(src1, hsv1, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src2, hsv2, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src3, hsv3, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src4, hsv4, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src5, hsv5, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src6, hsv6, CV_BGR2HSV);     //源图像->HSV格式图像

    //色调(hue) 饱和度(saturation) 明度(value)
    IplImage *h_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_5 = cvCreateImage(cvSize(hsv5->width, hsv5->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_5 = cvCreateImage(cvSize(hsv5->width, hsv5->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_5 = cvCreateImage(cvSize(hsv5->width, hsv5->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_6 = cvCreateImage(cvSize(hsv6->width, hsv6->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_6 = cvCreateImage(cvSize(hsv6->width, hsv6->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_6 = cvCreateImage(cvSize(hsv6->width, hsv6->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { v_plane_1 };                        //色相饱和度数组
    IplImage *planes2[] = { v_plane_2 };                        //色相饱和度数组
    IplImage *planes3[] = { v_plane_3 };                        //色相饱和度数组
    IplImage *planes4[] = { v_plane_4 };                        //色相饱和度数组
    IplImage *planes5[] = { v_plane_5 };                        //色相饱和度数组
    IplImage *planes6[] = { v_plane_6 };                        //色相饱和度数组

    cvCvtPixToPlane(hsv1, h_plane_1, s_plane_1, v_plane_1, NULL);       //图像分割
    cvCvtPixToPlane(hsv2, h_plane_2, s_plane_2, v_plane_2, NULL);       //图像分割
    cvCvtPixToPlane(hsv3, h_plane_3, s_plane_3, v_plane_3, NULL);       //图像分割
    cvCvtPixToPlane(hsv4, h_plane_4, s_plane_4, v_plane_4, NULL);       //图像分割
    cvCvtPixToPlane(hsv5, h_plane_5, s_plane_5, v_plane_5, NULL);       //图像分割
    cvCvtPixToPlane(hsv6, h_plane_6, s_plane_6, v_plane_6, NULL);       //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    //建立直方图
    CvHistogram *hist[6];

    int v_bins = 32;

    int hist_size[] = { v_bins };               //对应维数包含bins个数的数组

    float v_ranges[] = { 0, 255 };              //V通道划分范围
    float* ranges[] = { v_ranges }; //划分范围数对, ****均匀bin,range只要最大最小边界
    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist[0] = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist[1] = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist[2] = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist[3] = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist[4] = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist[5] = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    cvCalcHist(planes1, hist[0], 0, Imask); //计算直方图(图像,直方图结构,不累加,mask仅采集手掌)
    cvCalcHist(planes2, hist[1], 0, 0);     //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes3, hist[2], 0, Imask);     //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes4, hist[3], 0, 0);     //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes5, hist[4], 0, Imask);     //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes6, hist[5], 0, 0);     //计算直方图(图像,直方图结构,不累加,掩码)
    //直方图归一化
    cvNormalizeHist(hist[0], 1.0);
    cvNormalizeHist(hist[1], 1.0);
    cvNormalizeHist(hist[2], 1.0);
    cvNormalizeHist(hist[3], 1.0);
    cvNormalizeHist(hist[4], 1.0);
    cvNormalizeHist(hist[5], 1.0);

    for (int i = 1; i < 4; i++)
    {
        double min = 0,max=0;
        CvPoint point;
        CvMat* mat = cvCreateMat(1, 3, CV_64FC1);
        double value1 = cvCompareHist(hist[0], hist[2 * i - 1], CV_COMP_BHATTACHARYYA); //B距离方式比较
        double value2 = cvCompareHist(hist[2], hist[2 * i - 1], CV_COMP_BHATTACHARYYA); //B距离方式比较
        double value3 = cvCompareHist(hist[4], hist[2 * i - 1], CV_COMP_BHATTACHARYYA); //B距离方式比较
        printf("第 %d 幅与亮度模板比较值:\n",i);
        printf(" 室内:%lf, 室外:%lf, 室外阳光:%lf\n", value1, value2, value3);
        cvSet1D(mat, 0, cvScalar(value1));
        cvSet1D(mat, 1, cvScalar(value2));
        cvSet1D(mat, 2, cvScalar(value3));
        cvMinMaxLoc(mat, &min, &max, &point, NULL, NULL);
        switch (point.x)
        {
            case 0:
                printf("第 %d 幅与亮度模板比较值最小值:%lf\n判定在室内环境下拍摄.\n", i, min);
                break;
            case 1:
                printf("第 %d 幅与亮度模板比较值最小值:%lf\n判定在室外环境下拍摄.\n", i, min);
                break;
            case 2:
                printf("第 %d 幅与亮度模板比较值最小值:%lf\n判定在室外阳光下拍摄.\n", i, min);
                break;
        }
        cvReleaseMat(&mat);
        cout << endl;
    }

    cvNamedWindow("Room_model", 1);
    cvNamedWindow("Out_model", 1);
    cvNamedWindow("Out_Sun_model", 1);
    cvNamedWindow("IMASK", 1);
    cvNamedWindow("Room_test", 1);
    cvNamedWindow("Out_test", 1);
    cvNamedWindow("Out_Sun_test", 1);

    cvShowImage("Room_model", src1);
    cvShowImage("Out_model", src3);
    cvShowImage("Out_Sun_model", src5);
    cvShowImage("IMASK", Imask);
    cvShowImage("Room_test", src2);
    cvShowImage("Out_test", src4);
    cvShowImage("Out_Sun_test", src6);

    cvWaitKey(0);
    //system("pause");

    cvReleaseHist(&hist[0]);
    cvReleaseHist(&hist[1]);
    cvReleaseHist(&hist[2]);
    cvReleaseHist(&hist[3]);
    cvReleaseHist(&hist[4]);
    cvReleaseHist(&hist[5]);

    cvReleaseImage(&src1);
    cvReleaseImage(&src2);
    cvReleaseImage(&src3);
    cvReleaseImage(&src4);
    cvReleaseImage(&src5);
    cvReleaseImage(&src6);
    cvReleaseImage(&Imask);
    cvReleaseImage(&hsv1);
    cvReleaseImage(&hsv2);
    cvReleaseImage(&hsv3);
    cvReleaseImage(&hsv4);
    cvReleaseImage(&hsv5);
    cvReleaseImage(&hsv6);
    cvReleaseImage(&h_plane_1);
    cvReleaseImage(&s_plane_1);
    cvReleaseImage(&v_plane_1);
    cvReleaseImage(&h_plane_2);
    cvReleaseImage(&s_plane_2);
    cvReleaseImage(&v_plane_2);
    cvReleaseImage(&h_plane_3);
    cvReleaseImage(&s_plane_3);
    cvReleaseImage(&v_plane_3);
    cvReleaseImage(&h_plane_4);
    cvReleaseImage(&s_plane_4);
    cvReleaseImage(&v_plane_4);
    cvReleaseImage(&h_plane_5);
    cvReleaseImage(&s_plane_5);
    cvReleaseImage(&v_plane_5);
    cvReleaseImage(&h_plane_6);
    cvReleaseImage(&s_plane_6);
    cvReleaseImage(&v_plane_6);

    cvDestroyAllWindows();

}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第12张图片

例7-10 肤色“场景检测器”

在三种条件下建立两类肤色模板直方图。

 1. 从室内、室外阴影和室外阳光下得到的第一类直方图作为模型,用其中每一个分别跟第二类图进行B距离测试,检验肤色匹配效果;
 2. 利用(1)中设计的“场景检测器”确定要使用何种直方图模型:室内、室外阴影还是室外阳光;进行其他匹配方式,检验效果;

程序中三幅图像已经过处理,依次比前一幅亮度增加25,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int main(int argc, char* argv[])
{
    //源图像 HSV格式图像
    IplImage* src1, *src2, *src3, *src4, *src5, *src6, *Imask, *hsv1, *hsv2, *hsv3, *hsv4,*hsv5, *hsv6;     
    //src1 src2 亮度相同  src3 src4亮度相同 src5 src6亮度相同  每级依次增加20
    //模板 室内
    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template55_V_HS_Compare\\Debug\\handdd.jpg")))
        return -1;
    //测试 室内
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template55_V_HS_Compare\\Debug\\handd.jpg")))
        return -2;
    //模板 室外阴影
    if (!(src3 = cvLoadImage("D:\\Template\\OpenCV\\Template55_V_HS_Compare\\Debug\\handdd_out.jpg")))
        return -3;
    //测试 室外阴影
    if (!(src4 = cvLoadImage("D:\\Template\\OpenCV\\Template55_V_HS_Compare\\Debug\\handd_out.jpg")))
        return -4;
    //模板 室外光照
    if (!(src5 = cvLoadImage("D:\\Template\\OpenCV\\Template55_V_HS_Compare\\Debug\\handdd_out_sun.jpg")))
        return -5;
    if (!(src6 = cvLoadImage("D:\\Template\\OpenCV\\Template55_V_HS_Compare\\Debug\\handd_out_sun.jpg")))
        return -6;
    //Mask为手掌掩码 过滤掉其他背景 只分析手掌颜色直方图 可略
    if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template54_value_Compare\\Debug\\Imask.jpg", 
        CV_LOAD_IMAGE_GRAYSCALE)))
        return -7;

    hsv1 = cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    hsv2 = cvCreateImage(cvGetSize(src2), src2->depth, src2->nChannels);
    hsv3 = cvCreateImage(cvGetSize(src3), src3->depth, src3->nChannels);
    hsv4 = cvCreateImage(cvGetSize(src4), src4->depth, src4->nChannels);
    hsv5 = cvCreateImage(cvGetSize(src5), src5->depth, src5->nChannels);
    hsv6 = cvCreateImage(cvGetSize(src6), src6->depth, src6->nChannels);

    cvCvtColor(src1, hsv1, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src2, hsv2, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src3, hsv3, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src4, hsv4, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src5, hsv5, CV_BGR2HSV);     //源图像->HSV格式图像
    cvCvtColor(src6, hsv6, CV_BGR2HSV);     //源图像->HSV格式图像

    //色调(hue) 饱和度(saturation) 明度(value)
    IplImage *h_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_1 = cvCreateImage(cvSize(hsv1->width, hsv1->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_2 = cvCreateImage(cvSize(hsv2->width, hsv2->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_3 = cvCreateImage(cvSize(hsv3->width, hsv3->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_4 = cvCreateImage(cvSize(hsv4->width, hsv4->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_5 = cvCreateImage(cvSize(hsv5->width, hsv5->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_5 = cvCreateImage(cvSize(hsv5->width, hsv5->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_5 = cvCreateImage(cvSize(hsv5->width, hsv5->height), IPL_DEPTH_8U, 1);

    IplImage *h_plane_6 = cvCreateImage(cvSize(hsv6->width, hsv6->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane_6 = cvCreateImage(cvSize(hsv6->width, hsv6->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane_6 = cvCreateImage(cvSize(hsv6->width, hsv6->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { v_plane_1 };                        //亮度数组
    IplImage *planes2[] = { v_plane_2 };                        
    IplImage *planes3[] = { v_plane_3 };                        
    IplImage *planes4[] = { v_plane_4 };                        
    IplImage *planes5[] = { v_plane_5 };                        
    IplImage *planes6[] = { v_plane_6 };                        

    IplImage *planes7[] = { h_plane_1, s_plane_1 };             //色相饱和度数组
    IplImage *planes8[] = { h_plane_2, s_plane_2 };
    IplImage *planes9[] = { h_plane_3, s_plane_3 };
    IplImage *planes10[] = { h_plane_4, s_plane_4 };
    IplImage *planes11[] = { h_plane_5, s_plane_5 };
    IplImage *planes12[] = { h_plane_6, s_plane_6 };

    cvCvtPixToPlane(hsv1, h_plane_1, s_plane_1, v_plane_1, NULL);       //图像分割
    cvCvtPixToPlane(hsv2, h_plane_2, s_plane_2, v_plane_2, NULL);       //图像分割
    cvCvtPixToPlane(hsv3, h_plane_3, s_plane_3, v_plane_3, NULL);       //图像分割
    cvCvtPixToPlane(hsv4, h_plane_4, s_plane_4, v_plane_4, NULL);       //图像分割
    cvCvtPixToPlane(hsv5, h_plane_5, s_plane_5, v_plane_5, NULL);       //图像分割
    cvCvtPixToPlane(hsv6, h_plane_6, s_plane_6, v_plane_6, NULL);       //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    //建立直方图 v直方图:筛选合适的光照条件  hs直方图:肤色匹配
    CvHistogram *hist_v[6], *hist_h_s[6];

    int v_bins = 32;
    int hist_v_size[] = { v_bins };             //对应维数包含bins个数的数组
    int h_bins = 30, s_bins = 32;
    int hist_h_s_size[] = { h_bins, s_bins };   //对应维数包含bins个数的数组

    float v_range[] = { 0, 255 };       //V通道划分范围
    float* v_ranges[] = { v_range };    //划分范围数对, ****均匀bin,range只要最大最小边界
    float h_ranges[] = { 0, 180 };                  //H通道划分范围 饱和度0-180
    float s_ranges[] = { 0, 255 };                  //S通道划分范围
    float* h_s_ranges[] = { h_ranges, s_ranges };   //划分范围数对, ****均匀bin,range只要最大最小边界

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist_v[0] = cvCreateHist(1, hist_v_size, CV_HIST_ARRAY, v_ranges, 1);
    hist_v[1] = cvCreateHist(1, hist_v_size, CV_HIST_ARRAY, v_ranges, 1);
    hist_v[2] = cvCreateHist(1, hist_v_size, CV_HIST_ARRAY, v_ranges, 1);
    hist_v[3] = cvCreateHist(1, hist_v_size, CV_HIST_ARRAY, v_ranges, 1);
    hist_v[4] = cvCreateHist(1, hist_v_size, CV_HIST_ARRAY, v_ranges, 1);
    hist_v[5] = cvCreateHist(1, hist_v_size, CV_HIST_ARRAY, v_ranges, 1);

    hist_h_s[0] = cvCreateHist(1, hist_h_s_size, CV_HIST_ARRAY, h_s_ranges, 1);
    hist_h_s[1] = cvCreateHist(1, hist_h_s_size, CV_HIST_ARRAY, h_s_ranges, 1);
    hist_h_s[2] = cvCreateHist(1, hist_h_s_size, CV_HIST_ARRAY, h_s_ranges, 1);
    hist_h_s[3] = cvCreateHist(1, hist_h_s_size, CV_HIST_ARRAY, h_s_ranges, 1);
    hist_h_s[4] = cvCreateHist(1, hist_h_s_size, CV_HIST_ARRAY, h_s_ranges, 1);
    hist_h_s[5] = cvCreateHist(1, hist_h_s_size, CV_HIST_ARRAY, h_s_ranges, 1);

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    cvCalcHist(planes1, hist_v[0], 0, Imask);   //计算直方图(图像,直方图结构,不累加,mask仅采集手掌)
    cvCalcHist(planes2, hist_v[1], 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes3, hist_v[2], 0, Imask);   //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes4, hist_v[3], 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes5, hist_v[4], 0, Imask);   //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes6, hist_v[5], 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)

    cvCalcHist(planes7 , hist_h_s[0], 0, 0);//计算直方图(图像,直方图结构,不累加,mask仅采集手掌)
    cvCalcHist(planes8 , hist_h_s[1], 0, 0);    //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes9 , hist_h_s[2], 0, 0);//计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes10, hist_h_s[3], 0, 0);    //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes11, hist_h_s[4], 0, 0);//计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes12, hist_h_s[5], 0, 0);    //计算直方图(图像,直方图结构,不累加,掩码)

    //直方图归一化
    cvNormalizeHist(hist_v[0], 1.0);
    cvNormalizeHist(hist_v[1], 1.0);
    cvNormalizeHist(hist_v[2], 1.0);
    cvNormalizeHist(hist_v[3], 1.0);
    cvNormalizeHist(hist_v[4], 1.0);
    cvNormalizeHist(hist_v[5], 1.0);

    cvNormalizeHist(hist_h_s[0], 1.0);
    cvNormalizeHist(hist_h_s[1], 1.0);
    cvNormalizeHist(hist_h_s[2], 1.0);
    cvNormalizeHist(hist_h_s[3], 1.0);
    cvNormalizeHist(hist_h_s[4], 1.0);
    cvNormalizeHist(hist_h_s[5], 1.0);

    for (int i = 1; i < 4; i++)
    {
        double min = 0,max=0;
        CvPoint point;
        CvMat* mat = cvCreateMat(1, 3, CV_64FC1);
        double value1 = cvCompareHist(hist_v[0], hist_v[2 * i - 1], CV_COMP_BHATTACHARYYA); //B距离比较
        double value2 = cvCompareHist(hist_v[2], hist_v[2 * i - 1], CV_COMP_BHATTACHARYYA); //B距离比较
        double value3 = cvCompareHist(hist_v[4], hist_v[2 * i - 1], CV_COMP_BHATTACHARYYA); //B距离比较
        printf("第 %d 幅测试图像与亮度模板比较值:\n",i);
        printf(" 室内:%lf, 室外:%lf, 室外阳光:%lf\n", value1, value2, value3);
        cvSet1D(mat, 0, cvScalar(value1));
        cvSet1D(mat, 1, cvScalar(value2));
        cvSet1D(mat, 2, cvScalar(value3));
        cvMinMaxLoc(mat, &min, &max, &point, NULL, NULL);
        switch (point.x)
        {
            case 0:
                printf("第 %d 幅测试图像与亮度模板比较值最小值:%lf\n判定在室内环境下拍摄.\n", i, min);
                printf("应当选择室内环境肤色模板进行肤色匹配:\n");
                //不同比较方式的结果
                for (int j = 0; j < 4; j++)
                {
                    double h_s_value = cvCompareHist(hist_h_s[0], hist_h_s[2 * i - 1], j);
                    if (j == 0)
                    {
                        printf("  相关CV_COMP_CORREL: %lf;\n",h_s_value);
                    }
                    if (j == 1)
                    {
                        printf("  卡方CV_COMP_CHISQR: %lf;\n",h_s_value);
                    }
                    if (j == 2)
                    {
                        printf("  相交CV_COMP_INTERSECT: %lf;\n",h_s_value);
                    }
                    if (j == 3)
                    {
                        printf("  B距离CV_CCOMP_BHATTACHARYYA: %lf;\n",h_s_value);
                    }
                }
                break;
            case 1:
                printf("第 %d 幅测试图像与亮度模板比较值最小值:%lf\n判定在室外环境下拍摄.\n", i, min);
                printf("应当选择室外环境肤色模板进行肤色匹配:\n");
                //不同比较方式的结果
                for (int j = 0; j < 4; j++)
                {
                    double h_s_value = cvCompareHist(hist_h_s[2], hist_h_s[2 * i - 1], j);
                    if (j == 0)
                    {
                        printf("  相关CV_COMP_CORREL: %lf;\n", h_s_value);
                    }
                    if (j == 1)
                    {
                        printf("  卡方CV_COMP_CHISQR: %lf;\n", h_s_value);
                    }
                    if (j == 2)
                    {
                        printf("  相交CV_COMP_INTERSECT: %lf;\n", h_s_value);
                    }
                    if (j == 3)
                    {
                        printf("  B距离CV_CCOMP_BHATTACHARYYA: %lf;\n", h_s_value);
                    }
                }
                break;
            case 2:
                printf("第 %d 幅测试图像与亮度模板比较值最小值:%lf\n判定在室外阳光下拍摄.\n", i, min);
                printf("应当选择室外阳光肤色模板进行肤色匹配:\n");
                //不同比较方式的结果
                for (int j = 0; j < 4; j++)
                {
                    double h_s_value = cvCompareHist(hist_h_s[4], hist_h_s[2 * i - 1], j);
                    if (j == 0)
                    {
                        printf("  相关CV_COMP_CORREL: %lf;\n", h_s_value);
                    }
                    if (j == 1)
                    {
                        printf("  卡方CV_COMP_CHISQR: %lf;\n", h_s_value);
                    }
                    if (j == 2)
                    {
                        printf("  相交CV_COMP_INTERSECT: %lf;\n", h_s_value);
                    }
                    if (j == 3)
                    {
                        printf("  B距离CV_CCOMP_BHATTACHARYYA: %lf;\n", h_s_value);
                    }
                }
                break;
        }
        cvReleaseMat(&mat);
        cout << endl;
    }

    cvNamedWindow("Room_model", 1);
    cvNamedWindow("Out_model", 1);
    cvNamedWindow("Out_Sun_model", 1);
    cvNamedWindow("IMASK", 1);
    cvNamedWindow("Room_test_第1幅", 1);
    cvNamedWindow("Out_test_第2幅", 1);
    cvNamedWindow("Out_Sun_test_第3幅", 1);

    cvShowImage("Room_model", src1);
    cvShowImage("Out_model", src3);
    cvShowImage("Out_Sun_model", src5);
    cvShowImage("IMASK", Imask);
    cvShowImage("Room_test_第1幅", src2);
    cvShowImage("Out_test_第2幅", src4);
    cvShowImage("Out_Sun_test_第3幅", src6);

    cvWaitKey(0);
    //system("pause");

    cvReleaseHist(&hist_v[0]);
    cvReleaseHist(&hist_v[1]);
    cvReleaseHist(&hist_v[2]);
    cvReleaseHist(&hist_v[3]);
    cvReleaseHist(&hist_v[4]);
    cvReleaseHist(&hist_v[5]);  

    cvReleaseHist(&hist_h_s[0]);
    cvReleaseHist(&hist_h_s[1]);
    cvReleaseHist(&hist_h_s[2]);
    cvReleaseHist(&hist_h_s[3]);
    cvReleaseHist(&hist_h_s[4]);
    cvReleaseHist(&hist_h_s[5]);

    cvReleaseImage(&src1);
    cvReleaseImage(&src2);
    cvReleaseImage(&src3);
    cvReleaseImage(&src4);
    cvReleaseImage(&src5);
    cvReleaseImage(&src6);
    cvReleaseImage(&Imask);
    cvReleaseImage(&hsv1);
    cvReleaseImage(&hsv2);
    cvReleaseImage(&hsv3);
    cvReleaseImage(&hsv4);
    cvReleaseImage(&hsv5);
    cvReleaseImage(&hsv6);
    cvReleaseImage(&h_plane_1);
    cvReleaseImage(&s_plane_1);
    cvReleaseImage(&v_plane_1);
    cvReleaseImage(&h_plane_2);
    cvReleaseImage(&s_plane_2);
    cvReleaseImage(&v_plane_2);
    cvReleaseImage(&h_plane_3);
    cvReleaseImage(&s_plane_3);
    cvReleaseImage(&v_plane_3);
    cvReleaseImage(&h_plane_4);
    cvReleaseImage(&s_plane_4);
    cvReleaseImage(&v_plane_4);
    cvReleaseImage(&h_plane_5);
    cvReleaseImage(&s_plane_5);
    cvReleaseImage(&v_plane_5);
    cvReleaseImage(&h_plane_6);
    cvReleaseImage(&s_plane_6);
    cvReleaseImage(&v_plane_6);

    cvDestroyAllWindows();
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第13张图片
第七章 学习OpenCV——直方图与匹配_第14张图片

例7-11 RGB肤色区域检测器

本例进行的工作如下:

 1. 在室内条件下,利用一些手和脸来建立RGB直方图;
 2. 利用函数cvCalcBackProject()找到肤色区域;
 3. 利用本书第五章图像处理相关函数来清除噪声,并利用函数cvFloodFill()找到图像中肤色最大区域。

具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

CvPoint Current_Point;              //值为255点当前点 全局变量才可通过普通成员引用变更其值

bool find_point(IplImage *img, char val);

int main(int argc, char* argv[])
{
    IplImage* src1, *src2, *Imask, *rgb1, *rgb2, *Ithreshold, *Itemp, *Iclose, *Idst;   //源图像 HSV

    int threshold_type = CV_THRESH_BINARY;      //阈值类型
    CvPoint Last_Point;                         //值为255点的上一点
    //  CvPoint Current_Point;                  //值为255点当前点  为局部变量时,只能通过指针引用变更其值

    int Last_Area = 0;                          //上一个区域面积       
    int Current_Area = 0;                       //当前区域面积
    CvConnectedComp comp;                       //被填充区域统计属性

    Last_Point = cvPoint(0, 0);         //初始化上一点
    Current_Point = cvPoint(0, 0);      //初始化当前点

    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template56_RGB_BackProjection\\Debug\\handdd.jpg")))
        return -1;
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template56_RGB_BackProjection\\Debug\\handd.jpg")))
        return -2;
    //此处调入图像掩码应为单通道
    if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template56_RGB_BackProjection\\Debug\\Imask.jpg", CV_LOAD_IMAGE_GRAYSCALE)))
        return -3;

    //cvXorS(Imask, cvScalar(255), Imask);      //掩码图像按位异或,求反生成新的掩码处理背景色
    //cvSet(src1, cvScalarAll(0), Imask);       //背景变黑只提取肤色

    rgb1 = cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    rgb2 = cvCreateImage(cvGetSize(src2), src2->depth, src2->nChannels);

    cvCvtColor(src1, rgb1, CV_BGR2RGB); //源图像->HSV格式图像
    cvCvtColor(src2, rgb2, CV_BGR2RGB); //源图像->HSV格式图像

    //反向投影图像
    IplImage *back_projection = cvCreateImage(cvGetSize(src2), IPL_DEPTH_8U, 1);

    //阈值化 开运算图像
    Ithreshold=cvCreateImage(cvGetSize(src2), IPL_DEPTH_8U, 1);
    Itemp = cvCreateImage(cvGetSize(src2), IPL_DEPTH_8U, 1);
    Iclose = cvCreateImage(cvGetSize(src2), IPL_DEPTH_8U, 1);
    //最终目标区域图像
    Idst = cvCreateImage(cvGetSize(src2), IPL_DEPTH_8U, 1);

    //RGB
    IplImage *r_plane_1 = cvCreateImage(cvSize(rgb1->width, rgb1->height), IPL_DEPTH_8U, 1);
    IplImage *g_plane_1 = cvCreateImage(cvSize(rgb1->width, rgb1->height), IPL_DEPTH_8U, 1);
    IplImage *b_plane_1 = cvCreateImage(cvSize(rgb1->width, rgb1->height), IPL_DEPTH_8U, 1);

    IplImage *r_plane_2 = cvCreateImage(cvSize(rgb2->width, rgb2->height), IPL_DEPTH_8U, 1);
    IplImage *g_plane_2 = cvCreateImage(cvSize(rgb2->width, rgb2->height), IPL_DEPTH_8U, 1);
    IplImage *b_plane_2 = cvCreateImage(cvSize(rgb2->width, rgb2->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { r_plane_1, g_plane_1, b_plane_1 };                      //色相饱和度数组
    IplImage *planes2[] = { r_plane_2, g_plane_2, b_plane_2 };                      //色相饱和度数组

    cvCvtPixToPlane(rgb1, r_plane_1, g_plane_1, b_plane_1, NULL);       //图像分割
    cvCvtPixToPlane(rgb2, r_plane_2, g_plane_2, b_plane_2, NULL);       //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    int r_bins = 32, g_bins = 32, b_bins = 32;

    //建立直方图
    CvHistogram *hist_model,*hist_test;

    int hist_size[] = { r_bins, g_bins, b_bins };       //对应维数包含bins个数的数组
    float r_ranges[] = { 0, 255 };                      //R通道划分范围 
    float g_ranges[] = { 0, 255 };                      //G通道划分范围
    float b_ranges[] = { 0, 255 };                      //R通道划分范围 
    float* ranges[] = { r_ranges, g_ranges, b_ranges }; //划分范围数对,均匀bin,range只要最大最小边界
    hist_model = cvCreateHist(3, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test = cvCreateHist(3, hist_size, CV_HIST_ARRAY, ranges, 1);
    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)

    cvCalcHist(planes1, hist_model, 0, Imask);  //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes2, hist_test, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)

    //cvNormalizeHist(hist_model, 1.0);         //直方图归一化
    //cvNormalizeHist(hist_test, 1.0);          //直方图归一化

    cvCalcBackProject(planes2, back_projection, hist_model);        //像素点的反射投影

    //cvErode(back_projection, back_projection, NULL);              //腐蚀
    cvDilate(back_projection, back_projection, NULL);               //膨胀
    cvThreshold(back_projection, Ithreshold, 100, 255, threshold_type); //二值阈值化
    //闭运算,去除小暗区域,亮区域联结 NULL:3*3参考点为中心的核
    cvMorphologyEx(Ithreshold, Iclose, Itemp, NULL, CV_MOP_CLOSE, 1);

    cvNamedWindow("Mask", 1);
    cvNamedWindow("Model", 1);
    cvNamedWindow("Test", 1);
    cvNamedWindow("BACK_Projection", 1);
    cvNamedWindow("Threshhold", 1);
    cvNamedWindow("Iclose", 1);

    cvShowImage("Mask", Imask);
    cvShowImage("Model", src1);
    cvShowImage("Test", src2);
    cvShowImage("BACK_Projection", back_projection);
    cvShowImage("Threshhold", Ithreshold);
    cvShowImage("Iclose", Iclose);

    //漫水填充 获得手掌目标区域  效果不明显 图中没有太多噪声 闭运算后已达到要求
    cvNamedWindow("Destination", 1);
    cvCopy(Iclose, Idst);             //复制生成手掌目标图像
    do
    {
        if (find_point(Idst, 255))    //找像素值为255的像素点
        {

            cout << " X: " << Current_Point.x << " Y: " << Current_Point.y << endl;

            cvFloodFill(Idst, Current_Point, cvScalar(100), cvScalar(0), cvScalar(0),
                &comp, 8 | CV_FLOODFILL_FIXED_RANGE);           //对值为255的点进行漫水填充,值100
            Current_Area = comp.area;                           //当前区域面积

            if (Last_Area//当前区域大于上一区域,上一区域清0
            {
                if (Last_Area>0)
                    cvFloodFill(Idst, Last_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //上一区域赋值0
                cvShowImage("Destination", Idst);
                cvWaitKey(500);

                Last_Area = Current_Area;                               //当前区域赋值给上一区域
                Last_Point = Current_Point;                             //当前点赋值给上一点
                //memcpy(&Last_Point, &Current_Point, sizeof(CvPoint)); //错误,此方法复制无法正常使用掩码
            }
            else                                                //当前区域小于等于上一区域,当前区域清0
            {
                if (Current_Area>0)
                    cvFloodFill(Idst, Current_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //当前区域赋值0
                cvShowImage("Destination", Idst);
                cvWaitKey(500);
            }
        }
        else                                                    //最后剩余的最大区域赋值255
        {
            cvFloodFill(Idst, Last_Point, cvScalar(255), cvScalar(0), cvScalar(0), &comp, 8 | CV_FLOODFILL_FIXED_RANGE);
            cvShowImage("Destination", Idst);
            cvWaitKey(500);
            //上一区域赋值0
            break;
        }
    } while (true);

    cvWaitKey(0);
    //system("pause");

    cvReleaseHist(&hist_model);
    cvReleaseHist(&hist_test);

    cvReleaseImage(&Imask);
    cvReleaseImage(&src1);
    cvReleaseImage(&src2);
    cvReleaseImage(&rgb1);
    cvReleaseImage(&rgb2);
    cvReleaseImage(&Ithreshold);
    cvReleaseImage(&Itemp);
    cvReleaseImage(&Iclose);
    cvReleaseImage(&Idst);
    cvReleaseImage(&r_plane_1);
    cvReleaseImage(&g_plane_1);
    cvReleaseImage(&b_plane_1);
    cvReleaseImage(&r_plane_2);
    cvReleaseImage(&g_plane_2);
    cvReleaseImage(&b_plane_2);
    cvReleaseImage(&back_projection);

    cvDestroyAllWindows();
}

/******************遍历图像,指针算法********************/
//bool find_point(IplImage *img, char val,CvPoint* P_point)
bool find_point(IplImage *img, char val)
{
    char* ptr = NULL;
    //uchar* ptr = NULL;                    
    /**********    错,CvMat中为uchar* IplImage中为char*     ********/

    if (img->nChannels == 1)
    {
        ptr = img->imageData;
        //ptr = (uchar*)img->imageData;
        /**********    错,CvMat中为uchar* IplImage中为char*     ********/
        if (ptr != NULL)
        {
            for (int i = 0; i < img->height; i++)       //矩阵指针行寻址
            {
                ptr = (img->imageData + i*(img->widthStep));   //i 行 j 列
                //ptr = (uchar*)img->imageData + i*img->widthStep;   //index1 行 index2 列    
                /**********    错,mat中为uchar* IplImage中为char*     ********/

                for (int j = 0; j < img->width; j++)    //矩阵指针列寻址
                {
                    //if (ptr[j] == 255)            /********错误  ptr对应的值为char型********/
                    if (ptr[j] == val)              //判断某点像素是否为255
                    {
                        //P_point->x = j;               //列 ****Notice x为列坐标,若为行坐标会出现问题
                        //P_point->y = i;               //行
                        Current_Point.x = j;            /********局部变量此方式 无法实现赋值********/
                        Current_Point.y = i;
                        //cout << " j: " << j << " i: " << i << endl;
                        //cout << " X: " << P_point->x << " Y: " << P_point->y << endl;
                        //cout << " j: " <
                        //cout << " X: " << Current_Point.x << " Y: " << Current_Point.y << endl;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第15张图片

例7-12 手势梯度方向直方图

根据输入的手势图像,在每一个区域求取其梯度方向,计算出梯度方向直方图,并可视化。具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

void Create_Hist_1D(IplImage* src, IplImage* canny, IplImage* sobel, IplImage* hist_img);
void Compare_Gesture_Hist(IplImage *sobel1, IplImage *sobel2, IplImage *test, IplImage** canny);

int main(int argc, char* argv[])
{
    IplImage *src1, *Isobel1, *Ihist1;      //图像 
    IplImage *src2, *Isobel2, *Ihist2;      //图像 
    IplImage *src3, *Isobel3, *Ihist3;      //图像 
    IplImage *Icanny[3];

    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template57_HOG_Compare\\Debug\\Imask_1.jpg", 0)))
        return -1;
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template57_HOG_Compare\\Debug\\Imask_2.jpg", 0)))
        return -2;
    if (!(src3 = cvLoadImage("D:\\Template\\OpenCV\\Template57_HOG_Compare\\Debug\\DST.jpg", 0)))
        return -3;

    Icanny[0] = cvCreateImage(cvSize(src1->width, src1->height), 8, 1);     //canny图像 深度8
    Icanny[1] = cvCreateImage(cvSize(src2->width, src2->height), 8, 1);
    Icanny[2] = cvCreateImage(cvSize(src3->width, src3->height), 8, 1);

    Isobel1 = cvCreateImage(cvSize(src1->width, src1->height), 32, 1);
    Isobel2 = cvCreateImage(cvSize(src2->width, src2->height), 32, 1);
    Isobel3 = cvCreateImage(cvSize(src3->width, src3->height), 32, 1);

    Ihist1 = cvCreateImage(cvSize(320, 300), 8, 3);         //320*320
    Ihist2 = cvCreateImage(cvSize(320, 300), 8, 3);         //320*320
    Ihist3 = cvCreateImage(cvSize(320, 300), 8, 3);         //320*320

    Create_Hist_1D(src1, Icanny[0], Isobel1, Ihist1);
    Create_Hist_1D(src2, Icanny[1], Isobel2, Ihist2);
    Create_Hist_1D(src3, Icanny[2], Isobel3, Ihist3);

    Compare_Gesture_Hist(Isobel1, Isobel2, Isobel3, Icanny);

    cvNamedWindow("SRC1", 1);
    cvNamedWindow("SRC2", 2);
    cvNamedWindow("SRC3", 3);
    //cvNamedWindow("Canny_1", 1);
    //cvNamedWindow("Canny_2", 1);
    //cvNamedWindow("Canny_3", 1);
    cvNamedWindow("SOBEL_1", 1);
    cvNamedWindow("SOBEL_2", 1);
    cvNamedWindow("SOBEL_3", 1);
    cvNamedWindow("Hist_1", 1);
    cvNamedWindow("Hist_2", 1);
    cvNamedWindow("Hist_3", 1);

    cvShowImage("SRC1", src1);
    cvShowImage("SRC2", src2);
    cvShowImage("SRC3", src3);
    //cvShowImage("Canny_1", Icanny[0]);
    //cvShowImage("Canny_2", Icanny[1]);
    //cvShowImage("Canny_3", Icanny[2]);
    cvShowImage("SOBEL_1", Isobel1);
    cvShowImage("SOBEL_2", Isobel2);
    cvShowImage("SOBEL_3", Isobel3);
    cvShowImage("Hist_1", Ihist1);
    cvShowImage("Hist_2", Ihist2);
    cvShowImage("Hist_3", Ihist3);

    cvWaitKey();

    cvReleaseImage(&src1);
    cvReleaseImage(&src2);
    cvReleaseImage(&src3);
    cvReleaseImage(&Icanny[0]);
    cvReleaseImage(&Icanny[1]);
    cvReleaseImage(&Icanny[2]);
    cvReleaseImage(&Isobel1);
    cvReleaseImage(&Isobel2);
    cvReleaseImage(&Isobel3);
    cvReleaseImage(&Ihist1);
    cvReleaseImage(&Ihist2);
    cvReleaseImage(&Ihist3);

    cvDestroyAllWindows();

}

void Create_Hist_1D(IplImage* src, IplImage* canny, IplImage* gradient_dir, IplImage* hist_img)
{
    IplImage *sobel_x, *sobel_y;

    sobel_x = cvCreateImage(cvSize(src->width, src->height), 32, 1);
    sobel_y = cvCreateImage(cvSize(src->width, src->height), 32, 1);

    //边缘检测       src dst 边缘连接 边缘初始分割 核
    cvCanny(src, canny, 60, 180, 3);
    //方向导数
    cvSobel(src, sobel_x, 1, 0, 3);         //横向梯度dx
    cvSobel(src, sobel_y, 0, 1, 3);         //纵向梯度dy

    //梯度方向  dy/dx
    cvDiv(sobel_y, sobel_x, gradient_dir);

    //梯度方向

    char* ptr = NULL;
    float theta=0.0;                        //梯度方向角

    ptr = gradient_dir->imageData;

    if (ptr != NULL)
    {
        for (int i = 0; i < gradient_dir->height; i++)      //矩阵指针行寻址
        {
            ptr = (gradient_dir->imageData + i*(gradient_dir->widthStep));      //i 行 j 列
            for (int j = 0; j < gradient_dir->width; j++)                       //矩阵指针列寻址
            {
                if (cvGetReal2D(canny, i, j) && cvGetReal2D(sobel_x, i, j))     //dx!=0
                {
                    theta = cvGetReal2D(gradient_dir, i, j);
                    theta = atan(theta);
                    cvSetReal2D(gradient_dir, i, j, theta);
                }
                else                                                            //dx=0
                {
                    cvSetReal2D(gradient_dir, i, j, 0);
                }
            }
        }
    }

    float max = 0.0;

    int bins = 20;
    int hist_size[] = { bins };                 //对应维数包含bins个数的数组
    float range[] = { -CV_PI / 2, CV_PI / 2 };
    float* ranges[] = { range };
    CvHistogram* hist = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);

    cvZero(hist_img);
    IplImage *planes[] = { gradient_dir };      //梯度图像数组    
    cvCalcHist(planes, hist, 0, canny);         //只计算边界直方图

    cvGetMinMaxHistValue(hist, 0, &max, 0, 0);
    cvConvertScale(hist->bins, hist->bins, max ? 255. / max : 0., 0);   //缩放bin到[0,255]

    double bin_width = (double)hist_img->width / bins * 3 / 4;
    for (int i = 0; idouble val = cvGetReal1D(hist->bins, i)*hist_img->height / 255;
        CvPoint p0 = cvPoint(30 + i*bin_width, hist_img->height);
        CvPoint p1 = cvPoint(30 + (i + 1)*bin_width, hist_img->height - val);
        cvRectangle(hist_img, p0, p1, cvScalar(0, 255), 1, 8, 0);
    }

    cvReleaseHist(&hist);                   //释放直方图
    cvReleaseImage(&sobel_x);
    cvReleaseImage(&sobel_y);

}

void Compare_Gesture_Hist(IplImage *sobel1, IplImage *sobel2, IplImage *test, IplImage** canny)
{

    //建立直方图
    CvHistogram *hist_model1, *hist_model2, *hist_test;
    int bins = 20;
    int hist_size[] = { bins };     //对应维数包含bins个数的数组
    float range[] = { -CV_PI / 2, CV_PI / 2 };
    float* ranges[] = { range };    //划分范围数对, ****均匀bin,range只要最大最小边界

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist_model1 = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_model2 = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);

    IplImage *planes1[] = { sobel1 };
    IplImage *planes2[] = { sobel2 };
    IplImage *planes3[] = { test };

    cvCalcHist(planes1, hist_model1, 0, canny[0]);  //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes2, hist_model2, 0, canny[1]);  //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes3, hist_test, 0, canny[2]);    //计算直方图(图像,直方图结构,不累加,掩码)

    cvNormalizeHist(hist_model1, 1.0);      //直方图归一化
    cvNormalizeHist(hist_model2, 1.0);      //直方图归一化
    cvNormalizeHist(hist_test, 1.0);        //直方图归一化

    //比较直方图
    for (int j = 0; j < 4; j++)
    {
        double value1 = cvCompareHist(hist_test, hist_model1, j);           //相关方式比较
        double value2 = cvCompareHist(hist_test, hist_model2, j);           //相关方式比较
        if (j == 0)
        {
            std::printf("   Hist_test & Hist_model1 ,CV_COMP_CORREL: %lf;\n", value1);
            std::printf("   Hist_test & Hist_model2 ,CV_COMP_CORREL: %lf;\n", value2);
        }
        if (j == 1)
        {
            std::printf("   Hist_test & Hist_model1 ,CV_COMP_CHISQR: %lf;\n", value1);
            std::printf("   Hist_test & Hist_model2 ,CV_COMP_CHISQR: %lf;\n", value2);
        }
        if (j == 2)
        {
            std::printf("   Hist_test & Hist_model1 ,CV_COMP_INTERSECT: %lf;\n", value1);
            std::printf("   Hist_test & Hist_model2 ,CV_COMP_INTERSECT: %lf;\n", value2);
        }
        if (j == 3)
        {
            std::printf("   Hist_test & Hist_model1 ,CV_CCOMP_BHATTACHARYYA: %lf;\n", value1);
            std::printf("   Hist_test & Hist_model2 ,CV_CCOMP_BHATTACHARYYA: %lf;\n", value2);
        }
        std::printf("\n");
    }

    cvReleaseHist(&hist_model1);
    cvReleaseHist(&hist_model2);
    cvReleaseHist(&hist_test);

}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第16张图片
第七章 学习OpenCV——直方图与匹配_第17张图片
第七章 学习OpenCV——直方图与匹配_第18张图片

例7-13 识别手势

尝试识别手势。从摄像机中获取一个2英尺的手的图像,建立一些手势(不能动):手掌竖直和手掌水平。

 1. 利用例11得到的结果,在手周围的肤色区域求取梯度方向并对两种手势建立直方图模型;
 2. 利用网络摄像机做识别:利用感兴趣的区域找到“潜在的手”,利用例12的方法,在每一个区域求取其梯度方向,通过设定相应的阈值来检测手势;
 3. 本例中,竖直手势识别成功,在测试手势直方图中绘制100x100的蓝色矩形;水平手势识别成功,在测试手势直方图中绘制100x100的红色矩形;
 4. 本例中,经过测试进行卡方匹配时差异最大,便于区分识别故采用卡方方式进行直方图匹配;
 5. 本例中,有寻找手掌轮廓的程序,因为调用后程序实时性明显降低,故未调用;
 6. 本例中,白天黑夜识别准确度略有差异,可观察控制台输出的卡方距离变化值,灵活设置正确识别的阈值;

具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

CvPoint Current_Point;              //全局变量才可通过普通成员引用变更其值

void getContoursByC(IplImage* src, IplImage* dst, double minarea = 100, double whRatio = 1);
bool find_point(IplImage *img, char val);
void Create_Imask(IplImage *src, IplImage *dst);
void Find_Hand_Region(IplImage *model, IplImage *test, IplImage *mask, IplImage *dst);
void Create_Hist_1D(IplImage* src, IplImage* canny, IplImage* sobel, IplImage* hist_img);
void Compare_Gesture_Hist(IplImage *sobel1, IplImage *sobel2, IplImage *test, IplImage** canny, IplImage* hist_img);

int main(int argc, char* argv[])
{
    IplImage *src1, *Imask;                     //肤色模板 手掌掩码
    IplImage *src2, *Isobel1, *Ihist1;          //竖直手势模板 梯度方向 直方图
    IplImage *src3, *Isobel2, *Ihist2;          //水平手势模板 梯度方向 直方图
    IplImage *src4, *Isobel3, *Ihist3;          //测试手势模板 梯度方向 直方图
    IplImage *dst;                              //肤色区域图像
    IplImage *Icanny[3];                        //边缘图像
    CvCapture* capture;

    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template58_Hand_Track\\Debug\\handd.jpg")))
        return -1;      //肤色模板
    if (!(src2 = cvLoadImage("D:\\Template\\OpenCV\\Template58_Hand_Track\\Debug\\Imask_1.jpg",0)))
        return -2;      //手势模板1  竖直 
    if (!(src3 = cvLoadImage("D:\\Template\\OpenCV\\Template58_Hand_Track\\Debug\\Imask_2.jpg",0)))
        return -3;      //手势模板2  水平

    if (argc == 1)   //此处代码是做一个判断,有摄像头设备则读入摄像头设备的图像信息,没有则播放本地视频文件
        capture = cvCreateCameraCapture(0);
    else
        return -4;  //没有摄像头

    src4 = cvQueryFrame(capture);                                           //获取摄像头图像帧
    Imask = cvCreateImage(cvGetSize(src1), src1->depth, 1);                 //手掌掩码图像
    dst = cvCreateImage(cvGetSize(src4), IPL_DEPTH_8U, 1);                  //处理后的反射投影      

    Icanny[0] = cvCreateImage(cvSize(src2->width, src2->height), 8, 1);     //canny图像 深度8
    Icanny[1] = cvCreateImage(cvSize(src3->width, src3->height), 8, 1);
    Icanny[2] = cvCreateImage(cvSize(src4->width, src4->height), 8, 1);

    Isobel1 = cvCreateImage(cvSize(src2->width, src2->height), 32, 1);
    Isobel2 = cvCreateImage(cvSize(src3->width, src3->height), 32, 1);
    Isobel3 = cvCreateImage(cvSize(src4->width, src4->height), 32, 1);

    Ihist1 = cvCreateImage(cvSize(320, 300), 8, 3);         //320*320
    Ihist2 = cvCreateImage(cvSize(320, 300), 8, 3);         //320*320
    Ihist3 = cvCreateImage(cvSize(320, 300), 8, 3);         //320*320

    Create_Imask(src1, Imask);                              //创建肤色掩码图像
    Create_Hist_1D(src2, Icanny[0], Isobel1, Ihist1);
    Create_Hist_1D(src3, Icanny[1], Isobel2, Ihist2);

    cvNamedWindow("竖直手势直方图", 1);
    cvNamedWindow("横向手势直方图", 1);
    cvNamedWindow("测试手势直方图", 1);

    cvNamedWindow("BACK_Projection", 1);
    cvNamedWindow("Destination", 1);

    while (1)
    {
        src4 = cvQueryFrame(capture);

        Find_Hand_Region(src1, src4, Imask, dst);           //寻找肤色区域
        Create_Hist_1D(dst, Icanny[2], Isobel3, Ihist3);
        Compare_Gesture_Hist(Isobel1, Isobel2, Isobel3, Icanny, Ihist3);

        if (!src4) 
            break;
        //cvShowImage("Show_Camera", src4);
        char c = cvWaitKey(32);
        if (c == 27) 
            break;

        cvShowImage("竖直手势直方图", Ihist1);
        cvShowImage("横向手势直方图", Ihist2);
        cvShowImage("测试手势直方图", Ihist3);
    }

    cvWaitKey();

    cvReleaseCapture(&capture);
    cvReleaseImage(&src1);
    cvReleaseImage(&Imask);
    cvReleaseImage(&dst);

    cvDestroyAllWindows();
}
/*采用cvFindContours提取轮廓,并过滤掉小面积轮廓,最后将轮廓保存*/
void getContoursByC(IplImage* src, IplImage* dst, double minarea, double whRatio)
{
    //the parm. for cvFindContours  
    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq* contour = 0;
    double maxarea = 0;

    //for display  
    cvNamedWindow("Source", CV_WINDOW_NORMAL);
    cvShowImage("Source", src);

    //二值化  
    cvThreshold(src, src, 120, 255, CV_THRESH_BINARY);

    //提取轮廓
    //单通道二值边缘图像、轮廓点集向量、各种轮廓的索引编号向量、检索模式、定义轮廓的近似方法、点偏移量
    cvFindContours(src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    cvZero(dst);//清空数组  

    /*CvSeq* _contour为了保存轮廓的首指针位置,因为随后contour将用来迭代*/
    CvSeq* _contour = contour;


    int maxAreaIdx = -1, iteratorIdx = 0;//n为面积最大轮廓索引,m为迭代索引  
    for (int iteratorIdx = 0; contour != 0; contour = contour->h_next, iteratorIdx++/*更新迭代索引*/)
    {

        double tmparea = fabs(cvContourArea(contour));
        if (tmparea > maxarea)
        {
            maxarea = tmparea;
            maxAreaIdx = iteratorIdx;
            continue;
        }
        if (tmparea < minarea)
        {
            //删除面积小于设定值的轮廓  
            cvSeqRemove(contour, 0);
            continue;
        }
        CvRect aRect = cvBoundingRect(contour, 0);
        if ((aRect.width / aRect.height)//删除宽高比例小于设定值的轮廓  
            cvSeqRemove(contour, 0);
            continue;
        }
        //CvScalar color = CV_RGB( rand()&255, rand()&255, rand()&255 );//创建一个色彩值  
        //CvScalar color = CV_RGB(0, 255, 255);  

        //max_level 绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。  
        //如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。  
        //如果值为负数,函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓。   
        //cvDrawContours(dst, contour, color, color, -1, 1, 8);//绘制外部和内部的轮廓  
    }
    contour = _contour; /*int k=0;*/
    //统计剩余轮廓,并画出最大面积的轮廓  
    int count = 0;
    for (; contour != 0; contour = contour->h_next)
    {
        count++;
        double tmparea = fabs(cvContourArea(contour));
        if (tmparea == maxarea /*k==n*/)
        {
            CvScalar color = CV_RGB(255, 0, 0);
            cvDrawContours(dst, contour, color, color, -1, 1, 8);
        }
        /*k++;*/
    }
    printf("The total number of contours is:%d", count);
    cvNamedWindow("Components", CV_WINDOW_NORMAL);
    cvShowImage("Components", dst);
    cvSaveImage("dst.jpg", dst);
    //roateProcess(dst);  
    cvWaitKey(0);
    //销毁窗口和图像存储  
    cvDestroyWindow("Source");
    cvReleaseImage(&src);
    cvDestroyWindow("Components");
    cvReleaseImage(&dst);
}

/******************遍历图像-指针算法********************/
bool find_point(IplImage *img, char val)
{
    char* ptr = NULL;

    if (img->nChannels == 1)
    {
        ptr = img->imageData;
        if (ptr != NULL)
        {
            for (int i = 0; i < img->height; i++)       //矩阵指针行寻址
            {
                ptr = (img->imageData + i*(img->widthStep));   //i 行 j 列

                for (int j = 0; j < img->width; j++)    //矩阵指针列寻址
                {
                    if (ptr[j] == val)                  //判断某点像素是否为255
                    {
                        Current_Point.x = j;            
                        Current_Point.y = i;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

void Create_Imask(IplImage *src, IplImage *dst)
{
    int Last_Area = 0;                          //上一个区域面积       
    int Current_Area = 0;                       //当前区域面积
    int threshold_type = CV_THRESH_BINARY;      //阈值类型
    CvPoint Last_Point;                         //值为255点的上一点
    CvConnectedComp comp;                       //被填充区域统计属性
    IplImage *gray, *threshold, *temp,*open;    //灰度图像

    Last_Point = cvPoint(0, 0);         //初始化上一点
    Current_Point = cvPoint(0, 0);      //初始化当前点

    gray = cvCreateImage(cvGetSize(src), src->depth, 1);
    threshold = cvCreateImage(cvGetSize(src), src->depth, 1);
    temp = cvCreateImage(cvGetSize(src), src->depth, 1);
    open = cvCreateImage(cvGetSize(src), src->depth, 1);

    cvCvtColor(src, gray, CV_BGR2GRAY); //源图像->灰度图像
    //二值阈值化
    cvThreshold(gray, threshold, 100, 255, threshold_type);     
    //开运算,去除小亮区域,其他联结 NULL:3*3参考点为中心的核
    cvMorphologyEx(threshold, open, temp, NULL, CV_MOP_OPEN, 1);

    cvNamedWindow("肤色模板", 1);
    cvNamedWindow("肤色掩码", 1);
    cvShowImage("肤色模板", src);
    cvShowImage("肤色掩码", dst);

    //漫水填充 获得手掌掩码
    cvCopy(open, dst);               //复制生成手掌掩码
    do
    {
        if (find_point(dst, 255))    //找像素值为255的像素点
        {

            cout << " X: " << Current_Point.x << " Y: " << Current_Point.y << endl;

            cvFloodFill(dst, Current_Point, cvScalar(100), cvScalar(0), cvScalar(0),
                &comp, 8 | CV_FLOODFILL_FIXED_RANGE);       //对值为255的点进行漫水填充,值100
            Current_Area = comp.area;                       //当前区域面积

            if (Last_Area//当前区域大于上一区域,上一区域清0
            {
                if (Last_Area>0)
                    cvFloodFill(dst, Last_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //上一区域赋值0
                cvShowImage("肤色掩码", dst);
                cvWaitKey(500);

                Last_Area = Current_Area;                               //当前区域赋值给上一区域
                Last_Point = Current_Point;                             //当前点赋值给上一点
            }
            else                                            //当前区域小于等于上一区域,当前区域清0
            {
                if (Current_Area>0)
                    cvFloodFill(dst, Current_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //当前区域赋值0
                cvShowImage("肤色掩码", dst);
                cvWaitKey(500);
            }
        }
        else                                                //最后剩余的最大区域赋值255
        {
            cvFloodFill(dst, Last_Point, cvScalar(255), cvScalar(0), cvScalar(0), &comp, 8 | CV_FLOODFILL_FIXED_RANGE);
            cvShowImage("肤色掩码", dst);
            cvWaitKey(500);
            //上一区域赋值0
            break;
        }
    } while (true);

    //cvSaveImage("Imask.jpg", dst);

    cvReleaseImage(&gray);
    cvReleaseImage(&threshold);
    cvReleaseImage(&temp);
    cvReleaseImage(&open);
}

void Find_Hand_Region(IplImage *model, IplImage *test, IplImage *mask, IplImage *dst)
{

    int threshold_type = CV_THRESH_BINARY;      //阈值类型

    //临时图像  反向投影图像
    IplImage *temp = cvCreateImage(cvSize(model->width, model->height), IPL_DEPTH_8U, 1);
    IplImage *back_projection = cvCreateImage(cvSize(test->width, test->height), IPL_DEPTH_8U, 1);
    //RGB
    IplImage *r_plane_1 = cvCreateImage(cvSize(model->width, model->height), IPL_DEPTH_8U, 1);
    IplImage *g_plane_1 = cvCreateImage(cvSize(model->width, model->height), IPL_DEPTH_8U, 1);
    IplImage *b_plane_1 = cvCreateImage(cvSize(model->width, model->height), IPL_DEPTH_8U, 1);

    IplImage *r_plane_2 = cvCreateImage(cvSize(test->width, test->height), IPL_DEPTH_8U, 1);
    IplImage *g_plane_2 = cvCreateImage(cvSize(test->width, test->height), IPL_DEPTH_8U, 1);
    IplImage *b_plane_2 = cvCreateImage(cvSize(test->width, test->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { r_plane_1, g_plane_1, b_plane_1 };          //色相饱和度数组
    IplImage *planes2[] = { r_plane_2, g_plane_2, b_plane_2 };          //色相饱和度数组

    cvCvtPixToPlane(model, b_plane_1, g_plane_1, r_plane_1, NULL);      //图像分割
    cvCvtPixToPlane(test, b_plane_2, g_plane_2, r_plane_2, NULL);       //图像分割

    int r_bins = 32, g_bins = 32, b_bins = 32;

    //建立直方图
    CvHistogram *hist_model, *hist_test;

    int hist_size[] = { r_bins, g_bins, b_bins };       //对应维数包含bins个数的数组
    float r_ranges[] = { 0, 255 };                      //R通道划分范围 
    float g_ranges[] = { 0, 255 };                      //G通道划分范围
    float b_ranges[] = { 0, 255 };                      //R通道划分范围 
    float* ranges[] = { r_ranges, g_ranges, b_ranges }; //划分范围数对, ****均匀bin,range只要最大最小边界

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist_model = cvCreateHist(3, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test = cvCreateHist(3, hist_size, CV_HIST_ARRAY, ranges, 1);

    cvCalcHist(planes1, hist_model, 0, mask);   //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes2, hist_test, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)

    //cvNormalizeHist(hist_model, 1.0);         //直方图归一化
    //cvNormalizeHist(hist_test, 1.0);          //直方图归一化

    //像素点的反射投影 创建测试hist的图像数组 结果图像 模板hist
    cvCalcBackProject(planes2, back_projection, hist_model);

    cvSmooth(back_projection, dst, CV_MEDIAN, 11);  //中值滤波 去除椒盐噪声

    //开运算,去除小亮区域,其他联结 NULL:3*3参考点为中心的核
    cvMorphologyEx(dst, dst, temp, NULL, CV_MOP_OPEN, 1);

    cvThreshold(dst, dst, 0, 255, threshold_type);  //二值阈值化

    //边缘检测   src dst 边缘连接 边缘初始分割 核
    //cvCanny(dst, dst,90,180,3);
    //得到手掌轮廓 绘制轮廓线
    //getContoursByC(dst, dst);             

    cvShowImage("BACK_Projection", back_projection);
    cvShowImage("Destination", dst);

    //cvSaveImage("DST.jpg", dst);

    cvReleaseHist(&hist_model);
    cvReleaseHist(&hist_test);

    cvReleaseImage(&back_projection);
    cvReleaseImage(&temp);
    cvReleaseImage(&r_plane_1);
    cvReleaseImage(&g_plane_1);
    cvReleaseImage(&b_plane_1);
    cvReleaseImage(&r_plane_2);
    cvReleaseImage(&g_plane_2);
    cvReleaseImage(&b_plane_2);

}

void Create_Hist_1D(IplImage* src, IplImage* canny, IplImage* gradient_dir, IplImage* hist_img)
{
    IplImage *sobel_x, *sobel_y;

    sobel_x = cvCreateImage(cvSize(src->width, src->height), 32, 1);
    sobel_y = cvCreateImage(cvSize(src->width, src->height), 32, 1);

    //边缘检测, src dst 边缘连接 边缘初始分割 核
    cvCanny(src, canny, 60, 180, 3);
    //方向导数
    cvSobel(src, sobel_x, 1, 0, 3);     //横向偏导dx
    cvSobel(src, sobel_y, 0, 1, 3);     //纵向偏导dy
    //梯度方向  dy/dx
    cvDiv(sobel_y, sobel_x, gradient_dir);

    char* ptr = NULL;
    float theta = 0.0;                  //梯度方向角

    ptr = gradient_dir->imageData;

    if (ptr != NULL)
    {
        for (int i = 0; i < gradient_dir->height; i++)      //矩阵指针行寻址
        {
            ptr = (gradient_dir->imageData + i*(gradient_dir->widthStep));      //i 行 j 列
            for (int j = 0; j < gradient_dir->width; j++)                       //矩阵指针列寻址
            {
                if (cvGetReal2D(canny, i, j) && cvGetReal2D(sobel_x, i, j))     //dx!=0
                {
                    theta = cvGetReal2D(gradient_dir, i, j);
                    theta = atan(theta);
                    cvSetReal2D(gradient_dir, i, j, theta);
                }
                else                                                            //dx=0
                {
                    cvSetReal2D(gradient_dir, i, j, 0);
                }
            }
        }
    }

    float max = 0.0;

    int bins = 20;
    int hist_size[] = { bins };                 //对应维数包含bins个数的数组
    float range[] = { -CV_PI / 2, CV_PI / 2 };
    float* ranges[] = { range };
    CvHistogram* hist = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);

    cvZero(hist_img);
    IplImage *planes[] = { gradient_dir };      //梯度图像数组    
    cvCalcHist(planes, hist, 0, canny);         //只计算边界直方图

    cvGetMinMaxHistValue(hist, 0, &max, 0, 0);
    //src dst scale shift 缩放bin到[0,255]  (条件表达式 ? 真值 : 假值)
    cvConvertScale(hist->bins, hist->bins, max ? 255. / max : 0., 0);   

    //绘制直方图
    double bin_width = (double)hist_img->width / bins * 3 / 4;
    for (int i = 0; idouble val = cvGetReal1D(hist->bins, i)*hist_img->height / 255;
        CvPoint p0 = cvPoint(30 + i*bin_width, hist_img->height);
        CvPoint p1 = cvPoint(30 + (i + 1)*bin_width, hist_img->height - val);
        cvRectangle(hist_img, p0, p1, cvScalar(0, 255), 1, 8, 0);
    }

    cvReleaseHist(&hist);                   //释放直方图
    cvReleaseImage(&sobel_x);
    cvReleaseImage(&sobel_y);
}

void Compare_Gesture_Hist(IplImage *sobel1, IplImage *sobel2, IplImage *test, IplImage** canny, IplImage* hist_img)
{
    //建立直方图
    CvHistogram *hist_model1, *hist_model2, *hist_test;
    int bins = 20;
    int hist_size[] = { bins };     //对应维数包含bins个数的数组
    float range[] = { -CV_PI / 2, CV_PI / 2 };
    float* ranges[] = { range };    //划分范围数对, ****均匀bin,range只要最大最小边界

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist_model1 = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_model2 = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);

    IplImage *planes1[] = { sobel1 };
    IplImage *planes2[] = { sobel2 };
    IplImage *planes3[] = { test };

    cvCalcHist(planes1, hist_model1, 0, canny[0]);  //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes2, hist_model2, 0, canny[1]);  //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes3, hist_test, 0, canny[2]);    //计算直方图(图像,直方图结构,不累加,掩码)

    cvNormalizeHist(hist_model1, 1.0);      //直方图归一化
    cvNormalizeHist(hist_model2, 1.0);      //直方图归一化
    cvNormalizeHist(hist_test, 1.0);        //直方图归一化

    //比较直方图
    for (int j = 0; j < 4; j++)
    {
        double value1 = cvCompareHist(hist_test, hist_model1, j);           //相关方式比较
        double value2 = cvCompareHist(hist_test, hist_model2, j);           //相关方式比较
        //if (j == 0)
        //{
        //  std::printf("   Hist_test & Hist_model1 ,CV_COMP_CORREL: %lf;\n", value1);
        //  std::printf("   Hist_test & Hist_model2 ,CV_COMP_CORREL: %lf;\n", value2);
        //}
        if (j == 1)
        {
            std::printf("   Hist_test & Hist_model1 ,CV_COMP_CHISQR: %lf;\n", value1);
            std::printf("   Hist_test & Hist_model2 ,CV_COMP_CHISQR: %lf;\n", value2);
            if ((value1 <= 0.25) && (value2 >= 0.55))
            {
                cvDrawRect(hist_img, cvPoint(100, 100), cvPoint(200, 200), cvScalar(255, 0, 0), CV_FILLED, 8);
            }
            if ((value1 >= 0.45) && (value2 <= 0.4))
            {
                cvDrawRect(hist_img, cvPoint(100, 100), cvPoint(200, 200), cvScalar(0, 0, 255), CV_FILLED, 8);
            }
        }
        //if (j == 2)
        //{
        //  std::printf("   Hist_test & Hist_model1 ,CV_COMP_INTERSECT: %lf;\n", value1);
        //  std::printf("   Hist_test & Hist_model2 ,CV_COMP_INTERSECT: %lf;\n", value2);
        //}
        //if (j == 3)
        //{
        //  std::printf("   Hist_test & Hist_model1 ,CV_CCOMP_BHATTACHARYYA: %lf;\n", value1);
        //  std::printf("   Hist_test & Hist_model2 ,CV_CCOMP_BHATTACHARYYA: %lf;\n", value2);
        //}
        std::printf("\n");
    }
    cvReleaseHist(&hist_model1);
    cvReleaseHist(&hist_model2);
    cvReleaseHist(&hist_test);
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第19张图片
第七章 学习OpenCV——直方图与匹配_第20张图片
第七章 学习OpenCV——直方图与匹配_第21张图片

例7-14 识别手势(EMD)

尝试识别手势。实现例13功能,但是利用EMD匹配策略,本例仅对Compare_Gesture_Hist()函数做了适当修改,具体代码如下:

void Compare_Gesture_Hist(IplImage *sobel1, IplImage *sobel2, IplImage *test, IplImage** canny, IplImage* hist_img)
{
    //建立直方图
    CvHistogram *hist_model1, *hist_model2, *hist_test;
    int bins = 20;
    int hist_size[] = { bins };     //对应维数包含bins个数的数组
    float range[] = { -CV_PI / 2, CV_PI / 2 };
    float* ranges[] = { range };    //划分范围数对, ****均匀bin,range只要最大最小边界

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist_model1 = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_model2 = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);

    IplImage *planes1[] = { sobel1 };
    IplImage *planes2[] = { sobel2 };
    IplImage *planes3[] = { test };

    cvCalcHist(planes1, hist_model1, 0, canny[0]);  //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes2, hist_model2, 0, canny[1]);  //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes3, hist_test, 0, canny[2]);    //计算直方图(图像,直方图结构,不累加,掩码)

    cvNormalizeHist(hist_model1, 1.0);      //直方图归一化
    cvNormalizeHist(hist_model2, 1.0);      //直方图归一化
    cvNormalizeHist(hist_test, 1.0);        //直方图归一化

    //EMD
    CvMat *sig1, *sig2, *sig_test;
    int numrows = bins;

    sig1 = cvCreateMat(numrows, 2, CV_32FC1);   //numrows行 2列 矩阵
    sig2 = cvCreateMat(numrows, 2, CV_32FC1);
    sig_test = cvCreateMat(numrows, 2, CV_32FC1);

    for (int h = 0; h < bins; h++)
    {
        float bin_val = 0.0;

        bin_val = cvQueryHistValue_1D(hist_model1, h);
        //h:行数 s_bins:总列数(行长度)s:列数  h*s_bins+s 当前bin对应的sig行数   
        cvSet2D(sig1, h, 0, cvScalar(bin_val));
        cvSet2D(sig1, h, 1, cvScalar(h));

        bin_val = cvQueryHistValue_1D(hist_model2, h);
        //h:行数 s_bins:总列数(行长度)s:列数  h*s_bins+s 当前bin对应的sig行数   
        cvSet2D(sig2, h, 0, cvScalar(bin_val));
        cvSet2D(sig2, h, 1, cvScalar(h));

        bin_val = cvQueryHistValue_1D(hist_test, h);
        //h:行数 s_bins:总列数(行长度)s:列数  h*s_bins+s 当前bin对应的sig行数   
        cvSet2D(sig_test, h, 0, cvScalar(bin_val));
        cvSet2D(sig_test, h, 1, cvScalar(h));
    }

    float emd1 = cvCalcEMD2(sig1, sig_test, CV_DIST_L2);
    float emd2 = cvCalcEMD2(sig2, sig_test, CV_DIST_L2);

    printf("EMD距离1:%f; \n", emd1);
    printf("EMD距离2:%f; \n", emd2);

    if ((emd1 <= 1) && (emd2 >= 4))
        {
            cvDrawRect(hist_img, cvPoint(100, 100), cvPoint(200, 200), cvScalar(255, 0, 0), CV_FILLED, 8);
        }
    if ((emd1 >= 1.7) && (emd2 <= 3.4))
        {
            cvDrawRect(hist_img, cvPoint(100, 100), cvPoint(200, 200), cvScalar(0, 0, 255), CV_FILLED, 8);
        }

    printf("\n");
    cout << endl;

    ////比较直方图
    //for (int j = 0; j < 4; j++)
    //{
    //  double value1 = cvCompareHist(hist_test, hist_model1, j);           //相关方式比较
    //  double value2 = cvCompareHist(hist_test, hist_model2, j);           //相关方式比较
    //  //if (j == 0)
    //  //{
    //  //  std::printf("   Hist_test & Hist_model1 ,CV_COMP_CORREL: %lf;\n", value1);
    //  //  std::printf("   Hist_test & Hist_model2 ,CV_COMP_CORREL: %lf;\n", value2);
    //  //}
    //  if (j == 1)
    //  {
    //      std::printf("   Hist_test & Hist_model1 ,CV_COMP_CHISQR: %lf;\n", value1);
    //      std::printf("   Hist_test & Hist_model2 ,CV_COMP_CHISQR: %lf;\n", value2);
    //      if ((value1 <= 0.25) && (value2 >= 0.55))
    //      {
    //          cvDrawRect(hist_img, cvPoint(100, 100), cvPoint(200, 200), cvScalar(255, 0, 0), CV_FILLED, 8);
    //      }
    //      if ((value1 >= 0.45) && (value2 <= 0.4))
    //      {
    //          cvDrawRect(hist_img, cvPoint(100, 100), cvPoint(200, 200), cvScalar(0, 0, 255), CV_FILLED, 8);
    //      }
    //  }
    //  //if (j == 2)
    //  //{
    //  //  std::printf("   Hist_test & Hist_model1 ,CV_COMP_INTERSECT: %lf;\n", value1);
    //  //  std::printf("   Hist_test & Hist_model2 ,CV_COMP_INTERSECT: %lf;\n", value2);
    //  //}
    //  //if (j == 3)
    //  //{
    //  //  std::printf("   Hist_test & Hist_model1 ,CV_CCOMP_BHATTACHARYYA: %lf;\n", value1);
    //  //  std::printf("   Hist_test & Hist_model2 ,CV_CCOMP_BHATTACHARYYA: %lf;\n", value2);
    //  //}
    //  std::printf("\n");
    //}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第22张图片
第七章 学习OpenCV——直方图与匹配_第23张图片
第七章 学习OpenCV——直方图与匹配_第24张图片

例7-15 模板匹配寻找手掌区域

寻找手掌区域,利用模板匹配策略,通过cvMatchTemplate()函数找出手掌区域,本例中,结果一在main()函数中调用了Find_Hand_Region()函数去除了不相关区域,结果二未调用,具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

CvPoint Current_Point;              //全局变量才可通过普通成员引用变更其值

bool find_point(IplImage *img, char val);
void Create_Imask(IplImage *src, IplImage *dst);
void Create_RIO(IplImage *dst, IplImage *match_temp);
void Find_Hand_Region(IplImage *model, IplImage *test, IplImage *mask, IplImage *dst);

int main(int argc, char* argv[])
{
    IplImage *src1, *src2, *Imask;      //肤色模板 视频流 手掌掩码
    IplImage *dst, *match_temp;         //肤色区域图像 模板匹配图像                 
    CvCapture* capture;

    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template60_Hand_Track _Match_Template\\Debug\\hand.jpg")))
        return -1;      //肤色模板
    if (!(match_temp = cvLoadImage("D:\\Template\\OpenCV\\Template60_Hand_Track _Match_Template\\Debug\\match_temp.jpg")))
        return -2;      //匹配模板图像

    if (argc == 1)   //此处代码是做一个判断,有摄像头设备则读入摄像头设备的图像信息,没有则播放本地视频文件
        capture = cvCreateCameraCapture(0);
    else
        return -3;  //没有摄像头

    src2 = cvQueryFrame(capture);                                           //获取摄像头图像帧
    Imask = cvCreateImage(cvGetSize(src1), src1->depth, 1);                 //手掌掩码图像
    dst = cvCreateImage(cvGetSize(src2), IPL_DEPTH_8U, 1);                  //处理后的反射投影      

    int result_width = src2->width - match_temp->width + 1;
    int result_height = src2->height - match_temp->height + 1;
    CvSize result_size = cvSize(result_width, result_height);
    IplImage *result = cvCreateImage(result_size, IPL_DEPTH_32F, 1);

    Create_Imask(src1, Imask);                              //创建肤色掩码图像

    cvNamedWindow("Match_Template", 1);
    cvNamedWindow("BACK_Projection", 1);
    cvNamedWindow("Destination", 1);
    cvNamedWindow("Hand", 1);
    cvNamedWindow("SQDIFF_NORMED", 1); 

    cvShowImage("Match_Template", match_temp);

    while (1)
    {
        src2 = cvQueryFrame(capture);

        //Find_Hand_Region(src1, src2, Imask, dst);         //寻找肤色区域

        cvMatchTemplate(src2, match_temp, result, 1);       //模板匹配
        //cvNormalize(result, result, 1, 0, CV_MINMAX);     //元素规范化 平移缩放返回值[0,1]

        if (!src2)
            break;
        cvShowImage("SQDIFF_NORMED", result);
        char c = cvWaitKey(32);
        if (c == 27) 
            break;
    }

    cvWaitKey();

    cvReleaseCapture(&capture);

    cvReleaseImage(&src1);
    cvReleaseImage(&Imask);
    cvReleaseImage(&dst);
    cvReleaseImage(&match_temp);
    cvReleaseImage(&result);

    cvDestroyAllWindows();
}

/******************遍历图像-指针算法********************/
bool find_point(IplImage *img, char val)
{
    char* ptr = NULL;

    if (img->nChannels == 1)
    {
        ptr = img->imageData;
        if (ptr != NULL)
        {
            for (int i = 0; i < img->height; i++)       //矩阵指针行寻址
            {
                ptr = (img->imageData + i*(img->widthStep));   //i 行 j 列

                for (int j = 0; j < img->width; j++)    //矩阵指针列寻址
                {
                    if (ptr[j] == val)                  //判断某点像素是否为255
                    {
                        Current_Point.x = j;            
                        Current_Point.y = i;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

void Create_Imask(IplImage *src, IplImage *dst)
{
    int Last_Area = 0;                          //上一个区域面积       
    int Current_Area = 0;                       //当前区域面积
    int threshold_type = CV_THRESH_BINARY;      //阈值类型
    CvPoint Last_Point;                         //值为255点的上一点
    CvConnectedComp comp;                       //被填充区域统计属性
    IplImage *gray, *threshold, *temp,*open;    //灰度图像

    Last_Point = cvPoint(0, 0);         //初始化上一点
    Current_Point = cvPoint(0, 0);      //初始化当前点

    gray = cvCreateImage(cvGetSize(src), src->depth, 1);
    threshold = cvCreateImage(cvGetSize(src), src->depth, 1);
    temp = cvCreateImage(cvGetSize(src), src->depth, 1);
    open = cvCreateImage(cvGetSize(src), src->depth, 1);

    cvCvtColor(src, gray, CV_BGR2GRAY); //源图像->灰度图像
    //二值阈值化
    cvThreshold(gray, threshold, 100, 255, threshold_type);     
    //开运算,去除小亮区域,其他联结 NULL:3*3参考点为中心的核
    cvMorphologyEx(threshold, open, temp, NULL, CV_MOP_OPEN, 1);

    cvNamedWindow("肤色模板", 1);
    cvNamedWindow("肤色掩码", 1);
    cvShowImage("肤色模板", src);
    cvShowImage("肤色掩码", dst);

    //漫水填充 获得手掌掩码
    cvCopy(open, dst);               //复制生成手掌掩码
    do
    {
        if (find_point(dst, 255))    //找像素值为255的像素点
        {

            cout << " X: " << Current_Point.x << " Y: " << Current_Point.y << endl;

            cvFloodFill(dst, Current_Point, cvScalar(100), cvScalar(0), cvScalar(0),
                &comp, 8 | CV_FLOODFILL_FIXED_RANGE);       //对值为255的点进行漫水填充,值100
            Current_Area = comp.area;                       //当前区域面积

            if (Last_Area//当前区域大于上一区域,上一区域清0
            {
                if (Last_Area>0)
                    cvFloodFill(dst, Last_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //上一区域赋值0
                cvShowImage("肤色掩码", dst);
                cvWaitKey(500);

                Last_Area = Current_Area;                               //当前区域赋值给上一区域
                Last_Point = Current_Point;                             //当前点赋值给上一点
            }
            else                                            //当前区域小于等于上一区域,当前区域清0
            {
                if (Current_Area>0)
                    cvFloodFill(dst, Current_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //当前区域赋值0
                cvShowImage("肤色掩码", dst);
                cvWaitKey(500);
            }
        }
        else                                                //最后剩余的最大区域赋值255
        {
            cvFloodFill(dst, Last_Point, cvScalar(255), cvScalar(0), cvScalar(0), &comp, 8 | CV_FLOODFILL_FIXED_RANGE);
            cvShowImage("肤色掩码", dst);
            cvWaitKey(500);
            //上一区域赋值0
            break;
        }
    } while (true);

    //cvSaveImage("Imask.jpg", dst);

    cvReleaseImage(&gray);
    cvReleaseImage(&threshold);
    cvReleaseImage(&temp);
    cvReleaseImage(&open);
}

void Find_Hand_Region(IplImage *model, IplImage *test, IplImage *mask, IplImage *dst)
{

    int threshold_type = CV_THRESH_BINARY;      //阈值类型

    //临时图像  反向投影图像
    IplImage *temp = cvCreateImage(cvSize(model->width, model->height), IPL_DEPTH_8U, 1);
    IplImage *back_projection = cvCreateImage(cvSize(test->width, test->height), IPL_DEPTH_8U, 1);
    //RGB
    IplImage *r_plane_1 = cvCreateImage(cvSize(model->width, model->height), IPL_DEPTH_8U, 1);
    IplImage *g_plane_1 = cvCreateImage(cvSize(model->width, model->height), IPL_DEPTH_8U, 1);
    IplImage *b_plane_1 = cvCreateImage(cvSize(model->width, model->height), IPL_DEPTH_8U, 1);

    IplImage *r_plane_2 = cvCreateImage(cvSize(test->width, test->height), IPL_DEPTH_8U, 1);
    IplImage *g_plane_2 = cvCreateImage(cvSize(test->width, test->height), IPL_DEPTH_8U, 1);
    IplImage *b_plane_2 = cvCreateImage(cvSize(test->width, test->height), IPL_DEPTH_8U, 1);

    IplImage *planes1[] = { r_plane_1, g_plane_1, b_plane_1 };          //色相饱和度数组
    IplImage *planes2[] = { r_plane_2, g_plane_2, b_plane_2 };          //色相饱和度数组

    cvCvtPixToPlane(model, b_plane_1, g_plane_1, r_plane_1, NULL);      //图像分割
    cvCvtPixToPlane(test, b_plane_2, g_plane_2, r_plane_2, NULL);       //图像分割

    int r_bins = 32, g_bins = 32, b_bins = 32;

    //建立直方图
    CvHistogram *hist_model, *hist_test;

    int hist_size[] = { r_bins, g_bins, b_bins };       //对应维数包含bins个数的数组
    float r_ranges[] = { 0, 255 };                      //R通道划分范围 
    float g_ranges[] = { 0, 255 };                      //G通道划分范围
    float b_ranges[] = { 0, 255 };                      //R通道划分范围 
    float* ranges[] = { r_ranges, g_ranges, b_ranges }; //划分范围数对, ****均匀bin,range只要最大最小边界

    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist_model = cvCreateHist(3, hist_size, CV_HIST_ARRAY, ranges, 1);
    hist_test = cvCreateHist(3, hist_size, CV_HIST_ARRAY, ranges, 1);

    cvCalcHist(planes1, hist_model, 0, mask);   //计算直方图(图像,直方图结构,不累加,掩码)
    cvCalcHist(planes2, hist_test, 0, 0);       //计算直方图(图像,直方图结构,不累加,掩码)

    //cvNormalizeHist(hist_model, 1.0);         //直方图归一化
    //cvNormalizeHist(hist_test, 1.0);          //直方图归一化

    //像素点的反射投影 创建测试hist的图像数组 结果图像 模板hist
    cvCalcBackProject(planes2, back_projection, hist_model);

    cvSmooth(back_projection, dst, CV_MEDIAN, 11);  //中值滤波 去除椒盐噪声

    //开运算,去除小亮区域,其他联结 NULL:3*3参考点为中心的核
    cvMorphologyEx(dst, dst, temp, NULL, CV_MOP_OPEN, 1);

    cvThreshold(dst, dst, 0, 255, threshold_type);  //二值阈值化

    //边缘检测   src dst 边缘连接 边缘初始分割 核
    //cvCanny(dst, dst,90,180,3);
    //得到手掌轮廓 绘制轮廓线
    //getContoursByC(dst, dst);             

    cvShowImage("BACK_Projection", back_projection);
    cvShowImage("Destination", dst);

    cvXorS(dst, cvScalar(255), dst);        //掩码图像按位异或,求反生成新的掩码处理模板
    cvSet(test, cvScalarAll(0), dst);
    cvShowImage("Hand", test);
    //cvSaveImage("DST.jpg", dst);

    cvReleaseHist(&hist_model);
    cvReleaseHist(&hist_test);

    cvReleaseImage(&back_projection);
    cvReleaseImage(&temp);
    cvReleaseImage(&r_plane_1);
    cvReleaseImage(&g_plane_1);
    cvReleaseImage(&b_plane_1);
    cvReleaseImage(&r_plane_2);
    cvReleaseImage(&g_plane_2);
    cvReleaseImage(&b_plane_2);
}

运行结果如下图:
第七章 学习OpenCV——直方图与匹配_第25张图片
第七章 学习OpenCV——直方图与匹配_第26张图片

你可能感兴趣的:(OpenCV)