【图像处理】利用C++编写函数,绘制灰度图像直方图
1. 简介
利用OpenCV读取图像,转换为灰度图像,绘制该灰度图像直方图。点击直方图,控制台输出该灰度级像素个数。
2. 原理
(1) 实现原理较为简单,主要利用了OpenCV读取图像,并转换为灰度图像;
srcImg = imread(" ......"); // “....” 代表图像地址 if (srcImg.empty()) { return -1; } imshow(WINDOW_SRCIMG, srcImg); Mat grayImg; cvtColor(srcImg, grayImg, CV_BGR2GRAY); imshow(WINDOW_GRAYIMG, grayImg);
(2) 利用Mat类新建一个固定分辨率的画布,统计处于每一灰度级像素个数,在该画布上绘制灰度直方图。
int nRows = 700,nCols=600; Mat g_dstImg(nRows,nCols, CV_8UC1, Scalar::all(0)); // 新建画布
同时避免画布中该灰度级太高而超出画布范围,在本程序中采用了等比例缩小的方法。
int MaxCount = arrayMax(grayLevel,256);//寻找在处于某一灰度级中个数最多的像素个数 yscaleRate = double(nRows)/MaxCount ;//y缩放比例 double xscaleRate = nCols / 256;//x缩放比例
3. 实施细节
// 灰度直方图.cpp : 定义控制台应用程序的入口点。 // Topic: 绘制灰度图的直方图; 转载请注明出处:Chen_HW (https://www.cnblogs.com/chen-hw/p/11668119.html)
// Env: VS2015 + Debug x64 + OpenCV3.4.1 // Date:2018.11.22 by Chen_HW
#include "stdafx.h" #include <opencv2/opencv.hpp> #include <iostream> #define WINDOW_SRCIMG "【源图】" #define WINDOW_GRAYIMG "【灰度图】" #define WINDOW_HIST "【直方图】" using namespace std; using namespace cv; int arrayMax(int g_arr[], int num); Mat drawHist(Mat &g_srcImg); void MouseHandle(int event, int x, int y, int flags, void *param); Point clickPoint,displayPoint; bool downFlag = false; Mat srcImg; double yscaleRate = 0; int main() { system("color 3f"); srcImg = imread(" ......"); // “....” 代表图像地址 if (srcImg.empty()) { return -1; } imshow(WINDOW_SRCIMG, srcImg); Mat grayImg; cvtColor(srcImg, grayImg, CV_BGR2GRAY); imshow(WINDOW_GRAYIMG, grayImg); Mat histImg = drawHist(grayImg); imshow(WINDOW_HIST, histImg); setMouseCallback(WINDOW_HIST, MouseHandle, (void *)&histImg);//(void *)&srcImg传递给void *param waitKey(); return 0; }
Mat drawHist(Mat &g_srcImg) { int nRows = 700,nCols=600; Mat g_dstImg(nRows,nCols, CV_8UC1, Scalar::all(0)); // 新建画布 int grayLevel[256] = {0}; for (int i = 0; i < g_srcImg.rows; ++i) { for (int j = 0; j < g_srcImg.cols; ++j) { grayLevel[(int)g_srcImg.at<uchar>(i, j)]++; } } int MaxCount = arrayMax(grayLevel,256);//寻找在处于某一灰度级中个数最多的像素个数 yscaleRate = double(nRows)/MaxCount ;//y缩放比例 double xscaleRate = nCols / 256;//x缩放比例 int yAxis[256], xAxis[257]; for (int m = 0; m < 256 ; m++) { yAxis[m] = int(grayLevel[m]*yscaleRate); } //绘制直方图 for (int n = 0; n < 256; n++) { if (n == 255) { xAxis[n + 1] = xAxis[n]; } rectangle(g_dstImg, Point(xAxis[n], yAxis[n]), Point( xAxis[n+1], 0), Scalar(255), -1); //-1表示填充矩形框;正值表示不填充矩形框,更方便观察灰度级像素个数的分布; //但因为后面需要用到填充的情况,故设置成填充状态 } return g_dstImg; }
//num:数组元素个数;g_max:返回最大值 int arrayMax(int g_arr[],int num) { int g_max = 0; int i = 0; while (i < num) { if ( g_arr[i]>= g_max) { g_max = g_arr[i]; i++; } else { i++; } } return g_max; } //event 鼠标事件(如按下鼠标左键、左键抬起、鼠标移动等) x、y 鼠标坐标 void MouseHandle(int event, int x, int y, int flags, void *param) { Mat &g_srcImg = *(Mat *)(param); Mat g_tempImg = g_srcImg.clone(); int nCount=0;//该列白色像素点个数 int nLevelCount = 0; char text[20];//存储文本信息 float g_rate;//该列像素点占总像素点个数的比例 imshow(WINDOW_HIST, g_srcImg); switch (event) { case EVENT_LBUTTONDOWN: clickPoint.x = x; clickPoint.y = y; downFlag = true; break; default: break; } if (downFlag) { //displayPoint.x = clickPoint.x; for (int i = 0; i < g_srcImg.rows; ++i) { if (int(g_srcImg.at<uchar>(i, x)) == 255) { ++nCount; } } nLevelCount = (nCount / yscaleRate); g_rate = float(nCount/ yscaleRate) / (srcImg.rows*srcImg.cols);//此处计算的比例不是精确的 cout << "该灰度级像素点个数: " << nLevelCount << ";占总像素个数的比例: "<<g_rate << endl; sprintf_s(text, "Rate:%f",g_rate );//该灰度级像素个数占总个数的比例; putText(g_tempImg, text, clickPoint,FONT_HERSHEY_PLAIN,1,Scalar(255,0,255)); imshow(WINDOW_HIST, g_tempImg); downFlag = false; } }
4. 结果
结果如下图所示,点击右侧每一级灰度直方图,在控制台中会输出该灰度级像素个数,并显示占总像素比例。