OPENCV中画图像直方图(带刻度并可以通过滚动条调节直方图中直方条的个数)
本文是在《OpenCV教程基础篇》例题5-11的基础上进行的修改。
本文亮点:
(1)可以通过调节滚动条,来实现调节直方图中输出直方条的个数;
(2)在输出的直方图中添加了横纵坐标刻度,并且这些刻度可以随着滚动条的调节自适应改变。
-
#include "cv.h"
-
#include "highgui.h"
-
#include <stdio.h>
-
#include <ctype.h>
-
using
namespace
std;
-
using
namespace cv;
-
-
-
IplImage *src =
0;
-
IplImage *histimg =
0;
-
CvHistogram *hist =
0;
-
-
int hdims =
50;
// 划分HIST的初始个数,越高越精确
-
-
//滚动条函数
-
void HIST(int t)
-
{
-
float hranges_arr[] = {
0,
255};
-
float* hranges = hranges_arr;
-
int bin_w;
-
int bin_u;
-
float max;
-
int i;
-
char
string[
10];
-
CvFont font;
-
cvInitFont( &font, CV_FONT_HERSHEY_PLAIN,
1,
1,
0,
1,
8);
//字体结构初始化
-
if(hdims==
0)
-
{
-
printf(
"直方图条数不能为零!\n");
-
}
-
else
-
{
-
hist = cvCreateHist(
1, &hdims, CV_HIST_ARRAY, &hranges,
1 );
// 创建直方图
-
histimg = cvCreateImage(cvSize(
800,
512),
8,
3);
-
cvZero( histimg );
-
cvCalcHist( &src, hist,
0,
0 );
// 计算直方图
-
cvGetMinMaxHistValue(hist,
NULL,&max,
NULL,
NULL);
//寻找最大值及其位置
-
//printf("max_val:%f \n",max_val);
-
cvZero( histimg );
-
-
double bin_w =(
double) histimg->width / hdims;
// hdims: 条的个数,则 bin_w 为条的宽度
-
double bin_u = (
double)histimg->height/ max;
max: 最高条的像素个数,则 bin_u 为单个像素的高度
-
-
// 画直方图
-
for(
int i=
0;i<hdims;i++)
-
{
-
CvPoint p0=cvPoint(i*bin_w,histimg->height);
-
int val=cvGetReal1D(hist->bins,i);
-
CvPoint p1=cvPoint((i+
1)*bin_w,histimg->height-cvGetReal1D(hist->bins,i)*bin_u);
-
cvRectangle(histimg,p0,p1,cvScalar(
0,
255),
1,
8,
0);
-
}
-
//画纵坐标刻度(像素个数)
-
int kedu=
0;
-
for(
int i=
1;kedu<max;i++)
-
{
-
kedu=i*max/
10;
-
itoa(kedu,
string,
10);
//把一个整数转换为字符串
-
//在图像中显示文本字符串
-
cvPutText(histimg,
string , cvPoint(
0,histimg->height-kedu*bin_u), &font, CV_RGB(
0,
255,
255));
-
}
-
//画横坐标刻度(像素灰度值)
-
kedu=
0;
-
for(
int i=
1;kedu<
256;i++)
-
{
-
kedu=i*
20;
-
itoa(kedu,
string,
10);
//把一个整数转换为字符串
-
//在图像中显示文本字符串
-
cvPutText(histimg,
string , cvPoint(kedu*(histimg->width /
256),histimg->height), &font, CV_RGB(
255,
0,
0));
-
}
-
-
cvShowImage(
"Histogram", histimg );
-
}
-
}
-
-
int main( int argc, char** argv )
-
{
-
argc=
2;
-
argv[
1]=
"lena.jpg";
-
-
if( argc !=
2 || (src=cvLoadImage(argv[
1],
0)) ==
NULL)
// force to gray image
-
return
-1;
-
-
cvNamedWindow(
"src",
1);
-
cvShowImage(
"src", src);
-
cvNamedWindow(
"Histogram",
1 );
-
-
cvCreateTrackbar(
"hdims",
"src", &hdims,
256, HIST );
-
HIST(
0);
-
cvWaitKey(
0);
-
-
cvDestroyWindow(
"src");
-
cvDestroyWindow(
"Histogram");
-
cvReleaseImage( &src );
-
cvReleaseImage( &histimg );
-
cvReleaseHist ( &hist );
-
-
return
0;
-
}
效果如下:
1.分成50条:
2.分成256条:
上面是老版本的代码,2.0版本的代码采用了Mat图像存储方式,新代码如下:
-
#include <opencv2/opencv.hpp>
-
#include <iostream>
-
-
using
namespace
std;
-
using
namespace cv;
-
-
/* 全局变量的声明及初始化 */
-
Mat srcImage;
//读入的图片矩阵
-
Mat dstImage;
//读入的图片矩阵
-
MatND dstHist;
//直方图矩阵,对应老版本中的cvCreateHist()
-
int g_hdims =
50;
// 划分HIST的初始个数,越高越精确
-
-
/* 回调函数声明 */
-
void on_HIST(int t,void *);
-
-
-
/* 主函数 */
-
int main( int argc, char** argv )
-
{
-
-
srcImage=imread(
"lena.jpg",
0);
//"0"表示读入灰度图像
-
namedWindow(
"原图",
1 );
//对应老版本中的cvNamedWindow( )
-
imshow(
"原图",srcImage);
//对应老版本中的 cvShowImage()
-
-
createTrackbar(
"hdims",
"原图", &g_hdims,
256, on_HIST);
//对应旧版本中的cvCreateTrackbar( );
-
on_HIST(
0,
0);
//调用滚动条回调函数
-
cvWaitKey(
0);
-
return
0;
-
}
-
-
-
/* 滚动条回调函数 */
-
void on_HIST(int t,void *)
-
{
-
dstImage=Mat::zeros(
512,
800,CV_8UC3);
//每次都要初始化
-
float hranges[]={
0,
255};
//灰度范围
-
const
float *ranges[]={hranges};
//灰度范围的指针
-
-
if(g_hdims==
0)
-
{
-
printf(
"直方图条数不能为零!\n");
-
}
-
else
-
{
-
/*
-
srcImage:读入的矩阵
-
1:数组的个数为1
-
0:因为灰度图像就一个通道,所以选0号通道
-
Mat():表示不使用掩膜
-
dstHist:输出的目标直方图
-
1:需要计算的直方图的维度为1
-
g_hdims:划分HIST的个数
-
ranges:表示每一维度的数值范围
-
*/
-
//int channels=0;
-
calcHist( &srcImage,
1,
0, Mat(),dstHist,
1,&g_hdims,ranges);
// 计算直方图对应老版本的cvCalcHist
-
-
/* 获取最大最小值 */
-
double max=
0;
-
minMaxLoc(dstHist,
NULL,&max,
0,
0);
// 寻找最大值及其位置,对应旧版本的cvGetMinMaxHistValue();
-
-
/* 绘出直方图 */
-
-
double bin_w =(
double) dstImage.cols/g_hdims;
// hdims: 条的个数,则 bin_w 为条的宽度
-
double bin_u = (
double)dstImage.rows/ max;
max: 最高条的像素个数,则 bin_u 为单个像素的高度
-
-
// 画直方图
-
for(
int i=
0;i<g_hdims;i++)
-
{
-
Point p0=Point(i*bin_w,dstImage.rows);
//对应旧版本中的cvPoint()
-
-
int val=dstHist.at<
float>(i);
//注意一点要用float类型,对应旧版本中的 cvGetReal1D(hist->bins,i);
-
Point p1=Point((i+
1)*bin_w,dstImage.rows-val*bin_u);
-
rectangle(dstImage,p0,p1,cvScalar(
0,
255),
1,
8,
0);
//对应旧版中的cvRectangle();
-
}
-
-
/* 画刻度 */
-
char
string[
12];
//存放转换后十进制数,转化成十进制后的位数不超过12位,这个根据情况自己设定
-
//画纵坐标刻度(像素个数)
-
int kedu=
0;
-
for(
int i=
1;kedu<max;i++)
-
{
-
kedu=i*max/
10;
//此处选择10个刻度
-
itoa(kedu,
string,
10);
//把一个整数转换为字符串,这个当中的10指十进制
-
//在图像中显示文本字符串
-
putText(dstImage,
string , Point(
0,dstImage.rows-kedu*bin_u),
1,
1,Scalar(
0,
255,
255));
//对应旧版中的cvPutText()
-
}
-
//画横坐标刻度(像素灰度值)
-
kedu=
0;
-
for(
int i=
1;kedu<
256;i++)
-
{
-
kedu=i*
20;
//此处选择间隔为20
-
itoa(kedu,
string,
10);
//把一个整数转换为字符串
-
//在图像中显示文本字符串
-
putText(dstImage,
string , cvPoint(kedu*(dstImage.cols /
256),dstImage.rows),
1,
1,Scalar(
0,
255,
255));
-
}
-
namedWindow(
"Histogram",
1 );
-
imshow(
"Histogram", dstImage );
-
}
-
-
}