图像增强--自适应增强算法(Android-OpenCV)

自适应增强算法的具体原理可以参考如下博文:
彩色图像自适应对比度增强

事实上很多博文都有描叙这个,但都不愿公开完全代码,这里贴上我实现的代码。

首先对算法说明一些,如果直接按公式来,算法的复杂度就是n^2 * N^2 , 其中n为窗口大小,N为图像大小,但是我们可以很容易发现,可以通过已计算前值来计算后面的值,其中
M(x, y) = M(x, y-1) +{ SUM[f(xr)] - SUM[f(xl)]}/n;
其中,SUM[f(xr)] 表示右边要重新计算出来的那一列,SUM[f(xl)]为需要去掉的左边的那一列,这样算法复杂度就减少到n * N^2;

同样对Sigma的计算也可以推导得到类似结果,具体推导过程就不说了,直接上结果:
S(x,y) = S(x, y-1) + { SUM[f(xr)^2] - SUM[f(xl)^2]}/n + M(x, y-1)^2 - M(x,y)^2
补充:通过提前计算好每个窗口列的值,算法发杂度优化到 N^2,与窗口大小没多大关系,只与图像大小相关,1024*968的图片1秒内即可,当然如果添加多线程处理,速度会更快。

代码参考如下:(可能有些小错误)

private:
    // delta^2 = delta0^2 + [sumRight(f^2) - sumLeft(f^2)]/N + M0^2 - M^2
    void getMeanFromPreviousCol(const Mat &img, Mat &mean, Mat &sigma, const Mat &sumM, const Mat &sumS, int m, int n, int pI, int pJ) {
        int total = (2*m + 1) * (2*n + 1);
        /*float M = 0;
        for(int i = pI - m; i <= pI + m; ++i) {
            M += (img.at(i, pJ + n) - img.at(i, pJ -n -1));
        }*/
        float M = sumM.at<float>(pI, pJ + n) - sumM.at<float>(pI, pJ -n -1);
        M /= total;
        mean.at<float>(pI, pJ) = M + mean.at<float>(pI, pJ -1);

        /*float S = 0;
        for(int i = pI - m; i <= pI + m; ++i) {
            S += (pow(img.at(i, pJ + n), 2) - pow(img.at(i, pJ -n -1), 2));
        }*/
        float S = sumS.at<float>(pI, pJ + n) - sumS.at<float>(pI, pJ -n -1);
        S /= total;
        S += pow(mean.at<float>(pI, pJ -1), 2) - pow(mean.at<float>(pI, pJ), 2);
        sigma.at<float>(pI, pJ) = S + sigma.at<float>(pI, pJ -1);
    }

    void getMeanFromPreviousRow(const Mat &img, Mat &mean, Mat &sigma, const vector<float> &sumM, const vector<float> &sumS,
            int m, int n, int pI, int pJ) {
        if(pJ != n) {
            LOGE("line0-something is wrong!with pos=(%d, %d)", pI, pJ);
            return;
        }
        int total = (2*m + 1) * (2*n + 1);
        /*float M = 0;
        for(int j = pJ - n; j <= pJ + n; ++j) {
            M += (img.at(pI + n, j) - img.at(pI - n -1, j));
        }*/
        float M = sumM[pI + n] - sumM[pI - n -1];
        M /= total;
        mean.at<float>(pI, pJ) = M + mean.at<float>(pI - 1, pJ);

        /*float S = 0;
        for(int j = pJ - n; j <= pJ + n; ++j) {
            S += (pow(img.at(pI + n, j), 2) - pow(img.at(pI - n -1, j), 2));
        }
        S /= total;*/
        float S = (sumS[pI + n] - sumS[pI - n -1])/total;
        S += pow(mean.at<float>(pI - 1, pJ), 2) - pow(mean.at<float>(pI, pJ), 2);
        sigma.at<float>(pI, pJ) = S + sigma.at<float>(pI - 1, pJ);
    }

    //Count col sum
    void preCountColSum(const Mat &img, Mat &sumM, Mat &sumS, int m) {
        int colSize = (m << 1) + 1;
        for(int j=0; j<img.cols; ++j) {
            float tM = 0;
            float tS = 0;
            for(int i=0; i<colSize; ++i) {
                tM += img.at<uchar>(i, j);
                tS += (float)pow(img.at<uchar>(i, j), 2);
            }
            sumM.at<float>(m, j) = tM;
            sumS.at<float>(m, j) = tS;
            for(int i=m+1; i<img.rows - m; ++i) {
                sumM.at<float>(i, j) = sumM.at<float>(i-1, j) + img.at<uchar>(i + m, j) - img.at<uchar>(i - m - 1, j);
                sumS.at<float>(i, j) = sumS.at<float>(i-1, j) + (float)pow(img.at<uchar>(i + m, j), 2) - (float)pow(img.at<uchar>(i - m - 1, j), 2);
            }
        }
    }

    void preCountRowSum(const Mat &img, vector<float> &sumM, vector<float> &sumS, int n) {
        int rowSize = (n << 1) + 1;
        for(int i=0; i<img.rows; ++i) {
            sumM[i] = 0;
            sumS[i] = 0;
            for(int j=0; j<rowSize; ++j) {
                sumM[i] += img.at<uchar>(i, j);
                sumS[i] += (float)pow(img.at<uchar>(i, j), 2);
            }
        }
    }

    void getMean(const Mat &img, Mat &mean, Mat &sigma, const Mat &sumM, const Mat &sumS, vector<float> &rowSumM, vector<float> &rowSumS,
            int m, int n, int pI, int pJ) {
        if(pI - m < 0 || pI + m >= img.rows || pJ - n < 0 || pJ + n >= img.cols) {
            mean.at<float>(pI, pJ) = -1;
            sigma.at<float>(pI, pJ) = 0;
            return ;
        }
        if(pJ == n && pI > m) {
            return getMeanFromPreviousRow(img, mean, sigma, rowSumM, rowSumS, m, n, pI, pJ);
        } else if(pJ > n) {
            return getMeanFromPreviousCol(img, mean, sigma, sumM, sumS, m, n, pI, pJ);
        }
        float M = 0;
        int total = (2*m + 1) * (2*n + 1);
        for(int i=pI -m; i<= pI + m; ++i) {
            for(int j=pJ - n; j <= pJ + n; ++j) {
                M += img.at<uchar>(i, j);
            }
        }
        M /= total;
        mean.at<float>(pI, pJ) = M;

        float S = 0;
        float diff;
        for(int i=pI -m; i<= pI + m; ++i) {
            for(int j=pJ - n; j <= pJ + n; ++j) {
                diff =  img.at<uchar>(i, j) - M;
                S += (diff * diff);
            }
        }
        S /= total;
        sigma.at<float>(pI, pJ) = S;
    }

    void getMean(const Mat &img, Mat &mean, Mat &sigma, int m, int n){
        Mat colSumM = Mat::zeros(img.rows, img.cols, CV_32FC1);
        Mat colSumS = Mat::zeros(img.rows, img.cols, CV_32FC1);
        preCountColSum(img, colSumM, colSumS, m);

        vector<float> rowSumM;
        vector<float> rowSumS;
        rowSumM.reserve((unsigned)img.rows);
        rowSumS.reserve((unsigned)img.rows);
        preCountRowSum(img, rowSumM, rowSumS, n);

        for(int i=0;i<mImg.rows; ++i) {
            //LOGI("line0-row:%d", i);
            for (int j = 0; j < mImg.cols; ++j) {
                getMean(img, mean, sigma, colSumM, colSumS, rowSumM, rowSumS, m, n, i, j);
            }
        }
        float value;
        for(int i=0;i<mImg.rows; ++i) {
            for (int j = 0; j < mImg.cols; ++j) {
                value = sigma.at<float>(i, j);
                sigma.at<float>(i, j) = sqrt(value);
            }
        }
    }

其他非算法部分代码,可参考:
https://github.com/dingpwen/ImageProcess

对新手来说,最容犯的错误就是img.at(i, j),这里uchar一定不能写成float。

你可能感兴趣的:(Android,图像处理)