1.简介
如果一幅图像的区域中显示的是一种独特的纹理或是一个独特的物体,那么这个区域的直方图就可以看作是一个概率函数,它给出的是某个像素属于该纹理或物体的概率。我们可以采用特定区域的直方图来检测这个物体。
2.实现方法
在某一幅确定的图像中,我们定义一个感兴趣区域ROI,那么这块区域的直方图就可以看作一个概率函数,其给出的是每个像素属于该ROI的概率。
依照这种特性,我们可以利用直方图来检测特定的内容。由于灰度直方图将太多的信息忽略,极易造成匹配中所需要的关键信息丢失,因此我们采用带有颜色信息的直方图进行匹配。
我们的整体思路是:
在选定图片中选取一块你感兴趣的区域;得到这块区域的彩色(BGR)直方图;直方图归一化;用反投影方法计算出和这块区域类似的区域。
首先,我们先使用Rect方法选定一块感兴趣的区域。例如,在这张海滩上,我们选择左上角的蓝天作为我们感兴趣的区域。
由于我们需要使用反投影法确定蓝天的位置,我们先设置一个有关反投影过程的类 ContentFinder ,我们首先声明类的属性并进行初始化。
float hranges[2];
const float *ranges[3];
float threshold;
int channels[3];
cv::MatND histogram;
这些定义与之前的定义极为类似,hranges 为图像的色度的最大值和最小值的数组;ranges 代表三通道(BGR);channels 代表三个通道;histogram 代表带有颜色信息的直方图。
为了反投影直方图,我们需要指定图像,通道的范围,以及所需要使用的通道的列表。这就是函数 calcBackProject
void calcBackProject( const Mat* images, int nimages,
const int* channels, InputArray hist,
OutputArray backProject, const float** ranges,
double scale = 1, bool uniform = true );
其中第一个数据为输入的图像,第二个则为图像的数目,第三个则为图像的通道数目,BGR图像则为三通道;第四个则为直方图,函数支持灰度与彩色直方图。
这个函数输出量是全图中所有区域与感兴趣区域相符合的概率,我们使用 threshold 函数将概率高于一定值的区域高亮显示出来。
if (threshold > 0.0)
{
cv::threshold(result, result, 255 * threshold, 255, cv::THRESH_BINARY);
}
在这之后,我们确定感兴趣区域,就可以得到全图中和感兴趣区域相匹配的区域。我们选定云作为我们的感兴趣区域。
#include "stdafx.h"
#include <opencv2\opencv.hpp>
#include "Histogram1D.h"
#include "ColorHistogram.h"
#include "ContentFinder.h"
int main()
{
cv::Mat image = cv::imread("..\\waves.jpg");
cv::Mat image2 = cv::imread("..\\trump3.jpg");
cv::Mat imageROI;
imageROI = image(cv::Rect(0, 0, 165, 75));
cv::Mat showing;
int channels[3] = { 0, 1, 2 };
if (!image.data)
{
printf("读取失败");
exit(0);
}
ColorHistogram h;
cv::MatND hist = h.getHistogram(imageROI);
ContentFinder finder;
finder.setHistogram(hist);
finder.setThreshold(0.05);
showing = finder.find(image, 0, 255, channels, 3);
cv::imshow("ini", image);
cv::imshow("ROI", imageROI);
cv::imshow("show", showing);
cv::waitKey(0);
return 0;
}
#include <opencv2\opencv.hpp>
#pragma once
class ContentFinder
{
private:
float hranges[2];
const float *ranges[3];
float threshold;
int channels[3];
cv::MatND histogram;
public:
ContentFinder()
{
threshold = -1.0;
ranges[0] = hranges;
ranges[1] = hranges;
ranges[2] = hranges;
}
void setThreshold(float t);
float getThreshold();
void setHistogram(const cv::MatND &h);
cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels, int dim);
~ContentFinder();
};
#include "stdafx.h"
#include "ContentFinder.h"
void ContentFinder::setThreshold(float t)
{
threshold = t;
}
float ContentFinder::getThreshold()
{
return threshold;
}
void ContentFinder::setHistogram(const cv::MatND &h)
{
histogram = h;
cv::normalize(histogram, histogram, 1.0);
}
cv::Mat ContentFinder::find(const cv::Mat& image, float minValue, float maxValue, int *channels, int dim)
{
cv::Mat result;
hranges[0] = minValue;
hranges[1] = maxValue;
for (int i = 0; i < dim; i++)
{
this->channels[i] = channels[i];
}
cv::calcBackProject(&image,
1,
channels,
histogram,
result,
ranges,
255.0
);
if (threshold > 0.0)
{
cv::threshold(result, result, 255 * threshold, 255, cv::THRESH_BINARY);
}
return result;
}
ContentFinder::~ContentFinder()
{
}
#include <opencv2\opencv.hpp>
#pragma once
class ColorHistogram
{
private:
int histSize[3];
float hranges[2];
const float *ranges[3];
int channels[3];
public:
ColorHistogram()
{
histSize[0] = histSize[1] = histSize[2] = 256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
ranges[1] = hranges;
ranges[2] = hranges;
channels[0] = 0;
channels[1] = 1;
channels[2] = 2;
}
~ColorHistogram();
cv::MatND getHistogram(const cv::Mat &image);
cv::Mat getHistogramImage(const cv::Mat &image, int normal_type = 0);
};
#include "stdafx.h"
#include "ColorHistogram.h"
cv::MatND ColorHistogram::getHistogram(const cv::Mat &image)
{
cv::MatND hist;
cv::calcHist(&image, 1, channels, cv::Mat(), hist, 3, histSize, ranges);
//std::cout << hist.at<float>[1][1];
std::cout << hist.size;
return hist;
}
/*cv::Mat ColorHistogram::getHistogramImage(const cv::Mat &image, int normal_type)
{
cv::MatND hist = getHistogram(image);
}*/
ColorHistogram::~ColorHistogram()
{
}