【从零学习openCV】使用直方图统计像素

来源:互联网 发布:淘宝权重的因素 编辑:程序博客网 时间:2024/06/05 19:58

1. 计算图像直方图

图像是由像素组成的,在一个单通道的灰度图像中,每个像素的值介于0到255之间,而直方图就是一个简单的表,给出了一幅或者一组图像中拥有给定数值的像素数量。当然直方图也可以归一化,归一化后的所有项的和为1,在这种情况下,每一项给出的都是拥有特定数值的像素在图像中占的比例。

下面我们看看如何用opencv计算单通道图像的直方图,我用一个Histogram1D封装了与单通道直方图操作相关的变量和函数:

#ifndef HISTOGRAM1D_H#define HISTOGRAM1D_H#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc/imgproc.hpp>using namespace cv;class Histogram1D{private:    int histSize[1];       //项的数量    float hranges[2];   //像素的最小及最大值    const float* ranges[1];    int channels[1];    //仅用到一个通道public:    Histogram1D(){        //准备1D直方图的参数        histSize[0] =256;        hranges[0] = 0.0;        hranges[1] = 255.0;        ranges[0]  = hranges;        channels[0] = 0;  //默认情况,我们考察0号通道    }    //计算1D直方图    MatND getHistogram(const Mat&image)    {        MatND  hist;        //计算直方图        calcHist(&image,                 1,                     //计算单张图像的直方图                 channels,        //通道的数量                 Mat(),              //不使用图像作为掩码                 hist,                //返回的直方图                 1,                    //这是1D的直方图                 histSize,         //项的数量                 ranges            //像素值的范围                 );        return hist;    }    //计算1D直方图,并返回一幅图像    Mat getHistogramImage(const Mat &image)    {        //首先计算直方图        MatND hist = getHistogram(image);        //获取最大值和最小值        double maxVal = 0;        double minVal = 0;        minMaxLoc(hist,&minVal,&maxVal,0,0);        //显示直方图的图像        Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));        //设置最高点为nbins的90%        int hpt  = static_cast<int>(0.9*histSize[0]);        //每个条目都绘制一条垂直线        for(int h = 0;h<histSize[0];h++){            float binVal =hist.at<float>(h);            int intensity = static_cast<int>(binVal*hpt/maxVal);            //两点之间绘制一条线            line(histImg,Point(h,histSize[0]),                    Point(h,histSize[0]-intensity),                    Scalar::all(0));        }        return histImg;    }};#endif // HISTOGRAM1D_H
main函数如下,直接调用getHistogramImage即可得到直方图:

#include <QCoreApplication>#include "Histogram1D.h"int main(int argc, char *argv[]){    namedWindow( "src1", WINDOW_AUTOSIZE );    namedWindow( "src2", WINDOW_AUTOSIZE );    Mat image = imread( "test.jpg",0 );  //读取灰度图    Histogram1D h;    while(1)    {        imshow( "src1", image);        imshow( "src2", h.getHistogramImage(image));        char c =  waitKey(30);        if( 27==c )            return 0;    }}
效果如下:

原始灰度图像


直方图


当然我们还可以用calcHist函数来计算彩色BGR图像的直方图,这时候直方图变成三维的了:

#ifndef COLORHISTOGRAM_H#define COLORHISTOGRAM_H#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc/imgproc.hpp>#include<QDebug>using namespace cv;class ColorHistogram{private:    int histSize[3];       //项的数量    float hranges[2];   //像素的最小及最大值    const float* ranges[3];    int channels[3];    //用到三个通道public:    ColorHistogram(){        //准备彩色直方图的参数        histSize[0] = histSize[1]= histSize[2]=256;        hranges[0] = 0.0;        hranges[1] = 255.0;        ranges[0] =ranges[1]=ranges[2]=hranges;       //所有通道都有相同的范围        channels[0] = 0;  //3个通道        channels[1] = 1;        channels[2] = 2;    }    //计算三维直方图    MatND getHistogram(const Mat&image)    {        MatND  hist;        //计算直方图        calcHist(&image,                 1,                     //计算单张图像的直方图                 channels,        //通道的数量                 Mat(),              //不使用图像作为掩码                 hist,                //返回的直方图                 3,                    //这是三维的直方图                 histSize,         //项的数量                 ranges            //像素值的范围                 );        qDebug()<<hist.dims;        return hist;    }};#endif // COLORHISTOGRAM_H
这时候getHistogram返回的是一个256*256*256的矩阵,包含了超过1600万个元素,这计算代价是非常大的,可以参考opencv操作像素中所述的方法减少颜色的数量,或者可以使用cv::SparseMat数据结构,用于存储大型的稀疏矩阵,这样可以极大地减少内存的消耗。

PS:至于绘制三维直方图的绘制比较麻烦,以后有时间看看用qt实现个界面,感兴趣的话可以先参看下这篇文章http://blog.csdn.net/foolpanda1168/article/details/6078463

2. 查找表的使用

查找表实际上就是一个简单的一对一(或者多对一)的函数,定义了如何将像素值转换为新的值,本质上就是一个一维数组。

openCV中用cv::LUT的方法对图像应用查找表生成新图像,我们将这个功能加到Histogram1D类中:

//应用查找表,image为输入图像,lookup是1*256的unchar矩阵,代表查找表Mat applyLookUp(const Mat&image,const Mat&lookup){    Mat result;    //应用查找表    LUT(image,lookup,result);    return result;}
这时我们可以做个简单的实验,比如将原先每个像素强度进行反转,即x变成255-x:

    int dim(256);    Mat lut(1,&dim,CV_8U);    for(int i=0;i<256;i++)    {        lut.at<uchar>(i) = 255-i;    }    Mat reverse = h.applyLookUp(image,lut);

效果如下:



3. 直方图均衡化

直方图均衡化其实就是为了让直方图更加的平坦,能够大幅改善图像的外观。

openCV中提供了一个简单易用的函数cv::equalizeHist来执行直方图均衡化。

//直方图均衡化    Mat  equalize(const Mat &image)    {        Mat result;        equalizeHist(image,result);        return result;    }

应用与之前的图像,效果如下:



可以看出图像的对比度增强了,直方图也更加的平坦。


参考书籍
《openCV2计算机视觉编程手册》

(转载请注明作者和出处:Shawn-HT  http://blog.csdn.net/shawn_ht



0 0
原创粉丝点击