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閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容