码迷,mamicode.com
首页 > 其他好文 > 详细

图像去雾

时间:2020-01-31 14:09:09      阅读:437      评论:0      收藏:0      [点我收藏+]

标签:出现   mat   自适应   固定   otto   lte   超出   float   遇到   

我使用了导向图滤波来计算折射率t,而在opencv中,要使用它需要在一开始就采用编译安装增加contrib模块。

何凯明博士提出了“暗通道”的说法,他发现色彩明亮没有雾的图像其暗通道几乎“全是黑的”

而有雾的图像由于雾提高了图像的颜色,其暗通道就“不全是黑的”

而公式推导及计算网上很多博客都有写,我主要说一下我遇到的一些问题

一开始尝试了许多代码,但都一致地会出现色斑及图像失真问题,后来查询了许多资料发现是t的计算问题,由于对t设置了阈值min_t,而在实际计算中如果t大于min_t,那么此点的t就设置为min_t,就会导致图像中部分像素点使用一样的t值,出现较多的色斑色偏

我发现A与min_t并不能采用一个固定的值,对于不同的图片,如果想要效果较好,那么就得对A与min_t进行微调

以下是实现代码:

//
//  Created by swenw on 2019/10/30.
//  Copyright © 2019 swenw. All rights reserved.
//

#include <opencv2/opencv.hpp>
#include <opencv2/ximgproc/edge_filter.hpp>
using namespace cv;
using namespace std;

const float kernRatio=0.01; //自适应核比例
const float w=0.95; //w系数 用来调节
const float min_t =0.5; //最小透射率
void minFilter(Mat src, Mat &dst, int ksize);

int main(int argc, char *argv[])
{
    Mat src = imread("/Users/swenw/Documents/数字媒体技术/Image/3.png");
    imshow("src",src);
    
    //最小RGB
    Mat minRgb = Mat::zeros(src.rows,src.cols,CV_8UC1);
    for(int i=0;i<src.rows;i++){
        for(int j=0;j<src.cols;j++)
        {
            uchar g_minvalue =255;
            for(int c=0;c<3;c++)
            {
                if(g_minvalue>src.at<Vec3b>(i,j)[c])
                    g_minvalue=src.at<Vec3b>(i,j)[c];
            }
            minRgb.at<uchar>(i,j)=g_minvalue;
        }
    }
    
    //对RGB进行最小值滤波,获得暗通道
    Mat darkChannel;
    int ksize=max(3,max((int)(src.cols*kernRatio),(int)(src.rows*kernRatio)));
    minFilter(minRgb, darkChannel, ksize);
    
    //求大气A,A为含雾图像的像素的最大值
    double A;
    Point maxLoc;
    minMaxLoc(darkChannel, 0, 0, 0, &maxLoc);
    A=max(src.at<Vec3b>(maxLoc.y,maxLoc.x)[0],
          max(src.at<Vec3b>(maxLoc.y,maxLoc.x)[1],
              src.at<Vec3b>(maxLoc.y,maxLoc.x)[2]));
    
    //求透射率t=1-w*(darkChannel/A)
    //当I非常接近于A时,t会非常的小,t<mint_t,于是会出现比较多的色斑色偏
    //对t进行修正,
    Mat trans=Mat::zeros(src.rows,src.cols,CV_32FC1);
    uchar pix[3];
    for(int i=0; i<src.rows; i++){
        const uchar* inData = darkChannel.ptr<uchar>(i);
        const uchar* srcData = src.ptr<uchar>(i);
        float* outData = trans.ptr<float>(i);
        for(int j=0; j<src.cols; j++){
            pix[0]=*srcData++;
            pix[1]=*srcData++;
            pix[2]=*srcData++;
            uchar t=fabs((pix[0]+pix[1]+pix[2])/3.0-A);
            if(t<min_t) //当像素接近于大气光强时,加一个增幅量,使其不总比min_t小
                *outData++=1-w*(*inData++/A)+(min_t-t)/min_t;
            else
                *outData++=1-w*(*inData++/A);
        }
    }
    Mat t = Mat::zeros(src.rows,src.cols,CV_32FC1);
    //导向图滤波
    ximgproc::guidedFilter(src, trans, t, 0.25, 0.001);
    imshow("t", t);
    
    //J=(I-A)/max(t,min_t)+A
    Mat deFog=Mat::zeros(src.rows,src.cols,CV_8UC3);
    for(int i=0;i<src.rows;i++){
        for(int j=0;j<src.cols;j++){
            for(int k=0;k<src.channels();k++){ deFog.at<Vec3b>(i,j)[k]=saturate_cast<uchar>((src.at<Vec3b>(i,j)[k]-A)/max(t.at<float>(i,j), min_t)+A);
            }
        }
    }
    
    imshow("defog",deFog);
    while(char(waitKey(1))!=q){}
}

void minFilter(const Mat src,Mat &dst,int ksize)
{
    if(src.channels()!=1)
        return;
    if(src.depth()>8)
        return;
    
    int r=(ksize-1)/2; //核半径
    
    dst=Mat::zeros(src.rows,src.cols,CV_8UC1);
    for(int i=0; i<src.rows; i++){
        for(int j=0; j<src.cols; j++)
        {
            //初始化滤波核的上下左右边界
            int top=i-r;
            int bottom=i+r;
            int left=j-r;
            int right=j+r;
            
            //检查滤波核是否超出边界
            if(i-r<0)
                top=0;
            if(i+r>src.rows)
                bottom=src.rows;
            if(j-r<0)
                left=0;
            if(j+r>src.cols)
                right=src.cols;
            
            //求取模板下的最小值
            Mat ImROI=src(Range(top,bottom),Range(left,right));
            double min;
            minMaxLoc(ImROI, &min, 0, 0, 0);
            dst.at<uchar>(i,j)=min;
        }
    }
}

技术图片

 技术图片

 

 

由于最小值滤波带来的影响,会在图像的边缘出现透明的边框问题,而导向图滤波就是为了消除这一问题,但让我疑惑的是我的导向图滤波并没有达到很好的效果(如上图,透明边框很明显)

而使透明边框缩小一点的方法就是尽量的减小最小值滤波的滤波核,如改为3*3,而不是采用自适应滤波核,此时的效果会好一点

我的数媒老师说在测试的时候尽量不要找有水或有天的图片,因为会对最终效果带来影响,我也发现如果不改进t的计算,这类图片的色偏和色差是最大的

关于何凯明博士的方法计算后,得到的图片颜色会“偏暗”……

图像去雾

标签:出现   mat   自适应   固定   otto   lte   超出   float   遇到   

原文地址:https://www.cnblogs.com/swenw/p/12245013.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!