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

时间:2015-05-07 16:42:35

标签: c++ opencv qt-creator histogram

我正在开发Qt创建者,使用opencv。

我必须开发一个执行图像直方图均衡的程序。 我的图像是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参数添加到我的函数中,并更改了我的&#34; for&#34;的范围。像这样(我改变的代码行有箭头):

---->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)

我得到的图像非常暗

我想我在循环边界上犯了一个错误,但我无法找到位置! 也许你有个主意?

提前谢谢

2 个答案:

答案 0 :(得分: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->width*img->height))并且更整洁(您应该通过引用传递Mat,而不是使用指针,即C风格,不是C ++)

此外:

  • 为什么要从compute_hist_16U
  • 返回值
  • long hist[65535]应为long hist[65536],以便索引65535有效。首先,65535是图片中的白色值。此外,当您b+1 b=hist+65534(最后一个周期)

    时,您可以在周期中使用它
    for(b=hist ; b<hist+65535 ; b++)
    {
        *(b+1) += *b;
    }
    

答案 1 :(得分:0)

感谢您的回答。

<强>初步 我认为在粘贴代码时我犯了一个错误,我忘了改变你注意到的那一行,这一行应该是你写的最后一行。

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

我还从compute_hist_16U删除了返回的值,并将long hist[65535]更改为long hist[65536] 我改变了传递图像的方式并删除了变量ratio。 我使用.at,因为当我搜索“如何访问像素值opencv mat”时,它是访问文档和stackoverflow中描述的像素值的方法 我从来没有见过如何创建一个查找表,所以当我的程序完全是函数时我可以调查一下

我的新功能是:

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