选择性直方图均衡(仅在图像的指定区域)

Posted

技术标签:

【中文标题】选择性直方图均衡(仅在图像的指定区域)【英文标题】:Selective histogram equalization (only on a specified area of the image) 【发布时间】:2015-05-07 16:42:35 【问题描述】:

我正在使用 opencv 开发 Qt creator。

我必须开发一个对图像进行直方图均衡的程序。 我的图像是 16 位灰度图像,所以我不能使用 opencv 函数“equalizeHist”,因为它只适用于 8 位灰度图像。

我为此编写的代码如下:

void equalizeHist_16U(Mat* img)

    long hist[65535] = 0;
    double ratio;
    int i, j;

    assert(img->channels() == 1);
    assert(img->type() == CV_16U);

    ratio = 65535.0 / (img->cols*img->rows);

    //Cumulative histogram calculation
    compute_hist_16U(img, hist, true);

    for(i=0 ; i<img->cols ; i++)
    
        for(j=0 ; j<img->rows ; j++)
        
            img->at<unsigned short>(j,i) = ratio*hist[img->at<unsigned short>(j,i)];
        
    



long compute_hist_16U (Mat* img, long* hist, bool cumul)

    unsigned short i, j, k;
    long* b;
    long max = 0;

    //is the image 16bits grayscale ?
    assert(img->channels() == 1);
    assert(CV_16U == img->type());

    //histogram calculation
    for(i=0 ; i<img->cols ; i++)
    
        for(j=0 ; j<img->rows ; j++)
        
            hist[img->at<unsigned short>(j,i)]++;
            if(hist[img->at<unsigned short>(j,i)] > max)
                max = hist[img->at<unsigned short>(j,i)];
        
    

    //Cumulative histogram calculation (if cumul=true)
    if(cumul)
    
        for(b=hist ; b<hist+65535 ; b++)
        
            *(b+1) += *b;
        
    
    return (cumul ? hist[65535] : max);

它达到了我的预期,现在我想对我的图像进行直方图均衡化,但只在图像的指定部分进行。 我将 x1,x2,y1,y2 参数添加到我的函数中,并像这样更改了“for”的边界(我更改的代码行有箭头):

---->void equalizeHist_16U(Mat* img, int x1, int x2, int y1, int y2)

    long hist[65535] = 0;
    double ratio;
    int i, j;

    assert(img->channels() == 1);
    assert(img->type() == CV_16U);

    ratio = 65535.0 / (img->cols*img->rows);

    //Cumulative histogram calculation
    compute_hist_16U(img, hist, true);

    ---->for(i=x1 ; i<=x2 ; i++)
    
        ---->for(j=y1 ; j<=y2 ; j++)
        
            img->at<unsigned short>(j,i) = ratio*hist[img->at<unsigned short>(j,i)];
        
    



---->long compute_hist_16U (Mat* img, long* hist, bool cumul, int x1, int x2, int y1, int y2)

    unsigned short i, j, k;
    long* b;
    long max = 0;

    //is the image 16bits grayscale ?
    assert(img->channels() == 1);
    assert(CV_16U == img->type());

    //histogram calculation
    ---->for(i=x1 ; i<=x2  ; i++)
    
        ---->for(j=y1 ; j<=y2 ; j++)
        
            hist[img->at<unsigned short>(j,i)]++;
            if(hist[img->at<unsigned short>(j,i)] > max)
                max = hist[img->at<unsigned short>(j,i)];
        
    

    //Cumulative histogram calculation (if cumul=true)
    if(cumul)
    
        for(b=hist ; b<hist+65535 ; b++)
        
            *(b+1) += *b;
        
    
    return (cumul ? hist[65535] : max);

但它没有按预期工作,我的图像没有均衡,我的图像上没有极值(清晰的白色和深黑色)。 如果我尝试

equalizeHist_16U(&img, 0, 50, 0, 50)

我得到的图像非常非常明亮 如果我尝试

equalizeHist(&img, 300, 319, 220, 239)

我得到的图像非常非常暗

我想我在循环边界中犯了一个错误,但我找不到在哪里! 也许你有一个想法?

提前谢谢你

【问题讨论】:

【参考方案1】:

初步: 您是否注意到您根本没有使用累积直方图函数的第二个版本?

void equalizeHist_16U(Mat* img, int x1, int x2, int y1, int y2)

正在打电话

compute_hist_16U(img, hist, true);

而不是:

long compute_hist_16U (Mat* img, long* hist, bool cumul, int x1, int x2, int y1, int y2)

(我想你想发布最后一个,否则我看不出你为什么发布代码:))


实际答案:

如果您通过operator () 使用cv::Mat rois,一切都会变得容易得多。

你的函数会变成如下:

void equalizeHist_16U(Mat* img, int x1, int x2, int y1, int y2) 

   //Here you should check you have x2 > x1 and y2 > y1 and y1,x1 >0 and x2 <= img->width and y2 <= img->height

   cv::Rect roi(x1,y1,x2-x1,y2-y1); 
   //To reproduce exactly the behaviour you seem to target,
   //it should be x2-x2+1 and y2-y1+1. 
   //But you should get used on the fact that extremes are,
   //as a convention, excluded

   cv::Mat& temp = *img; //Otherwise using operator() is complicated
   cv::Mat roiMat = temp(roi); //This doesn't do any memory copy, just creates a new header!
   void equalizeHist_16U(&roiMat); //Your original function!!


就是这样!如果这不起作用,则意味着您处理整个图像的原始函数存在您以前无法注意到的错误。

当我有一点时间时,我会发布一些建议以使您的函数更快(例如,您应该避免使用 .at,您应该在结束时计算直方图中的最大值直方图计算,您应该创建一个short 的查找表,在其中将直方图乘以比率,以便应用直方图变得更快;而不是导致浮点转换的ratio 变量,您可以简单地将您的由常量(img-&gt;width*img-&gt;height) 组成的直方图元素)更简洁(你应该通过引用传递Mat,而不是使用指针,这是 C 风格,而不是 C++)

此外:

为什么要从compute_hist_16U 返回值?

long hist[65535] 应该是long hist[65536],这样索引65535 才有效。首先,65535 是您图像中的白色值。此外,当您拥有b+1b=hist+65534(最后一个循环)时,您可以在循环中使用它

for(b=hist ; b<hist+65535 ; b++)

    *(b+1) += *b;

【讨论】:

【参考方案2】:

感谢您的回答。

初步 我想我在粘贴代码时犯了一个错误,我忘记更改你注意到的那一行,这行应该是你写的最后一行。

实际答案 您的技术效果很好,无论所选区域如何,我的像素(清晰的白色和深黑色)都有极值。 唯一的问题(我没有在我的问题中提到它,所以你不可能知道)是它只保留选定的区域,图像的其余部分保持不变。 事实上,我想对图像的指定部分进行直方图计算,并将该直方图应用于我的所有图像。

我还从compute_hist_16U 中删除了返回值并将long hist[65535] 更改为long hist[65536] 我改变了传递图像的方式并删除了变量ratio。 我使用了.at,因为当我搜索“如何访问像素值opencv mat”时,它是访问文档和*** 中描述的像素值的方法 而且我从未见过如何创建查找表,所以当我的程序完全正常运行时,我可能会研究这个

我的新功能是:

void compute_hist_16U (Mat &img, long* hist)

    unsigned short i, j;
    long* b;

    assert(img.channels() == 1);
    assert(CV_16U == img.type());

    for(i=0 ; i<=img.cols-1  ; i++)
    
        for(j=0 ; j<=img.rows-1 ; j++)
        
            hist[img.at<unsigned short>(j,i)]++;
        
    

    //Calcul de l'histogramme cumulé
    for(b=hist ; b<hist+65535 ; b++)
    
        *(b+1) += *b;
    


void equalizeHist_16U(Mat &img, int x1, int x2, int y1, int y2)

    long hist[65536] = 0;
    double ratio;
    int i,j;

    assert(img.channels() == 1);
    assert(img.type() == CV_16U);
    assert(x1>=0 && y1>=0 && x2>x1 && y2>y1);
    assert(y2<img.rows && x2<img.cols);

   cv::Rect roi(x1,y1,x2-x1+1,y2-y1+1);
   cv::Mat& temp = img;
   cv::Mat roiMat = temp(roi);

   compute_hist_16U(roiMat, hist);

   for(i=0 ; i<=img.cols-1 ; i++)
   
       for(j=0 ; j<=img.rows-1 ; j++)
       
           img.at<unsigned short>(j,i) = 65536.0*hist[img.at<unsigned short>(j,i)] / (roiMat.cols*roiMat.rows);
       
   


void equalizeHist_16U(Mat &img)

    equalizeHist_16U(img, 0, img.cols-1, 0, img.rows-1);

我认为它有效,如果我选择图像的明亮部分,我在这部分有极值(亮白色和深黑色),而图像的其余部分很暗。例如,如果我选择右边的建筑物:http://www.noelshack.com/2015-20-1431349774-result-ok.png

但有时结果很奇怪,例如如果我选择最黑的云:http://www.noelshack.com/2015-20-1431349774-result-nok.png

【讨论】:

我刚刚发现为什么我选择了一个黑暗的区域时结果很奇怪。在双循环中的函数equalizeHist_16U(...) 中,我将累积直方图乘以65536.0 而不是65535.0 现在它就像一个魅力,如果我选择一个黑暗的区域,其余的都非常清晰,如果我选择一个清晰的其余区域为深黑色。 你只接受你自己的答案吗? :) 请注意,您的问题是now I want to do the histogram equialization of my image but only on a specified part of the image。这就是我的回答,正如你所说,我提供的解决方案有效。 对不起,我检查答案太快了,我现在有点调整! :) 我改变了我的看法,并将您的答案标记为问题的解决方案。问候

以上是关于选择性直方图均衡(仅在图像的指定区域)的主要内容,如果未能解决你的问题,请参考以下文章

[数字图像处理] 直方图均衡化

OpenCV使用python实现限制对比度的自适应直方图均衡化

图像直方图与直方图均衡化

直方图均衡化对有些图片不能达到好的效果, 为啥?

图像直方图与直方图均衡化

python opencv直方图均衡