当前位置:   article > 正文

【opencv】教程代码 —ImgProc (6)通过Wiener滤波器恢复运动模糊图像

【opencv】教程代码 —ImgProc (6)通过Wiener滤波器恢复运动模糊图像

6. motion_deblur_filter.cpp通过Wiener滤波器恢复运动模糊图像(参数难调)

041b19fe4367cd3ad97226acf09690f5.jpeg

1107288b85498e9aa15070fd3da23972.jpeg

03584f318a7bc14b8e13ea2098877960.jpeg

您将学习如何使用维纳滤波器恢复具有运动模糊失真的图像

d204a81fa2df0e6ddb530361fd762800.png

  1. /**
  2. * @brief 学习如何使用Wiener滤波器恢复运动模糊失真的图像。
  3. * @author 混沌鱼, karpushin@ngs.ru, https://github.com/VladKarpushin
  4. */
  5. #include <iostream>
  6. #include "opencv2/imgproc.hpp"
  7. #include "opencv2/imgcodecs.hpp"
  8. // 使用OpenCV和标准库的命名空间
  9. using namespace cv;
  10. using namespace std;
  11. // 函数声明
  12. void help();
  13. void calcPSF(Mat& outputImg, Size filterSize, int len, double theta);
  14. void fftshift(const Mat& inputImg, Mat& outputImg);
  15. void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H);
  16. void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr);
  17. void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma = 5.0, double beta = 0.2);
  18. // 定义程序可能接受的命令行参数。
  19. const String keys =
  20. "{help h usage ? | | print this message }"
  21. "{image |input.png | input image name }"
  22. "{LEN |125 | length of a motion }"
  23. "{THETA |0 | angle of a motion in degrees }"
  24. "{SNR |700 | signal to noise ratio }"
  25. ;
  26. // 主函数
  27. int main(int argc, char *argv[])
  28. {
  29. // 显示帮助信息
  30. help();
  31. // 解析命令行参数
  32. CommandLineParser parser(argc, argv, keys);
  33. // 如果请求帮助,则打印帮助信息并退出程序。
  34. if (parser.has("help"))
  35. {
  36. parser.printMessage();
  37. return 0;
  38. }
  39. // 从命令行参数中获取LEN, THETA, SNR和图像文件名的值。
  40. int LEN = parser.get<int>("LEN");
  41. double THETA = parser.get<double>("THETA");
  42. int snr = parser.get<int>("SNR");
  43. string strInFileName = parser.get<String>("image");
  44. // 检查解析的命令行参数是否有误。
  45. if (!parser.check())
  46. {
  47. parser.printErrors();
  48. return 0;
  49. }
  50. // 加载图像文件
  51. Mat imgIn;
  52. imgIn = imread(strInFileName, IMREAD_GRAYSCALE);
  53. // 如果图像为空,则载入失败,打印错误信息并退出。
  54. if (imgIn.empty())
  55. {
  56. cout << "ERROR : Image cannot be loaded..!!" << endl;
  57. return -1;
  58. }
  59. // 图像处理后保存的输出图像。
  60. Mat imgOut;
  61. // 主要的图像处理过程开始
  62. // 只处理偶数大小的图像
  63. Rect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);
  64. // 计算Hw(滤波器),过程开始
  65. Mat Hw, h;
  66. calcPSF(h, roi.size(), LEN, THETA);
  67. calcWnrFilter(h, Hw, 1.0 / double(snr));
  68. // 计算Hw(滤波器),过程结束
  69. // 将图像转换为浮点型并进行边缘锥化处理。
  70. imgIn.convertTo(imgIn, CV_32F);
  71. edgetaper(imgIn, imgIn);
  72. // 开始滤波处理
  73. filter2DFreq(imgIn(roi), imgOut, Hw);
  74. // 结束滤波处理
  75. // 主要的图像处理过程结束
  76. // 将处理后的图像转换回8位无符号整数型并进行归一化。
  77. imgOut.convertTo(imgOut, CV_8U);
  78. normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);
  79. // 将处理后的图像保存至文件。
  80. imwrite("result.jpg", imgOut);
  81. return 0;
  82. }
  83. // 显示帮助信息的函数实现。
  84. void help()
  85. {
  86. cout << "2018-08-14" << endl;
  87. cout << "Motion_deblur_v2" << endl;
  88. cout << "You will learn how to recover an image with motion blur distortion using a Wiener filter" << endl;
  89. }
  90. // 计算点扩散函数(PSF)的函数实现。
  91. void calcPSF(Mat& outputImg, Size filterSize, int len, double theta)
  92. {
  93. // 创建图像并用0填充。
  94. Mat h(filterSize, CV_32F, Scalar(0));
  95. // 获取滤波器的中心点。
  96. Point point(filterSize.width / 2, filterSize.height / 2);
  97. // 在图像中绘制椭圆(运动模糊PSF)。
  98. ellipse(h, point, Size(0, cvRound(float(len) / 2.0)), 90.0 - theta, 0, 360, Scalar(255), FILLED);
  99. // 对图像求和得到总和。
  100. Scalar summa = sum(h);
  101. // 将图像h除以总和summa[0]来规格化PSF。
  102. outputImg = h / summa[0];
  103. }
  104. // 频率域上对信号进行中心化的函数实现。
  105. void fftshift(const Mat& inputImg, Mat& outputImg)
  106. {
  107. // 克隆输入图像以获得与其大小相同的输出图像。
  108. outputImg = inputImg.clone();
  109. // 计算图像的中心点坐标。
  110. int cx = outputImg.cols / 2;
  111. int cy = outputImg.rows / 2;
  112. // 划分图像为四块。
  113. Mat q0(outputImg, Rect(0, 0, cx, cy));
  114. Mat q1(outputImg, Rect(cx, 0, cx, cy));
  115. Mat q2(outputImg, Rect(0, cy, cx, cy));
  116. Mat q3(outputImg, Rect(cx, cy, cx, cy));
  117. // 交换这四块图像,实现中心化。
  118. Mat tmp;
  119. q0.copyTo(tmp);
  120. q3.copyTo(q0);
  121. tmp.copyTo(q3);
  122. q1.copyTo(tmp);
  123. q2.copyTo(q1);
  124. tmp.copyTo(q2);
  125. }
  126. // 在频率域上应用滤波器的函数实现。
  127. void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H)
  128. {
  129. // 创建两个平面的数组,其中一个为浮点型的输入图像,另一个为和输入图像同等大小的零矩阵。
  130. Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
  131. Mat complexI;
  132. // 合并两个平面形成复数图像。
  133. merge(planes, 2, complexI);
  134. // 进行离散傅里叶变换。
  135. dft(complexI, complexI, DFT_SCALE);
  136. Mat planesH[2] = { Mat_<float>(H.clone()), Mat::zeros(H.size(), CV_32F) };
  137. Mat complexH;
  138. // 为H矩阵也创建复数形式。
  139. merge(planesH, 2, complexH);
  140. Mat complexIH;
  141. // 对输入图像和滤波器进行逐个元素的复数乘法。
  142. mulSpectrums(complexI, complexH, complexIH, 0);
  143. // 进行离散傅里叶逆变换。
  144. idft(complexIH, complexIH);
  145. // 分离平面得到结果图像。
  146. split(complexIH, planes);
  147. outputImg = planes[0];
  148. }
  149. // 计算维纳滤波器的函数实现。
  150. void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr)
  151. {
  152. Mat h_PSF_shifted;
  153. // 对输入的PSF进行中心化。
  154. fftshift(input_h_PSF, h_PSF_shifted);
  155. // 为h_PSF_shifted创建两个平面,其中一个为浮点型形式的h_PSF_shifted,另一个为同等大小的零矩阵。
  156. Mat planes[2] = { Mat_<float>(h_PSF_shifted.clone()), Mat::zeros(h_PSF_shifted.size(), CV_32F) };
  157. Mat complexI;
  158. // 合并两个平面形成复数图像。
  159. merge(planes, 2, complexI);
  160. // 进行离散傅里叶变换。
  161. dft(complexI, complexI);
  162. // 分离平面得到h_PSF的幅度。
  163. split(complexI, planes);
  164. Mat denom;
  165. // 计算|h_PSF|的平方和加上噪声功率谱比(nsr)。
  166. pow(abs(planes[0]), 2, denom);
  167. denom += nsr;
  168. // 对h_PSF除以denom得到Wiener滤波器。
  169. divide(planes[0], denom, output_G);
  170. }
  171. // 对图像边缘执行锥形衰减的函数实现。
  172. //! [edgetaper]
  173. // 执行边缘渐变处理的功能,减少频谱泄露
  174. void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma, double beta)
  175. {
  176. // 获取输入图像的列数Nx和行数Ny
  177. int Nx = inputImg.cols;
  178. int Ny = inputImg.rows;
  179. // 创建两个类型为浮点型的Mat矩阵w1和w2,w1的大小是1xNx,w2的大小是Nyx1
  180. // 并使用无效的黑色标量像素初始化(初始化为0)
  181. Mat w1(1, Nx, CV_32F, Scalar(0));
  182. Mat w2(Ny, 1, CV_32F, Scalar(0));
  183. // 获取w1和w2的指针,以便后面直接修改其值
  184. float* p1 = w1.ptr<float>(0);
  185. float* p2 = w2.ptr<float>(0);
  186. // dx是x方向的间隔参数,初始化为对应于-π到π的步长
  187. float dx = float(2.0 * CV_PI / Nx);
  188. // 初始化x的初始值为-π
  189. float x = float(-CV_PI);
  190. // 计算每一列的权重,存入w1中
  191. for (int i = 0; i < Nx; i++)
  192. {
  193. p1[i] = float(0.5 * (tanh((x + gamma / 2) / beta) - tanh((x - gamma / 2) / beta)));
  194. x += dx;
  195. }
  196. // dy是y方向的间隔参数,初始化为对应于-π到π的步长
  197. float dy = float(2.0 * CV_PI / Ny);
  198. // 初始化y的初始值为-π
  199. float y = float(-CV_PI);
  200. // 计算每一行的权重,存入w2中
  201. for (int i = 0; i < Ny; i++)
  202. {
  203. p2[i] = float(0.5 * (tanh((y + gamma / 2) / beta) - tanh((y - gamma / 2) / beta)));
  204. y += dy;
  205. }
  206. // 创建矩阵w,它是w1和w2的外积,代表整个图像的每个像素的权重
  207. Mat w = w2 * w1;
  208. // 使用权重矩阵w对输入图像进行点乘,以便对图像进行边缘渐变处理,结果存入输出图像outputImg
  209. multiply(inputImg, w, outputImg);
  210. }
  211. //! [edgetaper]

c3a957fe6732e173d484e08f2b1a4b55.png

d1c758cb7db1500ae5289aef7e260be3.png

2896aabdceb168562fd9ee9e6ac1cda5.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/333981
推荐阅读
相关标签
  

闽ICP备14008679号