论文原文

算法原理

这篇论文是在我之前写过的一篇:https://blog.csdn.net/just_sort/article/details/84539295 上提出了一些新的东西,接下来就一起来看看。首先在Local Color Correction中由 O ( i , j ) = 255 [ I ( i , j ) 255 ] γ O(i,j)=255[\frac{I(i,j)}{255}]^{\gamma} O(i,j)=255[255I(i,j)]γ指数部分为 γ [ i , j , N ( i , j ) ] = 2 [ 128 B F m a s k ( i , j ) / 128 ] \gamma[i,j,N(i,j)]=2^{[128-BFmask(i,j)/128]} γ[i,j,N(i,j)]=2[128BFmask(i,j)/128],这篇论文做出的修改有2个地方

  • 高斯模糊的mask使用双边滤波来代替,因为双边滤波的保边特性,可以更好的保持边缘信息。
  • 第二,常数2使用 α \alpha α来代替,并且是和图像内容相关的,当图像的整体平均值小于128时,使用: α = l n ( I a v e / 255 ) l n ( 0.5 ) \alpha=\frac{ln(I_{ave}/255)}{ln(0.5)} α=ln(0.5)ln(Iave/255)计算,当平均值大于128时,使用 α = l n ( 0.5 ) l n ( I a v e / 255 ) \alpha=\frac{ln(0.5)}{ln(I_{ave}/255)} α=ln(Iave/255)ln(0.5),意思就是说对于低对比度的图像,应该需要比较强的矫正,所以 α \alpha α应该偏大,反之。但是这里有个trick,看到一个文章提出来了:https://www.cnblogs.com/Imageshop/p/9129162.html 就是说对于第二条,实际上存在很大的问题,比如对于我们下面进行测试的原图,由于他上半部分为天空,下半部分比较暗,且基本各占一般,因此其平均值非常靠近128,因此计算出的α也非常接近1,这样如果按照改进后的算法进行处理,则基本上图像无什么变化,显然这是不符合实际的需求的,因此,个人认为作者这一改进是不合理的,还不如对所有的图像该值都取2,靠mask值来修正对比度。
    \quad 接下来我们实现算法需要对RGB图像进行处理,我们可以像我之前那篇论文那样对RGB通道分别处理,但是可能会存在色偏,所以可以在YUV或者CIELAB等等空间只对亮度的通道进行处理,最后再转回RGB,并且作者提出在对Y分量做处理后,再转换到RGB空间,图像会出现饱和度一定程度丢失的现象,看上去图像似乎色彩不足。所以作者提出了一个修正的公式为:

代码实现

Mat ContrastImageCorrection(Mat src){
    int rows = src.rows;
    int cols = src.cols;
    Mat yuvImg;
    cvtColor(src, yuvImg, CV_BGR2YUV_I420);
    vector <Mat> mv;
    split(yuvImg, mv);
    Mat OldY = mv[0].clone();
//    for(int i = 0; i < rows; i++){
//        for(int j = 0; j < cols; j++){
//            mv[0].at<uchar>(i, j) = 255 - mv[0].at<uchar>(i, j);
//        }
//    }
    Mat temp;
    bilateralFilter(mv[0], temp, 9, 50, 50);
    //GaussianBlur(mv[0], temp, Size(41, 41), BORDER_DEFAULT);
    for(int i = 0; i < rows; i++){
        for(int j = 0; j < cols; j++){
            float Exp = pow(2, (128 - (255 - temp.at<uchar>(i, j))) / 128.0);
            int value = int(255 * pow(OldY.at<uchar>(i, j) / 255.0, Exp));
            temp.at<uchar>(i, j) = value;
        }
    }
    Mat dst(rows, cols, CV_8UC3);
//    mv[0] = temp;
//    merge(mv, dst);
//    cvtColor(dst, dst, CV_YUV2BGRA_I420);
    for(int i = 0; i < rows; i++){
        for(int j = 0; j < cols; j++) {
            if (OldY.at<uchar>(i, j) == 0) {
                for (int k = 0; k < 3; k++) dst.at<Vec3b>(i, j)[k] = 0;
            } else {
                //channel B
                dst.at<Vec3b>(i, j)[0] =
                        (temp.at<uchar>(i, j)  * (src.at<Vec3b>(i, j)[0] + OldY.at<uchar>(i, j)) / OldY.at<uchar>(i, j) +
                         src.at<Vec3b>(i, j)[0] - OldY.at<uchar>(i, j)) >> 1;
                //channel G
                dst.at<Vec3b>(i, j)[1] =
                        (temp.at<uchar>(i, j)  * (src.at<Vec3b>(i, j)[1] + OldY.at<uchar>(i, j)) / OldY.at<uchar>(i, j) +
                         src.at<Vec3b>(i, j)[1] - OldY.at<uchar>(i, j)) >> 1;
                //channel R
                dst.at<Vec3b>(i, j)[2] =
                        (temp.at<uchar>(i, j) * (src.at<Vec3b>(i, j)[2] + OldY.at<uchar>(i, j)) / OldY.at<uchar>(i, j) +
                         src.at<Vec3b>(i, j)[2] - OldY.at<uchar>(i, j)) >> 1;
            }
        }
    }
//    for(int i = 0; i < rows; i++){
//        for(int j = 0; j < cols; j++){
//            for(int k = 0; k < 3; k++){
//                if(dst.at<Vec3b>(i, j)[k] < 0){
//                    dst.at<Vec3b>(i, j)[k] = 0;
//                }else if(dst.at<Vec3b>(i, j)[k] > 255){
//                    dst.at<Vec3b>(i, j)[k] = 255;
//                }
//            }
//        }
//    }
    return dst;
}

int main(){
    Mat src = imread(../1.jpg");
    Rect rect(0, 0, (src.cols-1)/2*2, (src.rows-1)/2*2); //保证长宽都是偶数
    Mat newsrc = src(rect);
    Mat dst = ContrastImageCorrection(newsrc);
    imshow("origin", newsrc);
    imshow("result", dst);
    waitKey(0);
    return 0;
}

就是按照公式一步步来的,十分简单。

效果

原图
YUV直接转回RGB
使用作者的修正公式

参考博客

https://www.cnblogs.com/Imageshop/p/9129162.html 这篇博客还是带SSE优化的,把耗时做到了很小,我现阶段没有做关于优化的东西,所以就只贴了朴素的C++实现了。