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<Mat> 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<float>(i-1)) ) ,
             cvPoint( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
             Scalar( 0, 0, 255), 2, 8, 0  );
        
        line( histImage, cvPoint( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) ,
             cvPoint( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
             Scalar( 0, 255, 0), 2, 8, 0  );
        
        line( histImage, cvPoint( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,
             cvPoint( bin_w*(i), hist_h - cvRound(b_hist.at<float>(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;

最后放一张效果图:
zhifangtu.gif

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容