1.使用直方图来统计像素
直方图就是对数据进行统计的一种方法,并且将统计值定义到一系列定义好的bin(组距)中,获得一张数据分布的统计图.
比如,现在有一个一维数组,其值从0-255,我们可以以20为组距,来分别统计数组中0-20的数据的总量,20-40的数据的总量,最后,以这个bin作为横轴,统计值作为y轴,得到一张统计图,这就是数据范围的直方图,再比如,一张灰度图像,值也是0-255,我们也可以这样做,这样也能得到一张灰度的统计图(实际上前面所说的直方图均衡化,第一步就是做的这个工作)
图像直方图,是表示数字图像中亮度的直方图,标识了图像中每个亮度值的像素数量,计算机领域中,常借助于图像的直方图来实现图像的二值化.
总结来说两点
1.直方图正对图像来说,是图像中像素强度分布的图形表达方式
2.统计的是每一个强度值所具有的像素个数.
直方图并不局限于统计像素灰度,他可以统计图像的任何特征,如梯度,方向等,
术语:
1.dims 需统计的特征的数量,仅统计灰度,dims = 1
2.bin 每个特征空间中子区段的数目,也叫直条或者组距.
3.range 每个特征空间的取值范围,如灰度特征空间,取值就是0-255
class Histogram1D{
private:
int histSize[1];//项的数量
float hranges[2];//像素的最大和最小值
const float*ranges[1];
int channels[1];//仅用到一个通道
public:
Histogram1D()
{
//准备1D直方图的参数
histSize[0]=256;
hranges[0]=0.0;
hranges[1]=255.0;
ranges[0]=hranges;
channels[0]=0;//默认情况下我们检查0号通道
}
//计算1D直方图
cv::MatND getHistogram(const cv::Mat &image){
cv::MatND hist;
//计算直方图
cv::calcHist(&image,1,channels,cv::Mat(),hist,1,histSize,ranges);
return hist;
}
//读取图像
cv::Mat image=cv::imread("../groupe.jpg",0)//以黑白模式打开
Histogram1D h;//histogram对象
cv::MatND histo=h.getHistogram(image);//计算直方图
for(int i=0;i<256;i++)//遍历每个histo
cout<<"value"<<i<<"="<<histo.at<float>(i)<<endl;//显示出每个数值
//画直方图
cv::Mat getHistogramImage(const cv::Mat &image){
cv::MatND hist=getHistogram(image);//首先计算直方图
double maxVal;//获取最大值和最小值
double minVal;
cv::minMaxLoc(hist,&minVal,&maxVal,0,0);//minMaxLoc用来获得最大值和最小值,后面两个参数为最小值和最大值的位置,0代表不需要获取
cv::Mat histImg(histSize[0],histSize[0],CV_8U,cv::Scalar(255));//展示直方图的画板:底色为白色
int hpt=static_cast<int>(0.9*histSize[0]);//最高点设为90%的宽度
for(int h=0;h<histSize[0];h++)
{
float binVal=hist.at<float>(h);
int intensity=static_cast<int>(binVal*hpt/maxVal);
cv::line(histImg,cv::Point(h,histSize[0]),cv::Point(h,histSize[0]-intensity),cv::Scalar::all(0));
}
return histImg;
}
cv::namedWindow("Histogram");
cv::imshow("Histogram",h.getHistogramImage(image));
}```
#2.threshold —— opencv阈值操作
##一、阈值操作的作用
阈值操作属于像素级处理。在灰度图像中,每个像素都有一个灰度值,我们可以对灰度值设置阈值,像素与阈值比较,来实现对图像进行灰度较小和较大的噪声滤波处理,或者突出图像与背景的灰度差等等功能。
##二、函数介绍
- 头文件: #include <opencv2/imgproc/imgproc.hpp>
CV_EXPORTS_W double threshold( InputArray src, OutputArray dst,double thresh, double maxval, int type );
- 函数参数介绍:
InputArray src, 源图像
OutputArray dst, 输出图像
double thresh, 门限值
double maxval, 最大值
int type, 函数类型选择:
THRESH_BINARY,THRESH_BINARY_INV,THRESH_TRUNC,THRESH_TOZERO,THRESH_TOZERO_INV
THRESH_BINARY(二进制阈值)
![](http://upload-images.jianshu.io/upload_images/3878531-147e6637334ce164.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
THRESH_BINARY_INV(反二进制阈值)
![](http://upload-images.jianshu.io/upload_images/3878531-8e99b7fb37043407.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
THRESH_TRUNC(截断阈值)
![](http://upload-images.jianshu.io/upload_images/3878531-45db6da52d19d745.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
THRESH_TOZERO(0阈值)
![](http://upload-images.jianshu.io/upload_images/3878531-b61524727c58f521.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
THRESH_TOZERO_INV(反0阈值)
![](http://upload-images.jianshu.io/upload_images/3878531-eee7aa39645273d7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/3878531-2a77926e65fa0264.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#3.反向投影
反向投影是一种首先寻找某一特征的直方图模型,然后根据这个模型去寻找图像中是否存在这个特征的解决方案.
***反向投影储存的亮度值,代表测试图像中该像素属于某个特征的概率,***也就是说,亮度值相同的位置,属于同一个特征的概率越大,亮起的地方概率更大,内部和边缘之间的阴影影响了检测的精度.
反向投影的作用是在输入图像中寻找特定图像中最匹配的点或者区域,也就是定位模版图像在输入图像的位置.
投影的结果以每个输入图像像素为起点的直方图对比结果,可以看作是单通道浮点型图像,或者是一个二维的概率数组集合.
API:void calcBackProject(mat* 输入图像数组指针,int 图像数组个数,int*需要统计的通道索引,inputarray 输入直方图,outputarray 目标反向投影阵列,float** 输入数组的每一维的边界阵列,int 缩放因子,bool 直方图是否均匀).
注:该函数用来计算反向投影
```
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#include "Histogram1D.h"
class ObjectFinder {
private:
floathranges[2];
constfloat* ranges[3];
intchannels[3];
floatthreshold;
cv::MatND histogram;
cv::SparseMat shistogram;
public:
ObjectFinder() : threshold(0.1f)
{
ranges[0]= hranges;
ranges[1]= hranges;
ranges[2]= hranges;
}
// 设置阈值
void setThreshold(float) {
threshold= t;
}
// 返回阈值
float getThreshold() {
return threshold;
}
// 设置目标直方图,进行归一化
void setHistogram(const cv::MatND& h) {
histogram= h;
cv::normalize(histogram,histogram,1.0);
}
// 查找属于目标直方图概率的像素
cv::Mat find(constcv::Mat& image)
{
cv::Mat result;
hranges[0]= 0.0;
hranges[1]= 255.0;
channels[0]= 0;
channels[1]= 1;
channels[2]= 2;
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;
}
};
int main()
{
//读取圆图像
cv::Mat initimage= cv::imread("f:\\img\\skin.jpg");
if(!initimage.data)
return 0;
//显示原图像
cv::namedWindow("原图像");
cv::imshow("原图像",initimage);
//读取灰度图像
cv::Mat image= cv::imread("f:\\img\\skin.jpg",0);
if(!image.data)
return0;
//设置目标区域
cv::Mat imageROI;
imageROI= image(cv::Rect(262,151,113,150));// 区域为小孩的脸部区域
//显示目标区域
cv::namedWindow("目标区域图像");
cv::imshow("目标区域图像",imageROI);
//计算目标区域直方图
Histogram1D h;
cv::MatND hist= h.getHistogram(imageROI);
cv::namedWindow("目标区域直方图");
cv::imshow("目标区域直方图",h.getHistogramImage(imageROI));
//创建检查类
ObjectFinder finder;
//将目标区域直方图传入检测类
finder.setHistogram(hist);
//初始化阈值
finder.setThreshold(-1.0f);
//进行反投影
cv::Mat result1;
result1= finder.find(image);
//创建负图像并显示概率结果
cv::Mat tmp;
result1.convertTo(tmp,CV_8U,-1.0,255.0);
cv::namedWindow("负图像概率结果图像越暗概率越大");
cv::imshow("负图像概率结果图像越暗概率越大",tmp);
//得到二值反投影图像
finder.setThreshold(0.01f);
result1= finder.find(image);
//在图像中绘制选中区域
cv::rectangle(image,cv::Rect(262,151,113,150),cv::Scalar(0,0,0));
//显示原图像
cv::namedWindow("原图像的灰度图");
cv::imshow("原图像的灰度图",image);
//二值结果图
cv::namedWindow("二值结果图");
cv::imshow("二值结果图",result1);
cv::waitKey();
return0;
}
```
![](http://upload-images.jianshu.io/upload_images/3878531-9aa5c7d50d9fb801.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#4.使用均值漂移(Mean Shift)算法查找物体
构造函数:
```TermCriteria**(**inttype**,**intmaxCount**,**doubleepsilon**);**```
参数说明:
type 迭代终止条件类型
type=TermCriteria::MAX_ITER/TermCriteria::COUNT 迭代到最大迭代次数终止
type= TermCriteria::EPS 迭代到阈值终止
type= TermCriteria::MAX_ITER+ TermCriteria::EPS 上述两者都作为迭代终止条件
maxCount 迭代的最大次数
epsilon 阈值(中心位移值)
调用参考:
```cv**::**TermCriteria criteria**(**cv**::**TermCriteria**::**MAX_ITER**,**10**,**0.01**);**```
程序参考:
```
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#include "Histogram1D.h"
#include<iostream>
#include<vector>
#include "ContentFinder.h"
#include "colorhistogram.h"
int main()
{
//读取參考图像
cv::Mat image= cv::imread("f:\\img\\ball.jpg");
if(!image.data)
return0;
//定义查找物体
cv::Mat imageROI= image(cv::Rect(85,200,64,64));
cv::rectangle(image, cv::Rect(85,200,64,64),cv::Scalar(0,0,255));
//显示參考图像
cv::namedWindow("第一张图片,标记篮球位置");
cv::imshow("第一张图片,标记篮球位置",image);
//获得色度直方图
ColorHistogram hc;
cv::MatND colorhist= hc.getHueHistogram(imageROI);
//读入目标图像
image= cv::imread("f:\\img\\ball2.jpg");
//显示目标图像
cv::namedWindow("第二张图片");
cv::imshow("第二张图片",image);
//将RGB图像图像转换为HSV图像
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
//分离图像通道
vector v;
cv::split(hsv,v);
//消除饱和度较低的像素点
intminSat=65;
cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
cv::namedWindow("第二张图片消除饱和度较低的像素点");
cv::imshow("第二张图片消除饱和度较低的像素点",v[1]);
//进行直方图反投影
ContentFinder finder;
finder.setHistogram(colorhist);
finder.setThreshold(0.3f);
intch[1]={0};
cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);
cv::namedWindow("第二张图片进行直方图反投影");
cv::imshow("第二张图片进行直方图反投影",result);
//利用位运算消除低饱和度像素
cv::bitwise_and(result,v[1],result);
cv::namedWindow("第二张图片利用位运算进一步消除低饱和度像素点");
cv::imshow("第二张图片利用位运算进一步消除低饱和度像素点",result);
// 得到反投影直方图概率图像
finder.setThreshold(-1.0f);
result= finder.find(hsv,0.0f,180.0f,ch,1);
cv::bitwise_and(result,v[1],result);
cv::namedWindow("第二张图片处理后的二值图像");
cv::imshow("第二张图片处理后的二值图像",result);
cv::Rect rect(85,200,64,64);
cv::rectangle(image, rect, cv::Scalar(0,0,255));
cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
cout <<"均值漂移迭代次数 = "<< cv::meanShift(result,rect,criteria) << endl;
cv::rectangle(image, rect, cv::Scalar(0,255,0));
//展示结果图
cv::namedWindow("查找结果,红框为第一幅图中篮球位置,绿框为现位置");
cv::imshow("查找结果,红框为第一幅图中篮球位置,绿框为现位置",image);
cv::waitKey();
return0;
}```
![](http://upload-images.jianshu.io/upload_images/3878531-9ee374a90c63b4b2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/3878531-588c462844eb26ce.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#5.通过比较直方图检索相似图片
CompareHist(),是比较两个统计直方图的分布,总共有四个方法,被定义如下:
```
#define **CV**_COMP_CORREL 0
#define **CV**_COMP_CHISQR 1
#define **CV**_COMP_INTERSECT2
#define **CV**_COMP_BHATTACHARYYA3```
程序参考:
```
#include "opencv2/highgui/highgui.hpp"
#include "opencv/cv.hpp"
//画直方图用
int HistogramBins = 256;
float HistogramRange1[2]={0,255};
float *HistogramRange[1]={&HistogramRange1[0]};
/*
* imagefile1:
* imagefile2:
* method: could be CV_COMP_CHISQR, CV_COMP_BHATTACHARYYA, CV_COMP_CORREL, CV_COMP_INTERSECT
*/
int CompareHist(constchar * imagefile1, constchar * imagefile2)
{
IplImage *image1=cvLoadImage(imagefile1, 0);
IplImage *image2=cvLoadImage(imagefile2, 0);
CvHistogram *Histogram1 = cvCreateHist(1, &HistogramBins, CV_HIST_ARRAY,HistogramRange);
CvHistogram *Histogram2 = cvCreateHist(1, &HistogramBins, CV_HIST_ARRAY,HistogramRange);
cvCalcHist(&image1, Histogram1);
cvCalcHist(&image2, Histogram2);
cvNormalizeHist(Histogram1, 1);
cvNormalizeHist(Histogram2, 1);
*// CV_COMP_CHISQR,CV_COMP_BHATTACHARYYA这两种都可以用来做直方图的比较,值越小,说明图形越相似*
printf("CV_COMP_CHISQR : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_CHISQR));
printf("CV_COMP_BHATTACHARYYA : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_BHATTACHARYYA));
*// CV_COMP_CORREL, CV_COMP_INTERSECT这两种直方图的比较,值越大,说明图形越相似*
printf("CV_COMP_CORREL : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_CORREL));
printf("CV_COMP_INTERSECT : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_INTERSECT));
cvReleaseImage(&image1);
cvReleaseImage(&image2);
cvReleaseHist(&Histogram1);
cvReleaseHist(&Histogram2);
return0;
}
int main(int argc, char* argv[])
{
CompareHist(argv[1], argv[2]);
//CompareHist("d:\\camera.jpg", "d:\\camera1.jpg");
system("pause");
return0;
}```
![](http://upload-images.jianshu.io/upload_images/3878531-36c2ccc69e0f6e43.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/3878531-cbe4140da14e6499.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/3878531-1b2eb854cee0dca1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
书上易于理解的代码:
```
class ImageComparator{
private:
cv::Mat reference;
cv::Mat input;
cv::MatND refH;
cv::MatND inputH;
ColorHistogram hist;
int div;
public:
ImageComparator():div(32){
}
//减色因子
//比较的将是减色后的图像
//色彩空间中的每个维度都将按照该变量进行减色
void setColorReduction(int factor){
div=factor;
}
int getColorReduction(){
return div;
}
void setReferenceImage(const cv::Mat &Image){
reference=hist.colorReduce(image,div);
refH=hist.getHistogram(reference);
}
double compare(const cv::Mat& image){
input=hist.colorReduce(image,div);
inputH=hist.getHistogram(input);
return cv::compareHist(refH,inputH,CV_COMP_INTERSECT);
}
};```