OpenCV图像处理之直方图计算

概念:
图像直方图是反映一个图像像素分布的统计表,其横坐标代表了图像像素的种类,可以是灰度的,也可以是彩色的。纵坐标代表了每一种颜色值在图像中的像素总数或者占所有像素个数的百分比。
图像是由像素构成,因为反映像素分布的直方图往往可以作为图像一个很重要的特征。在实际工程中,图像直方图在特征提取、图像匹配等方面都有很好的应用。

OpenCV的直方图计算:
OpenCV提供了一个简单的计算数据集(通常是图像或分割后的通道)的直方图函数:

void calcHist(const cv::Mat *images,
     int nimages,
     const int *channels,
     InputArray mask,
     OutputArray hist,
     int dims,
     const int *histSize,
     const float **ranges);

下面直接代码演示使用该函数计算直方图!

// 命名空间
    using namespace cv;
    using namespace std;
    
    // 加载图片
    image = [UIImage imageNamed:@"yixiao"];
    
    // UIImage -> Mat
    UIImageToMat(image, cvImage);
    
    if (!cvImage.data) {
        return;
    }

    // 用来存放分割的单通道图像:vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的.
    vector rgb_planes;
    
    // 分割成R, G, B 3个单通道图像
    split(cvImage, rgb_planes);
    /*
     void split(const cv::Mat &src, cv::Mat *mvbegin)
     OpenCV中的通道分离函数
     参数说明:
     const cv::Mat &src -- 要进行分离的图像矩阵
     cv::Mat *mvbegin -- 输出的则是Mat类型的的向量
     */    

    // 设定bin数
    int histSize = 255;
    
    // 设定像素值范围
    float rang[] = {0,255};
    
    // 设定每个区间的范围
    const float *histRange = {rang};
    
    // calcHist函数的最后两个参数值
    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 );
    /*
     void calcHist(const cv::Mat *images,
     int nimages,
     const int *channels,
     InputArray mask,
     OutputArray hist,
     int dims,
     const int *histSize,
     const float **ranges);
     参数说明:
     const cv::Mat *images -- 输入数组(或数组集)
     int nimages -- 要计算直方图的图像的个数,此函数可以为多图像求直方图,通常nimages=1。
     const int *channels -- 图像的通道,它是一个数组,如果是灰度图像则channels[1]={0};如果是彩色图像则channels[3]={0,1,2};如果是只是求彩色图像第2个通道的直方图,则channels[1]={1};
     InputArray mask -- 是一个遮罩图像用于确定哪些点参与计算,实际应用中是个很好的参数,默认情况我们都设置为一个空图像,即:Mat()。
     OutArray hist -- 计算得到的直方图
     int dim -- 得到的直方图的维数,灰度图像为1维,彩色图像为3维。
     const int* histSize -- 每个维度的bin数目
     const float** ranges -- 这是一个二维数组,用来指出每个区间的范围。
     后面两个参数都有默认值,uniform参数表明直方图是否等距,最后一个参数与多图像下直方图的显示与存储有关。
     */
    
    // 创建直方图画布
    int hist_w = 400; int hist_h = 400;
    int bin_w = cvRound( (double) hist_w/histSize );
    
    Mat histImage( hist_w, hist_h, CV_8UC3, Scalar( 0,0,0) );
    
    // 将直方图归一化到范围 [ 0, histImage.rows ]
    normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    /*
     归一化函数:http://blog.csdn.net/lanmeng_smile/article/details/49903865
     为了作图,原来很难在一张图上作出来,归一化后就可以很方便的给出图上的相对位置等
     void normalize(InputArray src,OutputArray dst, double alpha = 1, double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray())
     参数说明:把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内
     InputArray src -- 输入数组
     OutputArray dst -- 输出数组
     double alpha = 1 -- range normalization模式的最小值
     double beta = 0 -- range normalization模式的最大值,不用于norm normalization(范数归一化)模式
     int norm_type -- 归一化的类型,可以有以下的取值:
                      NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。
                      NORM_INF: 此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)
                      NORM_L1 :  归一化数组的L1-范数(绝对值的和)
                      NORM_L2: 归一化数组的(欧几里德)L2-范数
     int dtype -- type为负数时,输出数组的type与输入数组的type相同,否则,输出数组与输入数组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype)
     InputArray mask -- 操作掩膜,用于指示函数是否仅仅对指定的元素进行操作
     */
    
    // 在直方图画布上画出直方图
    for( int i = 1; i < histSize; i++ )
    {
        line( histImage, cvPoint( bin_w*(i-1), hist_h - cvRound(r_hist.at(i-1)) ) ,
             cvPoint( bin_w*(i), hist_h - cvRound(r_hist.at(i)) ),
             Scalar( 0, 0, 255), 2, 8, 0  );
        
        line( histImage, cvPoint( bin_w*(i-1), hist_h - cvRound(g_hist.at(i-1)) ) ,
             cvPoint( bin_w*(i), hist_h - cvRound(g_hist.at(i)) ),
             Scalar( 0, 255, 0), 2, 8, 0  );
        
        line( histImage, cvPoint( bin_w*(i-1), hist_h - cvRound(b_hist.at(i-1)) ) ,
             cvPoint( bin_w*(i), hist_h - cvRound(b_hist.at(i)) ),
             Scalar( 255, 0, 0), 2, 8, 0  );
    }
    /*
     int cvRound(double value):对一个double型的数进行四舍五入,并返回一个整型数
     
     void cvLine( CvArr* img,CvPoint pt1,CvPoint pt2,CvScalar color,int thickness=1,int line_type=8,int shift=0 )
     参数说明:
     CvArr* img -- 要划的线所在的图像
     CvPoint pt1 -- 直线起点
     CvPoint pt2 -- 直线终点
     CvScalar color -- 直线的颜色
     int thickness -- 线条粗细
     int line_type -- 线段的类型,可以取值8, 4, 和CV_AA, 分别代表8邻接连接线,4邻接连接线和反锯齿连接线。默认值为8邻接。为了获得更好地效果可以选用CV_AA(采用了高斯滤波)。
     int shift -- 坐标点的小数点位数
     */
    
    // 最后就是将画布显示了
    UIImage *hist_image = MatToUIImage(histImage);
    CGRect imgRect = CGRectMake(([UIScreen mainScreen].bounds.size.width - hist_image.size.width)*0.5, 200, hist_image.size.width, hist_image.size.height);
    self.imageView.frame = imgRect;
    self.imageView.image = hist_image;

最后放一张效果图:
OpenCV图像处理之直方图计算_第1张图片
zhifangtu.gif

参考:
OpenCV图像处理直方图计算

你可能感兴趣的:(OpenCV图像处理之直方图计算)