【练习7.6】漫水填充获取掩码并以此计算肤色直方图、用以查找肤色区域即颜色识别

 

提纲
题目要求
程序代码
结果图片
要言妙道
借鉴参考

 

 

 

  

 

题目要求:

 建立感兴趣的肤色区域检测器

a、在“室内条件下”,利用手来建立肤色直方图(原题是建立RGB直方图,我使用的是HSV直方图)

b、加载另一幅包含手的图像,利用函数cvClacBackProject找到肤色区域 

 

程序代码:

 

  1 // OpenCVExerciseTesting.cpp : 定义控制台应用程序的入口点。
  2 //
  3 //D:\\Work\\Work_Programming\\Source\\Image\\lena.jpg
  4 
  5 #include "stdafx.h"
  6 #include <cv.h>
  7 #include <highgui.h>
  8 #include <iostream>
  9 
 10 #include <opencv2/legacy/legacy.hpp>
 11 //#pragma comment(lib, "opencv_legacy2411.lib")
 12 
 13 using namespace cv;
 14 using namespace std;
 15 
 16 //全局变量-->--->-->--->-->--->-->--->/:
 17 
 18 const char * soutceFile_InDoor = "D:\\Work\\Work_Programming\\Source\\Image\\OpenCVExerciseImage\\第7章\\hand_sample3.jpg";
 19 IplImage *image_Source = cvLoadImage(soutceFile_InDoor, CV_LOAD_IMAGE_UNCHANGED);
 20 const char * soutceFile_Dst = "D:\\Work\\Work_Programming\\Source\\Image\\OpenCVExerciseImage\\第7章\\hand_sample2.jpg";
 21 IplImage *image_Dst = cvLoadImage(soutceFile_Dst, CV_LOAD_IMAGE_UNCHANGED);
 22 
 23 IplImage * image_mask = cvCreateImage(cvSize(image_Source->width+2,image_Source->height+2), IPL_DEPTH_8U, 1);//②注意mask图像的大小
 24 const char * mask_windowName = "掩码图像窗口";
 25 
 26 //<--<--<--<--<--<--<--<--<--全局变量/。
 27 
 28 
 29 //函数声明-->--->-->--->-->--->-->--->//
 30 
 31 void DrawHistogram(IplImage ** image_hist, const CvHistogram * histogram, int scaleValue);
 32 
 33 void onTrackbarSlide_low(int pos);
 34 void onTrackbarSlide_up(int pos);
 35 void GetMaskImage(int pos, bool isUpValue);
 36 CvHistogram * CalcHSHistogramByMask(const IplImage* image_RGB, IplImage *mask);
 37 void CalcHSBackProject(const IplImage* image_RGB, const CvHistogram *HSHist);
 38 
 39 //<--<--<--<--<--<--<--<--<--函数声明//
 40 
 41 int _tmain(int argc, _TCHAR* argv[])
 42 {
 43     const char * src_windowName = "原始图像窗口";
 44     cvNamedWindow(src_windowName, CV_WINDOW_AUTOSIZE);
 45 
 46     int slider_position_low = 5;
 47     int slider_position_up = 30;
 48     cvCreateTrackbar("loDiff", src_windowName, &slider_position_low, 255, onTrackbarSlide_low);
 49     cvCreateTrackbar("upDiff", src_windowName, &slider_position_up, 255, onTrackbarSlide_up);
 50 
 51     cvShowImage(src_windowName, image_Source);
 52     //接下来在TrackBar的回调函数中完成后续的一系列操作
 53 
 54     cvWaitKey();
 55     cvReleaseImage(&image_Source);
 56     cvReleaseImage(&image_Dst);
 57     cvReleaseImage(&image_mask);
 58 
 59     cvDestroyAllWindows();
 60 
 61     return 0;
 62 }
 63 
 64 
 65 //目前只实现绘制二维直方图
 66 void DrawHistogram(/*IplImage ** image_hist,*/ const CvHistogram * histogram, int scaleValue)
 67 {
 68     const char * draw_histWindow = "DrawHist";
 69     cvNamedWindow(draw_histWindow, CV_WINDOW_AUTOSIZE);
 70     //直方图:横坐标表示各个bin,纵坐标表示各个bin归一化后的值
 71     int hist_dims = histogram->mat.dims;
 72 
 73     int bin_size1, bin_size2/*, bin_size3*/;
 74 
 75     if (hist_dims == 2)
 76     {
 77         bin_size1 = histogram->mat.dim[0].size;
 78         bin_size2 = histogram->mat.dim[1].size;
 79         //bin_size3 = histogram->mat.dim[2].size;
 80     }
 81     else
 82     {
 83         return;
 84     }
 85 
 86     int bin_count = bin_size1*bin_size2/**bin_size3*/;
 87     float max_temp;
 88     cvGetMinMaxHistValue(histogram, NULL, &max_temp);
 89     int max_value = (int)(max_temp*scaleValue) + 1;
 90     CvSize hist_imageSize = cvSize(bin_count, max_value);
 91     IplImage*image_hist = cvCreateImage(hist_imageSize, IPL_DEPTH_8U, 1);
 92     (image_hist)->origin = 1;
 93     cvZero(image_hist);
 94 
 95     int x;
 96     int value;
 97 
 98     for (int r = 0; r < bin_size1; ++r)
 99     {
100         for (int g = 0; g < bin_size2; ++g)
101         {
102             //for (int b = 0; b < bin_size3; ++b)
103             {
104                 //x = r*(bin_size1*bin_size2) + g*bin_size2 + b;
105                 x = r* bin_size1 + g;
106                 value = (int)(cvQueryHistValue_2D(histogram, r, g)*scaleValue);
107 
108                 //value = (int)(cvQueryHistValue_3D(histogram, r, g, b)*scaleValue);
109                 /*        if (value == 0)
110                 {
111                 value = 10;
112                 }*/
113                 cvRectangle(image_hist, cvPoint(x, 0), cvPoint(x, value), cvScalar(255));
114             }
115         }
116     }
117     cvShowImage(draw_histWindow, image_hist);
118 }
119 
120 void onTrackbarSlide_low(int pos)
121 {
122     GetMaskImage(pos, false);
123 }
124 
125 void onTrackbarSlide_up(int pos)
126 {
127     GetMaskImage(pos, true);
128 }
129 
130 void GetMaskImage(int pos ,bool isUpValue)
131 {
132     static int lowValue = 0;
133     static int upValue = 0;
134     CvPoint seedPoint = cvPoint(image_Source->width / 3 * 2, image_Source->height / 3 * 2);
135 
136     if (isUpValue == true)
137     {
138         upValue = pos;
139     }
140     else
141     {
142         lowValue = pos;
143     }
144 
145     if (lowValue > upValue)
146     {
147         lowValue ^= upValue ^= lowValue ^= upValue;//交换两个变量的值
148     }
149 
150     int flags = 8
151         | CV_FLOODFILL_MASK_ONLY
152         | CV_FLOODFILL_FIXED_RANGE
153         | (255 << 8);
154 
155     cvZero(image_mask);//①重要
156     cvNamedWindow(mask_windowName, CV_WINDOW_AUTOSIZE);
157 
158     //漫水填充找掩码
159     cvFloodFill(image_Source, seedPoint, cvScalar(255, 255, 255), cvScalar(lowValue, lowValue, lowValue), cvScalar(upValue, upValue, upValue), NULL, flags, image_mask);
160 
161     //使用掩码计算直方图
162     CvRect rect_ROI = cvRect(1, 1, image_mask->width - 2, image_mask->height - 2);
163     cvSetImageROI(image_mask, rect_ROI); //
164     cvShowImage(mask_windowName, image_mask);
165     CvHistogram * hist_ImageSource = CalcHSHistogramByMask(image_Source, image_mask);
166     cvResetImageROI(image_mask);
167 
168     //反向投影
169     CalcHSBackProject(image_Dst, hist_ImageSource);
170 
171     cvNormalizeHist(hist_ImageSource, 1.0);//重要:注意归一化直方图与反向投影的顺序
172 
173     DrawHistogram(hist_ImageSource, 3000);
174 
175     cvReleaseHist(&hist_ImageSource);
176 }
177 
178 CvHistogram * CalcHSHistogramByMask(const IplImage* image_RGB,IplImage *mask)
179 {
180     IplImage* image_HSV = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 3);
181     cvCvtColor(image_RGB, image_HSV, CV_BGR2HSV);
182 
183     IplImage* h_plane = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 1);
184     IplImage* s_plane = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 1);
185     IplImage* v_plane = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 1);
186     IplImage* planes[] = { h_plane, s_plane };
187     cvCvtPixToPlane(image_HSV, h_plane, s_plane, v_plane, 0);
188 
189     int dims = 2;
190     int h_bins = 30, s_bins = 32;
191     CvHistogram* hist;
192     int hist_size[] = { h_bins, s_bins };
193     float h_ranges[] = { 0, 180 }; // hue is [0,180]
194     float s_ranges[] = { 0, 255 };
195     float* ranges[] = { h_ranges, s_ranges };
196 
197     hist = cvCreateHist(
198         dims,
199         hist_size,
200         CV_HIST_ARRAY,
201         ranges,
202         1
203         );
204     cvCalcHist(planes, hist, 0, mask);
205 
206     cvReleaseImage(&h_plane);
207     cvReleaseImage(&s_plane);
208     cvReleaseImage(&v_plane);
209     cvReleaseImage(&image_HSV);
210 
211     return hist;
212 }
213 
214 void CalcHSBackProject(const IplImage* image_RGB, const CvHistogram *HSHist)
215 {
216     IplImage* image_HSV = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 3);
217     cvCvtColor(image_RGB, image_HSV, CV_BGR2HSV);
218 
219     IplImage* h_plane = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 1);
220     IplImage* s_plane = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 1);
221     IplImage* v_plane = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 1);
222     IplImage* planes[] = { h_plane, s_plane };
223     cvCvtPixToPlane(image_HSV, h_plane, s_plane, v_plane, 0);
224     IplImage *  image_backProject = cvCreateImage(cvGetSize(image_RGB), IPL_DEPTH_8U, 1);
225     cvZero(image_backProject);
226 
227     cvCalcBackProject(planes, image_backProject, HSHist);
228     cvNamedWindow("反向投影", CV_WINDOW_AUTOSIZE);
229     cvShowImage("反向投影", image_backProject);
230 
231     cvReleaseImage(&h_plane);
232     cvReleaseImage(&s_plane);
233     cvReleaseImage(&v_plane);
234     cvReleaseImage(&image_HSV);
235 }

 

 

结果图片:

 

要言妙道:

  

①可以通过掩码操作来抓取手掌所在区域的直方图,也可以使用类似Cognex拖拽矩形的方式选取兴趣区域,获得目标颜色直方图,利用本章内容,实现颜色识别

②注意:cvFloodFill不会覆盖mask的非零像素点,因此,如果不希望mask阻碍填充操作时,将其中元素设为0,即代码中的 cvZero(image_mask);//①重要 ,如果没有这行,会发现掩码图像是一张黑白相间的竖线组成的图片

③注意:cvFloodFill填充时,mask图像必须是一个单通道、8位、像素宽度和高度均比源图像大两个像素的图像。

④但对于cvCalcHist的掩码,又要求掩码图像必须与用于计算的各plane图像相同大小,所以,要对cvFloodFill得到的掩码图像设置兴趣区域,当然,如果mask是矩阵,可以利用OpenCV的Range函数直接得到

⑤注意169行和171行计算方向投影和归一化直方图的先后顺序,如果先归一化直方图,反向投影得不到理想图像,尽管不算错,《学习OpenCV》针对此有如下描述:如果直方图是归一化的吗,此值便与一个条件概率值相关(即图像中像素点为直方图hist所表征的某种成员的概率) 

前几道题“肤色直方图”的计算不准确,应该用本题这种方式 ,在前面各篇中著注明

 

借鉴参考:

 反向投影_OpenCV中文论坛教程

 cvInRangeS参考

 

posted on 2015-05-10 22:21  毋忆典藏  阅读(1131)  评论(0编辑  收藏  举报