Opencv图像处理---直方图计算

理论

什么是直方图?

  • 收集直方图,将数据计数组织成一组预定义的箱
  • 当我们说数据时,我们并不将其限制为强度值(正如我们在前面的教程中看到的那样)。 收集的数据可以是您发现用于描述图像的任何功能。
  • 我们来看一个例子吧。 想象一下,矩阵包含图像的信息(即强度在0-255范围内):
  • 如果我们想要以有组织的方式统计这些数据,会发生什么? 因为我们知道这种情况下的信息值范围是256个值,所以我们可以在子部分(称为bin)中分割我们的范围,如:
  • 我们可以保持每个bini范围内的像素数量。 将此应用于上面的示例,我们得到下面的图像(轴x表示箱子,轴y表示每个箱子中的像素数)。
  • 这只是直方图如何工作以及为什么有用的简单示例。 直方图不仅可以保持颜色强度的计数,还可以计算我们想要测量的任何图像特征(即梯度,方向等)。
  • 让我们确定直方图的某些部分:
  1. dims:要收集数据的参数数量。 在我们的示例中,dims = 1,因为我们只计算每个像素的强度值(在灰度图像中)。
  2. bin:它是每个暗淡的细分数量。 在我们的示例中,bin = 16
  3. range:要测量的值的限制。 在这种情况下:范围= [0,255]
  • 如果要计算两个功能怎么办? 在这种情况下,您得到的直方图将是一个3D图(其中x和y将是每个要素的binx和biny,z将是(binx,biny)的每个组合的计数数量。同样适用于更多要素 (当然它变得更棘手)。

代码

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
int main( int, char** argv )
{
  Mat src, dst;
  src = imread( argv[1], 1 );
  if( src.empty() )
    { return -1; }
  vector<Mat> bgr_planes;
  split( src, bgr_planes );
  int histSize = 256;
  float range[] = { 0, 256 } ;
  const float* histRange = { range };
  bool uniform = true; bool accumulate = false;
  Mat b_hist, g_hist, r_hist;
  calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
  calcHist( &bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
  calcHist( &bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
  // Draw the histograms for B, G and R
  int hist_w = 512; int hist_h = 400;
  int bin_w = cvRound( (double) hist_w/histSize );
  Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
  normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
  normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
  normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
  for( int i = 1; i < histSize; i++ )
  {
      line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,
                       Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
                       Scalar( 255, 0, 0), 2, 8, 0  );
      line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) ,
                       Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
                       Scalar( 0, 255, 0), 2, 8, 0  );
      line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ) ,
                       Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
                       Scalar( 0, 0, 255), 2, 8, 0  );
  }
  namedWindow("calcHist Demo", WINDOW_AUTOSIZE );
  imshow("calcHist Demo", histImage );
  waitKey(0);
  return 0;
}

解释

  • 创建必要的矩阵
  • 加载源图像
  • 在三个R,G和B平面中分离源图像。 为此,我们使用OpenCV函数cv :: split:
  • 现在我们准备开始为每个平面配置直方图。 由于我们正在使用B,G和R平面,我们知道我们的值将在区间[0,255]范围内
  1. 建立箱数(5,10 ......):
  2. 设置值的范围(正如我们所说,在0到255之间)
  3. 我们希望我们的箱子具有相同的尺寸(均匀)并在开头清除直方图,因此:
  4. 最后,我们创建Mat对象来保存直方图。 创建3(每个平面一个):
  5. 我们继续使用OpenCV函数cv :: calcHist计算直方图:

(1)**&bgr_planes [0]:**源数组

(2)1:源数组的数量(在这种情况下我们使用1.我们也可以在这里输入一个数组列表)
(3)0:要测量的通道(暗淡)。 在这种情况下,它只是强度(每个数组是单通道)所以我们只写0。
(4)Mat():要在源数组上使用的掩码(指示要忽略的像素的零)。 如果没有定义,则不使用它
(5)b_hist:将存储直方图的Mat对象
(6)1:直方图维度。
(7)histSize:每个使用维度的bin数
(8)histRange:每个维度要测量的值范围
(9)均匀和累积:箱尺寸相同,直方图在开始时被清除。

  • 创建图像以显示直方图:
  • 请注意,在绘制之前,我们首先对直方图进行cv :: normalize,使其值落在输入参数指示的范围内:
  • 最后,观察访问bin(在本例中为1D-Histogram):
  • 我们使用表达式:
  • 其中i表示尺寸。 如果它是2D直方图,我们会使用类似的东西:
  • 最后,我们显示直方图并等待用户退出:

效果

使用如下所示的图像作为输入参数:

生成以下直方图:

猜你喜欢

转载自blog.csdn.net/LYKymy/article/details/83189170