本章内容接续https://blog.csdn.net/hhaowang/article/details/87278518
直方图处理:
**1. 灰度直方图(Histogram)**
灰度直方图能直观的显示出图像像素灰度分布情况,这些信息在图像灰度变换等处理过程中显得十分重要。
**灰度直方图**是一个**二维的统计图表**,它描述了图像个灰度值得统计特性,显示了各个灰度值出现的频率或者出现的概率大小。其横坐标表示的是**灰度值范围[0, 255],灰度深度为8,灰度级256**,其纵坐标为该灰度值出现的频率或次数。
C语言+OpenCV代码实现从摄像头中捕捉单帧并统计直方图:
```
//histogram from image / video / camera
#include <iostream>
#include <opencv2/opencv.hpp> // open cv general include file
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <stdio.h>
#include <algorithm> // contains max() function (amongst others)
using namespace std;
using namespace cv; // use c++ namespace so the timing stuff works consistently
/******************************************************************************/
// setup the cameras properly based on OS platform
// 0 in linux gives first camera for v4l
//-1 in windows gives first device or user dialog selection
#ifdef linux
#define CAMERA_INDEX 0
#else
#define CAMERA_INDEX -1
#endif
/******************************************************************************/
int main( int argc, char** argv )
{
IplImage* img = NULL; // image object
IplImage* grayImg = NULL; // image object
IplImage* hist_img = NULL; // histogram image object
CvCapture* capture = NULL; // capture object
char const * windowName = "Grayscale Image"; // window name
char const * windowNameHist = "Grayscale Image Histogram"; // window name
bool keepProcessing = true; // loop control flag
char key; // user input
int EVENT_LOOP_DELAY = 40; // delay for GUI window
// 40 ms equates to 1000ms/25fps = 40ms per frame
CvHistogram *hist = NULL; // pointer to histogram object
float max_value = 0; // max value in histogram
int hist_size = 256; // size of histogram (number of bins)
int bin_w = 0; // initial width to draw bars
float range_0[]={0,256};
float* ranges[] = { range_0 };
// if command line arguments are provided try to read image/video_name
// otherwise default to capture from attached H/W camera
if(
( argc == 2 && (img = cvLoadImage( argv[1], CV_LOAD_IMAGE_UNCHANGED)) != 0 ) ||
( argc == 2 && (capture = cvCreateFileCapture( argv[1] )) != 0 ) ||
( argc != 2 && (capture = cvCreateCameraCapture( CAMERA_INDEX )) != 0 )
)
{
// create window object (use flag=0 to allow resize, 1 to auto fix size)
cvNamedWindow(windowName, 0);
cvNamedWindow(windowNameHist, 0);
// create histogram + image to draw it in
hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);
hist_img = cvCreateImage(cvSize(255,200), 8, 1);
// if capture object in use (i.e. video/camera)
// get initial image from capture object
if (capture) {
// cvQueryFrame s just a combination of cvGrabFrame
// and cvRetrieveFrame in one call.
img = cvQueryFrame(capture);
if(!img){
if (argc == 2){
printf("End of video file reached\n");
} else {
printf("ERROR: cannot get next fram from camera\n");
}
exit(0);
}
}
// create grayscale image object
grayImg = cvCreateImage(cvSize(img->width,img->height), IPL_DEPTH_8U, 1);
// start main loop
while (keepProcessing) {
int64 timeStart = getTickCount(); // get time at start of loop
// if capture object in use (i.e. video/camera)
// get image from capture object
if (capture) {
// cvQueryFrame is just a combination of cvGrabFrame
// and cvRetrieveFrame in one call.
img = cvQueryFrame(capture);
if(!img){
if (argc == 2){
printf("End of video file reached\n");
} else {
printf("ERROR: cannot get next fram from camera\n");
}
exit(0);
}
} else {
// if not a capture object set event delay to zero so it waits
// indefinitely (as single image file, no need to loop)
EVENT_LOOP_DELAY = 0;
}
// get a grayscale version of the image
if (img->nChannels > 1){
cvCvtColor(img, grayImg, CV_BGR2GRAY);
} else {
grayImg = img;
}
grayImg->origin = img->origin;
// calc and display histogram (from opencv demhist.c example)
cvCalcHist( &grayImg, hist, 0, NULL );
cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );
cvScale( hist->bins, hist->bins, ((double)hist_img->height)/max_value, 0 );
cvSet( hist_img, cvScalarAll(255), 0 );
bin_w = cvRound((double)hist_img->width/hist_size);
for(int i = 0; i < hist_size; i++ )
{
cvRectangle( hist_img, cvPoint(i*bin_w, hist_img->height),
cvPoint((i+1)*bin_w, hist_img->height - cvRound(cvGetReal1D(hist->bins,i))),
cvScalarAll(0), -1, 8, 0 );
}
// display image in window
cvShowImage(windowNameHist, hist_img );
cvShowImage( windowName, grayImg );
// start event processing loop (very important,in fact essential for GUI)
// 40 ms roughly equates to 1000ms/25fps = 4ms per frame
// here we take account of processing time for the loop by subtracting the time
// taken in ms. from this (1000ms/25fps = 40ms per frame) value whilst ensuring
// we get a +ve wait time
key = cvWaitKey((int) std::max(2.0, EVENT_LOOP_DELAY -
(((getTickCount() - timeStart) / getTickFrequency()) * 1000)));
if (key == 'x'){
// if user presses "x" then exit
printf("Keyboard exit requested : exiting now - bye!\n");
keepProcessing = false;
}
}
// destroy window objects
// (triggered by event loop *only* window is closed)
cvDestroyAllWindows();
// destroy image object (if it does not originate from a capture object)
if (!capture){
cvReleaseImage( &img );
}
cvReleaseImage( &grayImg );
cvReleaseImage( &hist_img );
cvReleaseHist( &hist );
// all OK : main returns 0
return 0;
}
// not OK : main returns -1
return -1;
}
/******************************************************************************/
如果是对图像操作而不是对从camera中捕捉,简化实现方法:
#include<stdio.h>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.h>
#include<opencv2/imgproc.hpp>
#include<afx.h>
int main()
{
int hist_size=255;
float range_0[]={0,256};
float *ranges[]={range_0};
int i,bin_w;
float max_value=0,min_value=0;
int min_idx=0, max_idx=0;
double mean=0,variance=0;
IplImage* img=cvLoadImage("1.jpg",-1);
IplImage *pImage=NULL;
IplImage* pImgGray=NULL;
pImage=cvCloneImage(img);
pImgGray=cvCreateImage(cvSize(pImage->width,pImage->height),IPL_DEPTH_8U,1);
cvCvtColor(pImage,pImgGray,CV_BGR2GRAY);
//创建一个矩形区域
CvRect rect= cvRect(0,0,500,600);
cvSetImageROI(pImgGray,rect);
//创建一个图像用来存放直方图
IplImage *histImage=cvCreateImage(cvSize(320,200),8,1);
CvHistogram *hist=cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
//计算直方图并作用到hist变量中
cvCalcHist(&pImgGray, hist, 0, NULL);
//得到直方图的最值及标号
cvGetMinMaxHistValue(hist,&min_value,&max_value,&min_idx,&max_idx);
//缩放其最大值和最小值让其融入图像
cvScale(hist->bins,hist->bins,((double)histImage->height)/max_value,0);
//设置所有的直方图的数值为255
cvSet(histImage,cvScalarAll(255),0);
//建一个比例因子 沿宽度释放
bin_w=cvRound((double)histImage->width/hist_size);
//画直方图
for(i=0;i<hist_size;i++)
{
cvRectangle(histImage,cvPoint(i*bin_w,histImage->height),
cvPoint((i+1)*bin_w,histImage->height-cvRound(cvGetReal1D(hist->bins,i))),cvScalarAll(0),-1,8,0);
float *bins=cvGetHistValue_1D(hist,i);
//增加均值
mean+=bins[0];
//std::cout<<bins[0]<<" "<<bins[1]<<std::endl;
}
mean/=hist_size;
//根据均值计算变化量
for(i=0;i<hist_size;i++)
{
float* bins=cvGetHistValue_1D(hist,i);
variance+=pow((bins[0]-mean),2);
}
variance/=hist_size;
//std::cout<<"histgram Mean:"<<mean<<std::endl;
//创建窗口
cvNamedWindow("Original",0);
cvShowImage("Original",pImage);
cvNamedWindow("Gray",0);
cvShowImage("Gray",pImgGray);
cvNamedWindow("Histgram",0);
cvShowImage("Histgram",histImage);
CvFont *pfont= new CvFont;
cvInitFont(pfont,CV_FONT_HERSHEY_SIMPLEX,0.8,0.8,0.2);
CString Result="Histgram Mean: ";
CString str;
str.Format("%f",mean);
Result+=str+"/n";
cvPutText(histImage,Result,cvPoint(80,80),pfont,CV_RGB(255,0,100));
delete pfont;
cvWaitKey(0);
cvReleaseImage(&histImage);
cvReleaseImage(&pImgGray);
cvDestroyWindow("Original");
cvDestroyWindow("Gray");
cvDestroyWindow("Histgram");
return 0;
}
```
绘制单通道的直方图Gray或者R-G-B通道:
File 1. **hist_img.h**```
#ifndef HIST_IMG_H_INCLUDED
#define HIST_IMG_H_INCLUDED
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#define LOW_BOUND (0.f)
#define HIGH_BOUND (255.f)
#define BINS (10)
#define BINWIDTH (20)
/**-
Get a histogram image of the input image.
@param pImage an 1 channel image
@return Returns a histogram image
-**/
IplImage* HistImage(IplImage* pImage);
#endif // HIST_IMG_H_INCLUDED
```
File 2. hist_img.c
```
#include "hist_img.h"
using namespace std;
IplImage* HistImage(IplImage* pImage)
{
if (!pImage)
{
cerr<<"The input image is NULL."<<endl;
return NULL;
}
if (1 != pImage->nChannels)
{
cerr<<"The input image's channels is not equal to 1."<<endl;
return NULL;
}
// create a histogram
int histSize = BINS;
float** range = new float*;
range[0] = new float[2];
range[0][0] = LOW_BOUND;
range[0][1] = HIGH_BOUND;
CvHistogram* pHist = cvCreateHist(1,&histSize,CV_HIST_ARRAY,range);
cvCalcHist(&pImage,pHist);
float fMaxValue = 0.f;
cvGetMinMaxHistValue(pHist,0,&fMaxValue,0,0);
// generate the histogram image
int height = 240;
int interval = cvRound(BINWIDTH * 2 / 5);
int width = histSize * BINWIDTH + (histSize - 1)*interval;
IplImage* pHistImg = cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,3);
cvZero(pHistImg);
for (int i = 0;i < histSize;i++)
{
float fBinValue = cvQueryHistValue_1D(pHist,i);
int BinHeight = cvRound(fBinValue / fMaxValue * height);
CvScalar color = cvScalar(i*255/histSize,255,255,0);
CvPoint point1 = cvPoint(i*(BINWIDTH + interval),height);
CvPoint point2 = cvPoint(point1.x + BINWIDTH,height - BinHeight);
cvRectangle(pHistImg,point1,point2,color,-1,8,0);
}
cvReleaseHist(&pHist);
delete[] range[0];
delete range;
return pHistImg;
}
**2. 对比度变换**```
#include <iostream>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cout;
using std::endl;
using namespace cv;
namespace
{
/** Global Variables */
int alpha = 100;
int beta = 100;
int gamma_cor = 100;
Mat img_original, img_corrected, img_gamma_corrected;
void basicLinearTransform(const Mat &img, const double alpha_, const int beta_)
{
Mat res;
img.convertTo(res, -1, alpha_, beta_);
hconcat(img, res, img_corrected);
imshow("Brightness and contrast adjustments", img_corrected);
}
void gammaCorrection(const Mat &img, const double gamma_)
{
CV_Assert(gamma_ >= 0);
//! [changing-contrast-brightness-gamma-correction]
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
p[i] = saturate_cast<uchar>(pow(i / 255.0, gamma_) * 255.0);
Mat res = img.clone();
LUT(img, lookUpTable, res);
//! [changing-contrast-brightness-gamma-correction]
hconcat(img, res, img_gamma_corrected);
imshow("Gamma correction", img_gamma_corrected);
}
void on_linear_transform_alpha_trackbar(int, void *)
{
double alpha_value = alpha / 100.0;
int beta_value = beta - 100;
basicLinearTransform(img_original, alpha_value, beta_value);
}
void on_linear_transform_beta_trackbar(int, void *)
{
double alpha_value = alpha / 100.0;
int beta_value = beta - 100;
basicLinearTransform(img_original, alpha_value, beta_value);
}
void on_gamma_correction_trackbar(int, void *)
{
double gamma_value = gamma_cor / 100.0;
gammaCorrection(img_original, gamma_value);
}
}
int main( int argc, char** argv )
{
CommandLineParser parser( argc, argv, "{@input | ../data/lena.jpg | input image}" );
img_original = imread( parser.get<String>( "@input" ) );
if( img_original.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
img_corrected = Mat(img_original.rows, img_original.cols*2, img_original.type());
img_gamma_corrected = Mat(img_original.rows, img_original.cols*2, img_original.type());
hconcat(img_original, img_original, img_corrected);
hconcat(img_original, img_original, img_gamma_corrected);
namedWindow("Brightness and contrast adjustments");
namedWindow("Gamma correction");
createTrackbar("Alpha gain (contrast)", "Brightness and contrast adjustments", &alpha, 500, on_linear_transform_alpha_trackbar);
createTrackbar("Beta bias (brightness)", "Brightness and contrast adjustments", &beta, 200, on_linear_transform_beta_trackbar);
createTrackbar("Gamma correction", "Gamma correction", &gamma_cor, 200, on_gamma_correction_trackbar);
on_linear_transform_alpha_trackbar(0, 0);
on_gamma_correction_trackbar(0, 0);
waitKey();
imwrite("linear_transform_correction.png", img_corrected);
imwrite("gamma_correction.png", img_gamma_corrected);
return 0;
}
```
**3.直方图均衡化(Histogram Equalization)**
直方图均衡化是通过调整图像的灰阶分布,使得在0~255灰阶上的分布更加均衡,提高了图像的对比度,达到改善图像主观视觉效果的目的。对比度较低的图像适合使用直方图均衡化方法来增强图像细节。
效果展示:
**算法实例:**
```
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
int main(int argc, char *argv[])
{
Mat image = imread("fruits.jpg", 1);
if (image.empty())
{
std::cout << "打开图片失败,请检查" << std::endl;
return -1;
}
imshow("原图像", image);
Mat imageRGB[3];
split(image, imageRGB);
for (int i = 0; i < 3; i++)
{
equalizeHist(imageRGB[i], imageRGB[i]);
}
merge(imageRGB, 3, image);
imshow("直方图均衡化图像增强效果", image);
waitKey();
return 0;
```
处理结果:
**4.直方图规范化(Histogram Specification)**
____