其实刚开始的时候,看很多的书和教程讲绘图和彩色图像等,但是我觉得还是先学会灰度直方图,因为灰度的dims是1,如果dims是3的就是彩色,同时知道前面将的彩色图像的像素访问,相信很快就可以迁移过去的。
一、换个角度认识图像(直方图)
第一个就是当我们面对图像的时候,我们面对的是抽象的矩阵,如下图,下面是0-255的灰度图像的表示,密密麻麻的
那么我们做的直方图,其实就是对这些像素值的统计,看下图,其中Bin是条数,数据和范围是对图的解释,一看就懂
二、准备知识
如果想绘制出来直方图,先要知道几个函数
(1) Point类数据结构表示了二维坐标系下的点
Point point=Point(1,2);
(2)calcHist()绘制直方图
void calcHist(const Mat* arrays, intnarrays, const int* channels, InputArray mask, OutputArray hist, int dims,const int* histSize, const float** ranges, bool uniform=true, boolaccumulate=false );
参数解释:
- arrays:输入的图像的指针,可以是多幅图像,所有的图像必须有同样的深度(CV_8U orCV_32F)。同时一副图像可以有多个channes。
- narrays:输入的图像的个数。
- channels:用来计算直方图的channes的数组。比如输入是2副图像,第一副图像有0,1,2共三个channel,第二幅图像只有0一个channel,那么输入就一共有4个channes,如果int channels[3] = {3, 2, 0},那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通道来计算直方图。
- mask:掩码。如果mask不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和arrays[i]的大小相同,值为1的点将用来计算直方图。
- hist:计算出来的直方图
- dims:计算出来的直方图的维数。
- histSize:在每一维上直方图的个数。简单把直方图看作一个一个的竖条的话,就是每一维上竖条的个数。
- ranges:用来进行统计的范围。比如 float rang1[] = {0, 20};float rang2[] = {30, 40}; const float*rangs[] = {rang1, rang2};那么就是对0,20和30,40范围的值进行统计。
- uniform:每一个竖条的宽度是否相等。
- accumulate: 是否累加。如果为true,在下次计算的时候不会首先清空hist。
画直线,在图像img中画一条颜色为color,粗细为thickness,类型为lineType的直线
(3)line() rectangle()画出直方图
void line(Mat& img, Point pt1, Pointpt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0) //两点确认一条直线。 //lineType:直线类型 //shift:坐标小数点维数 //画一个单一的实矩形 void rectangle(Mat& img, Point pt1,Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0) //一条对角线的两个顶点可确定一个矩形 //pt1和pt2互为对顶点 //thickness为负值表示矩形为实矩形
三、绘制一维灰度直方图
<span style="font-size:18px;">#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> using namespace cv; using namespace std; void Help() { printf("\n\n\t\t\t欢迎来到直方图的世界!\n"); printf("\n\n ----------------------------------------------------------------------------\n"); } int main() { Mat srcImage = imread("lena.jpg", 0); imshow("原图",srcImage); if(!srcImage.data) {cout << "fail to load image" << endl; return 0;} system("color 1F"); Help(); MatND dstHist; // 在cv中用CvHistogram *hist = cvCreateHist int dims = 1; float hranges[2] = {0, 255}; const float *ranges[1] = {hranges}; // 这里需要为const类型 int size = 256; int channels = 0; //计算图像的直方图 calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges); // cv 中是cvCalcHist int scale = 1; Mat dstImage(size * scale, size, CV_8U, Scalar(0)); //获取最大值和最小值 double minValue = 0; double maxValue = 0; minMaxLoc(dstHist,&minValue, &maxValue, 0, 0); // 在cv中用的是cvGetMinMaxHistValue //绘制出直方图 int hpt = saturate_cast<int>(0.9 * size); for(int i = 0; i < 256; i++) { float binValue = dstHist.at<float>(i); // 注意hist中是float类型 int realValue = saturate_cast<int>(binValue * hpt/maxValue); //rectangle(dstImage,Point(i*scale, size - 1), Point((i+1)*scale - 1, size - realValue), Scalar(255)); line(dstImage,Point(i*scale,size-1),Point((i+1)*scale-1,size-realValue),Scalar(255)); } imshow("一维直方图", dstImage); waitKey(0); return 0; }</span>
其实我们有时候想改变量化,上面的表示是256,我们可以50 100都可以,这里用过滑块的知识,所以不想多讲,只是提供一个别人写的参考,尊重原作者,写的很好
<span style="font-size:18px;">#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; }</span>
三、彩色直方图
这里不想过多的介绍,以后讲彩色图像的时候会具体的说
四、matlab辅助
一个imhist()函数就搞定了
<span style="font-size:18px;">clear; %%读入图像 a=imread('cameraman.tif'); imhist(a); title('原始cameraman图像的直方图');</span>
时间: 2024-11-02 13:21:12