#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左右。