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(二進制閾值)

THRESH_BINARY_INV(反二進制閾值)

THRESH_TRUNC(截斷閾值)

THRESH_TOZERO(0閾值)

THRESH_TOZERO_INV(反0閾值)


#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;
}
```

#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;
}```


#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;
}```



書上易于理解的代碼:
```
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);
}
};```