本章中我们学习一下通过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