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

第七章 学习OpenCV(10)

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

运行结果如下图:

手势图像

梯度方向直方图

envi直方图匹配_匹配滤波原理_直方图匹配原理

梯度方向直方图匹配结果

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

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

具体代码如下:

#include <cv.h>
#include <highgui.h>  
#include <stdlib.h>  
#include <stdio.h> 
#include <math.h>
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)<whRatio)
        {
            //删除宽高比例小于设定值的轮廓  
            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<Current_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; i<bins; i++)
    {
        double 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);
}