因为对图像处理感兴趣,就去本部蹭了丁老师的《数字图像处理》课程,感觉比自学效率高多了;
业余时间打算把课上部分代码实现,于是就有了这个博客;这里会不定期更新一些基础算法,大都是老师课上讲的,只注重算法实现,没有进行代码优化;这方面比较厉害的同学可以多多赐教;
PS:编译环境为VS2013+OpenCV3.0alpha;
**第一篇 Frankle-Mccan去雾算法**
PPT上语焉不详;根据PPT写的代码改了好几天效果还是很差,于是参考网上的matlab程序,对原理进行了反推,有了一定的去雾效果;
原理:
一、载入图像,
分为RGB三个通道准备依次处理;
二、参数计算;
求出MaxR、MaxG、MaxB;以及p和S;以Max值生成图像OPR,OPG,OPB;
p=log(min(m,n)-1);
S=2^p;(像素间隔)
三、图像处理:(这里以B通道为例)
3.1 S为指定间隔,求出B通道水平方向上间隔为S的像素差值DP;
S>0时 , 求IP DP=B(i,j+S)-B(i,j); IP=DP+OPB(i,j);
判断IP是否大于Max,大于Max,则赋予Max if(IP>MaxB) IP=MaxB
求平均 并赋值给位置j+S的像素 OPB(i,j+S)=OPB(i,j+S)/2+IP/2;
S<0时 求IP DP=B(i,j)-B(i,j+S);IP=DP+OPB(i,j+S);
判断IP是否大于Max,大于Max,则赋予Max if(IP>MaxB) IP=MaxB
求平均 并赋值给位置j的像素 OPB(i,j)=OPB(i,j)/2+IP/2;
垂直方向也做类似处理;
3.2 把步骤3.1重复k次
3.3 像素间隔S更新 S/= -2;
我参考的matlab程序在这里
【Frankle-McCann Retinex】matlab代码注释
下面是源代码;欢迎指正
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#define DIEDAI 7
using namespace std;
using namespace cv;
int main()
{
//读入图像并分离为3个通道;
Mat src = imread("1.jpg");
imshow("原图", src);
Mat Blue, Green, Red;
vector<Mat> channels;
split(src, channels);
Blue = channels.at(0);
Green = channels.at(1);
Red = channels.at(2);
//参数计算,三个通道的这几个参数相同
int rows = Blue.rows;
int cols = Blue.cols;
int M = rows < cols ? rows : cols;
int p = log(M) / log(2) - 1;//阶,这里采用隐式转换
int S = pow(2, p); //初始间隔
//对图层进行处理 求初始OPB OPG OPR
uchar MaxB = 0, MaxG = 0, MaxR = 0;
cout << int(MaxB) << endl;
uchar* dataB = nullptr;
uchar* dataG = nullptr;
uchar* dataR = nullptr;
for (int i = 0; i < rows; i++)
{
dataB = Blue.ptr<uchar>(i);
dataG = Green.ptr<uchar>(i);
dataR = Red.ptr<uchar>(i);
for (int j = 0; j < cols; j++)
{
if (*dataB > MaxB)
MaxB = *dataB;
if (*dataG > MaxG)
MaxG = *dataG;
if (*dataR > MaxR)
MaxR = *dataR;
dataB++;
dataG++;
dataR++;
}
}
dataB = nullptr;
dataG = nullptr;
dataR = nullptr;
Mat OPB(Blue.size(), Blue.depth());
Mat OPG(Green.size(), Green.depth());
Mat OPR(Red.size(), Red.depth());
OPB.setTo(MaxB);
OPG.setTo(MaxG);
OPR.setTo(MaxR);
int DP, IP;
uchar* Data = nullptr;
uchar* Data2 = nullptr;
uchar* OPData = nullptr;
uchar* OPData2 = nullptr;
bool fushu = false; //S是否为负数
do
{
if (S < 0)
{
S *= -1;
fushu = true; //S为负数,DP=前-后,把差值与OP的J+S行相加得到IP;赋值给第一行
}
else
fushu = false; //S为正数,DP=后-前,这是把差值与OP的第一行相加得到IP;赋值给+S的位置;
for (int i = 0; i < DIEDAI; i++)
{
//B列间隔迭代
for (int i = 0; i < rows; i++)
{
Data = Blue.ptr<uchar>(i); //获取i行第一个元素的指针
OPData = OPB.ptr<uchar>(i);
for (int j = 0; j < cols - S; j++)
{
if (fushu)
{
DP = int(Data[j] - Data[j + S]); //求i行第j+S个元素与第j个元素的差值
IP = int(OPData[j + S]) + DP;
if (IP > MaxB)
IP = MaxB;
OPData[j ] = OPData[j] / 2 + IP / 2;
}
else
{
DP = int(Data[j + S]) - int(Data[j]);
IP = int(OPData[j]) + DP;
if (IP > MaxB)
IP = MaxB;
OPData[j + S] = OPData[j+S] / 2 + IP / 2;
}
}
}
//B行间隔迭代
for (int i = 0; i < rows - S; i++)
{
Data = Blue.ptr<uchar>(i);
Data2 = Blue.ptr<uchar>(i + S);
OPData = OPB.ptr<uchar>(i);
OPData2 = OPB.ptr<uchar>(i + S);
for (int j = 0; j < cols; j++)
{
if (fushu)
{
DP = int(*Data++ - *Data2++);
IP = int(*OPData2) + DP;
if (IP > MaxB)
IP = MaxB;
*OPData = *OPData / 2 + IP / 2;
OPData++;
OPData2++;
}
else
{
DP = int(*Data2++ - *Data++);
IP = int(*OPData) + DP;
if (IP > MaxB)
IP = MaxB;
*OPData2 = *OPData2 / 2 + IP / 2;
OPData++;
OPData2++;
}
}
}
//G列间隔迭代
for (int i = 0; i < rows; i++)
{
Data = Green.ptr<uchar>(i);
OPData = OPG.ptr<uchar>(i);
for (int j = 0; j < cols - S; j++)
{
if (fushu)
{
DP = int(Data[j] - Data[j + S]);
IP = int(OPData[j + S]) + DP;
if (IP > MaxB)
IP = MaxB;
OPData[j] = OPData[j] / 2 + IP / 2;
}
else
{
DP = int(Data[j + S]) - int(Data[j]);
IP = int(OPData[j]) + DP;
if (IP > MaxB)
IP = MaxB;
OPData[j + S] = OPData[j + S] / 2 + IP / 2;
}
}
}
//G行间隔迭代
for (int i = 0; i < rows - S; i++)
{
Data = Green.ptr<uchar>(i);
Data2 = Green.ptr<uchar>(i + S);
OPData = OPG.ptr<uchar>(i);
OPData2 = OPG.ptr<uchar>(i + S);
for (int j = 0; j < cols; j++)
{
if (fushu)
{
DP = int(*Data++ - *Data2++);
IP = int(*OPData2) + DP;
if (IP > MaxB)
IP = MaxB;
*OPData = *OPData / 2 + IP / 2;
OPData++;
OPData2++;
}
else
{
DP = int(*Data2++ - *Data++);
IP = int(*OPData) + DP;
if (IP > MaxB)
IP = MaxB;
*OPData2 = *OPData2 / 2 + IP / 2;
OPData++;
OPData2++;
}
}
}
//R列间隔迭代
for (int i = 0; i < rows; i++)
{
Data = Red.ptr<uchar>(i);
OPData = OPR.ptr<uchar>(i);
for (int j = 0; j < cols - S; j++)
{
if (fushu)
{
DP = int(Data[j] - Data[j + S]);
IP = int(OPData[j + S]) + DP;
if (IP > MaxB)
IP = MaxB;
OPData[j] = OPData[j] / 2 + IP / 2;
}
else
{
DP = int(Data[j + S]) - int(Data[j]);
IP = int(OPData[j]) + DP;
if (IP > MaxB)
IP = MaxB;
OPData[j + S] = OPData[j + S] / 2 + IP / 2;
}
}
}
//R行间隔迭代
for (int i = 0; i < rows - S; i++)
{
Data = Red.ptr<uchar>(i);
Data2 = Red.ptr<uchar>(i + S);
OPData = OPR.ptr<uchar>(i);
OPData2 = OPR.ptr<uchar>(i + S);
for (int j = 0; j < cols; j++)
{
if (fushu)
{
DP = int(*Data++ - *Data2++);
IP = int(*OPData2) + DP;
if (IP > MaxB)
IP = MaxB;
*OPData = *OPData / 2 + IP / 2;
OPData++;
OPData2++;
}
else
{
DP = int(*Data2++ - *Data++);
IP = int(*OPData) + DP;
if (IP > MaxB)
IP = MaxB;
*OPData2 = *OPData2 / 2 + IP / 2;
OPData++;
OPData2++;
}
}
}
}
if (fushu)
S *= -1; //恢复S的符号
S /= -2;
cout << S << endl;
} while (S != 0);
Data = nullptr;
Data2 = nullptr;
OPData = nullptr;
OPData2 = nullptr;
channels.at(0) = OPB;
channels.at(1) = OPG;
channels.at(2) = OPR;
Mat dst(src.size(), src.depth());
merge(channels, dst); //合并通道
imshow("结果", dst);
waitKey(0);
}
原图:
效果图(迭代7次)