您的位置:首页 > 运维架构

opencv CvHistogram 求直方图的均值

2016-06-19 20:39 411 查看
转载请注明 t1234xy4原创 : http://blog.csdn.net/t1234xy4/article/details/51713895
相关源码下载连接:http://download.csdn.net/detail/t1234xy4/9554431

目标:利用opencv的直方图表示,求出直方图均值。

一、理解opencv直方图的数据类型

 学习连接:http://wiki.opencv.org.cn/index.php/Cv图像处理#CvHistogram

typedef struct CvHistogram
{
int     type;
CvArr*  bins;
float   thresh[CV_MAX_DIM][2]; /* for uniform histograms */
float** thresh2; /* for non-uniform histograms */
CvMatND mat; /* embedded matrix header for array histograms */
}
CvHistogram;

bins : 用于存放直方图每个灰度级数目的数组指针,数组在cvCreateHist 的时候创建,其维数由cvCreateHist 确定(一般以一维比较常见)

bins:也就是我们对像素的分类形成的容器。这里的CvArr* 内部是void的指针,具体解释成什么类型,与使用有关。



可以看到CvHistogram 中的数据保存在CvMatND的结构体中,下面我再学习学习CvMatND。


CvMatND

多维、多通道密集数组
typedef struct CvMatND
{
int type; /* CvMatND 标识(CV_MATND_MAGIC_VAL), 元素类型和标号*/
int dims; /* 数组维数 */

int* refcount; /* 数据参考计数 */

union
{
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data; /* data 指针*/

/* 每维的数据结构 (元素号,以字节为单位的元素之间的距离)是配套定义的 */
struct
{
int size;
int step;
}
dim[CV_MAX_DIM];

} CvMatND;


特别注意(一维直方图):CvHistogram结构的直方图数据是保存在CvMatND中的,CvMatND结构中dims与type就不说了,struct中的size与step分别是保存在dim[0](一维直方图),二维、三维保存类推。size保存了数据空间的大小,step保存每一维数据的宽度。union
data中保存了数据,我猜测是根据直方图输入的数据类型来判定使用哪一种数据指针,经过一些测试,我得到的数据保存在float* fl这个指针所指位置。(估计是默认的)如果不在你试试其他指针。

二、学会使用操作cvHistogram的函数


CreateHist

创建直方图
CvHistogram* cvCreateHist( int dims, int* sizes, int type,
float** ranges=NULL, int uniform=1 );

dims直方图维数的数目sizes直方图维数尺寸的数组type直方图的表示格式: CV_HIST_ARRAY 意味着直方图数据表示为多维密集数组 CvMatND; CV_HIST_TREE 意味着直方图数据表示为多维稀疏数组 CvSparseMat.ranges图中方块范围的数组. 它的内容取决于参数 uniform 的值。这个范围的用处是确定何时计算直方图或决定反向映射(backprojected ),每个方块对应于输入图像的哪个/哪组值。uniform归一化标识。 如果不为0,则ranges[i](0<=i<cDims,译者注:cDims为直方图的维数,对于灰度图为1,彩色图为3)是包含两个元素的范围数组,包括直方图第i维的上界和下界。在第i维上的整个区域 [lower,upper]被分割成 dims[i] 个相等的块(译者注:dims[i]表示直方图第i维的块数),这些块用来确定输入象素的第 i 个值(译者注:对于彩色图像,i确定R,
G,或者B)的对应的块;如果为0,则ranges[i]是包含dims[i]+1个元素的范围数组,包括lower0, upper0, lower1, upper1 == lower2, ..., upperdims[i]-1, 其中lowerj 和upperj分别是直方图第i维上第 j 个方块的上下界(针对输入象素的第 i 个值)。任何情况下,输入值如果超出了一个直方块所指定的范围外,都不会被 cvCalcHist 计数,而且会被函数 cvCalcBackProject 置零。
函数 cvCreateHist 创建一个指定尺寸的直方图,并且返回创建的直方图的指针。 如果数组的 ranges 是 0, 则直方块的范围必须由函数 cvSetHistBinRanges 稍后指定。虽然 cvCalcHist 和 cvCalcBackProject 可以处理 8-比特图像而无需设置任何直方块的范围,但它们都被假设等分 0..255 之间的空间。


MakeHistHeaderForArray

从数组中创建直方图
CvHistogram*  cvMakeHistHeaderForArray( int dims, int* sizes, CvHistogram* hist,
float* data, float** ranges=NULL, int uniform=1 );

dims直方图维数.sizes直方图维数尺寸的数组hist为函数所初始化的直方图头data用来存储直方块的数组ranges直方块范围,见 cvCreateHist.uniform归一化标识,见 cvCreateHist.
函数 cvMakeHistHeaderForArray 初始化直方图,其中头和直方块为用户所分配。以后不需要调用 cvReleaseHist 只有稠密直方图可以采用这种方法,函数返回 hist.


三、测试过程及相关代码

1、我使用的测试图片的规格为41×41的jpeg。

2、测试直方图统计是不是按每一个像素来统计的,首先41×41的总像素为1681个,我统计ranges{0,255}范围,也就是整个图片的所有像素。

对于原图我利用以下代码统计:

(1)、直接读取指针内存

float sum = 0;
for (int i= 0; i<hist1->mat.dim->size;i++)
{
float m = (float)(hist1->mat.data.fl[i]);

cout << m <<" ";
sum+=m;
}
cout<<"sum: "<<sum <<endl;
(2)、利用函数读取
sum = 0;
for (int i= 0; i<hist1->mat.dim->size;i++)
{
float m = cvQueryHistValue_1D(hist1,i);
data[i] = m;
cout << m <<" ";
sum+=m;
}
cout<<"sum: "<<sum <<endl;
结果一样(如下)



为了好看,把对应的直方图画出来:(只帖了红色通道的像素值)

其保存代码如下:

void saveHistogram(string filename,CvHistogram* hist)
{
if(!hist) return;
if(hist->mat.dim->size<=0) return;
int scale = 2;
int hist_height = 100;
IplImage* hist_image = cvCreateImage(cvSize(hist->mat.dim->size*scale,hist_height),8,3);
cvZero(hist_image);

float max_value = 0;
cvGetMinMaxHistValue(hist, 0,&max_value,0,0);
for(int i=0;i<hist->mat.dim->size;i++)
{
float bin_val = cvQueryHistValue_1D(hist,i);
int intensity = cvRound(bin_val*hist_height/max_value);
cvRectangle(
hist_image,
cvPoint(i*scale,hist_height-1),
cvPoint((i+1)*scale - 1, hist_height - intensity),
CV_RGB(255,0,0));
}
cvSaveImage(filename.c_str(),hist_image);
cvReleaseImage(&hist_image);
}




 结果说明:数据确实保存在CvHistogram中的CvMatND中的data中的fl指针中。


四、利用opencv CvHistogram求多个直方图均值想法

1、对直方图归一化,读取各个直方图中的数据
2、对两个直方图数据相同bins下的指做均值处理,并保存在float* data中

3、利用cvMakeHistHeaderForArray函数,生成一个新的CvHistogram

五、具体实现过程

1、读取图片,将图片分成BGR三通道,并分别生成直方图

<span style="white-space:pre">	</span>IplImage* img_source,*img_source2;
img_source = cvLoadImage("E:/MFCPlat/MutiTrackingApp/MutiTrackingApp/sequences/fishModel/fish_1/0134.jpeg",1);
img_source2 = cvLoadImage("E:/MFCPlat/MutiTrackingApp/MutiTrackingApp/sequences/fishModel/fish_2/0134.jpeg",1);
if((!img_source) || (!img_source2)) return;

IplImage* RedChannel = cvCreateImage( cvGetSize(img_source), 8, 1);
IplImage* GreenChannel = cvCreateImage( cvGetSize(img_source), 8, 1);
IplImage* BlueChannel = cvCreateImage( cvGetSize(img_source), 8, 1);

//分割为单通道图像
cvCvtPixToPlane(img_source,BlueChannel,GreenChannel,RedChannel,0);
cvCvtColor(img_source,gray_plane,CV_BGR2GRAY);

//然后为这四幅图创建对应的直方图结构。
int hist_size = 255;
in hist_height = 100;
float range[] = {0,256};
float* ranges[]={range};

CvHistogram* r_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
CvHistogram* g_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
CvHistogram* b_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
2、计算三个通道直方图,并归一化

<span style="white-space:pre">	</span>cvCalcHist(&RedChannel,r_hist,0,0);
cvCalcHist(&GreenChannel,g_hist,0,0);
cvCalcHist(&BlueChannel,b_hist,0,0);
<pre name="code" class="cpp">	cvNormalizeHist(r_hist,1.0);
cvNormalizeHist(g_hist,1.0);
cvNormalizeHist(b_hist,1.0);



3、读取直方图中的值,并做均值处理,保存在float* data中
bool CalcMeanOfHists(CvHistogram* hist1,CvHistogram* hist2,float* data)
{
if( !hist1 || !hist2 || !data) return false;
if(hist1->mat.dims != hist2->mat.dims) return false;
if( (hist1->thresh[0][0]!=hist2->thresh[0][0]) || (hist1->thresh[0][1]!=hist2->thresh[0][1]) ) return false;
if(hist1->mat.dim->size != hist2->mat.dim->size) return false;

for (int i= 0; i<hist1->mat.dim->size;i++)
{
float m1 = (float)(hist1->mat.data.fl[i]);
float m2 = (float)(hist2->mat.data.fl[i]);
data[i] = (m1+m2)/2.0f;
}
}
4、通过float* data 生成新的直方图

<span style="white-space:pre">	</span>float* data = new float[r_hist->mat.dim->size];
CalcMeanOfHists(r_hist,r_hist2,data);
CvHistogram* mean_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
cvMakeHistHeaderForArray(1,&hist_size,mean_hist,data,ranges,1);


六、测试结果

1、使用了两张测试图片如下:


 fish1       

fish2

2、原图红色通道直方图

fish1 的红色通道直方图


  

fish2的红色通道直方图



3、归一化直方图显示与以上无差别

4、均值化两张红色通道之后的比较







第一张为fish1归一化直方图,第二张为fish2直方图,第三张是前两张的均值化后的结果。

源码下载连接:http://download.csdn.net/detail/t1234xy4/9554431
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息