直方图的内容在《学习OpenCV》第七章有详细介绍。
#include "cv.h"
#include "highgui.h"
#include "stdio.h"
/*声明要画出的矩形结构体,结构体包含的成员有起始点坐标(x,y),宽(width),高(height)*/
CvRect g_rect;
/*声明鼠标左键状态全局标志位,若为0则鼠标左键为弹起,若为1则鼠标左键按下*/
bool g_mouse_flag = 0;
/*声明我的鼠标回调函数*/
void my_callback( int event, int x, int y, int flag, void* param );
/*声明画出图像高亮部分函数*/
void high_light( IplImage* image, CvRect rect );
/*声明画出部分指定区域颜色直方图函数*/
void Draw_hist( IplImage* img, CvRect Rect );
/*主函数*/
int main()
{
IplImage* orginal_image = cvLoadImage( "homework_4-3.jpg" ); //载入源图像
assert( orginal_image != NULL ); //检测源图像是否载入
IplImage* temp_image = cvCreateImage( cvGetSize(orginal_image),
orginal_image->depth,
orginal_image->nChannels); //以源图像的尺寸、颜色深度(数据类型)、颜色通道创建一图像
assert( temp_image != NULL );
cvNamedWindow( "homework_4-3" );
cvSetMouseCallback( "homework_4-3", my_callback, (void*)temp_image ); //在homework_4-3窗口处为temp_image的鼠标动作注册
while (1)
{
IplImage* temp_image= cvCloneImage( orginal_image ); //大循环,temp_image为源图像的克隆,不断更新
if (g_mouse_flag == true) //如果鼠标左键被按下
{
high_light( temp_image, g_rect ); //在temp_image中用高亮画出矩形g_rect
Draw_hist( temp_image, g_rect ); //画出高亮矩形g_rect部分的颜色分布直方图
}
else //如果鼠标没按下,还是照样保持
{
high_light( temp_image, g_rect );
Draw_hist( temp_image, g_rect );
}
cvShowImage( "homework_4-3", temp_image ); //把画出高亮矩形了的图像temp_image显示
if (cvWaitKey(30)==27) //按ESC键退出
{
break;
}
}
cvDestroyAllWindows();
return 0;
}
//*****************************************************
//鼠标回调函数:根据不同鼠标动作相应不同事件
//event :鼠标事件类型
//x,y :事件发生时鼠标相对图像像素位置的坐标值
//flag :指定事件发生时鼠标左键、右键等的不同状态
//param :空指针,一般用来指向鼠标作用的图像
//*****************************************************
void my_callback( int event, int x, int y, int flag, void* param )
{
IplImage* image = (IplImage*)(param);
switch (event)
{
case CV_EVENT_LBUTTONDOWN : //如果鼠标左键被按下
g_mouse_flag = true; //全局变量变为1
g_rect = cvRect( x, y, 0, 0 ); //以鼠标按下坐标(x,y)为初始点画一个宽为0、长为0的矩形(没有移动)
break;
case CV_EVENT_MOUSEMOVE: //如果鼠标左键按下时鼠标移动
if (g_mouse_flag==true)
{
g_rect.width = x - g_rect.x; //要画出的矩形的宽为鼠标横坐标减去矩形初始点的横坐标
g_rect.height = y-g_rect.y; //要画出的矩形的高为鼠标纵坐标减去矩形初始点的纵坐标
}
break;
case CV_EVENT_LBUTTONUP : //如果鼠标左键按下后弹起
g_mouse_flag=false; //修改全局变量
if (g_rect.width < 0) //如果要画的矩形的宽、高小于零(向左上角方向画)时的处理
{
g_rect.x = g_rect.x + g_rect.width; //交换矩形起始点与终点的坐标
g_rect.width *= -1;
}
if (g_rect.height < 0)
{
g_rect.y = g_rect.y + g_rect.height;
g_rect.height *= -1;
}
high_light(image,g_rect); //矩形起始点、宽、高都得到处理后以高亮显示矩形
Draw_hist(image,g_rect); //并画出矩形区域内的颜色分布直方图
break;
}
}
//*****************************************************
//区域高亮函数:在指定图像上以高亮显示指定区域
//img :输入指定图像的指针
//rect :输入指定矩形结构体
//*****************************************************
void high_light( IplImage* img, CvRect rect )
{
assert( img != NULL ); //判断是否有图像输入
int row, col;
for (row=rect.y; row<rect.y+rect.height; row++) //for循环,row在指定矩形区域一行一行进行循环
{
uchar* ptr=(uchar*)(img->imageData + row*img->widthStep); //将指针ptr指向图像每一行的首地址
for (col=rect.x; col<rect.x+rect.width; col++) //for循环,col在指定矩形区域内一列一列进行循环
{
ptr[(col*3+1)]=150; //高亮处理
ptr[(col*3+2)]=110;
}
if (row >= img->height - 1)
{
row = img->height;
}
}
}
//**************************************************************************
//画颜色分布直方图函数:统计指定图像指定区域的颜色分布,在新窗口画出颜色分布直方图
//img :输入指定图像的指针
//rect :输入指定矩形结构体
//**************************************************************************
void Draw_hist( IplImage* img, CvRect Rect )
{
if (Rect.x<=0||Rect.y<=0||Rect.width<=0||Rect.height<=0) //判断输入的矩形是否有效
{
return;
}
cvSetImageROI( img, Rect ); //在输入图像上设置感兴趣区域,即矩形Rect
IplImage* hsv = cvCreateImage(/*cvSize(Rect.width ,Rect.height)*/cvGetSize(img), 8, 3 );
IplImage* h_plane = cvCreateImage(/*cvSize(Rect.width ,Rect.height)*/cvGetSize(img), 8, 1 );
IplImage* s_plane = cvCreateImage( cvSize(Rect.width ,Rect.height), 8, 1 );
IplImage* v_plane = cvCreateImage( cvSize(Rect.width ,Rect.height), 8, 1 );
IplImage* planes[] = { h_plane, s_plane };
int h_bins = 16, s_bins = 8;
int hist_size[] = {h_bins, s_bins};
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 255 };
float* ranges[] = { h_ranges, s_ranges };
cvCvtColor( img, hsv, CV_BGR2HSV ); //将颜色为RGB的输入图像img转化为HSV
cvResetImageROI( img ); //释放感兴趣区域
cvSplit( hsv, h_plane, s_plane, v_plane, 0 ); //将hsv图像分离到四个图像
CvHistogram* hist = cvCreateHist( 2, hist_size, CV_HIST_ARRAY, ranges, 1 ); //画出颜色分布直方图
cvCalcHist( planes, hist, 0, 0 );
float max_value;
cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );
int height = 480;
int width = (h_bins*s_bins*6);
IplImage* hist_img = cvCreateImage( cvSize(width,height), 8, 3 );
cvZero( hist_img );
IplImage* hsv_color = cvCreateImage( cvSize(1,1), 8, 3 );
IplImage* rgb_color = cvCreateImage( cvSize(1,1), 8, 3 );
int bin_w = width / (h_bins*s_bins);
for(int h=0; h<h_bins; h++)
{
for(int s=0; s<s_bins; s++)
{
int i = h*s_bins + s;
/*获得直方图中的统计次数,计算显示在图像中的高度*/
float bin_val = cvQueryHistValue_2D( hist, h, s );
int intensity = cvRound( bin_val * height / max_value );
/*获得当前直方图代表的颜色,转换成RGB用于绘制*/
cvSet2D( hsv_color, 0, 0, cvScalar(h*180.f/h_bins, s*255.f/s_bins, 255, 0));
cvCvtColor( hsv_color, rgb_color, CV_HSV2BGR );
CvScalar color = cvGet2D( rgb_color, 0, 0 );
cvRectangle( hist_img, cvPoint(i*bin_w,height),
cvPoint((i+1)*bin_w,
height - intensity),
color, -1, 8, 0 );
}
}
cvNamedWindow( "H-S Histogram", 1 );
cvShowImage( "H-S Histogram", hist_img );
}