OpenCV自动色阶算法(可用于去雾)

本文介绍了一个使用OpenCV库进行自动色阶调整的函数,通过统计图像各通道的直方图,计算阈值,创建映射表,并利用并行计算加速,优化后的处理时间从150ms降低到22ms。
摘要由CSDN通过智能技术生成
#include <iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
#include<vector>
#include <algorithm>
#include <sys/time.h>
//
#include <omp.h>
using namespace cv;

#define OMP_ON

//自动色阶调整:用于图像去雾
void AutoLevelsAdjust(cv::Mat& src, cv::Mat& dst)
{
    timeval begin_time;
    timeval end_time;

    CV_Assert(!src.empty() && src.channels() == 3);

    //统计灰度直方图
    gettimeofday(&begin_time, NULL);
    int BHist[256] = { 0 };    //B分量
    int GHist[256] = { 0 };    //G分量
    int RHist[256] = { 0 };    //R分量
    //
#ifdef OMP_ON
    int omp_thread_nums = omp_get_num_procs() * 2;
    omp_set_num_threads(omp_thread_nums);
#pragma omp parallel for
#endif
    for(unsigned char* pixel = src.data; pixel < src.dataend; pixel += 3)
    {
        BHist[*pixel]++;
    }
#ifdef OMP_ON
#pragma omp parallel for
#endif
    for(unsigned char* pixel = src.data + 1; pixel < src.dataend; pixel += 3)
    {
        GHist[*pixel]++;
    }
#ifdef OMP_ON
#pragma omp parallel for
#endif
    for(unsigned char* pixel = src.data + 2; pixel < src.dataend; pixel += 3)
    {
        RHist[*pixel]++;
    }

    gettimeofday(&end_time, NULL);
    printf("Hist Time: %f ms\n", (end_time.tv_sec*1000000 - begin_time.tv_sec*1000000 + end_time.tv_usec - begin_time.tv_usec) / 1000.0);

    gettimeofday(&begin_time, NULL);
    //设置LowCut和HighCut
    float LowCut = 0.5;
    float HighCut = 0.5;

    //根据LowCut和HighCut查找每个通道最大值最小值
    int BMax = 0, BMin = 0;
    int GMax = 0, GMin = 0;
    int RMax = 0, RMin = 0;

    int TotalPixels = src.cols * src.rows;
    float LowTh = LowCut * 0.01 * TotalPixels;
    float HighTh = HighCut * 0.01 * TotalPixels;

    //B通道查找最小最大值
    int sumTempB = 0;
    for (int i = 0; i < 256; i++)
    {
        sumTempB += BHist[i];
        if (sumTempB >= LowTh)
        {
            BMin = i;
            break;
        }
    }
    //
    sumTempB = 0;
    for (int i = 255; i >= 0; i--)
    {
        sumTempB += BHist[i];
        if (sumTempB >= HighTh)
        {
            BMax = i;
            break;
        }
    }

    //G通道查找最小最大值
    int sumTempG = 0;
    for (int i = 0; i < 256; i++)
    {
        sumTempG += GHist[i];
        if (sumTempG >= LowTh)
        {
            GMin = i;
            break;
        }
    }
    //
    sumTempG = 0;
    for (int i = 255; i >= 0; i--)
    {
        sumTempG += GHist[i];
        if (sumTempG >= HighTh)
        {
            GMax = i;
            break;
        }
    }
    //R通道查找最小最大值
    int sumTempR = 0;
    for (int i = 0; i < 256; i++)
    {
        sumTempR += RHist[i];
        if (sumTempR >= LowTh)
        {
            RMin = i;
            break;
        }
    }
    //
    sumTempR = 0;
    for (int i = 255; i >= 0; i--)
    {
        sumTempR += RHist[i];
        if (sumTempR >= HighTh)
        {
            RMax = i;
            break;
        }
    }
    gettimeofday(&end_time, NULL);
    printf("Get Max Min Time: %f ms\n", (end_time.tv_sec*1000000 - begin_time.tv_sec*1000000 + end_time.tv_usec - begin_time.tv_usec) / 1000.0);

    gettimeofday(&begin_time, NULL);
    //对每个通道建立分段线性查找表
    //B分量查找表
    int BTable[256] = { 0 };
#ifdef OMP_ON
    omp_set_num_threads(omp_thread_nums);
#pragma omp parallel for
#endif
    for (int i = 0; i < 256; i++)
    {
        if (i <= BMin)
            BTable[i] = 0;
        else if (i > BMin && i < BMax)
            BTable[i] = cvRound((float)(i - BMin) / (BMax - BMin) * 255);
        else
            BTable[i] = 255;
    }

    //G分量查找表
    int GTable[256] = { 0 };
#ifdef OMP_ON
    omp_set_num_threads(omp_thread_nums);
#pragma omp parallel for
#endif
    for (int i = 0; i < 256; i++)
    {
        if (i <= GMin)
            GTable[i] = 0;
        else if (i > GMin && i < GMax)
            GTable[i] = cvRound((float)(i - GMin) / (GMax - GMin) * 255);
        else
            GTable[i] = 255;
    }

    //R分量查找表
    int RTable[256] = { 0 };
#ifdef OMP_ON
    omp_set_num_threads(omp_thread_nums);
#pragma omp parallel for
#endif
    for (int i = 0; i < 256; i++)
    {
        if (i <= RMin)
            RTable[i] = 0;
        else if (i > RMin && i < RMax)
            RTable[i] = cvRound((float)(i - RMin) / (RMax - RMin) * 255);
        else
            RTable[i] = 255;
    }
    gettimeofday(&end_time, NULL);
    printf("Create Color Table Time: %f ms\n", (end_time.tv_sec*1000000 - begin_time.tv_sec*1000000 + end_time.tv_usec - begin_time.tv_usec) / 1000.0);

    gettimeofday(&begin_time, NULL);
    //对每个通道用相应的查找表进行分段线性拉伸
    cv::Mat dst_ = src.clone();
#ifdef OMP_ON
    omp_set_num_threads(omp_thread_nums);
#pragma omp parallel for
#endif
    for(unsigned char* pixel = dst_.data; pixel <= dst_.dataend; pixel += 3)
    {
        *pixel = BTable[*pixel];
    }
#ifdef OMP_ON
#pragma omp parallel for
#endif
    for(unsigned char* pixel = dst_.data + 1; pixel <= dst_.dataend; pixel += 3)
    {
        *(pixel) = GTable[*pixel];
    }
#ifdef OMP_ON
#pragma omp parallel for
#endif
    for(unsigned char* pixel = dst_.data + 2; pixel <= dst_.dataend; pixel += 3)
    {
        *(pixel) = RTable[*pixel];
    }
    gettimeofday(&end_time, NULL);
    printf("Mapping Time: %f ms\n", (end_time.tv_sec*1000000 - begin_time.tv_sec*1000000 + end_time.tv_usec - begin_time.tv_usec) / 1000.0);
    dst = dst_;
}

int main()
{
    Mat image = imread("test.jpg");
    cv::resize(image, image, cv::Size(1920, 1080));
    Mat dst = Mat::zeros(image.rows, image.cols, CV_8UC1);

    timeval begin_time;
    timeval end_time;
    gettimeofday(&begin_time, NULL);
    AutoLevelsAdjust(image, dst);
    gettimeofday(&end_time, NULL);
    printf("Auto Level Adjust Time: %f ms\n", (end_time.tv_sec*1000000 - begin_time.tv_sec*1000000 + end_time.tv_usec - begin_time.tv_usec) / 1000.0);
    //
    imshow("src", image);
    imshow("dst", dst);
    while (char(waitKey(1)) != 'q') {}
    imwrite("dst.jpg", dst);
}

第一步,分别统计各通道(红/绿/蓝)的直方图。

第二步,分别计算各通道按照给定的参数所确定的上下限值。比如对于蓝色通道,我们从色阶0开始向上累加统计直方图,当累加值大于LowCut所有像素数时,以此时的色阶值计为BMin。然后从色阶255开始向下累计直方图,如果累加值大于HighCut所有像素时,以此时的色阶值计为BMax。

第三步,按照计算出的MinBlue/MaxBlue构建一个映射表,映射表的规则是,对于小于MinBlue的值,则映射为0(实际上这句话也不对,映射为多少是和那个自动颜色校正选项对话框中的阴影所设定的颜色有关,默认情况下是黑色,对应的RGB分量都为0,所以我们这里就映射为0,有兴趣你们也可以指定为其他的参数),对于大于MaxBlue的值,则映射为255(同理,这个值和高光的颜色设置有关),对于介于MinBlue和MaxBlue之间的值,则进行线性映射,默认是映射为0到255之间(当然实际是和我们的暗调和高光的设置有关,并且这里其实也不是线性映射,是有一个Gamma校正,为了简便,用线性替代效果也没太大的问题)。

最后一步,对各通道图像数据进行映射。

引用自https://www.cnblogs.com/ybqjymy/p/13807586.html

对代码进行运行效率优化。优化前,1920x1080分辨率处理时间150ms左右;优化后,1920x1080分辨率处理时间22ms左右。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV 中提供了一些去算法,以下是其中两种比较常用的算法: 1. 基于暗通道先验的去算法: 该算法的基本思想是利用图像中的暗通道先验,通过估计图像中的全局大气光来去除霾。具体步骤如下: 1.1 计算暗通道 对于输入的彩图像 $I(x)$,我们首先需要计算出该图像的暗通道 $J(x)$,公式如下: $$ J(x)=\min_{c\in\{R,G,B\}}(I_c(x)) $$ 其中,$c$ 表示颜通道,$R,G,B$ 分别表示红、绿、蓝三个颜通道。 1.2 估计大气光 在暗通道图像上进行滤波,得到最亮的像素点,即为图像中的大气光 $A$。具体做法是对暗通道图像进行一定半径的最大值滤波,得到一幅图像 $J_{max}(x)$,然后在 $J_{max}(x)$ 中找到一个像素点 $p$,使得: $$ A=\max_{p\in I}J_{max}(p) $$ 1.3 恢复图像 根据大气光的估计值,我们可以得到恢复后的图像 $I_t(x)$,公式如下: $$ I_t(x)=\frac{I(x)-A}{\max(\alpha(x),t)}+A $$ 其中,$t$ 是一个常数,用来控制图像的对比度,$\alpha(x)$ 是透射率,可以通过下面的公式计算得到: $$ \alpha(x)=1-\omega\min_{c\in\{R,G,B\}}\left(\frac{I_c(x)}{A}\right) $$ 其中,$\omega$ 是一个常数,用来控制透射率的平滑程度。 2. 基于暗通道先验和双边滤波的去算法: 该算法在基于暗通道先验的算法基础上,加入了双边滤波,可以更好地保留图像的细节。具体步骤如下: 2.1 估计大气光 同基于暗通道先验的算法。 2.2 估计透射率 对原始图像进行双边滤波,得到模糊图像 $I_b(x)$,然后在模糊图像上进行暗通道先验计算,得到透射率 $\alpha(x)$。 2.3 恢复图像 同基于暗通道先验的算法。 以上就是 OpenCV 中两种常见的去算法。你可以在 OpenCV 的文档中找到相应的 API 和使用方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值