OpenCV 学习笔记(直方图反向投影 BackProject)
Posted liyuanbhu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV 学习笔记(直方图反向投影 BackProject)相关的知识,希望对你有一定的参考价值。
OpenCV 学习笔记(直方图反向投影 BackProject)
上个笔记简单讲了讲如何计算直方图。这个笔记就来讲讲直方图的一种简单应用。直方图反向投影,是一种从图像中提取某个特定区域的方法。直方图可以理解为图像中各种颜色的分布情况,如果归一化了,就可以认为是一种概率分布。
如果我们知道某个物体或者某个特征的直方图,也就是知道了这个物体各种颜色的分布概率。根据这个概率,我们可以根据图像中每一个点的颜色,都给一个 对应的概率。概率高的地方就更可能是存在这种物体的地方。概率为 0 的地方可以认为不存在这种物体。
在 OpenCV 中有个专门的函数用来做直方图反向投影:
CV_EXPORTS void calcBackProject( const Mat* images, int nimages,
const int* channels, InputArray hist,
OutputArray backProject, const float** ranges,
double scale = 1, bool uniform = true );
/** @overload */
CV_EXPORTS void calcBackProject( const Mat* images, int nimages,
const int* channels, const SparseMat& hist,
OutputArray backProject, const float** ranges,
double scale = 1, bool uniform = true );
/** @overload */
CV_EXPORTS_W void calcBackProject( InputArrayOfArrays images, const std::vector<int>& channels,
InputArray hist, OutputArray dst,
const std::vector<float>& ranges,
double scale );
calcBackProject 有这三种函数原型,我们常用到前两种。这三个函数都不太好用,我们还是可以封装一下:
class ContentFinder
{
public:
ContentFinder();
// Sets the threshold on histogram values [0,1]
void setThreshold(float t);
// Gets the threshold
float getThreshold();
// Sets the reference histogram
void setHistogram(const cv::Mat& h);
// Sets the reference histogram
void setHistogram(const cv::SparseMat& h);
// Simplified version in which
// all channels used, with range [0,256[
cv::Mat find(const cv::Mat& image);
// Finds the pixels belonging to the histogram
cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels);
private:
// histogram parameters
float hranges[2];
const float * ranges[3];
int channels[3];
float threshold; // decision threshold
cv::Mat histogram; // histogram can be sparse
cv::SparseMat shistogram; // or not
bool isSparse;
};
ContentFinder::ContentFinder() : threshold(0.1f), isSparse(false)
{
// in this class,
// all channels have the same range
ranges[0]= hranges;
ranges[1]= hranges;
ranges[2]= hranges;
}
void ContentFinder::setThreshold(float t)
{
threshold = t;
}
// Gets the threshold
float ContentFinder::getThreshold()
{
return threshold;
}
// Sets the reference histogram
void ContentFinder::setHistogram(const cv::Mat& h)
{
isSparse = false;
cv::normalize(h,histogram,1.0);
}
void ContentFinder::setHistogram(const cv::SparseMat& h)
{
isSparse = true;
cv::normalize(h, shistogram, 1.0, cv::NORM_L2);
}
// Simplified version in which
// all channels used, with range [0,256[
cv::Mat ContentFinder::find(const cv::Mat& image)
{
cv::Mat result;
hranges[0] = 0.0; // default range [0,256[
hranges[1] = 256.0;
channels[0] = 0; // the three channels
channels[1] = 1;
channels[2] = 2;
return find(image, hranges[0], hranges[1], channels);
}
cv::Mat ContentFinder::find(const cv::Mat& image, float minValue, float maxValue, int *channels)
{
cv::Mat result;
hranges[0] = minValue;
hranges[1] = maxValue;
if (isSparse)
{ // call the right function based on histogram type
for (int i = 0; i < shistogram.dims(); i++)
{
this->channels[i] = channels[i];
}
cv::calcBackProject(&image,
1, // we only use one image at a time
channels, // vector specifying what histogram dimensions belong to what image channels
shistogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
}
else
{
for (int i = 0; i < histogram.dims; i++)
{
this->channels[i] = channels[i];
}
cv::calcBackProject(&image,
1, // we only use one image at a time
channels, // vector specifying what histogram dimensions belong to what image channels
histogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
}
// Threshold back projection to obtain a binary image
if (threshold > 0.0)
{
cv::threshold(result, result, 255.0 * threshold, 255.0, cv::THRESH_BINARY);
}
return result;
}
下面是个例子:
cv::Mat image = cv::imread("D:\\\\向日葵.jpg");
//cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);
cv::Mat imageROI = image(cv::Rect(130, 250, 75, 75));
ColorHistogram hist;
hist.setSize(16);
cv::Mat h = hist.getHistogram(imageROI);
cv::normalize(h, h, 1);
cv::imshow("pic", image);
ContentFinder finder;
finder.setHistogram(h);
finder.setThreshold(0.01);
cv::Mat mask = finder.find(image);
cv::Mat img ;
image.copyTo(img, mask);
cv::imshow("img", img);
下面是运行结果,可以看到将原图中几个向日葵所在区域都提取出来了。提取的效果还不错。当然提取之后应该用个形态学的闭运算,把里面的小空洞给填上,结果会更好。这里就不演示了。
以上是关于OpenCV 学习笔记(直方图反向投影 BackProject)的主要内容,如果未能解决你的问题,请参考以下文章