您现在的位置:首页 > 教案格式 > 正文

第七章 学习OpenCV(8)

2019-07-04 21:05 网络整理 教案网

运行结果如下图:

肤色图像

匹配结果

本例进行的工作如下:

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

具体代码如下:

#include <cv.h>
#include <highgui.h>  
#include <stdlib.h>  
#include <stdio.h> 
#include <math.h>
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<Current_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: " <<j<< " i: " << i << endl;
                        //cout << " X: " << Current_Point.x << " Y: " << Current_Point.y << endl;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}