本章中我们学习一下通过backproject直方图,得到一副图像中每个像素属于该直方图的概率。在下边原始图中(左图),我们框选了一块四边形的区域,计算该区域的灰度直方图,然后通过下面的函数calcBackProject,计算图像src中每个像素在直方图中的概率,最终的结果在result中,result中每个像素表示该像素在直方图中的概率,我们对得到的结果进行二值化,就得到下边右图的结果。
我们框选了一块白云区域,但从背投影结果中,海浪的边缘在直方图中的概率也很高,这是因为它们的灰度比较相似,如果我们只想白云的位置,最好使用三通道的直方图,然后背投影。
cv::calcBackProject(&src, 1, // 一副图像 channels, // 使用的channel hist, // backprojecting的直方图 result, // 结果图像 ranges, //像素值范围 255.0 //缩放因子 );
完整的代码:
int main( int argc, char** argv ) { Mat src, dst; /// 以单通道方式打开图像 src = cv::imread("../waves.jpg", 0); if( !src.data ) { return -1; } //选择一个ROI区域 cv::Mat imageROI; imageROI = src(cv::Rect(360,55,40,50)); int histSize[1]; //bins的数目,对灰度图像通常是256 float hranges[2];//最大和最小的像素值 const float* ranges[1];//指向hranges int channels[1]; //在本程序中,只用了一个channel bool uniform = true; bool accumulate = false; histSize[0]= 256; hranges[0]= 0.0; //最小像素值 hranges[1]= 255.0; //最大像素值 ranges[0]= hranges; channels[0]= 0; // 缺省状态下,我们取channel0 //结果直方图 Mat hist; Mat result; /// 计算直方图 //第二个参数1表示只对一副图像进行直方图处理 //第三个参数表示只处理channel 0,对多个channel的图像,可以选1,2等等。 //第四个参数Mat(),表示不使用掩码 //hist是直方图结果 //第六个参数1表示是1维直方图 //第七个参数,直方图bin的数目 //第八个参数是像素取值范围,第九个参数是各维取值范围相同,第十个参数是是否累加,如果处理多个图像,需要这个参数。 calcHist( &imageROI, 1, 0, Mat(), hist, 1, histSize, ranges, uniform, accumulate ); //直方图结果归一化 cv::normalize(hist,hist,1.0); //计算back cv::calcBackProject(&src, 1, // 一副图像 channels, // 使用的channel hist, // backprojecting的直方图 result, // 结果图像 ranges, //像素值范围 255.0 //缩放因子 ); // 二值化图像,看是否能分开前景和背景 cv::threshold(result,result,20,255,cv::THRESH_BINARY); /// 显示直方图 namedWindow("result", CV_WINDOW_AUTOSIZE ); imshow("result", result ); // 显示原图像 cv::rectangle(src, cv::Rect(360,55,40,50), cv::Scalar(0,0,255)); namedWindow("image", CV_WINDOW_AUTOSIZE ); imshow("image", src); while(1) waitKey(0); return 0; }
程序代码:工程FirstOpenCV20
下面的程序中,使用BGR三通道直方图,注意我们框选的范围包括蓝天,也包括白云,之所以包括蓝天白云,是因为这样可以剔除海水边缘白色,我们还对原始图像进行了减色处理。
程序代码:工程FirstOpenCV21