opencv2(2017.5.4)

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);
}
};```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容