灰度直方圖介紹:
http://hi.baidu.com/wen_sift/blog/item/83fd56ca3e6b1b36b600c887.html
灰度直方圖均衡化:
http://hi.baidu.com/wen_sift/blog/item/b808fd0d9f67392b6b60fb54.html
利用OpenCV計算並繪制灰度直方圖 :
View Code
#include <cv.h> #include <highgui.h> #pragma comment( lib, "cv.lib" ) #pragma comment( lib, "cxcore.lib" ) #pragma comment( lib, "highgui.lib" ) int main() { IplImage* src=cvLoadImage("lena.jpg",0); int width=src->width; int height=src->height; int step=src->widthStep; uchar* data=(uchar *)src->imageData; int hist[256]={0}; for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { hist[data[i*step+j]]++; } } int max=0; for(i=0;i<256;i++) { if(hist[i]>max) { max=hist[i]; } } IplImage* dst=cvCreateImage(cvSize(400,300),8,3); cvSet(dst,cvScalarAll(255),0); double bin_width=(double)dst->width/256; double bin_unith=(double)dst->height/max; for(i=0;i<256;i++) { CvPoint p0=cvPoint(i*bin_width,dst->height); CvPoint p1=cvPoint((i+1)*bin_width,dst->height-hist[i]*bin_unith); cvRectangle(dst,p0,p1,cvScalar(0,255),-1,8,0); } cvNamedWindow("src",1); cvShowImage("src",src); cvNamedWindow("dst",1); cvShowImage("dst",dst); cvWaitKey(0); cvDestroyAllWindows(); cvReleaseImage(&src); cvReleaseImage(&dst); return 0; }
利用opencv現有函數:
View Code
#include <cv.h> #include <highgui.h> #pragma comment( lib, "cv.lib" ) #pragma comment( lib, "cxcore.lib" ) #pragma comment( lib, "highgui.lib" ) int main() { IplImage* src=cvLoadImage("lena.jpg",0); int size=256; float range[]={0,255}; float* ranges[]={range}; CvHistogram* hist=cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1); cvCalcHist(&src,hist,0,NULL); float max=0; cvGetMinMaxHistValue(hist,NULL,&max,NULL,NULL); IplImage* dst=cvCreateImage(cvSize(400,300),8,3); cvSet(dst,cvScalarAll(255),0); double bin_width=(double)dst->width/size; double bin_unith=(double)dst->height/max; for(int i=0;i<size;i++) { CvPoint p0=cvPoint(i*bin_width,dst->height); CvPoint p1=cvPoint((i+1)*bin_width,dst->height-cvGetReal1D(hist->bins,i)*bin_unith); cvRectangle(dst,p0,p1,cvScalar(0,255),-1,8,0); } cvNamedWindow("src",1); cvShowImage("src",src); cvNamedWindow("dst",1); cvShowImage("dst",dst); cvWaitKey(0); cvDestroyAllWindows(); cvReleaseImage(&src); cvReleaseImage(&dst); return 0; }
通過復用上面的代碼。可以得到彩色圖像各通道的直方圖,RGB直方圖代碼如下:
View Code
#include <cv.h> #include <highgui.h> #pragma comment( lib, "cv.lib" ) #pragma comment( lib, "cxcore.lib" ) #pragma comment( lib, "highgui.lib" ) int main() { IplImage* src=cvLoadImage("lena.jpg",1); IplImage* r=cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); IplImage* g=cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); IplImage* b=cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); cvSplit(src,b,g,r,NULL); IplImage* gray = cvCreateImage(cvGetSize(src),8,1); cvCvtColor(src,gray,CV_BGR2GRAY); int size=256; float range[]={0,255}; float* ranges[]={range}; CvHistogram* r_hist = cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1); CvHistogram* g_hist = cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1); CvHistogram* b_hist = cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1); CvHistogram* hist = cvCreateHist(1,&size,CV_HIST_ARRAY,ranges,1); cvCalcHist(&r,r_hist,0,NULL); IplImage* r_dst=cvCreateImage(cvSize(400,300),8,3); cvSet(r_dst,cvScalarAll(255),0); float r_max=0; cvGetMinMaxHistValue(r_hist,NULL,&r_max,NULL,NULL); double r_bin_width=(double)r_dst->width/size; double r_bin_unith=(double)r_dst->height/r_max; for(int i=0;i<size;i++) { CvPoint p0=cvPoint(i*r_bin_width,r_dst->height); CvPoint p1=cvPoint((i+1)*r_bin_width,r_dst->height-cvGetReal1D(r_hist->bins,i)*r_bin_unith); cvRectangle(r_dst,p0,p1,cvScalar(255,0,0),-1,8,0); } cvCalcHist(&g,g_hist,0,NULL); IplImage* g_dst=cvCreateImage(cvSize(400,300),8,3); cvSet(g_dst,cvScalarAll(255),0); float g_max=0; cvGetMinMaxHistValue(g_hist,NULL,&g_max,NULL,NULL); double g_bin_width=(double)g_dst->width/size; double g_bin_unith=(double)g_dst->height/g_max; for(i=0;i<size;i++) { CvPoint p0=cvPoint(i*g_bin_width,g_dst->height); CvPoint p1=cvPoint((i+1)*g_bin_width,g_dst->height-cvGetReal1D(g_hist->bins,i)*g_bin_unith); cvRectangle(g_dst,p0,p1,cvScalar(0,255,0),-1,8,0); } cvCalcHist(&b,b_hist,0,NULL); IplImage* b_dst=cvCreateImage(cvSize(400,300),8,3); cvSet(b_dst,cvScalarAll(255),0); float b_max=0; cvGetMinMaxHistValue(b_hist,NULL,&b_max,NULL,NULL); double b_bin_width=(double)b_dst->width/size; double b_bin_unith=(double)b_dst->height/b_max; for(i=0;i<size;i++) { CvPoint p0=cvPoint(i*b_bin_width,b_dst->height); CvPoint p1=cvPoint((i+1)*b_bin_width,b_dst->height-cvGetReal1D(b_hist->bins,i)*b_bin_unith); cvRectangle(b_dst,p0,p1,cvScalar(0,0,255),-1,8,0); } cvCalcHist(&gray,hist,0,NULL); IplImage* gray_dst=cvCreateImage(cvSize(400,300),8,3); cvSet(gray_dst,cvScalarAll(255),0); float max=0; cvGetMinMaxHistValue(hist,NULL,&max,NULL,NULL); double bin_width=(double)gray_dst->width/size; double bin_unith=(double)gray_dst->height/max; for(i=0;i<size;i++) { CvPoint p0=cvPoint(i*bin_width,gray_dst->height); CvPoint p1=cvPoint((i+1)*bin_width,gray_dst->height-cvGetReal1D(hist->bins,i)*bin_unith); cvRectangle(gray_dst,p0,p1,cvScalar(0),-1,8,0); } IplImage* dst=cvCreateImage(cvSize(800,600),8,3); cvSetZero(dst); CvRect rect = cvRect(0, 0, 400, 300); cvSetImageROI(dst, rect); cvCopy(r_dst, dst); rect = cvRect(400, 0, 400, 300); cvSetImageROI(dst, rect); cvCopy(g_dst, dst); rect = cvRect(0, 300, 400, 300); cvSetImageROI(dst, rect); cvCopy(b_dst, dst); rect = cvRect(400, 300, 400, 300); cvSetImageROI(dst, rect); cvCopy(gray_dst, dst); cvResetImageROI(dst); cvNamedWindow("src",1); cvShowImage("src",src); cvNamedWindow("dst",1); cvShowImage("dst",dst); cvSaveImage("dst.jpg",dst); cvWaitKey(0); cvDestroyAllWindows(); cvReleaseImage(&src); cvReleaseImage(&dst); cvReleaseImage(&r); cvReleaseImage(&g); cvReleaseImage(&b); cvReleaseImage(&gray); cvReleaseImage(&r_dst); cvReleaseImage(&g_dst); cvReleaseImage(&b_dst); cvReleaseImage(&gray_dst); return 0; }
HSV通道直方圖如下:
View Code
// Color_2DHistogram.cpp : 定義控制台應用程序的入口點。 // #pragma comment(lib, "cv210.lib") #pragma comment(lib, "cxcore210.lib") #pragma comment(lib, "highgui210.lib") #pragma comment(lib, "cvaux210.lib") #include <cv.h> #include <highgui.h> #include <iostream> using namespace std; int main( int argc, char** argv ) { IplImage * src; if(argc<2) { printf("Usage: main <image-file-name>\n\7"); exit(0); } // 載入圖像 src=cvLoadImage(argv[1],-1); if(!src) { printf("Could not load image file: %s\n",argv[1]); exit(0); } IplImage* hsv = cvCreateImage( cvGetSize(src), 8, 3 ); //第一個為size,第二個為位深度(8為256度),第三個通道數 IplImage* h_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* s_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* v_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* planes[] = { h_plane, s_plane,v_plane }; //** H 分量划分為16個等級,S分量划分為8個等級 */ int h_bins =16 , s_bins =8, v_bins = 8; int hist_size[] = {h_bins, s_bins, v_bins}; //** H 分量的變化范圍 */ float h_ranges[] = { 0, 180 }; //** S 分量的變化范圍*/ float s_ranges[] = { 0, 255 }; float v_ranges[] = { 0, 255 }; float* ranges[] = { h_ranges, s_ranges,v_ranges}; //** 輸入圖像轉換到HSV顏色空間 */ cvCvtColor( src, hsv, CV_BGR2HSV ); cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 ); //** 創建直方圖,二維, 每個維度上均分 */ CvHistogram * hist = cvCreateHist( 3, hist_size, CV_HIST_ARRAY, ranges, 1 ); //** 根據H,S兩個平面數據統計直方圖 */ cvCalcHist( planes, hist, 0, 0 ); //** 獲取直方圖統計的最大值,用於動態顯示直方圖 */ float max_value; cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 ); //** 設置直方圖顯示圖像 */ int height = 100; int width = (h_bins*s_bins*v_bins*5); IplImage* hist_img = cvCreateImage( cvSize(width,height), 8, 3 ); cvZero( hist_img ); //** 用來進行HSV到RGB顏色轉換的臨時單位圖像 */ IplImage * hsv_color = cvCreateImage(cvSize(1,1),8,3); IplImage * rgb_color = cvCreateImage(cvSize(1,1),8,3); int bin_w = width / (h_bins * s_bins); for(int h = 0; h < h_bins; h++) { for(int s = 0; s < s_bins; s++) { for(int v = 0; v < v_bins; v++) { int i = h*s_bins + s*v_bins + v; /** 獲得直方圖中的統計次數,計算顯示在圖像中的高度 */ float bin_val = cvQueryHistValue_3D( hist, h, s,v ); int intensity = cvRound(bin_val*height/max_value); /** 獲得當前直方圖代表的顏色,轉換成RGB用於繪制 */ cvSet2D(hsv_color,0,0,cvScalar(h*180.f / h_bins,s*255.f/s_bins,v*255.f/v_bins,0)); cvCvtColor(hsv_color,rgb_color,CV_HSV2BGR); CvScalar color = cvGet2D(rgb_color,0,0); cvRectangle( hist_img, cvPoint(i*bin_w,height), cvPoint((i+1)*bin_w,height - intensity), color, -1, 8, 0 ); } } } cvNamedWindow( "Source", 1 ); cvShowImage( "Source", src ); cvNamedWindow( "H-S-V Histogram",1); cvShowImage( "H-S-V Histogram", hist_img ); cvWaitKey(0); cvReleaseImage(&src); cvReleaseImage(&hist_img); cvDestroyWindow("Source"); cvDestroyWindow("H-S-V Histogram"); return 0; }
直方圖均衡化代碼:
View Code
// Gray_Hist.cpp : 定義控制台應用程序的入口點。 // #include <iostream> #include <stdio.h> #include <cv.h> #include <highgui.h> #include <math.h> using namespace std; using namespace cv; int main(int argc, char **argv) { IplImage* src=cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE); //"C:\\Users\\hellmonky\\Desktop\\LeastSquaresMethod\\Debug\\1.bmp" cvNamedWindow("原始圖像",1); cvShowImage("原始圖像",src); int width=src->width; int height=src->height; int sum = width*height; int step=src->widthStep; uchar* data=(uchar *)src->imageData; int hist[256]={0}; int CalHist[256] = {0}; int CH[256] = {0}; int max1 = 0; int max2=0; ////////////////////////////////////////////////////////////////////////// //計算輸入圖像的灰度分布 ////////////////////////////////////////////////////////////////////////// for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { hist[data[i*step+j]]++; } } for(int i=0;i<256;i++) { if(hist[i]>max1) { max1=hist[i]; } } for (int i=0;i<256;i++) { for (int j=0;j<=i;j++) { CalHist[i] += (int)(255* (float)hist[j]/sum ); } } IplImage* dst1=cvCreateImage(cvSize(400,300),8,1); cvSet(dst1,cvScalarAll(255),0); double bin_width=(double)dst1->width/256;//建立比例因子 double bin_unith=(double)dst1->height/max1; for(int i=0;i<256;i++) { CvPoint p0=cvPoint(i*bin_width,dst1->height); CvPoint p1=cvPoint((i+1)*bin_width,dst1->height-hist[i]*bin_unith); cvRectangle(dst1,p0,p1,cvScalar(1),-1,8,0); } ////////////////////////////////////////////////////////////////////////// //對原始圖像進行重新計算 ////////////////////////////////////////////////////////////////////////// for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { data[i*step+j] = CalHist[data[i*step+j]]; } } ////////////////////////////////////////////////////////////////////////// //計算變換以后的圖像的灰度分布 ////////////////////////////////////////////////////////////////////////// for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { CH[data[i*step+j]]++; } } for(int i=0;i<256;i++) { if(CH[i]>max2) { max2=CH[i]; } } IplImage* you=cvCreateImage(cvSize(400,300),8,1); cvSet(you,cvScalarAll(255),0); double binwidth=(double)you->width/256;//建立比例因子 double binunith=(double)you->height/max2; for(int i=0;i<256;i++) { CvPoint p0=cvPoint(i*binwidth,you->height); CvPoint p1=cvPoint((i+1)*binwidth,you->height-CH[i]*binunith); cvRectangle(you,p0,p1,cvScalar(1),-1,8,0); } cvNamedWindow("原始圖像灰度分布",1); cvNamedWindow("直方圖均衡化圖像",1); cvNamedWindow("均衡化后直方圖",1); cvShowImage("原始圖像灰度分布",dst1); cvShowImage("直方圖均衡化圖像",src); cvShowImage("均衡化后直方圖",you); waitKey(0); cvDestroyWindow("原始圖像"); cvDestroyWindow("原始圖像灰度分布"); cvDestroyWindow("直方圖均衡化圖像"); cvDestroyWindow("均衡化后直方圖"); return 0; }
opencv里也自帶直方圖均衡化代碼:
View Code
#include "cv.h" #include "highgui.h" #define HDIM 256 // bin of HIST, default = 256 int main( int argc, char** argv ) { IplImage *src = 0, *dst = 0; CvHistogram *hist = 0; int n = HDIM; double nn[HDIM]; uchar T[HDIM]; CvMat *T_mat; int x; int sum = 0; // sum of pixels of the source image 圖像中象素點的總和 double val = 0; if( argc != 2 || (src=cvLoadImage(argv[1], 0)) == NULL) // force to gray image return -1; cvNamedWindow( "source", 1 ); cvNamedWindow( "result", 1 ); // 計算直方圖 hist = cvCreateHist( 1, &n, CV_HIST_ARRAY, 0, 1 ); cvCalcHist( &src, hist, 0, 0 ); // Create Accumulative Distribute Function of histgram val = 0; for ( x = 0; x < n; x++) { val = val + cvGetReal1D (hist->bins, x); nn[x] = val; } // 歸一化直方圖 sum = src->height * src->width; for( x = 0; x < n; x++ ) { T[x] = (uchar) (255 * nn[x] / sum); // range is [0,255] } // Using look-up table to perform intensity transform for source image dst = cvCloneImage( src ); T_mat = cvCreateMatHeader( 1, 256, CV_8UC1 ); cvSetData( T_mat, T, 0 ); // 直接調用內部函數完成 look-up-table 的過程 cvLUT( src, dst, T_mat ); cvShowImage( "source", src ); cvShowImage( "result", dst ); cvWaitKey(0); cvDestroyWindow("source"); cvDestroyWindow("result"); cvReleaseImage( &src ); cvReleaseImage( &dst ); cvReleaseHist ( &hist ); return 0; }
圖像對比度增強的方法可以分成兩類:一類是直接對比度增強方法;另一類是間接對比度增強方法。直方圖拉伸和直方圖均衡化是兩種最常見的間接對比度增強方法。直方圖拉伸是通過對比度拉伸對直方圖進行調整,從而“擴大”前景和背景灰度的差別,以達到增強對比度的目的,這種方法可以利用線性或非線性的方法來實現;直方圖均衡化則通過使用累積函數對灰度值進行“調整”以實現對比度的增強。
直方圖拉伸進行圖像增強的代碼如下:
View Code
// Contrast_Enhance.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #include "cv.h" #include "highgui.h" int ImageStretchByHistogram(IplImage *src,IplImage *dst); int _tmain(int argc, _TCHAR* argv[]) { IplImage * pImg; pImg=cvLoadImage("F:/test_photo/12.jpg",-1); //創建一個灰度圖像 IplImage* GrayImage = cvCreateImage(cvGetSize(pImg), IPL_DEPTH_8U, 1); IplImage* dstGrayImage = cvCreateImage(cvGetSize(pImg), IPL_DEPTH_8U, 1); cvCvtColor(pImg, GrayImage, CV_BGR2GRAY); ImageStretchByHistogram(GrayImage,dstGrayImage); cvNamedWindow( "dstGrayImage", 1 ); //創建窗口 cvNamedWindow( "GrayImage", 1 ); //創建窗口 cvShowImage( "dstGrayImage", dstGrayImage ); //顯示圖像 cvShowImage( "GrayImage", GrayImage ); //顯示圖像 cvWaitKey(0); //等待按鍵 cvDestroyWindow( "dstGrayImage" );//銷毀窗口 cvDestroyWindow( "GrayImage" );//銷毀窗口 cvReleaseImage( &pImg ); //釋放圖像 cvReleaseImage( &GrayImage ); //釋放圖像 cvReleaseImage( &dstGrayImage ); //釋放圖像 return 0; } int ImageStretchByHistogram(IplImage *src,IplImage *dst) /************************************************* Function: Description: 因為攝像頭圖像質量差,需要根據直方圖進行圖像增強, 將圖像灰度的域值拉伸到0-255 Calls: Called By: Input: 單通道灰度圖像 Output: 同樣大小的單通道灰度圖像 Return: Others: http://www.xiaozhou.net/ReadNews.asp?NewsID=771 DATE: 2007-1-5 *************************************************/ { //p[]存放圖像各個灰度級的出現概率; //p1[]存放各個灰度級之前的概率和,用於直方圖變換; //num[]存放圖象各個灰度級出現的次數; assert(src->width==dst->width); float p[256],p1[256],num[256]; //清空三個數組 memset(p,0,sizeof(p)); memset(p1,0,sizeof(p1)); memset(num,0,sizeof(num)); int height=src->height; int width=src->width; long wMulh = height * width; //求存放圖象各個灰度級出現的次數 // to do use openmp for(int x=0;x<width;x++) { for(int y=0;y<height;y++) { uchar v=((uchar*)(src->imageData + src->widthStep*y))[x]; num[v]++; } } //求存放圖像各個灰度級的出現概率 for(int i=0;i<256;i++) { p[i]=num[i]/wMulh; } //求存放各個灰度級之前的概率和 for(int i=0;i<256;i++) { for(int k=0;k<=i;k++) p1[i]+=p[k]; } //直方圖變換 // to do use openmp for(int x=0;x<width;x++) { for(int y=0;y<height;y++) { uchar v=((uchar*)(src->imageData + src->widthStep*y))[x]; ((uchar*)(dst->imageData + dst->widthStep*y))[x]= p1[v]*255+0.5; } } return 0; }
