联合双边滤波器(joint bilateral filter) 代码及详细注释【OpenCV】

举报
ShaderJoy 发表于 2021/12/30 00:01:38 2021/12/30
【摘要】 原理部分可以参看前一篇博客 void jointBilateralFilter(const Mat &src, Mat &dst, int d, double sigma_color, double sigma_space, Mat &joint...


原理部分可以参看前一篇博客



  
  1. void jointBilateralFilter(const Mat &src, Mat &dst, int d, double sigma_color,
  2. double sigma_space, Mat &joint = Mat(), int borderType =
  3. BORDER_REPLICATE)
  4. {
  5. Size size = src.size();
  6. if (dst.empty())
  7. dst = Mat::zeros(src.size(), src.type());
  8. CV_Assert(
  9. (src.type() == CV_8UC1 || src.type() == CV_8UC3)
  10. && src.type() == dst.type() && src.size() == dst.size()
  11. && src.data != dst.data);
  12. if (sigma_color <= 0)
  13. sigma_color = 1;
  14. if (sigma_space <= 0)
  15. sigma_space = 1;
  16. double gauss_color_coeff = -0.5 / (sigma_color * sigma_color);
  17. double gauss_space_coeff = -0.5 / (sigma_space * sigma_space);
  18. if (joint.empty())
  19. src.copyTo(joint);
  20. const int cn = src.channels();
  21. const int cnj = joint.channels();
  22. int radius;
  23. if (d <= 0)
  24. radius = cvRound(sigma_space * 1.5); // 根据 sigma_space 计算 radius
  25. else
  26. radius = d / 2;
  27. radius = MAX(radius, 1);
  28. d = radius * 2 + 1; // 重新计算 像素“矩形”邻域的直径d,确保是奇数
  29. // 扩展 src 和 joint 长宽各2*radius
  30. Mat jim;
  31. Mat sim;
  32. copyMakeBorder(joint, jim, radius, radius, radius, radius, borderType);
  33. copyMakeBorder(src, sim, radius, radius, radius, radius, borderType);
  34. // cnj: joint的通道数
  35. vector<float> _color_weight(cnj * 256);
  36. vector<float> _space_weight(d * d); // (2*radius + 1)^2
  37. vector<int> _space_ofs_jnt(d * d);
  38. vector<int> _space_ofs_src(d * d);
  39. float *color_weight = &_color_weight[0];
  40. float *space_weight = &_space_weight[0];
  41. int *space_ofs_jnt = &_space_ofs_jnt[0];
  42. int *space_ofs_src = &_space_ofs_src[0];
  43. // initialize color-related bilateral filter coefficients
  44. // 色差的高斯权重
  45. for (int i = 0; i < 256 * cnj; i++)
  46. color_weight[i] = (float) std::exp(i * i * gauss_color_coeff);
  47. int maxk = 0; // 0 - (2*radius + 1)^2
  48. // initialize space-related bilateral filter coefficients
  49. for (int i = -radius; i <= radius; i++)
  50. {
  51. for (int j = -radius; j <= radius; j++)
  52. {
  53. double r = std::sqrt((double) i * i + (double) j * j);
  54. if (r > radius)
  55. continue;
  56. space_weight[maxk] = (float) std::exp(r * r * gauss_space_coeff);
  57. space_ofs_jnt[maxk] = (int) (i * jim.step + j * cnj); // joint 邻域内的相对坐标 (i, j)【偏移量】, 左上角为(-radius, -radius),右下角为(radius, radius)
  58. space_ofs_src[maxk++] = (int) (i * sim.step + j * cn); // src 邻域内的相对坐标 (i, j)
  59. }
  60. }
  61. #pragma omp parallel for
  62. for (int i = 0; i < size.height; i++)
  63. {
  64. const uchar *jptr = jim.data + (i + radius) * jim.step + radius * cnj; // &jim.ptr(i+radius)[radius]
  65. const uchar *sptr = sim.data + (i + radius) * sim.step + radius * cn; // &sim.ptr(i+radius)[radius]
  66. uchar *dptr = dst.data + i * dst.step; // dst.ptr(i)
  67. // src 和 joint 通道数不同的四种情况
  68. if (cn == 1 && cnj == 1)
  69. {
  70. for (int j = 0; j < size.width; j++)
  71. {
  72. float sum = 0, wsum = 0;
  73. int val0 = jptr[j]; // jim.ptr(i + radius)[j + radius]
  74. for (int k = 0; k < maxk; k++)
  75. {
  76. int val = jptr[j + space_ofs_src[k]]; // jim.ptr(i + radius + offset_x)[j + radius + offset_y]
  77. int val2 = sptr[j + space_ofs_src[k]]; // sim.ptr(i + radius + offset_x)[j + radius + offset_y]
  78. // 根据joint当前像素和邻域像素的 距离权重 和 色差权重,计算综合的权重
  79. float w = space_weight[k]
  80. * color_weight[std::abs(val - val0)];
  81. sum += val2 * w; // 统计 src 邻域内的像素带权和
  82. wsum += w; // 统计权重和
  83. }
  84. // overflow is not possible here => there is no need to use CV_CAST_8U
  85. // 归一化 src 邻域内的像素带权和,并赋给 dst对应的像素
  86. dptr[j] = (uchar) cvRound(sum / wsum);
  87. }
  88. }
  89. else if (cn == 3 && cnj == 3)
  90. {
  91. for (int j = 0; j < size.width * 3; j += 3)
  92. {
  93. float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
  94. int b0 = jptr[j], g0 = jptr[j + 1], r0 = jptr[j + 2]; // jim.ptr(i + radius)[j + radius][0...2]
  95. for (int k = 0; k < maxk; k++)
  96. {
  97. const uchar *sptr_k = jptr + j + space_ofs_src[k];
  98. const uchar *sptr_k2 = sptr + j + space_ofs_src[k];
  99. int b = sptr_k[0], g = sptr_k[1], r = sptr_k[2]; // jim.ptr(i + radius + offset_x)[j + radius + offset_y][0...2]
  100. float w = space_weight[k]
  101. * color_weight[std::abs(b - b0) + std::abs(g - g0)
  102. + std::abs(r - r0)];
  103. sum_b += sptr_k2[0] * w; // sim.ptr(i + radius + offset_x)[j + radius + offset_y][0...2]
  104. sum_g += sptr_k2[1] * w;
  105. sum_r += sptr_k2[2] * w;
  106. wsum += w;
  107. }
  108. wsum = 1.f / wsum;
  109. b0 = cvRound(sum_b * wsum);
  110. g0 = cvRound(sum_g * wsum);
  111. r0 = cvRound(sum_r * wsum);
  112. dptr[j] = (uchar) b0;
  113. dptr[j + 1] = (uchar) g0;
  114. dptr[j + 2] = (uchar) r0;
  115. }
  116. }
  117. else if (cn == 1 && cnj == 3)
  118. {
  119. for (int j = 0, l = 0; j < size.width * 3; j += 3, l++)
  120. {
  121. float sum_b = 0, wsum = 0;
  122. int b0 = jptr[j], g0 = jptr[j + 1], r0 = jptr[j + 2]; // jim.ptr(i + radius)[j + radius][0...2]
  123. for (int k = 0; k < maxk; k++)
  124. {
  125. int val = *(sptr + l + space_ofs_src[k]); // sim.ptr(i + radius + offset_x)[l + radius + offset_y]
  126. const uchar *sptr_k = jptr + j + space_ofs_jnt[k];
  127. int b = sptr_k[0], g = sptr_k[1], r = sptr_k[2]; // jim.ptr(i + radius + offset_x)[j + radius + offset_y][0...2]
  128. float w = space_weight[k]
  129. * color_weight[std::abs(b - b0) + std::abs(g - g0)
  130. + std::abs(r - r0)];
  131. sum_b += val * w;
  132. wsum += w;
  133. }
  134. wsum = 1.f / wsum;
  135. b0 = cvRound(sum_b * wsum);
  136. dptr[l] = (uchar) b0;
  137. }
  138. }
  139. else if (cn == 3 && cnj == 1)
  140. {
  141. for (int j = 0, l = 0; j < size.width * 3; j += 3, l++)
  142. {
  143. float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
  144. int val0 = jptr[l]; // jim.ptr(i + radius)[l + radius]
  145. for (int k = 0; k < maxk; k++)
  146. {
  147. int val = jptr[l + space_ofs_jnt[k]]; // jim.ptr(i + radius + offset_x)[l + radius + offset_y]
  148. const uchar *sptr_k = sptr + j + space_ofs_src[k]; // sim.ptr(i + radius + offset_x)[j + radius + offset_y]
  149. float w = space_weight[k]
  150. * color_weight[std::abs(val - val0)];
  151. sum_b += sptr_k[0] * w; // sim.ptr(i + radius + offset_x)[j + radius + offset_y] [0...2]
  152. sum_g += sptr_k[1] * w;
  153. sum_r += sptr_k[2] * w;
  154. wsum += w;
  155. }
  156. // overflow is not possible here => there is no need to use CV_CAST_8U
  157. wsum = 1.f / wsum;
  158. dptr[j] = (uchar) cvRound(sum_b * wsum);
  159. dptr[j + 1] = (uchar) cvRound(sum_g * wsum);
  160. dptr[j + 2] = (uchar) cvRound(sum_r * wsum);
  161. }
  162. }
  163. }
  164. }




文章来源: panda1234lee.blog.csdn.net,作者:panda1234lee,版权归原作者所有,如需转载,请联系作者。

原文链接:panda1234lee.blog.csdn.net/article/details/52858124

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。