基于自适应暗原色的单幅图像去雾算法代码复现

基于自适应暗原色的单幅图像去雾算法代码复现

  • 前言
  • 一、原文
  • 二、代码
  • 三、实验效果
    • (1)透射率对比
    • (2)PSNR与SSIM
  • 说明


前言

大学四年快毕业了,准备把大学期间做的一些东西给写一下,感兴趣的可以持续关注。


提示:以下是本篇文章正文内容,下面案例可供参考

一、原文

基于自适应暗原色的单幅图像去雾算法代码复现_第1张图片

算法原理可以直接在论文中看,论文好像没有给出代码,所以我自己复现了一下,但没有达到论文中的优秀效果。

二、代码

环境是OpenCV,C++,软件用的VS 2022

/*自适应暗通道*/
#include  
#include 
#include 
#include 
#include 
using namespace cv;
using namespace std;

int rows, cols;
//获取三通道最小值矩阵
int** getMinChannel(cv::Mat img) 
{
    rows = img.rows;
    cols = img.cols;
    if (img.channels() != 3)
    {
        fprintf(stderr, "Input Error!");
        exit(-1);
    }
    int** imgGray;
    imgGray = new int* [rows];
    for (int i = 0; i < rows; i++) 
    {
        imgGray[i] = new int[cols];
    }
    for (int i = 0; i < rows; i++) 
    {
        for (int j = 0; j < cols; j++) 
        {
            int loacalMin = 255;
            for (int k = 0; k < 3; k++)
            {
                if (img.at<Vec3b>(i, j)[k] < loacalMin)
                {
                    loacalMin = img.at<Vec3b>(i, j)[k];
                }
            }
            imgGray[i][j] = loacalMin;
        }
    }
    return imgGray;
}

//获取自适应暗原色
float** getgoodMinchannel(int** img, int blockSize)
{
    if (blockSize % 2 == 0 || blockSize < 3)
    {
        fprintf(stderr, "blockSize is not odd or too small!");
        exit(-1);
    }//判断窗口大小是否合适
    int halflength= (blockSize - 1) / 2;//确定窗口边长 7

    int newHeight = rows;
    int newWidth = cols;
    
    while(newHeight % blockSize != 0)
    {
        newHeight = newHeight + 1;
    }
    while (newWidth % blockSize != 0)
    {
        newWidth = newWidth + 1;
    }

    int** imgGray2;
    imgGray2 = new int* [newHeight];
    for (int i = 0; i < newHeight; i++)
    {
        imgGray2[i] = new int[newWidth];
    }
    
    for (int i = 0; i < newHeight; i++)
    {
        for (int j = 0; j < newWidth; j++)
        {
            if (i < rows && j < cols)
            {
                imgGray2[i][j] = img[i][j];
            }
            else
            {
                imgGray2[i][j] = 255;
            }
        }
    }//结束
    
    float** a;
    float pingjun;
    int m = halflength, n = halflength;
    a = new float* [newHeight];
    for (int i = 0; i < newHeight; i++)
    {
        a[i] = new float[newWidth];
    }
    std::cout << newHeight << ' ' << newWidth << std::endl;
    for (int i = 0; i < newHeight; i++)//newheight 450/15 = 30 
    {
       
        for (int j = 0; j < newWidth; j++)//newwidth  450/15 = 30
        { 
            
            //std::cout << "imgGray2[i][j]" << imgGray2[i][j] << std::endl;
            //std::cout << "imgGray2[m][n]" << imgGray2[m][n] << std::endl;
            
            a[i][j] = fabs(imgGray2[i][j] - imgGray2[m][n]) / (imgGray2[m][n]+1);
           // std::cout << "a[i][j]" << a[i][j] << std::endl;
            n = (j / blockSize) * blockSize + halflength; //(j/15) *15 + 7
            //std::cout << "n " << n << std::endl;
        }
        m = (i / blockSize) * blockSize + halflength;
        //std::cout << "m " << m << std::endl;
    }//
    std::cout << '2' << std::endl;
    float f = 0.1;
    float** imgGray3;
    imgGray3 = new float* [rows];
    for (int i = 0; i < rows; i++)
    {
        imgGray3[i] = new float[cols];
    }
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            if (double(a[i][j]) > f)
            {
                imgGray3[i][j] = 255;
            }
            else
            {
                imgGray3[i][j] = imgGray2 [i][j];
            }

        }
    }
   
    return imgGray3;
}

//求暗通道最小值滤波
float** getDarkChannel(float** img, int blockSize )
{
    if (blockSize % 2 == 0 || blockSize < 3)
    {
        fprintf(stderr, "blockSize is not odd or too small!");
        exit(-1);
    }
    //计算pool Size
    int poolSize = (blockSize - 1) / 2;
    int newHeight = rows + blockSize - 1;
    int newWidth = cols + blockSize - 1;
    float** imgMiddle;
    imgMiddle = new float* [newHeight];
    for (int i = 0; i < newHeight; i++) 
    {
        imgMiddle[i] = new float[newWidth];
    }
    for (int i = 0; i < newHeight; i++) 
    {
        for (int j = 0; j < newWidth; j++) 
        {
            if (i < rows && j < cols) 
            {
                imgMiddle[i][j] = img[i][j];
            }
            else 
            {
                imgMiddle[i][j] = 255;
            }
        }
    }//进行边缘拓展,并赋值为255
    float** imgDark;
    imgDark = new float* [rows];
    for (int i = 0; i < rows; i++)
    {
        imgDark[i] = new float[cols];
    }
    int localMin = 255;
    for (int i = poolSize; i < newHeight - poolSize; i++) 
    {
        for (int j = poolSize; j < newWidth - poolSize; j++) 
        {
            localMin = 255;
            for (int k = i - poolSize; k < i + poolSize + 1; k++)
            {
                for (int l = j - poolSize; l < j + poolSize + 1; l++) 
                {
                    if (imgMiddle[k][l] < localMin) 
                    {
                        localMin = imgMiddle[k][l];
                    }
                }
            }
            imgDark[i - poolSize][j - poolSize] = localMin;
        }
    }
    return imgDark;
}

struct node
{
    int x, y, val;
    node() {}
    node(int _x, int _y, int _val) :x(_x), y(_y), val(_val) {}
    bool operator<(const node& rhs)
    {
        return val > rhs.val;
    }
};

//估算全局大气光值
float getGlobalAtmosphericLightValue(float** darkChannel, cv::Mat img, bool meanMode = false, float percent = 0.001)
{
    int size = rows * cols;
    std::vector <node> nodes;
    for (int i = 0; i < rows; i++) 
    {
        for (int j = 0; j < cols; j++) 
        {
            node tmp;
            tmp.x = i, tmp.y = j, tmp.val = darkChannel[i][j];
            nodes.push_back(tmp);
        }
    }
    sort(nodes.begin(), nodes.end());
    float atmosphericLight = 0;
    if (int(percent * size) == 0)
    {
        for (int i = 0; i < 3; i++)
        {
            if (img.at<Vec3b>(nodes[0].x, nodes[0].y)[i] > atmosphericLight) 
            {
                atmosphericLight = img.at<Vec3b>(nodes[0].x, nodes[0].y)[i];
            }
        }
    }
    //开启均值模式
    if (meanMode == true)
    {
        int sum = 0;
        for (int i = 0; i < int(percent * size); i++)
        {
            for (int j = 0; j < 3; j++) 
            {
                sum = sum + img.at<Vec3b>(nodes[i].x, nodes[i].y)[j];
            }
        }
    }
    //获取暗通道在前0.1%的位置的像素点在原图像中的最高亮度值
    for (int i = 0; i < int(percent * size); i++) 
    {
        for (int j = 0; j < 3; j++)
        {
            if (img.at<Vec3b>(nodes[i].x, nodes[i].y)[j] > atmosphericLight) 
            {
                atmosphericLight = img.at<Vec3b>(nodes[i].x, nodes[i].y)[j];
            }
        }
    }
    return atmosphericLight;
}

//恢复原图像
// Omega 去雾比例 参数
//t0 最小透射率值
cv::Mat getRecoverScene(cv::Mat img, float omega = 0.95, float t0 = 0.1, int blockSize = 15, bool meanModel = false, float percent = 0.001) 
{
   
    int blocksize2 = 25;
    int** imgGray = getMinChannel(img);
    float** imgGray3 = getgoodMinchannel(imgGray, blocksize2); 
    float** imgDark = getDarkChannel(imgGray3, blockSize);
    float atmosphericLight = getGlobalAtmosphericLightValue(imgDark, img, meanModel = meanModel, percent = percent);
    std::cout << "atmospher" << atmosphericLight << std::endl;
    float** imgDark4, ** transmission;
    imgDark4 = new float* [rows];
    for (int i = 0; i < rows; i++) 
    {
        imgDark4[i] = new float[cols];
    }
    transmission = new float* [rows];
    for (int i = 0; i < rows; i++) 
    {
        transmission[i] = new float[cols];
    }
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            imgDark4[i][j] = float(imgDark[i][j]);
            transmission[i][j] = 1 - omega * imgDark4[i][j] / atmosphericLight;
            if (transmission[i][j] < 0.1)
            {
                transmission[i][j] = 0.1;
            }
        }
    }
    Mat t(rows, cols, CV_32FC1);
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            t.at<float>(i,j) = transmission[i][j];
        }
    }
    imshow("改进t", t);
    cv::imwrite("../透射率k=1.jpg", t);
    cv::Mat dst(img.rows, img.cols, CV_8UC3);
    for (int channel = 0; channel < 3; channel++) 
    {
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++) 
            {
                int temp = (img.at<Vec3b>(i, j)[channel] - atmosphericLight) / transmission[i][j] + atmosphericLight;
                if (temp > 255) 
                {
                    temp = 255;
                }
                if (temp < 0) 
                {
                    temp = 0;
                }
                dst.at<Vec3b>(i, j)[channel] = temp;
            }
        }
    }
    
    return dst;
}

int main()
{
    cv::Mat src = cv::imread("C:\\Users\\杨秉霖\\Desktop\\图片\\图_4.png");
    rows = src.rows;
    cols = src.cols;
    
    cv::Mat dst = getRecoverScene(src);
    cv::imshow("origin", src);
    cv::imshow("result算法改进后", dst);
    
    cv::imwrite("../图_4.png", dst);
    waitKey(0);
}

三、实验效果

(1)透射率对比

图片是改进前的算法与本文算法对比
基于自适应暗原色的单幅图像去雾算法代码复现_第2张图片

基于自适应暗原色的单幅图像去雾算法代码复现_第3张图片

有效果,但没有原文中的那么优越。

(2)PSNR与SSIM

图片来源于两个图片数据集,可以从以下链接获取
基于自适应暗原色的单幅图像去雾算法代码复现_第4张图片

基于自适应暗原色的单幅图像去雾算法代码复现_第5张图片

基于自适应暗原色的单幅图像去雾算法代码复现_第6张图片


说明

使用代码请注明出处。

你可能感兴趣的:(算法,计算机视觉,opencv)