-
opencv 数字图像直方图
2014-07-30 16:16:15灰度直方图是数字图像中最简单且有用的工具,这一篇主要总结OpenCV中直方图CvHistogram的结构和应用。 灰度直方图的定义 灰度直方图是灰度级的函数,描述图像中该灰度级的像素个数(或该灰度级像素出现的频率...(转载处:http://blog.csdn.net/xiaowei_cqu )
灰度直方图是数字图像中最简单且有用的工具,这一篇主要总结OpenCV中直方图CvHistogram的结构和应用。
灰度直方图的定义
灰度直方图是灰度级的函数,描述图像中该灰度级的像素个数(或该灰度级像素出现的频率):其横坐标是灰度级,纵坐标表示图像中该灰度级出现的个数(频率)。一维直方图的结构表示为高维直方图可以理解为图像在每个维度上灰度级分布的直方图。常见的是二维直方图。如红-蓝直方图的两个分量分别表示红光图像的灰度值和蓝光图像灰度值的函数。其图像坐标(Dr,Db)处对应在红光图像中具有灰度级Dr同时在蓝光图像中具有灰度级Db的像素个数。这是基于多光谱——每个像素有多个变量——的数字图像,二维中对应每个像素统计个变量。OpenCV中的直方图CvHistogram
注意我们在上面理解直方图的意义时更多把他想象成一幅“图”,继而理解图中横坐标,纵坐标的意义。而在OpenCV中,应该更多把直方图看做“数据结构”来理解。OpenCV中用CvHistogram表示多维直方图(http://www.opencv.org.cn/index.php/Cv%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86#CvHistogram):- typedef struct CvHistogram
- {
- int type;
- CvArr* bins; //存放每个灰度级数目的数组指针
- float thresh[CV_MAX_DIM][2]; //均匀直方图
- float** thresh2; //非均匀直方图
- CvMatND mat; //直方图数组的内部数据结构
- }
- CvHistogram;
第一个成员type用来指定第二个成员bins的类型。OpenCv中常见到CvArr*的接口,可以用以指定诸如CvMat、CvMatND、IplImage的类型,其实CvArr*的是一个指向void的指针。在函数内部有时需要得到确切的指向类型,这就需要type来指定。thresh用来指定统计直方图分布的上下界。比如[0 255]表示用来统计图像中像素分别在灰度级[0 255]区间的分布情况,CV_MAX_DIM对应直方图的维数,假如设定二维红-蓝直方图的thresh为[0 255;100 200],就是分别统计红色图像灰度级在[0 255]以及蓝色图像在灰度级[100 200]的分布情况。thresh用以指定均匀直方图的分布,我们按每个像素理解自然是“均匀分布”,其实也可以统计像素在几个区间的分布。如果统计像素在2个区间的分布,则对应[0 255]的上下界,均匀分布统计的区间即[0 127] [127 255]分布的概率,这也是为什么thresh第二个维数默认为2——会自动均分上下界;而thresh2指定非均匀的分布,这就需要指定每个区间的上下界,如果要统计直方图在区间(0,10,100,255)的分布,那需要指定thresh2的一个维度为[0 10 100 255],所以用float**形式表示。mat简单说就是存储了直方图的信息,即我们统计的直方图分布概率。创建直方图 cvCreateHist()
OpenCV中用cvCreateHist()创建一个直方图:- CvHistogram* cvCreateHist(
- int dims, //直方图维数
- int* sizes,//直翻图维数尺寸
- int type, //直方图的表示格式
- float** ranges=NULL, //图中方块范围的数组
- int uniform=1 //归一化标识
- );
type有两种:CV_HIST_ARRAY 意味着直方图数据表示为多维密集数组 CvMatND; CV_HIST_TREE 意味着直方图数据表示为多维稀疏数组 CvSparseMat。ranges就是那个复杂的不好理解的thresh的范围,他的内容取决于uniform的值。uniform为0是均匀的,非0时不均匀。计算图像直方图的函数为CalcHist():- void cvCalcHist(
- IplImage** image, //输入图像(也可用CvMat**)
- CvHistogram* hist, //直方图指针
- int accumulate=0, //累计标识。如果设置,则直方图在开始时不被清零。
- const CvArr* mask=NULL //操作 mask, 确定输入图像的哪个象素被计数
- );
实践:一维直方图
下面实践一下用OpenCV生成图像的一维直方图- int main( )
- {
- IplImage * src= cvLoadImage("baboon.jpg");
- IplImage* gray_plane = cvCreateImage(cvGetSize(src),8,1);
- cvCvtColor(src,gray_plane,CV_BGR2GRAY);
- int hist_size = 256; //直方图尺寸
- int hist_height = 256;
- float range[] = {0,255}; //灰度级的范围
- float* ranges[]={range};
- //创建一维直方图,统计图像在[0 255]像素的均匀分布
- CvHistogram* gray_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
- //计算灰度图像的一维直方图
- cvCalcHist(&gray_plane,gray_hist,0,0);
- //归一化直方图
- cvNormalizeHist(gray_hist,1.0);
- int scale = 2;
- //创建一张一维直方图的“图”,横坐标为灰度级,纵坐标为像素个数(*scale)
- IplImage* hist_image = cvCreateImage(cvSize(hist_size*scale,hist_height),8,3);
- cvZero(hist_image);
- //统计直方图中的最大直方块
- float max_value = 0;
- cvGetMinMaxHistValue(gray_hist, 0,&max_value,0,0);
- //分别将每个直方块的值绘制到图中
- for(int i=0;i<hist_size;i++)
- {
- float bin_val = cvQueryHistValue_1D(gray_hist,i); //像素i的概率
- int intensity = cvRound(bin_val*hist_height/max_value); //要绘制的高度
- cvRectangle(hist_image,
- cvPoint(i*scale,hist_height-1),
- cvPoint((i+1)*scale - 1, hist_height - intensity),
- CV_RGB(255,255,255));
- }
- cvNamedWindow( "GraySource", 1 );
- cvShowImage("GraySource",gray_plane);
- cvNamedWindow( "H-S Histogram", 1 );
- cvShowImage( "H-S Histogram", hist_image );
- cvWaitKey(0);
- }
对应的,我们可以用一样的思路统计每个通道的直方图,并绘制图像每个通道像素的分布:我们也可以结合OpenCV的例子生成二维直方图:- IplImage* r_plane = cvCreateImage( cvGetSize(src), 8, 1 );
- IplImage* g_plane = cvCreateImage( cvGetSize(src), 8, 1 );
- IplImage* b_plane = cvCreateImage( cvGetSize(src), 8, 1 );
- IplImage* planes[] = { r_plane, g_plane };
- //将HSV图像分离到不同的通道中
- cvCvtPixToPlane( src, b_plane, g_plane, r_plane, 0 );
- // 生成二维直方图数据结构
- int r_bins =256, b_bins = 256;
- CvHistogram* hist;
- {
- int hist_size[] = { r_bins, b_bins };
- float r_ranges[] = { 0, 255 }; // hue is [0,180]
- float b_ranges[] = { 0, 255 };
- float* ranges[] = { r_ranges,b_ranges };
- hist = cvCreateHist( 2, hist_size, CV_HIST_ARRAY, ranges, 1);
- }
- //计算一张或多张单通道图像image(s) 的直方图
- cvCalcHist( planes, hist, 0, 0 );
- for( int h = 0; h < r_bins; h++ ) {
- for( int s = 0; s < b_bins; s++ ) {
- float bin_val = cvQueryHistValue_2D( hist, h, s ); //查询直方块的值
- int intensity = cvRound( bin_val * 255 / max_value );
- cvRectangle( hist_img,
- cvPoint( h*scale, s*scale ),
- cvPoint( (h+1)*scale - 1, (s+1)*scale - 1),
- CV_RGB(intensity,intensity,intensity),
- CV_FILLED);
- }
- }
最终生成二维直方图:直方图的应用以后再讨论。转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7600666
实验代码下载:http://download.csdn.net/detail/xiaowei_cqu/4328881
Mat格式的参考这里:
http://blog.csdn.net/xiaowei_cqu/article/details/8833799
(转载请注明作者和出处:http://blog.csdn.net/xiaowei_cqu 未经允许请勿用于商业用途)
-
图像直方图MATLAB代码
2018-03-26 22:18:00图像直方图MATLAB代码图像直方图MATLAB代码图像直方图MATLAB代码图像直方图MATLAB代码图像直方图MATLAB代码图像直方图MATLAB代码 -
图像直方图
2019-04-11 17:25:08图像直方图概述 1.图像直方图概述 直方图广泛运用于很多计算机视觉运用当中,通过标记帧与帧之间显著的边缘和颜色的统计变化,来检测视频中场景的变化。在每个兴趣点设置一个有相近特征的直方图所构成 “标签”...文章目录
1.图像直方图概述
直方图广泛运用于很多计算机视觉运用当中,通过标记帧与帧之间显著的边缘和颜色的统计变化,来检测视频中场景的变化。在每个兴趣点设置一个有相近特征的直方图所构成 “标签”,用以确定图像中的兴趣点。边缘、色彩、角度等直方图构成了可以被传递给目标识别分类器的一个通用特征类型。色彩和边缘的直方图序列还可以用来识别网络视频是否被复制。
其实,简单来说,直方图就是对数据进行统计的一种方法,并且将统计值组织到一系列实现定义好的 bin 当中。其中, bin 为直方图中经常用到的一个概念,可以译为 “直条” 或 “组距”,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度、方向、色彩或任何其他特征。且无论如何,直方图获得的是数据分布的统计图。通常直方图的维数要低于原始数据。
图像直方图(Image Histogram)是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此一张较暗图片的直方图中的数据多集中于左侧和中间部分,而整体明亮、只有少量阴影的图像则相反。CV 领域常借助图像直方图来实现图像的二值化。
直方图的意义如下:
● 直方图是图像中像素强度分布的图形表达方式。
● 它统计了每一个强度值所具有的像素个数。直方图是对数据的统计集合,并将统计结果分布于一系列预定义的 bins 中。这里的数据不仅仅指的是灰度值,且统计数据可能是任何有效描述图像的特征。
假设有一个矩阵包含一张图像的信息(灰度值 0 - 255),既然已知数字的范围包含 256 个值,于是可以按一定规律将这个范围分割成子区域(也就是 bins)。如:
然后再统计每一个 bin(i) 的像素数目。采用这一方法来统计上面的数字矩阵,可以得到下图(其中 x 轴表示 bin,y 轴表示各个 bin 中的像素个数):
直方图的一些术语和细节:
● dims:需要统计的特征数目。在上例中,dims = 1 ,因为仅仅统计了灰度值(灰度图像)。
● bins:每个特征空间子区段的数目,可译为 “直条” 或 “组距”,在上例中, bins = 16。
● range:每个特征空间的取值范围。在上例中,range = [0, 255]。2.直方图的计算与绘制
直方图的计算在 OpenCV 中可使用 calcHist() 函数,而计算完成之后,可以采用 OpenCV 中的绘图函数,如绘制矩形的 rectangle() 函数,绘制线段的 line() 来完成。
2.1 计算直方图:calcHist() 函数
calcHist() 函数用于计算一个或多个阵列的直方图。
void calcHist( const Mat* images, //输入的数组或数据集 int nimages, //输入数组的个数 const int* channels, //需要统计的通道(dim)索引 InputArray mask, //可选的操作掩码,用于标记出统计直方图的数组元素数据 OutputArray hist, //输出的目标直方图 int dims, //需要计算的直方图的维数 const int* histSize, //存放每个直方图尺寸的数组 const float** ranges, //每一维数值的取值范围 bool uniform = true, //指示直方图是否均匀的标识符 bool accumulate = false //累计标识符,主要是允许多从多个阵列中计算单个直方图, //或者用于在特定的时间更新直方图。 )
● 第一个参数:const Mat* 类型的 iamges,输入的数组(或数据集),它们需为相同的深度(CV_8U 或 CV_32F)和相同的尺寸。
● 第二个参数:int 类型的 nimages,输入数组的个数,也就是第一个参数中存放了多少张 “图像”,有几个原数组。
● 第三个参数:const int* 类型的 channels,需要统计的通道(dim)索引。第一个数组通道从 0 到 images[0].channels() - 1,而第二个数组通道从 images[0].channels() 计算到 images[0].channels() + images[1].channels() - 1。
● 第四个参数:InputArray 类型的 mask,可选的操作掩码。如果此掩码不为空,那么它必须为 8 位,并且与 images[i] 有同样的大小和尺寸。这里的非零掩码元素用于标记出统计直方图的数组元素数据。
● 第五个参数:OutputArray 类型的 hist,输出的目标直方图,一个二维数组。
● 第六个参数:int 类型的 dims,需要计算的直方图的维数,必须是正数,且不大于 CV_MAX_DIMS(在 OpenCV3 中等于 32)。
● 第七个参数:const int* 类型的 histSize,存放每个维度的直方图尺寸的数组。
● 第八个参数:const float** 类型的 ranges,表示每一个维度数组(第六个参数 dims)的每一维的边界阵列,可以理解为每一维数值的取值范围。
● 第九个参数:bool 类型的 uniform,指示直方图是否均匀的标识符,有默认值 true。
● 第十个参数:bool 类型的 accumulate,累计标识符,有默认值 false。若其为 true,直方图在配置阶段不会被清零。此功能主要是允许多从多个阵列中计算单个直方图,或者用于在特定的时间更新直方图。2.2 寻找最值:minMaxLoc() 函数
minMaxLoc() 函数的作用是在数组中找到全局最小值和最大值。
void minMaxLoc( InputArray src, double* minVal, double* maxVal = 0, Point* minLoc = 0, Point* maxLoc = 0, InputArray mask = noArray() )
● 第一个参数:InputArray 类型的 src,输入的单通道阵列。
● 第二个参数:double* 类型的 minVal,返回最小值的指针。若无需返回,此参数设置为 NULL。
● 第三个参数:double* 类型的 maxVal,返回最大值的指针,若无需返回,此参数设置为 NULL。
● 第四个参数:Point* 类型的 minLoc,返回最小位置的指针(二维情况下)。若无需返回,此值置为 NULL。
● 第五个参数:Point* 类型的 maxLoc,返回最大位置的指针(二维情况下)。若无需返回,此值置为 NULL。
● 第六个参数:InputArray 类型的 mask,用于选择子阵列的可选掩膜。2.3 示例程序:绘制 H-S 直方图
下面的示例说明如何计算彩色图像的色调,饱和度二维直方图。
● 色调(Hue),饱和度(Saturation)。所以 H-S 直方图就是 色调——饱和度 直方图。#include<opencv2/opencv.hpp> #include<time.h> #include<iostream> using namespace std; using namespace cv; int main() { system("color 2F"); //载入原图,转化为 HSV 颜色模型 Mat srcImage, hsvImage; srcImage = imread("1.jpg"); cvtColor(srcImage, hsvImage, COLOR_BGR2HSV); //将色调量化为 30 个等级,将饱和度量化为 32 个等级 int hueBinNum = 30; //色调的直方图直条数量 int saturationBinNum = 32; //饱和度的直方图直条数量 int histSize[] = { hueBinNum,saturationBinNum }; //定义色调的变化范围为 0 到 179 float hueRanges[] = { 0,180 }; //定义饱和度的变化范围为 0(黑、白、灰)到 255(纯光谱颜色) float saturationRanges[] = { 0,256 }; const float* ranges[] = { hueRanges,saturationRanges }; MatND dstHist; //Mat 一般指二维矩阵,MatND 指多维矩阵( > 2 ) //calcHist 函数中将计算第 0 通道和第 1 通道的直方图 int channels[] = { 0,1 }; //正式调用 calcHist ,进行直方图计算 calcHist( &hsvImage, //输入的数组 1, //数组个数为 1 channels, //通道索引 Mat(), //不使用掩膜 dstHist, //输出的目标直方图 2, //需要计算的直方图的维度为 2 histSize, //存放每个维度的直方图尺寸的数组 ranges, //每一维数值的取值范围数组 true, //指示直方图是否均匀的标识符,true 表示均匀的直方图 false //累计标识符,false 表示直方图在配置阶段会被清零 ); //为绘制直方图准备参数 double maxValue = 0; //最大值 //查找数组和子数组的全局最小值和最大值存入 maxValue 中 minMaxLoc(dstHist, 0, &maxValue, 0, 0); int scale = 10; //直方图放大倍数 Mat histImage = Mat::zeros(saturationBinNum * scale, hueBinNum * 10, CV_8UC3); //双重循环,实现直方图绘制 for (int hue = 0; hue < hueBinNum; hue++) { for (int saturation = 0; saturation < saturationBinNum; saturation++) { //直方图直条的值 float binValue = dstHist.at<float>(hue, saturation); int intensity = cvRound(binValue * 255 / maxValue); //强度 //正式进行绘制 rectangle( histImage, Point(hue * scale, saturation * scale), Point((hue + 1) * scale - 1, (saturation + 1) * scale - 1), Scalar::all(intensity), FILLED ); } } imshow("素材图", srcImage); imshow("H-S 直方图", histImage); waitKey(0); return 0; }
素材图:
H-S 图:
2.4 示例程序:计算并绘制图像一维直方图
#include<opencv2/opencv.hpp> #include<time.h> #include<iostream> using namespace std; using namespace cv; int main() { system("color 2F"); Mat srcImage = imread("1.jpg", 0); imshow("原图", srcImage); if (!srcImage.data) { printf("图像读取失败!"); return 0; } MatND dstHist; int dims = 1; float hranges[] = { 0,255 }; const float *ranges[] = { hranges }; //这里需为 const 类型 int size = 256; int channels = 0; //计算图像的直方图 calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges); int scale = 1; Mat dstImage(size * scale, size, CV_8U, Scalar(0)); //获取最大值和最小值 double minValue = 0; double maxValue = 0; minMaxLoc(dstHist, &minValue, &maxValue, 0, 0); //绘制出直方图 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)); } imshow("一维直方图", dstImage); waitKey(0); return 0; }
素材图与其一维直方图:
Mat特指2维矩阵,MatND是多维矩阵(>=3维)
2.5 示例程序:绘制 RGB 三色直方图
#include<opencv2/opencv.hpp> #include<time.h> #include<iostream> using namespace std; using namespace cv; int main() { system("color 2F"); Mat srcImage = imread("1.jpg"); imshow("素材图", srcImage); if (!srcImage.data) { printf("图像读取失败!"); return 0; } int bins = 256; int hist_size[] = { bins }; float range[] = { 0,256 }; const float *ranges[] = { range }; MatND redHist, greenHist, blueHist; int channels_r[] = { 0 }; //进行直方图的计算(红色分量部分) calcHist(&srcImage, 1, channels_r, Mat(), //不使用掩膜 redHist, 1, hist_size, ranges, true, false); //进行直方图计算(绿色分量部分) int channels_g[] = { 1 }; calcHist(&srcImage, 1, channels_g, Mat(), greenHist, 1, hist_size, ranges, true, false); //进行直方图计算(蓝色分量部分) int channels_b[] = { 2 }; calcHist(&srcImage, 1, channels_b, Mat(), blueHist, 1, hist_size, ranges, true, false); //准备参数绘制三色直方图 double maxValue_red, maxValue_green, maxValue_blue; minMaxLoc(redHist, 0, &maxValue_red, 0, 0); minMaxLoc(greenHist, 0, &maxValue_green, 0, 0); minMaxLoc(blueHist, 0, &maxValue_blue, 0, 0); int scale = 1; int histHeight = 256; //bins * 3 是因为要绘制三个通道,每个通道的像素取值在 0-bins Mat histImage = Mat::zeros(histHeight, bins * 3, CV_8UC3); //开始绘制 for (int i = 0; i < bins; i++) { float binValue_red = redHist.at<float>(i); float binValue_green = greenHist.at<float>(i); float binValue_blue = blueHist.at<float>(i); //计算高度时的乘除与下面绘图的 histHeight - intensity 是为了便于显示,否则有的色度很低 //要绘制的高度 int intensity_red = cvRound(binValue_red * histHeight / maxValue_red); int intensity_green = cvRound(binValue_green * histHeight / maxValue_green); int intensity_blue = cvRound(binValue_blue * histHeight / maxValue_blue); //绘制红色分量直方图 rectangle(histImage, Point(i * scale, histHeight - 1), Point((i + 1) * scale - 1, histHeight - intensity_red), Scalar(255,0,0)); //绘制绿色分量直方图 rectangle(histImage, Point((i + bins) * scale, histHeight - 1), Point((i + bins + 1) * scale - 1, histHeight - intensity_green), Scalar(0, 255, 0)); //绘制分量直方图 rectangle(histImage, Point((i + bins * 2) * scale, histHeight - 1), Point((i + bins * 2 + 1) * scale - 1, histHeight - intensity_blue), Scalar(0, 0, 255)); } imshow("图像的 RGB 直方图", histImage); waitKey(0); return 0; }
素材图:
三色直方图:
-
六、图像直方图---opencv(图像直方图、图像均衡化)
2019-09-18 14:29:25一、图像直方图: 直方图是对图像像素的统计分布,它统计了每个像素(0到L-1)的数量。 直方图均衡化就是将原始的直方图拉伸,使之均匀分布在全部灰度范围内,从而增强图像的对比度。 直方图均衡化的...一、图像直方图:
-
直方图是对图像像素的统计分布,它统计了每个像素(0到L-1)的数量。
-
直方图均衡化就是将原始的直方图拉伸,使之均匀分布在全部灰度范围内,从而增强图像的对比度。
-
直方图均衡化的中心思想是把原始图像的的灰度直方图从比较集中的某个区域变成在全部灰度范围内的均匀分布。
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) #返回hist
- images代表输入的图像,可以不用方括号括起来
- channels代表通道数,若为灰度图像,就使用第一个即可;
- mask代表掩码,使用就输入对应的mask,没使用的话就设为None
- histSize,代表bin的数目,使用中括号;
- ranges,为像素值的取值范围,使用中括号;一般为[0,256];
- accumulate是一个布尔值,用来表示直方图是否叠加。
人们把照片的亮度分为0到255共256个数值,数值越大,代表的亮度越高。其中0代表纯黑色的最暗区域,255表示最亮的纯白色,而中间的数字就是不同亮度的灰色。
下面看一个示例:原始图像如下:我们来求其直方图!
import cv2 import matplotlib.pyplot as plt import numpy as np def cv_show(name,img): cv2.imshow(img,name) cv2.waitKey(0) cv2.destroyAllWindows() img = cv2.imread('../image/cat.jpg',0) # cv_show(img,'img') """ calcHist()参数: 1、图像 2、通道 3、mask 4、histSize,bin的数目,使用中括号 5、像素值的取值范围;使用中括号 """ img1 = cv2.calcHist(img,[0],None,[256],[0,256]) print(img1.shape) plt.hist(img.ravel(),256); plt.imsave('../image/cat_hist.png',img1) plt.show()
刚刚是针对灰度图像;下面我们输出三个通道的颜色分布;分别对应(B,G,R)
#输出三个通道的颜色分布 img = cv2.imread('../image/cat.jpg') color = ('b','g','r') #枚举,i对应的是index,而col对应的是相应的Index对应的颜色 for i,col in enumerate(color): histr = cv2.calcHist([img],[i],None,[256],[0,256]) plt.plot(histr,color=col) plt.xlim([0,256]) plt.show() #不添加这个不显示图片
二、mask掩码操作:
掩码可以简单理解为;保留图像的某个部分!而掩盖掉不想要的部分;这个掩码可以自己设置;下面我们创建一个和对应图像尺寸相同的mask;如下图所示;
1、创建掩码
#创建掩码 mask = np.zeros(img.shape[:2],np.uint8) print(mask.shape) mask[90:300,90:400] = 255 # cv_show(mask,'mask')
2、进行图像掩码操作
img = cv2.imread('../image/cat.jpg',0) mask_img = cv2.bitwise_and(img,img,mask=mask) #取与 # cv_show(mask_img,'mask_img') #完整的图像直方图信息 hist_full = cv2.calcHist([img],[0],None,[256],[0,256]) #掩码后的直方图 hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256]) plt.subplot(221),plt.imshow(img,'gray') plt.subplot(222),plt.imshow(mask,'gray') plt.subplot(223),plt.imshow(mask_img,'gray') plt.subplot(224),plt.plot(hist_full),plt.plot(hist_mask) plt.xlim([0,256]) plt.show()
三、图像均衡化
直方图均衡化就是将原始的直方图拉伸,使之均匀分布在全部灰度范围内,从而增强图像的对比度。直方图均衡化的中心思想是把原始图像的的灰度直方图从比较集中的某个区域变成在全部灰度范围内的均匀分布。旨在使得图像整体效果均匀,黑与白之间的各个像素级之间的点更均匀一点。
左边为原图,右边为均衡化的图像;明显右边图像更明显些,但是部分细节却因此消失了!
""" 图像均衡化 图像均衡化后,像素点更加明显了,图片看起来也更加清晰 但是会丢失一些细节 """ img2 = cv2.imread('../image/clahe.jpg',0) #cv_show(img2,'img2') plt.hist(img2.ravel(),256) # plt.show() equ = cv2.equalizeHist(img2) plt.hist(equ.ravel(),256) # plt.show() res = np.hstack((img2,equ)) cv_show(res,'res')
四、自适应均衡化
自适应均衡化,比均衡化相比而言,保留了图像的细节,同时图像的清晰度也显著提高了!
""" 自适应直方图均衡化 """ clahe = cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8)) res_clahe = clahe.apply(img2) res = np.hstack((img2,equ,res_clahe)) cv_show(res,'res')
-
-
图像直方图与直方图均衡化
2016-01-29 12:47:11图像直方图与直方图均衡化图像直方图以及灰度与彩色图像的直方图均衡化图像直方图:概述:图像的直方图用来表征该图像像素值的分布情况。用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区...图像直方图与直方图均衡化
图像直方图以及灰度与彩色图像的直方图均衡化
图像直方图:
概述:
图像的直方图用来表征该图像像素值的分布情况。用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目。
图像直方图图形化显示不同的像素值在不同的强度值上的出现频率,对于灰度图像来说强度范围为[0~255]之间,对于RGB的彩色图像可以独立显示三种颜色的图像直方图。
同时直方图是用来寻找灰度图像二值化阈值常用而且是有效的手段之一,如果一幅灰度图像的直方图显示为两个波峰,则二值化阈值应该是这两个波峰之间的某个灰度值。
并且直方图是调整图像对比度的重要依据,直方图拉伸和直方图均衡化是两种最常见的间接对比度增强方法。
下面我将介绍如何用python绘制图像直方图,以及直方图均衡化的原理并给出灰度图与彩色图的直方图均衡化。
使用python绘制图像直方图:
在之前写的《python基本图像操作》中已经提到了如何用python去绘制图像的直方图,python的优点就是具有众多扩展库和更加易用,所以可以避免陷入算法细节从更宏观的角度实现自己的想法,所以用python绘制直方图非常简单:
使用Matplotlib绘制直方图
(灰度)图像的直方图可以使用 hist() 函数绘制:
hist() 函数的第二个参数指定小区间的数目。需要注意的是,因为 hist() 只接受一维数组作为输入,所以我们在绘制图像直方图之前,必须先对图像进行压平处理。flatten() 方法将任意数组按照行优先准则转换成一维数组。
# -*- coding: utf-8 -*- from PIL import Image from pylab import * # 读取图像到数组中,并灰度化 im = array(Image.open('./source/test.jpg').convert('L')) # 直方图图像 hist(im.flatten(),128) # 显示 show()
测试图片
Test.jpg
运行结果
直方图的原理也很简单了,自己实现也非常简单,所以也没有必要再过多叙述,具体细节可以去看一下C++的实现。
直方图均衡化(Histogram Equalization):
概述:
直方图均衡化是非常有用的一种变换,直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。在对图
像做进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好的方法,并且可以增强图像的对比度。如果一副图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是一种能仅靠输入图像直方图信息自动达到这种效果的变换函数。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展像原取值的动态范围,提高了对比度和灰度色调的变化,使图像更加清晰。
图像对比度增强的方法可以分成两类:一类是直接对比度增强方法;另一类是间接对比度增强方法。直方图拉伸和直方图均衡化是两种最常见的间接对比度增强方法。直方图拉伸是通过对比度拉伸对直方图进行调整,从而“扩大”前景和背景灰度的差别,以达到增强对比度的目的,这种方法可以利用线性或非线性的方法来实现;直方图均衡化则通过使用累积函数对灰度值进行“调整”以实现对比度的增强。
直方图均衡化是图像处理领域中利用图像直方图对对比度进行调整的方法。这种方法通常用来增加许多图像的局部对比度,尤其是当图像的有用数据的对比度相当接近的时候。通过这种方法,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。
这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。这种方法的一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。
这种方法的一个缺点是它对处理的数据不加选择,它可能会增加背景杂讯的对比度并且降低有用信号的对比度;变换后图像的灰度级减少,某些细节消失;某些图像,如直方图有高峰,经处理后对比度不自然的过分增强。实现:
直方图均衡化处理的“中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改变成“均匀”分布直方图分布。
直方图均衡化的基本思想是把原始图的直方图变换为均匀分布的形式,这样就增加了象素灰度值的动态范围从而可达到增强图像整体对比度的效果。设原始图像在(x,y)处的灰度为f,而改变后的图像为g,则对图像增强的方法可表述为将在(x,y)处的灰度f映射为g。在灰度直方图均衡化处理中对图像的映射函数可定义为:g = EQ (f),这个映射函数EQ(f)必须满足两个条件(其中L为图像的灰度级数):
(1)EQ(f)在0≤f≤L-1范围内是一个单值单增函数。这是为了保证增强处理没有打乱原始图像的灰度排列次序,原图各灰度级在变换后仍保持从黑到白(或从白到黑)的排列。
(2)对于0≤f≤L-1有0≤g≤L-1,这个条件保证了变换前后灰度值动态范围的一致性。
累积分布函数(cumulative distribution function,CDF)即可以满足上述两个条件,并且通过该函数可以完成将原图像f的分布转换成g的均匀分布。此时的直方图均衡化映射函数为:
累计分布函数(CDF)就是是概率密度函数(probability density function/pdf)的积分,相信学过概率论的同学对他一定不陌生:
累积分布函数(cumulative distribution function)定义 对连续函数,所有小于等于a的值,其出现概率的和。F(a)=P(x<=a)
所以总结一下灰度图直方图均衡化算法的步骤就是:
- (1) 根据输入的灰度图像计算其原始直方图
- (2) 对输入的原始直方图进行累加,计算其cdf
- (3) 使用累计分布函数的插值计算新的灰度值(这里我使用比较常用的线性插值)
使用python及其数学库的话求直方图和cdf的方法已经为我们封装好了,所以实现起来代码量是很少的:
# -*- coding: utf-8 -*- from PIL import Image from pylab import * #读取图像到数组中,并灰度化 im = array(Image.open('./source/test.jpg').convert('L')) #绘制原始直方图 subplot(231) hist(im.flatten(),256) #计算图像直方图(每个bins数组的区间值对应一个imhist数组中的强度值) imhist,bins = histogram(im.flatten(),256,normed=True) #计算累积分布函数 cdf = imhist.cumsum() #累计函数归一化(由0~1变换至0~255) cdf = cdf*255/cdf[-1] #绘制累计分布函数 subplot(232) plot(bins[:256],cdf) #依次对每一个灰度图像素值(强度值)使用cdf进行线性插值,计算其新的强度值 #interp(x,xp,yp) 输入原函数的一系列点(xp,yp),使用线性插值方法模拟函数并计算f(x) im2 = interp(im.flatten(),bins[:256],cdf) #将压平的图像数组重新变成二维数组 im2 = im2.reshape(im.shape) # 显示均衡化之后的直方图图像 subplot(233) hist(im2.flatten(),256) #显示原始图像 gray() subplot(234) imshow(im) #显示变换后图像 subplot(236) imshow(im2) show()
运行结果
其中线性插值公式是最常用、最简单的插值公式。线性插值和双线性插值是图形学中常用的技术,旋转变换后图片会有一些不连续点,就是通过双线性插值法解决的,之后我会单独写一篇博客进行详细介绍,这里简单介绍一下线性插值的原理:
处理分离的数据,如果想知道分离点之间的某些值,需要用到某种类型的插值。
使用线性插值,通过连接两点的线段找到X=2.7对应的Y值
0.7* (maxY-min Y)+minY=0.7*(20-10)+10=0.7*10+10=17
(14-minX)/(maxX-minX) =(14-13)/(16-13)=0.33
0.33* (maxY-minY)+minY=0.33*(46-35)+35=0.33*11+35=3.67+35=38.67
顺便说一下,基本所有的线性插值返回的都是浮点数。
所以我们使用插值技术利用离散的cdf得出新的灰度强度值。
彩色图像的直方图均衡化(Histogram Equalization):
一幅彩色图像由RGB三色通道构成,每个通道都是描述了该种颜色的强度(0-255)的一幅灰度图,所以比较简单的应用于彩色图像的均衡化就是把每个颜色通道均衡化之后进行合成,下面是我的具体实现:
# -*- coding: utf-8 -*- from PIL import Image from pylab import * import copy # 读取图像到数组中 im = array(Image.open('./source/test.jpg')) #获取通道 r = im[:,:,0] g = im[:,:,1] b = im[:,:,2] #显示各个通道原始直方图,均值化之后的直方图以及累计分布函数 figure() #计算各通道直方图 imhist_r,bins_r = histogram(r,256,normed=True) imhist_g,bins_g = histogram(g,256,normed=True) imhist_b,bins_b = histogram(b,256,normed=True) subplot(331) hist(r.flatten(),256) subplot(332) hist(g.flatten(),256) subplot(333) hist(b.flatten(),256) #各通道累积分布函数 cdf_r = imhist_r.cumsum() cdf_g = imhist_g.cumsum() cdf_b = imhist_b.cumsum() #累计函数归一化(由0~1变换至0~255) cdf_r = cdf_r*255/cdf_r[-1] cdf_g = cdf_g*255/cdf_g[-1] cdf_b = cdf_b*255/cdf_b[-1] #绘制累计分布函数 subplot(334) plot(bins_r[:256],cdf_r) subplot(335) plot(bins_g[:256],cdf_g) subplot(336) plot(bins_b[:256],cdf_b) #绘制直方图均衡化之后的直方图 im_r = interp(r.flatten(),bins_r[:256],cdf_r) im_g = interp(g.flatten(),bins_g[:256],cdf_g) im_b = interp(b.flatten(),bins_b[:256],cdf_b) # 显示直方图图像 subplot(337) hist(im_r,256) subplot(338) hist(im_g,256) subplot(339) hist(im_b,256) #显示原始通道图与均衡化之后的通道图 figure() gray() #原始通道图 im_r_s = r.reshape([im.shape[0],im.shape[1]]) im_g_s = g.reshape([im.shape[0],im.shape[1]]) im_b_s = b.reshape([im.shape[0],im.shape[1]]) #均衡化之后的通道图 im_r = im_r.reshape([im.shape[0],im.shape[1]]) im_g = im_g.reshape([im.shape[0],im.shape[1]]) im_b = im_b.reshape([im.shape[0],im.shape[1]]) subplot(231) imshow(im_r_s) subplot(232) imshow(im_g_s) subplot(233) imshow(im_b_s) subplot(234) imshow(im_r) subplot(235) imshow(im_g) subplot(236) imshow(im_b) #显示原始图像与均衡化之后的图像 figure() #均衡化之后的图像 im_p = copy.deepcopy(im) im_p[:,:,0] = im_r im_p[:,:,1] = im_g im_p[:,:,2] = im_b subplot(121) imshow(im) subplot(122) imshow(im_p) show()
运行结果
figure1:RGB通道的原始直方图,cdf,均衡化后的直方图
figure2:RGB通道的原始图像,均衡化后的图像
figure3:原始图像,均衡化后的图像
结语:
本篇博客介绍了图像直方图和图像直方图均衡化的原理和方法,希望我的博客对大家有所帮助~
-
OpenCV图像直方图
2019-03-28 08:28:53OpenCV图像直方图 微信公众号:幼儿园的学霸 个人的学习笔记,关于OpenCV,关于机器学习, …。问题或建议,请公众号留言; 灰度直方图(Histogram)是数字图像处理中最简单、最有用的工具之一,它概括了一幅图像的灰度... -
OpenCv生成图像直方图
2012-05-25 12:02:01数字图像直方图(一维和二维直方图生成) 参考博客:http://blog.csdn.net/xiaowei_cqu/article/details/7600666 -
Python opencv图像处理基础总结(三) 图像直方图 直方图应用 直方图反向投影
2020-08-23 13:32:19python与opencv进行图像处理基础总结,图像直方图的绘制;直方图应用,利用直方图均衡化可以实现增强图像对比度的效果,还可以利用直方图比较来对比两张图像相似度;直方图反向投影,用于图像分割或查找图像中感兴趣... -
python图像直方图与直方图均衡化
2018-01-31 23:10:22图像直方图以及灰度与彩色图像的直方图均衡化 图像直方图: 图像的直方图用来表征该图像像素值的分布情况。用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目。... -
opencv 图像直方图
2017-12-19 15:13:30图像直方图 -
Opencv学习笔记四(图像ROI、图像直方图、图像均衡化、图像直方图比较、直方图反向投影)
2019-07-14 16:12:40图像的ROI(region of interest)是指图像中感兴趣区域、在OpenCV中图像设置图像ROI区域,实现只对ROI区域操作。 矩形ROI区域提取 矩形ROI区域copy 不规则ROI区域 ROI区域mask生成 像素位 and操作 提取到ROI区域... -
什么是图像直方图?
2018-03-26 18:19:56什么是图像直方图?直方图显示图像数据时会以左暗又亮的分布曲线形式呈现出来,而不是显示原图像数据,并且可以通过算法来对图像进行按比例缩小,且具有图像平移、旋转、缩放不变性等众多优点。直方图在进行图像计算... -
vc图像直方图均衡化和直方图显示
2010-04-21 20:55:57vc代码实现图像直方图均衡化,并显示原始图像和处理后图像的直方图。 -
真正搞懂图像直方图
2019-10-12 18:45:45一、什么是图像直方图?图像直方图有什么用处? 二、计算图像直方图 1、OpenCV版 2、PLT版 3、numpy版 4、HSV图像,H-S直方图 三、直方图均衡 1、全局均衡 2、自定义均衡程度(clahe) 四、直方图之间的... -
python之图像直方图
2020-08-08 20:22:51目录1 图像直方图1.1 直方图定义2 pyplot绘制直方图3 opencv绘制直方图4 直方图均衡化4 实例 1 图像直方图 1.1 直方图定义 定义:横坐标:图像中各个像素点的灰度级 ;纵坐标:就有该灰度级的像素个数 归一化直方图:... -
数字图像处理之图像直方图
2017-07-31 21:02:52图像直方图反映了图像像素分布的统计特性,是图像处理中简单有效的工具。本文主要包括图像直方图计算,直方图均衡和直方图规定化三个部分。 -
图像直方图之图像对比度
2018-08-14 21:51:48图像直方图之图像对比度 直方图也可以描述图像对比度的情况。对比度是衡量某一图像某一场景中亮区域和暗区域之间亮度差异的尺度。宽广的直方图可以反映某一图像具有较高对比度,反之,较为窄小的直方图反映了某一... -
matlab创建图像直方图以及直方图均衡化
2020-11-24 22:40:521. 显示图像直方图:imhist (img) === img需要为单通道图像 2. 直方图均衡化: histeq(img) === img需要为单通道图像 上述函数都需要保证图像为单通道图像,否则会提示“需要的 ... -
MATLAB-数字图像处理 图像直方图归一化
2019-09-02 21:16:05图像直方图归一化 图像直方图概念: 图像直方图是反映一个图像像素分布的统计表,其实横坐标代表了图像像素的种类,可以是灰度的,也可以是彩色的。纵坐标代表了每一种颜色值在图像中的像素总数或者占所有像素个数的... -
计算机视觉—图像直方图与直方图均衡化
2019-06-03 15:05:54图像直方图: 一副灰度图像是由不同的灰度的像素值组成的,而图像中的灰度值的分布则反映了一定的图像特征,即图像亮度特征,如果直方图的值偏向于0,则表示该图像亮度偏暗,如果直方图的值偏向于255(我们通常... -
图像处理之图像直方图
2020-07-03 19:43:48目的:直方图是对图像像素的统计分布,它统计了每个像素(0到255)的数量。 函数:cv2.calcHist(images, channels, mask, histSize, ranges) 参数说明:参数1:待统计图像,需用中括号括起来; 参数2:待计算的通道... -
什么是图像直方图&直方图均衡的原理和作用&图像信噪比的概念
2017-03-27 15:24:13图像直方图:图像直方图是反映一个图像像素分布的统计表,其实横坐标代表了图像像素的种类,可以是灰度的,也可以是彩色的。纵坐标代表了每一种颜色值在图像中的像素总数或者占所有像素个数的百分比。 图像是由像素... -
MATLAB--数字图像处理 图像直方图均衡化
2019-09-03 15:43:01图像直方图均衡化 首先,我们要理解什么是图像直方图均衡化: 把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定... -
opencv 图像直方图 calcHist
2016-03-07 09:58:43图像直方图的概念 图像直方图是反映一个图像像素分布的统计表,其实横坐标代表了图像像素的种类,可以是灰度的,也可以是彩色的。纵坐标代表了每一种颜色值在图像中的像素总数或者占所有像素个数的百分比。 ...
-
性能测试面面观
-
poj1159题解(动态规划)
-
ACDSee.exe
-
python数据分析基础
-
add.zip vue 三级联动
-
JS事件冒泡
-
java----抽象类与最终类注意点
-
SQL Server 2016 高可用灾备技术合集
-
GCJ2gps.bas
-
信息系统项目管理师考前必做170题.pdf
-
Centos6.5安装Oracle11g.doc
-
微信支付2021系列之付款码支付一学就会java版
-
仿真钢琴-javascript实战
-
NC 同一个表,执行insert生效,执行update无效
-
WPF上位机数据采集与监控系统零基础实战
-
ProBuilder快速原型开发技术
-
hadoop自动化运维工具Ambari应用实践
-
【数据分析-随到随学】Python语法强化与数据处理
-
Java无损导出及转换word文档
-
从零开始搭建一个Spring boot项目的坑及爬坑