优化对比度增强的实时图像和视频去雾Optimized contrast enhancement for real-time image and video dehazing
里面有相关算法的去雾效果比较主要=环节,大气光估计,透射率粗估计只估计一个数,引导滤波精估计,gamma增强,根据估计进行去雾6. 一个比较清晰的代码:https://github.com/Accustomer/TheThingsWithImage/tree/main/Code/6_OCEDehazing7. https://www.cnblogs.com/Imageshop/p/3925461.
·
1.相关博客介绍
1.https://zhuanlan.zhihu.com/p/413747554
2.https://github.com/wendell-0x0/ImageAlgorithmDraft/tree/master 非作者源代码
3.作者论文和源代码,可能需要翻墙:http://mcl.korea.ac.kr/projects/dehazing/#userconsent#
里面有相关算法的去雾效果比较
4.博客介绍https://zhuanlan.zhihu.com/p/149947284
主要=环节,大气光估计,透射率粗估计只估计一个数,引导滤波精估计,gamma增强,根据估计进行去雾

5. https://blog.csdn.net/xx116213/article/details/51848429 这篇博客介绍的更深入一些,虽然有些公式错别,看不到原论文的可以看下这篇文章有些贴图。

6. 一个比较清晰的代码:https://github.com/Accustomer/TheThingsWithImage/tree/main/Code/6_OCEDehazing
7. https://www.cnblogs.com/Imageshop/p/3925461.html 有一个大概介绍
8. https://github.com/BBuf/giantpandacv.com/blob/master/docs/academic/%E4%BC%A0%E7%BB%9F%E5%9B%BE%E5%83%8F/%E4%B8%80%E4%BA%9B%E6%9C%89%E8%B6%A3%E7%9A%84%E5%9B%BE%E5%83%8F%E7%AE%97%E6%B3%95/%E3%80%8AOptimized%20contrast%20enhancement%20for%20real-time%20image%20and%20video%20dehazin%E3%80%8B%E8%AE%BA%E6%96%87C%2B%2B%E5%A4%8D%E7%8E%B0.md知名博主giantpanda
2.官方核心代码介绍
void dehazing::ImageHazeRemoval(IplImage* imInput, IplImage* imOutput)
{
IplImage* imAir;
IplImage* imSmallInput;
// 步骤1:查找表初始化 - 预计算常用数学函数以提高运行效率
MakeExpLUT(); // 指数函数查找表,用于传输图计算
GuideLUTMaker(); // 引导滤波查找表,用于边缘保持滤波
GammaLUTMaker(0.7f); // 伽马校正查找表,用于图像增强
// 步骤2:大气光估计准备
// 2.1 设置大气光估计的感兴趣区域(ROI)
// 通常选择图像上方区域,因为天空区域更可能包含大气光
cvSetImageROI(imInput, cvRect(m_nTopLeftX, m_nTopLeftY,
m_nBottomRightX-m_nTopLeftX, m_nBottomRightY-m_nTopLeftY));
// 2.2 创建临时图像用于大气光估计和小尺寸处理
imAir = cvCreateImage(cvSize(m_nBottomRightX-m_nTopLeftX,
m_nBottomRightY-m_nTopLeftY), IPL_DEPTH_8U, 3);
imSmallInput = cvCreateImage(cvSize(320, 240), IPL_DEPTH_8U, 3);
cvCopyImage(imInput, imAir);
// 2.3 执行大气光估计:基于四叉树递归分解的大气光估计算法
AirlightEstimation(imAir);
// 2.4 清理大气光估计临时资源
cvReleaseImage(&imAir);
cvResetImageROI(imInput);
// 步骤3:图像格式转换 - 将IplImage转换为RGB整型数组便于数值计算
IplImageToIntColor(imInput);
// 步骤4:传输图估计 - 基于暗通道先验的彩色图像传输图估计
// 参数说明:R通道、G通道、B通道、传输图输出、R通道、G通道、B通道、传输图输出、
// 帧序号(单幅图像为0)、图像宽度、图像高度
// 注意:单幅图像处理直接在原始分辨率下进行,无需下采样
TransmissionEstimationColor(m_pnRImg, m_pnGImg, m_pnBImg, m_pfTransmission,
m_pnRImg, m_pnGImg, m_pnBImg, m_pfTransmission, 0, m_nWid, m_nHei);
// 步骤5:引导滤波 - 对传输图进行边缘保持的平滑处理
// 使用标准引导滤波算法,参数0.001为正则化参数,控制平滑程度
GuidedFilter(m_nWid, m_nHei, 0.001);
// 备选方案:可移动窗口引导滤波(注释掉的代码)
//GuidedFilterShiftableWindow(0.001);
/*
// 调试代码:可视化传输图R通道
IplImage *test = cvCreateImage(cvSize(m_nWid, m_nHei),IPL_DEPTH_8U, 1);
for(int nK = 0; nK < m_nWid*m_nHei; nK++)
test->imageData[nK] = (uchar)(m_pfTransmissionR[nK]*255);
cvNamedWindow("tests");
cvShowImage("tests", test);
cvWaitKey(-1);
*/
// 步骤6:图像复原 - 基于大气散射模型的逆变换恢复无雾图像
RestoreImage(imInput, imOutput);
// 步骤7:清理资源
cvReleaseImage(&imSmallInput);
}
上面代码主要可以分为4个核心步骤
步骤一 Atmospheric light estimation

/*
=============================================================================
大气光估计函数 (Atmospheric Light Estimation)
=============================================================================
【算法原理】
基于分治策略的递归大气光估计算法,通过四叉树分解找到最接近纯白色的像素点。
该算法假设大气光在图像中表现为最亮且最均匀的区域。
【理论基础】
1. 大气散射模型:I(x) = J(x)t(x) + A(1-t(x))
其中A为大气光值,是去雾算法的关键参数
2. 大气光特征:通常出现在天空区域,具有高亮度、低方差特性
3. 四叉树分解:递归细分图像,逐步缩小搜索范围
【算法流程】
第一阶段:递归分块评估(当块大小 > 200像素时)
1. 将当前图像块分为4个子块(左上、右上、左下、右下)
2. 对每个子块计算评分:Score = Σ(Mean_channel - Std_channel)
3. 选择评分最高的子块进行递归处理
第二阶段:像素级精确搜索(当块大小 ≤ 200像素时)
1. 遍历当前块中的每个像素
2. 计算到纯白色(255,255,255)的欧氏距离
3. 选择距离最小的像素作为大气光值
【评分函数设计】
Score = Σ(μ_c - σ_c),其中c ∈ {R,G,B}
- μ_c:通道c的均值(反映亮度)
- σ_c:通道c的标准差(反映均匀性)
- 高评分区域:亮度高且纹理均匀,符合天空特征
【距离计算公式】
Distance = √[(255-R)² + (255-G)² + (255-B)²]
选择距离最小的像素,即最接近纯白色的点
【算法优势】
1. 自适应性:根据图像内容自动调整搜索精度
2. 鲁棒性:通过统计特征避免噪声干扰
3. 效率性:分治策略减少计算复杂度
4. 准确性:结合全局和局部信息提高估计精度
【参数说明】
@param imInput - 输入的有雾图像 (IplImage格式)
BGR通道顺序,8位无符号整数
【返回值】
通过成员变量m_anAirlight[3]返回估计的大气光值:
- m_anAirlight[0]:B通道大气光值 (0-255)
- m_anAirlight[1]:G通道大气光值 (0-255)
- m_anAirlight[2]:R通道大气光值 (0-255)
【性能特性】
- 时间复杂度:O(log(W×H) + 200) ≈ O(log n)
- 空间复杂度:O(log(min(W,H))) (递归栈深度)
- 阈值200:平衡精度与效率的经验值
【应用场景】
- 单张图像去雾:提供准确的大气光估计
- 视频去雾:为每帧提供稳定的大气光参考
- 自适应去雾:根据场景特征动态调整
*/
void dehazing::AirlightEstimation(IplImage* imInput)
{
// =========================================================================
// 变量初始化:距离计算和统计分析
// =========================================================================
int nMinDistance = 65536; // 到纯白色的最小距离(初始化为最大值)
int nDistance; // 当前像素到纯白色的距离
int nX, nY; // 像素坐标索引
int nMaxIndex; // 最高评分子块的索引
double dpScore[3]; // 各通道评分 (Mean - Std)
double dpMean[3]; // 各通道均值
double dpStds[3]; // 各通道标准差
float afMean[4] = {0}; // 四个子块的均值(预留)
float afScore[4] = {0}; // 四个子块的综合评分
float nMaxScore = 0; // 当前最高评分
// =========================================================================
// 图像参数获取
// =========================================================================
int nWid = imInput->width; // 图像宽度
int nHei = imInput->height; // 图像高度
int nStep = imInput->widthStep; // 图像行步长(字节)
// =========================================================================
// 四个子块图像创建:实现四叉树分解
// =========================================================================
IplImage *iplUpperLeft = cvCreateImage(cvSize(nWid/2, nHei/2),IPL_DEPTH_8U, 3); // 左上子块
IplImage *iplUpperRight = cvCreateImage(cvSize(nWid/2, nHei/2),IPL_DEPTH_8U, 3); // 右上子块
IplImage *iplLowerLeft = cvCreateImage(cvSize(nWid/2, nHei/2),IPL_DEPTH_8U, 3); // 左下子块
IplImage *iplLowerRight = cvCreateImage(cvSize(nWid/2, nHei/2),IPL_DEPTH_8U, 3); // 右下子块
// 单通道图像:用于统计分析
IplImage *iplR = cvCreateImage(cvSize(nWid/2, nHei/2),IPL_DEPTH_8U, 1); // R通道
IplImage *iplG = cvCreateImage(cvSize(nWid/2, nHei/2),IPL_DEPTH_8U, 1); // G通道
IplImage *iplB = cvCreateImage(cvSize(nWid/2, nHei/2),IPL_DEPTH_8U, 1); // B通道
// =========================================================================
// 图像分块:将原图分为四个相等子块
// =========================================================================
cvSetImageROI(imInput, cvRect(0, 0, nWid/2, nHei/2)); // 左上区域
cvCopyImage(imInput, iplUpperLeft);
cvSetImageROI(imInput, cvRect(nWid/2+nWid%2, 0, nWid, nHei/2)); // 右上区域
cvCopyImage(imInput, iplUpperRight);
cvSetImageROI(imInput, cvRect(0, nHei/2+nHei%2, nWid/2, nHei)); // 左下区域
cvCopyImage(imInput, iplLowerLeft);
cvSetImageROI(imInput, cvRect(nWid/2+nWid%2, nHei/2+nHei%2, nWid, nHei)); // 右下区域
cvCopyImage(imInput, iplLowerRight);
// =========================================================================
// 递归终止条件判断:块大小阈值为200像素
// =========================================================================
if(nHei*nWid > 200)
{
// =====================================================================
// 第一阶段:统计分析各子块,寻找最优候选区域
// =====================================================================
// ---------------------------------------------------------------------
// 左上子块统计分析
// ---------------------------------------------------------------------
cvCvtPixToPlane(iplUpperLeft, iplR, iplG, iplB, 0); // 分离RGB通道
cvMean_StdDev(iplR, dpMean, dpStds); // R通道均值和标准差
cvMean_StdDev(iplG, dpMean+1, dpStds+1); // G通道均值和标准差
cvMean_StdDev(iplB, dpMean+2, dpStds+2); // B通道均值和标准差
// 计算评分:均值-标准差(高亮度低方差区域得高分)
dpScore[0] = dpMean[0]-dpStds[0]; // R通道评分
dpScore[1] = dpMean[1]-dpStds[1]; // G通道评分
dpScore[2] = dpMean[2]-dpStds[2]; // B通道评分
afScore[0] = (float)(dpScore[0]+dpScore[1]+dpScore[2]); // 综合评分
nMaxScore = afScore[0]; // 初始化最高评分
nMaxIndex = 0; // 初始化最优子块索引
// ---------------------------------------------------------------------
// 右上子块统计分析
// ---------------------------------------------------------------------
cvCvtPixToPlane(iplUpperRight, iplR, iplG, iplB, 0);
cvMean_StdDev(iplR, dpMean, dpStds);
cvMean_StdDev(iplG, dpMean+1, dpStds+1);
cvMean_StdDev(iplB, dpMean+2, dpStds+2);
dpScore[0] = dpMean[0]-dpStds[0];
dpScore[1] = dpMean[1]-dpStds[1];
dpScore[2] = dpMean[2]-dpStds[2];
afScore[1] = (float)(dpScore[0]+dpScore[1]+dpScore[2]);
if(afScore[1] > nMaxScore) // 更新最优子块
{
nMaxScore = afScore[1];
nMaxIndex = 1;
}
// ---------------------------------------------------------------------
// 左下子块统计分析
// ---------------------------------------------------------------------
cvCvtPixToPlane(iplLowerLeft, iplR, iplG, iplB, 0);
cvMean_StdDev(iplR, dpMean, dpStds);
cvMean_StdDev(iplG, dpMean+1, dpStds+1);
cvMean_StdDev(iplB, dpMean+2, dpStds+2);
dpScore[0] = dpMean[0]-dpStds[0];
dpScore[1] = dpMean[1]-dpStds[1];
dpScore[2] = dpMean[2]-dpStds[2];
afScore[2] = (float)(dpScore[0]+dpScore[1]+dpScore[2]);
if(afScore[2] > nMaxScore) // 更新最优子块
{
nMaxScore = afScore[2];
nMaxIndex = 2;
}
// ---------------------------------------------------------------------
// 右下子块统计分析
// ---------------------------------------------------------------------
cvCvtPixToPlane(iplLowerRight, iplR, iplG, iplB, 0);
cvMean_StdDev(iplR, dpMean, dpStds);
cvMean_StdDev(iplG, dpMean+1, dpStds+1);
cvMean_StdDev(iplB, dpMean+2, dpStds+2);
dpScore[0] = dpMean[0]-dpStds[0];
dpScore[1] = dpMean[1]-dpStds[1];
dpScore[2] = dpMean[2]-dpStds[2];
afScore[3] = (float)(dpScore[0]+dpScore[1]+dpScore[2]);
if(afScore[3] > nMaxScore) // 更新最优子块
{
nMaxScore = afScore[3];
nMaxIndex = 3;
}
// =====================================================================
// 递归调用:对最优子块继续细分搜索
// =====================================================================
switch (nMaxIndex)
{
case 0:
AirlightEstimation(iplUpperLeft); break; // 递归处理左上子块
case 1:
AirlightEstimation(iplUpperRight); break; // 递归处理右上子块
case 2:
AirlightEstimation(iplLowerLeft); break; // 递归处理左下子块
case 3:
AirlightEstimation(iplLowerRight); break; // 递归处理右下子块
}
}
else
{
// =====================================================================
// 第二阶段:像素级精确搜索,寻找最接近纯白色的像素
// =====================================================================
for(nY=0; nY<nHei; nY++) // 遍历所有行
{
for(nX=0; nX<nWid; nX++) // 遍历所有列
{
// 计算当前像素到纯白色(255,255,255)的欧氏距离
// 距离公式:√[(255-B)² + (255-G)² + (255-R)²]
nDistance = int(sqrt(float(255-(uchar)imInput->imageData[nY*nStep+nX*3])*float(255-(uchar)imInput->imageData[nY*nStep+nX*3])
+float(255-(uchar)imInput->imageData[nY*nStep+nX*3+1])*float(255-(uchar)imInput->imageData[nY*nStep+nX*3+1])
+float(255-(uchar)imInput->imageData[nY*nStep+nX*3+2])*float(255-(uchar)imInput->imageData[nY*nStep+nX*3+2])));
if(nMinDistance > nDistance) // 找到更接近纯白色的像素
{
nMinDistance = nDistance; // 更新最小距离
// 保存大气光值(BGR顺序)
m_anAirlight[0] = (uchar)imInput->imageData[nY*nStep+nX*3]; // B通道
m_anAirlight[1] = (uchar)imInput->imageData[nY*nStep+nX*3+1]; // G通道
m_anAirlight[2] = (uchar)imInput->imageData[nY*nStep+nX*3+2]; // R通道
}
}
}
}
// =========================================================================
// 内存清理:释放临时创建的图像资源
// =========================================================================
cvReleaseImage(&iplUpperLeft); // 释放左上子块
cvReleaseImage(&iplUpperRight); // 释放右上子块
cvReleaseImage(&iplLowerLeft); // 释放左下子块
cvReleaseImage(&iplLowerRight); // 释放右下子块
cvReleaseImage(&iplR); // 释放R通道图像
cvReleaseImage(&iplG); // 释放G通道图像
cvReleaseImage(&iplB); // 释放B通道图像
}
步骤二Optimal transmission estimation
主要是 信息loss和对比度loss

然后代码就是遍历 t从0.3到1,计算E,然后确定最好的t也就是粗糙的透射率估计,每块都有一个估计。
/*
函数名:TransmissionEstimation
功能:灰度图像的传输估计
算法原理:
--------
该函数实现了灰度图像的传输估计,是彩色版本的简化形式。
对于灰度图像,只需要处理单一的亮度通道(Y通道)。
处理流程:
1. 将图像分割成固定大小的块(m_nTBlockSize × m_nTBlockSize)
2. 对每个块调用相应的传输估计函数:
- 如果启用前帧信息且不是第一帧:调用NFTrsEstimationP
- 否则:调用NFTrsEstimation
3. 将估计的传输值赋给块内所有像素
与彩色版本的区别:
- 只处理单一通道,计算量更小
- 适用于灰度图像或亮度通道处理
- 算法逻辑与彩色版本完全一致
参数说明:
- pnImageY: 当前帧的灰度/亮度通道数据
- pfTransmission: 输出的传输图
- pnImageYP: 前一帧的灰度/亮度通道数据(用于时间一致性)
- pfTransmissionP: 前一帧的传输图
- nFrame: 帧编号
- nWid, nHei: 图像宽度和高度
返回值:
- 通过pfTransmission参数返回估计的传输图
*/
void dehazing::TransmissionEstimation(int *pnImageY, float *pfTransmission, int *pnImageYP, float *pfTransmissionP, int nFrame, int nWid, int nHei)
{
int nX, nY, nXstep, nYstep; // 循环变量:块的起始位置和块内像素位置
float fTrans; // 当前块估计的传输值
// 判断是否使用前帧信息进行时间一致性优化
if(m_bPreviousFlag == true&&nFrame>0)
{
// 使用前帧信息的传输估计
for(nY=0; nY<nHei; nY+=m_nTBlockSize) // 按块大小遍历Y方向
{
for(nX=0; nX<nWid; nX+=m_nTBlockSize) // 按块大小遍历X方向
{
// 调用基于前帧信息的灰度传输估计函数
// __max确保坐标不为负数
fTrans = NFTrsEstimationP(pnImageY, pnImageYP, pfTransmissionP, __max(nX, 0), __max(nY, 0), nWid, nHei);
// 将估计的传输值赋给当前块内的所有像素
for(nYstep=nY; nYstep<nY+m_nTBlockSize; nYstep++)
{
for(nXstep=nX; nXstep<nX+m_nTBlockSize; nXstep++)
{
pfTransmission[nYstep*nWid+nXstep] = fTrans;
}
}
}
}
}
else
{
// 不使用前帧信息的传输估计(第一帧或禁用时间一致性)
for(nY=0; nY<nHei; nY+=m_nTBlockSize)
{
for(nX=0; nX<nWid; nX+=m_nTBlockSize)
{
// 调用基础的灰度传输估计函数
fTrans = NFTrsEstimation(pnImageY, __max(nX, 0), __max(nY, 0), nWid, nHei);
// 将估计的传输值赋给当前块内的所有像素
for(nYstep=nY; nYstep<nY+m_nTBlockSize; nYstep++)
{
for(nXstep=nX; nXstep<nX+m_nTBlockSize; nXstep++)
{
pfTransmission[nYstep*nWid+nXstep] = fTrans;
}
}
}
}
}
}
/*
函数名:NFTrsEstimation
功能:基础传输估计算法(灰度图像)
算法原理:
--------
这是传输估计的核心算法,使用穷举搜索方法在指定块内寻找最优传输值。
数学模型:
基于大气散射模型:I(x) = J(x) * t(x) + A * (1 - t(x))
恢复公式:J(x) = (I(x) - A) / t(x) + A
优化目标函数:
Cost(t) = λ₁ * InformationLoss(t) - Contrast(t)
其中:
1. InformationLoss(t) = Σ(max(0, J-255)² + max(0, -J)²) / N
- 惩罚恢复像素值超出[0,255]范围的情况
- 防止过度恢复导致的信息丢失
2. Contrast(t) = Var(J) = E[J²] - E[J]²
- 使用方差衡量恢复图像的对比度
- 最大化对比度以获得更清晰的图像
搜索策略:
- 搜索范围:t ∈ [0.3, 1.0]
- 搜索步长:0.1
- 搜索次数:7次(0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)
- 选择使代价函数最小的传输值
实现细节:
- 使用整数运算提高效率:nTrans = (int)(1/t * 128)
- 恢复公式:nOut = ((I - A) * nTrans + 128 * A) >> 7
- 这相当于:nOut = (I - A) / t + A,但使用整数运算
参数说明:
- pnImageY: 输入的灰度图像数据
- nStartX, nStartY: 处理块的左上角坐标
- nWid, nHei: 图像宽度和高度
返回值:
- fOptTrs: 最优传输值
*/
float dehazing::NFTrsEstimation(int *pnImageY, int nStartX, int nStartY, int nWid, int nHei)
{
int nCounter; // 传输值搜索计数器(0.3~0.9,共7次迭代)
int nX, nY; // 块内像素坐标索引
int nEndX; // 块的右边界
int nEndY; // 块的下边界
int nOut; // 恢复后的像素值 J(x)
int nSquaredOut; // 恢复像素值的平方 J²(x)
int nSumofOuts; // 恢复像素值的总和 Σ J(x)
int nSumofSquaredOuts; // 恢复像素值平方的总和 Σ J²(x)
float fTrans, fOptTrs; // 当前传输值和最优传输值
int nTrans; // 传输值的整数表示(用于快速计算)
int nSumofSLoss; // 信息损失的总和
float fCost, fMinCost, fMean; // 代价函数值、最小代价、像素均值
int nNumberofPixels, nLossCount; // 像素总数、损失像素计数
// 计算处理块的边界,确保不超出图像范围
nEndX = __min(nStartX+m_nTBlockSize, nWid);
nEndY = __min(nStartY+m_nTBlockSize, nHei);
nNumberofPixels = (nEndY-nStartY)*(nEndX-nStartX); // 块内像素总数
fTrans = 0.3f; // 初始传输值从0.3开始
nTrans = 427; // 对应的整数表示:1/0.3*128 ≈ 427
// 穷举搜索最优传输值
for(nCounter=0; nCounter<7; nCounter++)
{
// 初始化统计变量
nSumofSLoss = 0; // 信息损失总和
nLossCount = 0; // 损失像素计数
nSumofSquaredOuts = 0; // 像素平方和
nSumofOuts = 0; // 像素总和
// 遍历块内所有像素
for(nY=nStartY; nY<nEndY; nY++)
{
for(nX=nStartX; nX<nEndX; nX++)
{
// 使用整数运算计算恢复像素值
// 公式:J = (I-A)/t + A = ((I-A)*k*128 + A*128)/128
// 其中 k = 1/t,右移7位相当于除以128
nOut = ((pnImageY[nY*nWid+nX] - m_nAirlight)*nTrans + 128*m_nAirlight)>>7;
nSquaredOut = nOut * nOut;
// 计算信息损失:像素值超出[0,255]范围的惩罚
if(nOut>255)
{
nSumofSLoss += (nOut - 255)*(nOut - 255); // 超出255的惩罚
nLossCount++;
}
else if(nOut < 0)
{
nSumofSLoss += nSquaredOut; // 小于0的惩罚
nLossCount++;
}
// 累积统计量用于计算对比度
nSumofSquaredOuts += nSquaredOut; // Σ J²
nSumofOuts += nOut; // Σ J
}
}
// 计算像素均值:E[J] = Σ J / N
fMean = (float)(nSumofOuts)/(float)(nNumberofPixels);
// 计算代价函数:Cost = λ₁ * InformationLoss - Contrast
// Contrast = Var(J) = E[J²] - E[J]²
fCost = m_fLambda1 * (float)nSumofSLoss/(float)(nNumberofPixels) // 信息损失项
- ((float)nSumofSquaredOuts/(float)nNumberofPixels - fMean*fMean); // 对比度项(负号表示最大化)
// 更新最优传输值
if(nCounter==0 || fMinCost > fCost)
{
fMinCost = fCost;
fOptTrs = fTrans;
}
// 更新传输值进行下一次搜索
fTrans += 0.1f; // 传输值增加0.1
nTrans = (int)(1.0f/fTrans*128.0f); // 更新对应的整数表示
}
return fOptTrs; // 返回最优传输值
}
步骤三,对初始估计的穿透率进行refine,利用引导滤波,原图作为引导图像
具体代码就不贴了,可以通过http://mcl.korea.ac.kr/projects/dehazing/#userconsent#下载作者原始引导滤波代码
步骤四,图像去雾
已知 大气估计值和 透射率估计图,就可以利用公式恢复 图像
/*
=============================================================================
图像恢复函数 (Image Restoration)
=============================================================================
【算法原理】
基于大气散射模型的图像恢复算法,通过估计的传输图和大气光值恢复清晰图像。
该函数是去雾算法的核心步骤,将有雾图像转换为无雾的清晰图像。
【理论基础】
1. 大气散射模型:I(x) = J(x)t(x) + A(1-t(x))
- I(x):观测到的有雾图像
- J(x):场景辐射(无雾图像)
- t(x):传输图(透射率)
- A:大气光值
2. 图像恢复公式:J(x) = (I(x) - A) / t(x) + A
通过逆向求解大气散射模型得到清晰图像
【算法流程】
第一阶段:后处理判断
- 如果启用后处理标志(m_bPostFlag),调用PostProcessing函数
- 否则执行标准恢复流程
第二阶段:像素级图像恢复(并行处理)
1. 对每个像素应用恢复公式:J = (I - A) / t + A
2. 使用CLIP函数确保像素值在有效范围内
3. 使用CLIP_Z函数避免传输图为零的除零错误
4. 应用伽马校正查找表优化视觉效果
【数学模型详解】
恢复公式推导:
I(x) = J(x)t(x) + A(1-t(x))
I(x) = J(x)t(x) + A - At(x)
I(x) - A = J(x)t(x) - At(x)
I(x) - A = t(x)(J(x) - A)
J(x) = (I(x) - A) / t(x) + A
【关键技术】
1. 传输图约束:使用CLIP_Z确保t(x) ≥ t_min,避免过度增强
2. 像素值约束:使用CLIP确保输出在[0,255]范围内
3. 伽马校正:通过查找表m_pucGammaLUT调整亮度和对比度
4. 并行处理:使用OpenMP提高计算效率
【性能优化】
1. 预计算查找表:伽马校正使用LUT避免重复计算
2. 内存访问优化:按行优先顺序访问提高缓存命中率
3. 并行计算:OpenMP并行化像素级操作
4. 数据类型优化:使用float进行中间计算,uchar存储结果
【参数说明】
@param imInput - 输入的有雾图像 (IplImage格式)
BGR通道顺序,8位无符号整数
@param imOutput - 输出的去雾图像 (IplImage格式)
与输入图像相同格式和尺寸
【算法特性】
- 时间复杂度:O(W×H) - 线性复杂度
- 空间复杂度:O(1) - 原地处理,不需要额外存储
- 并行度:像素级并行,理论加速比接近CPU核心数
- 数值稳定性:通过约束函数避免数值异常
【应用场景】
- 单张图像去雾:恢复清晰的无雾图像
- 视频去雾:逐帧处理视频序列
- 实时去雾:结合优化技术实现实时处理
- 批量处理:大规模图像去雾任务
【质量控制】
1. 传输图质量:直接影响恢复效果
2. 大气光估计:准确性决定色彩还原度
3. 伽马参数:影响最终图像的视觉效果
4. 约束参数:平衡去雾强度和噪声抑制
*/
void dehazing::RestoreImage(IplImage* imInput, IplImage* imOutput)
{
// =========================================================================
// 图像参数获取和大气光值初始化
// =========================================================================
int nStep = imInput->widthStep; // 图像行步长(字节)
int nX, nY; // 像素坐标索引
float fA_R, fA_G, fA_B; // 各通道大气光值(浮点型)
// 将整型大气光值转换为浮点型,便于后续计算
fA_B = (float)m_anAirlight[0]; // B通道大气光值
fA_G = (float)m_anAirlight[1]; // G通道大气光值
fA_R = (float)m_anAirlight[2]; // R通道大气光值
// =========================================================================
// 处理模式选择:后处理 vs 标准恢复
// =========================================================================
if(m_bPostFlag == true) // 启用后处理模式
{
// 调用专门的后处理函数,处理视频序列的块效应等问题
PostProcessing(imInput,imOutput);
}
else // 标准图像恢复模式
{
// =====================================================================
// 并行图像恢复:应用大气散射逆模型
// =====================================================================
#pragma omp parallel for // OpenMP并行化处理,提高计算效率
for(nY=0; nY<m_nHei; nY++) // 遍历图像的每一行
{
for(nX=0; nX<m_nWid; nX++) // 遍历图像的每一列
{
// =============================================================
// 图像恢复公式:J = (I - A) / t + A
// 同时应用伽马校正查找表优化视觉效果
// =============================================================
// B通道恢复:
// 1. 提取输入像素值:(uchar)imInput->imageData[nY*nStep+nX*3+0]
// 2. 减去大气光:((float)pixel - fA_B)
// 3. 除以传输图:result / CLIP_Z(transmission)
// 4. 加回大气光:result + fA_B
// 5. 像素值约束:CLIP(result) 确保在[0,255]范围
// 6. 伽马校正:m_pucGammaLUT[pixel] 应用预计算的伽马表
imOutput->imageData[nY*nStep+nX*3] = (uchar)m_pucGammaLUT[(uchar)CLIP((((float)((uchar)imInput->imageData[nY*nStep+nX*3+0])-fA_B)/CLIP_Z(m_pfTransmissionR[nY*m_nWid+nX]) + fA_B))];
// G通道恢复(处理流程同B通道)
imOutput->imageData[nY*nStep+nX*3+1] = (uchar)m_pucGammaLUT[(uchar)CLIP((((float)((uchar)imInput->imageData[nY*nStep+nX*3+1])-fA_G)/CLIP_Z(m_pfTransmissionR[nY*m_nWid+nX]) + fA_G))];
// R通道恢复(处理流程同B通道)
imOutput->imageData[nY*nStep+nX*3+2] = (uchar)m_pucGammaLUT[(uchar)CLIP((((float)((uchar)imInput->imageData[nY*nStep+nX*3+2])-fA_R)/CLIP_Z(m_pfTransmissionR[nY*m_nWid+nX]) + fA_R))];
}
}
}
}
更多推荐


所有评论(0)