一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

自动色阶

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

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

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

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

  1 // AutoLevel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2 //
  3 
  4 #include <iostream>
  5 #include<opencv2\core\core.hpp>
  6 #include<opencv2\highgui\highgui.hpp>
  7 #include<opencv2\imgproc\imgproc.hpp>
  8 #include<iostream>
  9 #include<vector>
 10 #include <algorithm>
 11 using namespace cv;
 12 
 13 void AutoLevelsAdjust(cv::Mat& src, cv::Mat& dst)
 14 {
 15     CV_Assert(!src.empty() && src.channels() == 3);
 16 
 17     //统计灰度直方图
 18     int BHist[256] = { 0 };    //B分离
 19     int GHist[256] = { 0 };    //G分量
 20     int RHist[256] = { 0 };    //R分量
 21     cv::MatIterator_<Vec3b> its, ends;
 22     for (its = src.begin<Vec3b>(), ends = src.end<Vec3b>(); its != ends; its++)
 23     {
 24         BHist[(*its)[0]]++;
 25         GHist[(*its)[1]]++;
 26         RHist[(*its)[2]]++;
 27     }
 28 
 29     //设置LowCut和HighCut
 30     float LowCut = 0.5;
 31     float HighCut = 0.5;
 32 
 33     //根据LowCut和HighCut查找每个通道最大值最小值
 34     int BMax = 0, BMin = 0;
 35     int GMax = 0, GMin = 0;
 36     int RMax = 0, RMin = 0;
 37 
 38     int TotalPixels = src.cols * src.rows;
 39     float LowTh = LowCut * 0.01 * TotalPixels;
 40     float HighTh = HighCut * 0.01 * TotalPixels;
 41 
 42     //B通道查找最小最大值
 43     int sumTempB = 0;
 44     for (int i = 0; i < 256; i++)
 45     {
 46         sumTempB += BHist[i];
 47         if (sumTempB >= LowTh)
 48         {
 49             BMin = i;
 50             break;
 51         }
 52     }
 53     sumTempB = 0;
 54     for (int i = 255; i >= 0; i--)
 55     {
 56         sumTempB += BHist[i];
 57         if (sumTempB >= HighTh)
 58         {
 59             BMax = i;
 60             break;
 61         }
 62     }
 63 
 64     //G通道查找最小最大值
 65     int sumTempG = 0;
 66     for (int i = 0; i < 256; i++)
 67     {
 68         sumTempG += GHist[i];
 69         if (sumTempG >= LowTh)
 70         {
 71             GMin = i;
 72             break;
 73         }
 74     }
 75     sumTempG = 0;
 76     for (int i = 255; i >= 0; i--)
 77     {
 78         sumTempG += GHist[i];
 79         if (sumTempG >= HighTh)
 80         {
 81             GMax = i;
 82             break;
 83         }
 84     }
 85 
 86     //R通道查找最小最大值
 87     int sumTempR = 0;
 88     for (int i = 0; i < 256; i++)
 89     {
 90         sumTempR += RHist[i];
 91         if (sumTempR >= LowTh)
 92         {
 93             RMin = i;
 94             break;
 95         }
 96     }
 97     sumTempR = 0;
 98     for (int i = 255; i >= 0; i--)
 99     {
100         sumTempR += RHist[i];
101         if (sumTempR >= HighTh)
102         {
103             RMax = i;
104             break;
105         }
106     }
107 
108     //对每个通道建立分段线性查找表
109     //B分量查找表
110     int BTable[256] = { 0 };
111     for (int i = 0; i < 256; i++)
112     {
113         if (i <= BMin)
114             BTable[i] = 0;
115         else if (i > BMin && i < BMax)
116             BTable[i] = cvRound((float)(i - BMin) / (BMax - BMin) * 255);
117         else
118             BTable[i] = 255;
119     }
120 
121     //G分量查找表
122     int GTable[256] = { 0 };
123     for (int i = 0; i < 256; i++)
124     {
125         if (i <= GMin)
126             GTable[i] = 0;
127         else if (i > GMin && i < GMax)
128             GTable[i] = cvRound((float)(i - GMin) / (GMax - GMin) * 255);
129         else
130             GTable[i] = 255;
131     }
132 
133     //R分量查找表
134     int RTable[256] = { 0 };
135     for (int i = 0; i < 256; i++)
136     {
137         if (i <= RMin)
138             RTable[i] = 0;
139         else if (i > RMin && i < RMax)
140             RTable[i] = cvRound((float)(i - RMin) / (RMax - RMin) * 255);
141         else
142             RTable[i] = 255;
143     }
144 
145     //对每个通道用相应的查找表进行分段线性拉伸
146     cv::Mat dst_ = src.clone();
147     cv::MatIterator_<Vec3b> itd, endd;
148     for (itd = dst_.begin<Vec3b>(), endd = dst_.end<Vec3b>(); itd != endd; itd++)
149     {
150         (*itd)[0] = BTable[(*itd)[0]];
151         (*itd)[1] = GTable[(*itd)[1]];
152         (*itd)[2] = RTable[(*itd)[2]];
153     }
154     dst = dst_;
155 }
156 
157 int main()
158 {
159     Mat image = imread("E:\\picture\\wutian.jpg");
160     Mat dst = Mat::zeros(image.rows, image.cols, CV_8UC1);
161     AutoLevelsAdjust(image, dst);
162     imshow("src", image);
163     imshow("dst", dst);
164     while (char(waitKey(1)) != 'q') {}
165 }

在这里插入图片描述

自动色阶用来做去雾,还是最为稳定的,虽然现在有很多种其他增强方法,例如暗通道去雾,优化对比度去雾,基于Color-Lines的去雾,但是这些算法还不是很稳定,当先验知识失效时,处理失真比较严重。但自动色阶相对就比较稳定。

posted on 2020-10-13 11:19  一杯清酒邀明月  阅读(1581)  评论(0编辑  收藏  举报