图像处理之图像直方图
1、什么是图像灰度直方图?什么是直方图均衡化?什么是直方图规定化?
OpenCV实现图像直方图:
// RGB三色分量直方图.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include //包含核心功能,尤其是底层数据结构和算法函数。
#include //包含读写图像及视频的函数,以及操作图形用户界面函数。
#include //包含图像处理函数。
#include
using namespace std;
//绘制 RGB 图像分离通道后的每一单通道的直方图
void f_getRGBHist(cv::Mat& img)
{
int bins = 256;
int hist_size[] = {bins};
float range[] = {0, 256};
const float* ranges[] = {range};
cv::MatND hist_r, hist_g, hist_b;
int nImages = 1;
int dims = 1;
int channels_r[] ={0};
cv::calcHist(&img, 1, channels_r, cv::Mat(), hist_r, 1, hist_size, ranges, true, false);
int channels_g[] = {1};
cv::calcHist(&img, 1, channels_g, cv::Mat(), hist_g, 1, hist_size, ranges, true, false);
int channels_b[] = {2};
cv::calcHist(&img, 1, channels_b, cv::Mat(), hist_b, 1, hist_size, ranges, true, false);
double max_val_r, max_val_g, max_val_b;
double min_val_r, min_val_g, min_val_b;
cv::minMaxLoc(hist_r, &min_val_r, &max_val_r, 0, 0);
cv::minMaxLoc(hist_g, &min_val_g, &max_val_g, 0, 0);
cv::minMaxLoc(hist_b, &min_val_b, &max_val_b, 0, 0);
int scale = 1;
int hist_height = 256;
cv::Mat hist_img = cv::Mat::zeros(hist_height, bins*3, CV_8UC3);
for (int i=0; i(i);
float bin_val_g = hist_g.at(i);
float bin_val_b = hist_b.at(i);
int intensity_r = cvRound(bin_val_r*hist_height/max_val_r); //要绘制的高度
int intensity_g = cvRound(bin_val_g*hist_height/max_val_g);
int intensity_b = cvRound(bin_val_b*hist_height/max_val_b);
//cv::rectangle();
cv::rectangle(hist_img, cv::Point(i*scale, hist_height-1), cv::Point((i+1)*scale-1,hist_height-intensity_r), CV_RGB(255,0,0));
cv::rectangle(hist_img, cv::Point((i+bins)*scale, hist_height-1), cv::Point((i+bins+1)*scale-1,hist_height-intensity_g), CV_RGB(0,255,0));
cv::rectangle(hist_img, cv::Point((i+bins*2)*scale, hist_height-1), cv::Point((i+bins*2+1)*scale, hist_height-intensity_b), CV_RGB(0,0,255));
}
cout << "finish drawing histogram." << endl;
cv::imwrite("../RGB_Histogram.bmp", hist_img);
cv::namedWindow("RGB Histogram", 1);
cv::imshow("RGB Histogram", hist_img);
cv::waitKey();
}
int _tmain(int argc, _TCHAR* argv[])
{
cv::Mat src;
src = cv::imread("../baboon.bmp");
if (src.data==NULL)
{
cout << "fail to load image." << endl;
return 0;
}
cv::namedWindow("source", 1);
cv::imshow("source", src);
f_getRGBHist(src);
f_getRGBHist2(src);
return 0;
}
图1 原图src
图2 RGB三色分量的直方图
int f_getRGBHist2(cv::Mat& src)
{
//Mat src;
Mat dst;
/ 装载图像
//src = imread( "../baboon.jpg", 1 );
if( !src.data )
{
return -1;
}
Mat gray_Image;
cvtColor(src, gray_Image, CV_BGR2GRAY);
imshow("gray_Image", gray_Image);
imwrite("../gray_Image.bmp", gray_Image);
Mat result;
//直方图均衡化
equalizeHist(gray_Image, result);
imshow("equalizeHist", result);
imwrite("../equalizeHist.bmp", result);
/// BGR彩色图像分割成3个单通道图像 ( R, G 和 B )
vector rgb_planes;
split( src, rgb_planes );
/// 设定bin数目
int histSize = 255;
/// 设定取值范围 ( R,G,B) )
float range[] = { 0, 255 } ;
const float* histRange = { range };
bool uniform = true;
bool accumulate = false;
Mat r_hist, g_hist, b_hist;
/// 计算直方图:
calcHist( &rgb_planes[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
calcHist( &rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
calcHist( &rgb_planes[2], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
// 创建直方图画布
int hist_w = 600; int hist_h = 400;
int bin_w = cvRound( (double) hist_w/histSize );
Mat rgb_hist[3];
//初始化
for(int i=0; i<3; ++i)
{
rgb_hist[i] = Mat(hist_h, hist_w, CV_8UC3, Scalar::all(0));
}
Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
/// 将直方图归一化到范围 [ 0, histImage.rows ]
normalize(r_hist, r_hist, 0, histImage.rows-10, NORM_MINMAX, -1, Mat() );
normalize(g_hist, g_hist, 0, histImage.rows-10, NORM_MINMAX, -1, Mat() );
normalize(b_hist, b_hist, 0, histImage.rows-10, NORM_MINMAX, -1, Mat() );
/// 在直方图画布上画出直方图
for( int i = 1; i < histSize; i++ )
{
line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at(i-1)) ) ,
Point( bin_w*(i), hist_h - cvRound(r_hist.at(i)) ),
Scalar( 0, 0, 255), 2, 8, 0 );
line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at(i-1)) ) ,
Point( bin_w*(i), hist_h - cvRound(g_hist.at(i)) ),
Scalar( 0, 255, 0), 2, 8, 0 );
line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at(i-1)) ) ,
Point( bin_w*(i), hist_h - cvRound(b_hist.at(i)) ),
Scalar( 255, 0, 0), 2, 8, 0 );
}
/// 显示直方图
namedWindow("calcHist Demo", 1);
imshow("calcHist Demo", histImage );
imwrite("../calcHist_Demo.bmp", histImage );
for (int j=0; j(r_hist.at(j));
rectangle(rgb_hist[0], Point(j*2+10, rgb_hist[0].rows), Point((j+1)*2+10, rgb_hist[0].rows-val), Scalar(0,0,255),1,8);
val = saturate_cast(g_hist.at(j));
rectangle(rgb_hist[1], Point(j*2+10, rgb_hist[1].rows), Point((j+1)*2+10, rgb_hist[1].rows-val), Scalar(0,255,0),1,8);
val = saturate_cast(b_hist.at(j));
rectangle(rgb_hist[2], Point(j*2+10, rgb_hist[2].rows), Point((j+1)*2+10, rgb_hist[2].rows-val), Scalar(255,0,0),1,8);
}
imshow("R", rgb_hist[0]);
imshow("G", rgb_hist[1]);
imshow("B", rgb_hist[2]);
cv::imwrite("../R_Histogram.bmp", rgb_hist[0]);
cv::imwrite("../G_Histogram.bmp", rgb_hist[1]);
cv::imwrite("../B_Histogram.bmp", rgb_hist[2]);
waitKey(0);
return 0;
}
图3 灰度图像(BGR--->GRAY)
图4 经过灰度直方图均衡化处理后的图像
对比图3和图4 ,可以明显的看出图4 的整体对比度要比图3 强很多,亮度也要明亮很多。由此可以看出图像直方图均衡化的作用就是:增加像素灰度值的动态范围,增强图像整体对比度。 由此可见,可以通过图像直方图均衡化的方式来达到图像增强的目的。
图5 R通道的图像直方图
图6 G通道的图像直方图
图7 B通道的图像直方图
图8 灰度图像直方图
//绘制H-S二维直方图
int f_HS_hist(cv::Mat src)
{
//cv::Mat src;
//src=cv::imread("../baboon.jpg");
cv::Mat hsv;
cv::cvtColor(src, hsv, CV_BGR2HSV);
cv::namedWindow( "HSV", 1 );
cv::imshow( "HSV", hsv );
cv::waitKey();
cv::imwrite("../hsvImg.bmp", hsv );
// Quantize the hue to 30 levels
// and the saturation to 32 levels
int hbins = 256, sbins = 180;
int histSize[] = {hbins, sbins}; //每个维度的直方图尺寸的数组
// hue varies from 0 to 179, see cvtColor
//定义色调变化范围为 0到179
float hranges[] = { 0, 180 };
// saturation varies from 0 (black-gray-white) to
// 255 (pure spectrum color)
float sranges[] = { 0, 256 };
const float* ranges[] = { hranges, sranges }; //每一维数值的取值范围
cv::MatND hist; //输出的目标直方图
// we compute the histogram from the 0-th and 1-st channels
int channels[] = {0, 1};//H 通道, S 通道
cv::calcHist(
&hsv, //输入的数组
1, //数组个数为 1
channels, //通道索引
cv::Mat(), //不使用掩模
hist, //输出的目标直方图
2, //需要计算的直方图维数为2
histSize, //存放每个维度的直方图尺寸的数组
ranges, //每一维数值的取值范围数组
true, //指示直方图是否均匀的标识,true表示均匀
false ); //累计标识,false表示直方图在配置阶段会被清零
double maxVal=0;
cv::minMaxLoc(hist, 0, &maxVal, 0, 0);
int scale = 2;
cv::Mat histImg = cv::Mat::zeros(sbins*scale, hbins*scale, CV_8UC3);
//双层循环,绘制直方图
for( int h = 0; h < hbins; h++ )
{
for( int s = 0; s < sbins; s++ )
{
float binVal = hist.at(h, s); //直方图直条的值
int intensity = cvRound(binVal*255/maxVal); //强度
//正式进行绘制
cv::rectangle( histImg, cv::Point(h*scale, s*scale),
cv::Point( (h+1)*scale - 1, (s+1)*scale - 1),
cv::Scalar::all(intensity), CV_FILLED );
}
}
cv::namedWindow( "Source", 1 );
cv::imshow( "Source", src );
cv::namedWindow( "H-S Histogram", 1 );
cv::imshow( "H-S Histogram", histImg );
cv::waitKey();
cv::imwrite("../H-S_hist.bmp", histImg );
return 0;
}
图9 - HSV图像
图10 - H-S直方图