C++ OpenCv提供了函数calcHist(),可以很方便的帮助我们统计一幅8Bit图像的直方图,但是对于10Bit,12Bit,14Bit,16Bit,24Bit,32Bit等高位图像就不适用了,无法统计,这时候就需要我们自己手写代码统计高位图像的直方图。本人是在自己手撸统计8Bit图像直方图的基础上研究出统计16Bit图像的代码,关于手撸统计8Bit图像直方图的代码见我上一篇文章,有详细讲解,链接为:添加链接描述
下面给出代码中处理的原始16Bit图像,大家想测试的可以自行下载查看,注意:提供的图像由于是我将16Bit图像上传到网页,可以被网页转换为不是16Bit了,大家用自己的16Bit图像进行测试。
自己手撸统计16Bit图像直方图的代码见下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("Images/16bit/16bit_3M_whq/3M_Lj(7).png", -1); //输入16Bit图像
Mat histogram = Mat::zeros(Size(16384, 1), CV_32SC1); //注意,Size对应的是x和y,也就是第一个元数是矩阵的列数,这里表示的是创建一个16384列,1行的画布
int rows = image.rows; //输入图像的行数
int cols = image.cols; //输入图像的列数
for (int i = 0; i(i, j)); //获取每个点的像素值
histogram.at(0, index) += 1; //获取了一个像素值,在相应的位置上加1
}
}
//开始直观的显示直方图——绘制直方图
//首先先创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像,3通道图像画直方图的时候就可以画彩色的直方图了
Mat drawImage1 = Mat::zeros(Size(1451,1000), CV_8UC3); //这个画布的创建是为了最后画直方图,大小为1451列,1000行,这里可以根据自己的需求改动画布大小
//因为任何一个图像的某个像素的总个数,都有可能会有很多,会超出所定义的图像的尺寸,针对这种情况,先对个数进行范围的限制
//先用 minMaxLoc函数来得到计算直方图后的像素的最大个数
double g_dHistMaxValue; //最大的像素值数目
minMaxLoc(histogram, 0, &g_dHistMaxValue, 0, 0);
cout << "像素点个数最大值:g_dHistMaxValue = " << g_dHistMaxValue << endl;
cout << "histogram = " << histogram << endl;
//minMaxLoc(hist, &minValue, &maxValue, 0, 0);//找到全局最小、最大的像素值数目
//将像素的个数整合到 图像的最大范围内
//遍历直方图得到的数据
//创建下面这个画布是了只提取有灰度值的部分,因为16Bit图像的灰度范围是0-16383,太大了,如果将直方图统计后全部画在一个画布上,由于我们的电脑显示器显示不了那么大,就会都是黑的,看不到最下面的统计结果,所有创建temporary来保存只有灰度值的部分
Mat temporary = Mat::zeros(Size(1451, 1), CV_32SC1); //申请一个1451列,1行的黑色画布 这里为什么是1451,是因为我提供的16Bit图像的有灰度的这段,跨度大概在1451左右,这里大家可以根据自己的16Bit图像自行设置
int j = 0;
for (int i = 6650; i < 8101; i++) //为什么i从6650开始,结束是8101,是因为我提供的这张16Bit图像,最大灰度值是7954,最小灰度值是6701,所以我这里设置了刚好是有灰度值张部分,其他全是0的部分就舍弃了。这里的取值范围,大家先统计统一下16Bit图像的最大值和最小值后自行设置。
{
int value = cvRound(histogram.at(0, i) * 16384 * 0.06 / g_dHistMaxValue); //这里公式中乘的0.06是为了使显示的直方图更低更好看一些,可以适当调整此值,自己看着舒服
//cout << "value = " << value << endl;
temporary.at(0, j) = value; //将有值的部分区间,全都保存到temporary中
j++;
}
cout << "temporary = " << temporary;
//下面是将保存了值的temporary绘画出来
for (int i = 0; i < 1351; i++)
{
int temporary_value = temporary.at(0, i);
//line(drawImage1, Point(i, drawImage1.rows - 1), Point(i, drawImage1.rows - 1 - temporary_value), Scalar(255, 255, 0)); //用线条的方式绘画直方图
rectangle(drawImage1, Point(i, drawImage1.rows - 1), Point(i+1, drawImage1.rows - 1 - temporary_value), Scalar(255, 255, 0)); //用矩形的方式绘画直方图
}
imshow("【16Bit图像直方图】", drawImage1);
imshow("image", image);
waitKey();
return 0;
}
上面代码运行的结果见下:
下面是histogram里存放的数据,有很多的0,有用灰度值占比区间只是很小的一部分:
下面是temporary里存放的数据,将上面的有用灰度值区间提取出来,只保存有用的灰度区间部分:
下面是统计后16Bit图像的直方图:
最后补充一下代码中几个关键函数参数的具体说明,省的大家再去查,见下:
void minMaxLoc(InputArray src, double* minVal, double* maxVal=0, Point* minLoc=0,
Point* maxLoc=0, InputArray mask=noArray())
找出矩阵中最大和最小的值以及他们的坐标
src – Source single-channel array. 单通道数组
minVal – Pointer to the returned minimum value. NULL is used if not required. 指向最小值的指针
maxVal – Pointer to the returned maximum value. NULL is used if not required. 指向最大值的指针
minLoc – Pointer to the returned minimum location (in 2D case). NULL is used if not required. 最小值的二维坐标
maxLoc – Pointer to the returned maximum location (in 2D case). NULL is used if not required. 最大值得二维坐标
mask – Optional mask used to select a sub-array. 掩码
void cvLine( CvArr* img,CvPoint pt1, CvPoint pt2, CvScalar color,int thickness=1,
int line_type=8, int shift=0 );
第一个参数img:要划的线所在的图像;
第二个参数pt1:直线起点
第二个参数pt2:直线终点
第三个参数color:直线的颜色 e.g:Scalor(0,0,255)
第四个参数thickness=1:线条粗细
第五个参数line_type=8, 8 (or 0) - 8-connected line(8邻接)连接 线。4 - 4-connected line(4邻接)连接线。CV_AA - antialiased 线条。
第六个参数:坐标点的小数点位数。
这里也补充一下C++ OpenCv中calcHist()函数,见下:
void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims,
const int* histSize, const float** ranges, booluniform=true, bool accumulate=false )
计算直方图,统计各灰度的像素个数,其输出为多维矩阵
const Mat* images:为输入图像的指针。
int nimages:要计算直方图的图像的个数。此函数可以为多图像求直方图,我们通常情况下都只作用于单一图像,所以通常nimages=1。
const int* channels:图像的通道,它是一个数组,如果是灰度图像则channels[1]={0};如果是彩色图像则channels[3]={0,1,2};如果是只是求彩色图像第2个通道的直方图,则channels[1]={1};
IuputArray mask:是一个遮罩图像用于确定哪些点参与计算,实际应用中是个很好的参数,默认情况我们都设置为一个空图像,即:Mat()。
OutArray hist:计算得到的直方图
int dims:得到的直方图的维数,灰度图像为1维,彩色图像为3维。
const int* histSize:直方图横坐标的区间数。如果是10,则它会横坐标分为10份,然后统计每个区间的像素点总和。
const float** ranges:这是一个二维数组,用来指出每个区间的范围。后面两个参数都有默认值,uniform参数表明直方图是否等距,最后一个参数与多图像下直方图的显示与存储有关。
以上通过自己手撸代码统计16Bit图像直方图的详细过程,同理用类似的统计方法,可以统计出其他高位图像的直方图,如果我的这种方法对你有用,有所启发,点个赞,鼓励一下,谢谢!