【练习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中文论坛教程
‖==========钟于原创 乐于分享 宁静致远 毋忆典藏==========‖