《OpenCV2》编程手册——使用直方图统计像素

计算图像的直方图

  • 图像是由像素组成的,在一个单通道的灰度图像中,每个像素的值介于0~255之间。直方图是一个简单的表,给出了一副图像或一组图像中拥有给定数值的像素数量。灰度图像 的直方图有256个条目(容器)。0号容器给出值为0的像素个数,1号容器给出值为1的像素个数,以此类推。

定义一个专门的类处理单通道的灰度图像。

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号通道
    }

计算一个灰度直方图的方法,返回对象是一个拥有256个条目的一维数组。

//计算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;
}

结果图像峰值处的值可做为二值化的阈值。

使用查找表修改图像外观

  • 查找表是一个简单的一对一(多对一)函数,定义了如何将像素值转换为新的值,本质上是一个一维数组,对于常规灰度图像而言有256个项目,表的第i项表示相应的灰度的新值。

创建一个查找表反转像素的强度。

//创建图像的反向查找表
int dim(256);
Mat lut(1, //1D
    &dim, //256项
    CV_8U);//uchar
for(int i = 0; i<256; i++){
    lut.at<char>(i) = 255-i;
}

对图像应用查找表以生成新图像。

Mat applyLookUp(const Mat& image, const Mat& lookup){
    Mat result;
    //应用查找表
    LUT(image, lookup, result);
    return result;
}

通过拉伸直方图能得到扩展后的对比度,旨在检测直方图中非零项的最低(imin)和最高(imax)强度值,强度值可以被重新映射,这样imin值是重新定位在强度0,imax是分配值255。

Mat stretch (const Mat&image, int minValue=0){
    //计算直方图
    MatND hist = getHistogram(image);
    //寻找直方图的左端
    int imin = 0;
    for(;imin<histSize[0]; imin++){
        std::cout<<hist.at<float>(imin)<<std::endl;
        if(hist.at<float>(imin)>minValue){
            break;
        }
    }
    //寻找直方图的右端
    int imax = histSize[0]-1;
    for(; imax>=0; imax--){
        if(hist.at<float>(imax)>minValue){
            break;
        }
    }
    //创建查找表
    int dim(256);
    Mat lookup(1, &dim, CV_8U);
    //填充查找表
    for(int i=0; i<256; i++){
        //确保数值位于imin与imax之间
        if(i<imin){
            lookup.at<uchar>(i)=0;
        }
        else{
            if(i>imax){
                lookup.at<uchar>(i) =255;
            }
            //线性映射
            else {
                lookup.at<uchar>(i) = static_cast<uchar>
                    //中间强度线性映射
                    (255.0*(i-imin)/(imax-imin)+0.5);
            }
        }
    }
    //应用查找表
    Mat result;
    result = applyLookUp(image, lookup);
    return result;
}

你可能感兴趣的:(《OpenCV2》编程手册——使用直方图统计像素)