同态滤波的原理很简单,详细介绍请自行search,本文主要专注于opencv下homofilter的实现,并给出代码和运行结果,此前的参考了一些网上opencv的homofilter实现代码,然并卵啊,花了点时间自己做。
算法基本流程如下:
1. 原始输入图像: 通常是亮度分量,Y分量,色度分量不用处理
2. ln:对数变化,原理上是说将乘性信号分解为加信信号,具体原理自己搜一下
3. 变换到频域:dft,fft,dct都行,我用的是dct,dct变换后的结果是实数,节省空间,低频分量集中在左上角,高频分量集中在右下角
4. high pass filter:关键部分,homofilter观点认为光照不均匀的部分集中在低频分量,将低频分量滤除可以消去光照不均的影响,从而提升暗处和高亮处物体的可见度,但整体亮度变化区间会变小。具体滤波器的设计不多讲了,这里值得注意的是dct结果的原点处的值是原始图像的能量总和,是不能直接滤除的,否则你得到的是一张黑色的图片,什么也看不见,因为总能量被减小后,图像的整理亮度下降。所以滤波器原点值设为1,这样保证滤波后的图片和原始图片能量一致,既平均亮度一致,如果你想提升整体亮度,也可以将滤波原点值设为大于1。
5. 频域变换到空域:idft, idct...
6. exp:指数运算,ln的反变换
其中3,4,5只是完成高通滤波,也可以直接在空域用滑动窗口滤波实现,尝试了7x7的gaussianblur,构建一个高通滤波,效果不好
代码如下:
void my_HomoFilter(Mat srcImg, Mat &dst) { srcImg.convertTo(srcImg, CV_64FC1); dst.convertTo(dst, CV_64FC1); //1. ln for (int i = 0; i < srcImg.rows; i++) { double* srcdata = srcImg.ptr<double>(i); double* logdata = srcImg.ptr<double>(i); for (int j = 0; j < srcImg.cols; j++) { logdata[j] = log(srcdata[j]+0.0001); } } //spectrum //2. dct Mat mat_dct = Mat::zeros(srcImg.rows, srcImg.cols, CV_64FC1); dct(srcImg, mat_dct); imshow("dct", mat_dct); //3. linear filter Mat H_u_v; double gammaH = 1.5; double gammaL = 0.5; double C = 1; double d0 = (srcImg.rows/2)*(srcImg.rows/2) + (srcImg.cols/2)*(srcImg.cols/2); double d2 = 0; H_u_v = Mat::zeros(srcImg.rows, srcImg.cols, CV_64FC1); double totalWeight = 0.0; for (int i = 0; i < srcImg.rows; i++) { double * dataH_u_v = H_u_v.ptr<double>(i); for (int j = 0; j < srcImg.cols; j++) { d2 = pow((i), 2.0) + pow((j), 2.0); dataH_u_v[j] = (gammaH - gammaL)*(1 - exp(-C*d2/d0)) + gammaL; totalWeight += dataH_u_v[j]; } } H_u_v.ptr<double>(0)[0] = 1.1; //H_u_v = Mat::ones(srcImg.rows, srcImg.cols, CV_64FC1); imshow("H_u_v", H_u_v); //imshow("before filter", mat_dct); mat_dct = mat_dct.mul(H_u_v); //Mat tmp = mat_dct.mul(H_u_v); //tmp.copyTo(mat_dct); //4. idct idct(mat_dct, dst); #if 0 //spatial high high pass filter Mat tmp = Mat::zeros(srcImg.rows, srcImg.cols, CV_64FC1); GaussianBlur(srcImg, tmp, Size(9, 9), 1.5, 1.5); const double alpha = 0.5; for (int i = 0; i < srcImg.rows; i++) { double* srcdata = srcImg.ptr<double>(i); double* blurdata = tmp.ptr<double>(i); double* dstdata = dst.ptr<double>(i); for (int j = 0; j < srcImg.cols; j++) { dstdata[j] = (1+alpha)*srcdata[j] - alpha*blurdata[j]; //dstdata[j] = blurdata[j]; } } #endif //5. exp for (int i = 0; i < srcImg.rows; i++) { double* srcdata = dst.ptr<double>(i); double* dstdata = dst.ptr<double>(i); for (int j = 0; j < srcImg.cols; j++) { dstdata[j] = exp(srcdata[j]); } } //imshow("dst", dst); dst.convertTo(dst, CV_8UC1); }
Mat src_mat = imread("/Users/lilingyu1/Documents/video_enhance/data/dark_1136x1136.jpg", 0); imshow("src:", src_mat); Mat dst_mat(src_mat.rows, src_mat.cols, src_mat.type()); my_HomoFilter(src_mat, dst_mat); imshow("dst:", dst_mat); cvWaitKey(0);