图像增强(Image Enhancement)-直方图处理技术

本章内容接续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)**


____

猜你喜欢

转载自blog.csdn.net/hhaowang/article/details/87357863