cv::Mat_ generalizedJointBilateralFilter()

in source/depth_estimation/TemporalBilateralFilter.h [39:123]


cv::Mat_<TPixel> generalizedJointBilateralFilter(
    const cv::Mat_<TPixel>& image, // Either float, Vec2f or Vec3f
    const cv::Mat_<TGuide>& guide,
    const cv::Mat_<TGuide>& neighborTGuide,
    const cv::Mat_<bool>& mask,
    const int radius,
    const float sigma,
    const float weight0 = 1.0f,
    const float weight1 = 1.0f,
    const float weight2 = 1.0f,
    const int numThreads = -1) {
  CHECK_EQ(guide.size(), neighborTGuide.size());
  CHECK_EQ(image.size(), guide.size());
  CHECK_EQ(guide.size(), mask.size());

  const TPixel zero = 0.0;

  cv::Mat_<TPixel> dest(image.size());
  ThreadPool threadPool(numThreads);
  const int edgeX = image.cols;
  const int edgeY = 1;
  for (int yBegin = 0; yBegin < image.rows; yBegin += edgeY) {
    for (int xBegin = 0; xBegin < image.cols; xBegin += edgeX) {
      const int xEnd = std::min(xBegin + edgeX, image.cols);
      const int yEnd = std::min(yBegin + edgeY, image.rows);
      threadPool.spawn([&, xBegin, yBegin, xEnd, yEnd] {
        for (int y = yBegin; y < yEnd; ++y) {
          for (int x = xBegin; x < xEnd; ++x) {
            if (!mask(y, x)) {
              dest(y, x) = image(y, x);
              continue;
            }

            const auto guideColor = guide(y, x);
            float sumWeight = 0.0f;
            TPixel weightedAvg = zero;

            const float guideFactor = 1 / cv_util::maxPixelValue(guide);
            const float neighborTGuideFactor = 1 / cv_util::maxPixelValue(neighborTGuide);

            for (int v = -radius; v <= radius; ++v) {
              for (int u = -radius; u <= radius; ++u) {
                const int sampleX = math_util::clamp(x + u, 0, image.cols - 1);
                const int sampleY = math_util::clamp(y + v, 0, image.rows - 1);

                if (!mask(sampleY, sampleX)) {
                  continue;
                }

                const TGuide& neighborTGuideColor = neighborTGuide(sampleY, sampleX);

                const float colorDiffSq = // BGR
                    weight0 *
                        math_util::square(
                            (guideColor[0] * guideFactor) -
                            (neighborTGuideColor[0] * neighborTGuideFactor)) +
                    weight1 *
                        math_util::square(
                            (guideColor[1] * guideFactor) -
                            (neighborTGuideColor[1] * neighborTGuideFactor)) +
                    weight2 *
                        math_util::square(
                            (guideColor[2] * guideFactor) -
                            (neighborTGuideColor[2] * neighborTGuideFactor));
                const float weight =
                    expf((-colorDiffSq / 3.0f) / (2.0f * math_util::square(sigma)));

                sumWeight += weight;
                weightedAvg += weight * image(sampleY, sampleX);
              }
            }
            if (sumWeight != 0.0f) {
              weightedAvg /= sumWeight;
              dest(y, x) = weightedAvg;
            } else {
              dest(y, x) = image(y, x);
            }
          }
        }
      });
    }
  }
  threadPool.join();
  return dest;
}