CvHistogram可以提供直方圖的計算,並且可以支援多個維度的直方圖設計,但是在繪製直方圖圖形的時候就需要自己用繪圖函式來繪製,CvHistogram算是一個比較複雜的資料結構,由於它在維度小於二的可以用密集的資料結構,CvMatND來設計,可以當它的直方圖維度大於二的時候,就必須要用到稀疏矩陣,CvSparseMat的方式,下面就是用CvHistogram結構計算一維直方圖的例子
1、CvHistogram一維設計
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
int HistogramBins = 256;
float HistogramRange1[2]={0,255};
float *HistogramRange[1]={
int main()
{
IplImage *Image1;
CvHistogram *Histogram1;
IplImage *HistogramImage1;
Image1=cvLoadImage("Riverbank.jpg",0);
Histogram1 = cvCreateHist(1,
HistogramImage1 = cvCreateImage(cvSize(256,300),8,3);
HistogramImage1->origin=1;
cvCalcHist(
printf("type is : %d\n",Histogram1->type);
printf("Low Bound is : %.f\n",Histogram1->thresh[0][0]);
printf("Up Bound is : %.f\n",Histogram1->thresh[0][1]);
printf("The Bins are : %d\n",((CvMatND *) Histogram1->bins)->dim[0].size);
printf("\nGray Level Values:\n");
for(int i=0;i<HistogramBins;i++)
{
printf("%.f \n",((CvMatND *) Histogram1->bins)->data.fl[i]);
cvLine(HistogramImage1,cvPoint(i,0),cvPoint(i,(int)(cvQueryHistValue_1D(Histogram1,i)/10)),CV_RGB(127,127,127));
}
cvNamedWindow("Histogram",1);
cvNamedWindow("Riverbank",1);
cvShowImage("Riverbank",Image1);
cvShowImage("Histogram",HistogramImage1);
cvWaitKey(0);
}
原始圖片:
執行結果:
跟前面直接用矩陣累加設計出的統計直方圖一樣,到最後還是要自己用繪圖函式自己畫,CvHistogram這個資料結構的特色,它可以設定要用幾個區塊來呈現,這邊設立為256個區塊,而實際上,它可以用比較模糊的方式設定直方圖的區域,也就是可以小於256個的區塊數,是利用切割成n等分的方式,然後它可以自行設定上界(Up Bound)以及下界(Low Bound),在灰階圖裡面數據的範圍為0~255,而CvHistogram資料結構可以設立為上界30,下界200這樣的方式縮小範圍,而0~29,201~266這範圍內的數據將不會被計算,下面是CvHistogram資料結構的內容,分別為CV_HIST_ARRAY及CV_HIST_SPARSE兩種
1.
2.
CvHistogram的type固定都為CV_HIST_MAGIC_VAL這的參數,與上面程式cvCreateHist()所設定的CV_HIST_ARRAY無關,這也許是OpenCV裡面對於CvHistogram這個結構設計不良的地方,而對於CvHistogram資料結構的參數,定義如下
#define CV_HIST_ARRAY 0
#define CV_HIST_SPARSE 1
#define CV_HIST_TREE CV_HIST_SPARSE
因此,它的參數只有CV_HIST_ARRAY的多維矩陣CvMatND以及CV_HIST_SPARSE的稀疏矩陣CvSparseMat資料結構,而它的thresh是放上下界的資料,thresh2是放動態的維度上下界資料,而一般的直方圖數據資料都是放在bins裡面.而CvHistogram內的CvMatND結構則是在做快速初始化,bins以及mat都是使用同一個記憶體空間,而將直方圖資料的提取就要用到cvQueryHistValue_1D()這個函式了.
接著是將直方圖的空間分區塊的方式實作,將它分為50塊,並且上界為30,下界為200
CvHistogram結構區塊與上下界
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
int HistogramBins = 50;
int HistogramBinWidth;
float HistogramRange1[2]={30,200};
float *HistogramRange[1]={
int main()
{
IplImage *Image1;
CvHistogram *Histogram1;
IplImage *HistogramImage1;
CvPoint Point1;
CvPoint Point2;
Image1=cvLoadImage("Riverbank.jpg",0);
Histogram1 = cvCreateHist(1,
HistogramImage1 = cvCreateImage(cvSize(256,300),8,3);
cvSetZero(HistogramImage1);
HistogramImage1->origin=1;
HistogramBinWidth=256/HistogramBins;
printf("The Bolck Width is : %d\n",HistogramBinWidth);
cvCalcHist(
printf("Gray Level Values:\n");
for(int i=0;i<HistogramBins;i++)
{
printf("%.f \n",((CvMatND *) Histogram1->bins)->data.fl[i]);
Point1=cvPoint(i*HistogramBinWidth,0);
Point2=cvPoint((i+1)*HistogramBinWidth,(int)cvQueryHistValue_1D(Histogram1,i)/50);
cvRectangle(HistogramImage1,Point1,Point2,CV_RGB(127,127,127));
}
cvNamedWindow("Histogram",1);
cvNamedWindow("Riverbank",1);
cvShowImage("Riverbank",Image1);
cvShowImage("Histogram",HistogramImage1);
cvWaitKey(0);
}
執行結果:
因此,這邊就用cvRectangle()的方式來表達,對於CvHistogram這個資料結構,為什麼可以任意定義上下界以及區塊的個數呢?因為CvHistogram使用的是Look-up table(LUT)的方式,也就是查表法,開一個256大小空間的陣列,利用比例的縮放,縮放的數據存入Look-up table裡面,在利用索引的方式對應,而使用Look-up table最大的缺點為,無法取得很精確的數據,由於它是被比例縮放過的,因此除了範圍為256的可以求的完整的數值,LUT的用法會在後面使用到.
對於其他維度的直方圖,它的做法如下
2、三維直方圖擷取
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
int Histogram3DBins[3] = {256,256,256};
float HistogramRange1[6]={0,255,0,255,0,255};
float *HistogramRange[3]={
void Print3DHistogram(CvHistogram *Histogram,int BinSize);
int main()
{
CvHistogram *Histogram1;
IplImage *Image1=cvLoadImage("Riverbank.jpg",1);
IplImage *RedImage=cvCreateImage(cvGetSize(Image1),8,1);
IplImage *GreenImage=cvCreateImage(cvGetSize(Image1),8,1);
IplImage *BlueImage=cvCreateImage(cvGetSize(Image1),8,1);
IplImage *ImageArray[3]={RedImage,GreenImage,BlueImage};
cvSplit(Image1,BlueImage,GreenImage,RedImage,0);
Histogram1 = cvCreateHist(3,Histogram3DBins,CV_HIST_SPARSE,HistogramRange);
cvCalcHist(ImageArray,Histogram1);
printf("3D Historgram Data\n");
Print3DHistogram(Histogram1,256);
cvNamedWindow("Riverbank",1);
cvShowImage("Riverbank",Image1);
cvWaitKey(0);
}
void Print3DHistogram(CvHistogram *Histogram,int BinSize)
{
for(int i=0;i<BinSize;i++)
{
for(int j=0;j<BinSize;j++)
{
for(int k=0;k<BinSize;k++)
{
if(cvQueryHistValue_3D(Histogram,i,j,k)>10)
{
printf("%.f\n",cvQueryHistValue_3D(Histogram,i,j,k));
}
}
}
}
}
執行結果:
上面是三個維度的統計直方圖,分別為R維度,G維度,B維度,並且分別被量化成256等分的區塊,同樣的,也是使用LUT的方法,由於這個統計直方圖非常的大,它所佔的區域為256*256*256的大小,而實際上累積的分佈只有一點點,所以說,對於高維度的統計直方圖就要用到稀疏矩陣,要不然會浪費大量的記憶體空間,但是對於多維度的圖表呈現,OpenCV最多也只能支援到一維的方式,二維的方式可以用OpenGL(glut)呈現,當然高的維度本來就很難用視覺化的方式呈現.
cvCreateHist()
初始化CvHistogram資料結構,可以選擇密集矩陣(CvMatND)CV_HIST_ARRAY以及稀疏矩陣(CvSparseMat)CV_HIST_SPARSE,第一個引數為維度的選擇,第二個引數為要將直方圖切割為多少區塊,第三個引數為選擇cvCreateHist()函數的參數或代號,第四個引數為每一個維度的上下界
cvCreateHist(輸入int型別直方圖維度,輸入int型別直方圖區塊數,輸入參數或代號,輸入每個維度的上下界數據)
cvCalcHist()
計算直方圖累積數據,第一個引數為輸入目標IplImage圖形陣列資料結構,第二個引數輸出為CvHistogram資料結構
cvCalcHist(輸入目標IplImage圖形陣列資料結構,輸出為CvHistogram資料結構)
cvQueryHistValue_1D()
讀取CvHistogram資料結構一維空間的圖形直方圖資料,它在OpenCV的"cvcompat.h"函式庫被定義為
#define cvQueryHistValue_1D( hist, idx0 ) ((float)cvGetReal1D( (hist)->bins, (idx0)))
因此回傳的是float型別的資料,輸入CvHistogram資料結構,以及輸入一維資料結構的Index索引數據
cvQueryHistValue_1D(輸入CvHistogram資料結構,輸入一維int型別數據索引)
cvQueryHistValue_2D()
讀取CvHistogram資料結構二維空間的圖形直方圖資料,它在OpenCV的"cvcompat.h"函式庫被定義為
#define cvQueryHistValue_2D( hist, idx0, idx1 ) ((float)cvGetReal2D( (hist)->bins, (idx0), (idx1)))
回傳float型別的資料,第一個引數為輸入CvHistogram資料結構,第二個引數為輸入二維資料結構的x軸索引數據,第三個引數為y軸索引數據
cvQueryHistValue_2D(輸入CvHistogram資料結構,輸入二維int型別x軸數據索引,輸入二維int型別y軸數據索引)
cvQueryHistValue_3D()
讀取CvHistogram資料結構三維空間的圖形直方圖資料,它在OpenCV的"cvcompat.h"函式庫被定義為
#define cvQueryHistValue_3D( hist, idx0, idx1, idx2 ) ((float)cvGetReal3D( (hist)->bins, (idx0), (idx1), (idx2)))
回傳float型別的資料,第一個引數為輸入CvHistogram資料結構,第二個引數為輸入三維資料結構的x軸索引數據,第三個引數為輸入y軸索引數據,第四個引數為輸入z軸索引數據
cvQueryHistValue_3D(輸入CvHistogram資料結構,輸入三維int型別x軸數據索引,輸入三維int型別y軸數據索引,輸入三維int型別z軸數據索引)
1、CvHistogram一維設計
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
int HistogramBins = 256;
float HistogramRange1[2]={0,255};
float *HistogramRange[1]={
&
HistogramRange1[0]};int main()
{
IplImage *Image1;
CvHistogram *Histogram1;
IplImage *HistogramImage1;
Image1=cvLoadImage("Riverbank.jpg",0);
Histogram1 = cvCreateHist(1,
&
HistogramBins,CV_HIST_ARRAY,HistogramRange);HistogramImage1 = cvCreateImage(cvSize(256,300),8,3);
HistogramImage1->origin=1;
cvCalcHist(
&
Image1,Histogram1);printf("type is : %d\n",Histogram1->type);
printf("Low Bound is : %.f\n",Histogram1->thresh[0][0]);
printf("Up Bound is : %.f\n",Histogram1->thresh[0][1]);
printf("The Bins are : %d\n",((CvMatND *) Histogram1->bins)->dim[0].size);
printf("\nGray Level Values:\n");
for(int i=0;i<HistogramBins;i++)
{
printf("%.f \n",((CvMatND *) Histogram1->bins)->data.fl[i]);
cvLine(HistogramImage1,cvPoint(i,0),cvPoint(i,(int)(cvQueryHistValue_1D(Histogram1,i)/10)),CV_RGB(127,127,127));
}
cvNamedWindow("Histogram",1);
cvNamedWindow("Riverbank",1);
cvShowImage("Riverbank",Image1);
cvShowImage("Histogram",HistogramImage1);
cvWaitKey(0);
}
原始圖片:
執行結果:
跟前面直接用矩陣累加設計出的統計直方圖一樣,到最後還是要自己用繪圖函式自己畫,CvHistogram這個資料結構的特色,它可以設定要用幾個區塊來呈現,這邊設立為256個區塊,而實際上,它可以用比較模糊的方式設定直方圖的區域,也就是可以小於256個的區塊數,是利用切割成n等分的方式,然後它可以自行設定上界(Up Bound)以及下界(Low Bound),在灰階圖裡面數據的範圍為0~255,而CvHistogram資料結構可以設立為上界30,下界200這樣的方式縮小範圍,而0~29,201~266這範圍內的數據將不會被計算,下面是CvHistogram資料結構的內容,分別為CV_HIST_ARRAY及CV_HIST_SPARSE兩種
1.
2.
CvHistogram的type固定都為CV_HIST_MAGIC_VAL這的參數,與上面程式cvCreateHist()所設定的CV_HIST_ARRAY無關,這也許是OpenCV裡面對於CvHistogram這個結構設計不良的地方,而對於CvHistogram資料結構的參數,定義如下
#define CV_HIST_ARRAY 0
#define CV_HIST_SPARSE 1
#define CV_HIST_TREE CV_HIST_SPARSE
因此,它的參數只有CV_HIST_ARRAY的多維矩陣CvMatND以及CV_HIST_SPARSE的稀疏矩陣CvSparseMat資料結構,而它的thresh是放上下界的資料,thresh2是放動態的維度上下界資料,而一般的直方圖數據資料都是放在bins裡面.而CvHistogram內的CvMatND結構則是在做快速初始化,bins以及mat都是使用同一個記憶體空間,而將直方圖資料的提取就要用到cvQueryHistValue_1D()這個函式了.
接著是將直方圖的空間分區塊的方式實作,將它分為50塊,並且上界為30,下界為200
CvHistogram結構區塊與上下界
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
int HistogramBins = 50;
int HistogramBinWidth;
float HistogramRange1[2]={30,200};
float *HistogramRange[1]={
&
HistogramRange1[0]};int main()
{
IplImage *Image1;
CvHistogram *Histogram1;
IplImage *HistogramImage1;
CvPoint Point1;
CvPoint Point2;
Image1=cvLoadImage("Riverbank.jpg",0);
Histogram1 = cvCreateHist(1,
&
HistogramBins,CV_HIST_ARRAY,HistogramRange);HistogramImage1 = cvCreateImage(cvSize(256,300),8,3);
cvSetZero(HistogramImage1);
HistogramImage1->origin=1;
HistogramBinWidth=256/HistogramBins;
printf("The Bolck Width is : %d\n",HistogramBinWidth);
cvCalcHist(
&
Image1,Histogram1);printf("Gray Level Values:\n");
for(int i=0;i<HistogramBins;i++)
{
printf("%.f \n",((CvMatND *) Histogram1->bins)->data.fl[i]);
Point1=cvPoint(i*HistogramBinWidth,0);
Point2=cvPoint((i+1)*HistogramBinWidth,(int)cvQueryHistValue_1D(Histogram1,i)/50);
cvRectangle(HistogramImage1,Point1,Point2,CV_RGB(127,127,127));
}
cvNamedWindow("Histogram",1);
cvNamedWindow("Riverbank",1);
cvShowImage("Riverbank",Image1);
cvShowImage("Histogram",HistogramImage1);
cvWaitKey(0);
}
執行結果:
因此,這邊就用cvRectangle()的方式來表達,對於CvHistogram這個資料結構,為什麼可以任意定義上下界以及區塊的個數呢?因為CvHistogram使用的是Look-up table(LUT)的方式,也就是查表法,開一個256大小空間的陣列,利用比例的縮放,縮放的數據存入Look-up table裡面,在利用索引的方式對應,而使用Look-up table最大的缺點為,無法取得很精確的數據,由於它是被比例縮放過的,因此除了範圍為256的可以求的完整的數值,LUT的用法會在後面使用到.
對於其他維度的直方圖,它的做法如下
2、三維直方圖擷取
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
int Histogram3DBins[3] = {256,256,256};
float HistogramRange1[6]={0,255,0,255,0,255};
float *HistogramRange[3]={
&
HistogramRange1[0],&
HistogramRange1[2],&
HistogramRange1[4]};void Print3DHistogram(CvHistogram *Histogram,int BinSize);
int main()
{
CvHistogram *Histogram1;
IplImage *Image1=cvLoadImage("Riverbank.jpg",1);
IplImage *RedImage=cvCreateImage(cvGetSize(Image1),8,1);
IplImage *GreenImage=cvCreateImage(cvGetSize(Image1),8,1);
IplImage *BlueImage=cvCreateImage(cvGetSize(Image1),8,1);
IplImage *ImageArray[3]={RedImage,GreenImage,BlueImage};
cvSplit(Image1,BlueImage,GreenImage,RedImage,0);
Histogram1 = cvCreateHist(3,Histogram3DBins,CV_HIST_SPARSE,HistogramRange);
cvCalcHist(ImageArray,Histogram1);
printf("3D Historgram Data\n");
Print3DHistogram(Histogram1,256);
cvNamedWindow("Riverbank",1);
cvShowImage("Riverbank",Image1);
cvWaitKey(0);
}
void Print3DHistogram(CvHistogram *Histogram,int BinSize)
{
for(int i=0;i<BinSize;i++)
{
for(int j=0;j<BinSize;j++)
{
for(int k=0;k<BinSize;k++)
{
if(cvQueryHistValue_3D(Histogram,i,j,k)>10)
{
printf("%.f\n",cvQueryHistValue_3D(Histogram,i,j,k));
}
}
}
}
}
執行結果:
上面是三個維度的統計直方圖,分別為R維度,G維度,B維度,並且分別被量化成256等分的區塊,同樣的,也是使用LUT的方法,由於這個統計直方圖非常的大,它所佔的區域為256*256*256的大小,而實際上累積的分佈只有一點點,所以說,對於高維度的統計直方圖就要用到稀疏矩陣,要不然會浪費大量的記憶體空間,但是對於多維度的圖表呈現,OpenCV最多也只能支援到一維的方式,二維的方式可以用OpenGL(glut)呈現,當然高的維度本來就很難用視覺化的方式呈現.
cvCreateHist()
初始化CvHistogram資料結構,可以選擇密集矩陣(CvMatND)CV_HIST_ARRAY以及稀疏矩陣(CvSparseMat)CV_HIST_SPARSE,第一個引數為維度的選擇,第二個引數為要將直方圖切割為多少區塊,第三個引數為選擇cvCreateHist()函數的參數或代號,第四個引數為每一個維度的上下界
cvCreateHist(輸入int型別直方圖維度,輸入int型別直方圖區塊數,輸入參數或代號,輸入每個維度的上下界數據)
cvCalcHist()
計算直方圖累積數據,第一個引數為輸入目標IplImage圖形陣列資料結構,第二個引數輸出為CvHistogram資料結構
cvCalcHist(輸入目標IplImage圖形陣列資料結構,輸出為CvHistogram資料結構)
cvQueryHistValue_1D()
讀取CvHistogram資料結構一維空間的圖形直方圖資料,它在OpenCV的"cvcompat.h"函式庫被定義為
#define cvQueryHistValue_1D( hist, idx0 ) ((float)cvGetReal1D( (hist)->bins, (idx0)))
因此回傳的是float型別的資料,輸入CvHistogram資料結構,以及輸入一維資料結構的Index索引數據
cvQueryHistValue_1D(輸入CvHistogram資料結構,輸入一維int型別數據索引)
cvQueryHistValue_2D()
讀取CvHistogram資料結構二維空間的圖形直方圖資料,它在OpenCV的"cvcompat.h"函式庫被定義為
#define cvQueryHistValue_2D( hist, idx0, idx1 ) ((float)cvGetReal2D( (hist)->bins, (idx0), (idx1)))
回傳float型別的資料,第一個引數為輸入CvHistogram資料結構,第二個引數為輸入二維資料結構的x軸索引數據,第三個引數為y軸索引數據
cvQueryHistValue_2D(輸入CvHistogram資料結構,輸入二維int型別x軸數據索引,輸入二維int型別y軸數據索引)
cvQueryHistValue_3D()
讀取CvHistogram資料結構三維空間的圖形直方圖資料,它在OpenCV的"cvcompat.h"函式庫被定義為
#define cvQueryHistValue_3D( hist, idx0, idx1, idx2 ) ((float)cvGetReal3D( (hist)->bins, (idx0), (idx1), (idx2)))
回傳float型別的資料,第一個引數為輸入CvHistogram資料結構,第二個引數為輸入三維資料結構的x軸索引數據,第三個引數為輸入y軸索引數據,第四個引數為輸入z軸索引數據
cvQueryHistValue_3D(輸入CvHistogram資料結構,輸入三維int型別x軸數據索引,輸入三維int型別y軸數據索引,輸入三維int型別z軸數據索引)