多波段混合使接缝更亮、更明显

Multi band blending makes seams brighter and more visible

我正在尝试使用 multi-band 混合将两个 pre-warped 图像无缝拼接在一起。我有两张输入图像(已经变形)和一个蒙版。然而,当我应用 MBB 时,接缝周围的区域会发光得更亮,因此它们变得更加明显,这与此处的 objective 相反。我完全不知道我做错了什么。

为了更好地解释问题,这里是图像和输出:

目标:

来源:

掩码:

一旦我将源图像混合到目标中,这就是我得到的:

这是我的代码供参考:

import cv2 as cv2
import numpy as np
import sys

def blend(A, B, m, canvas, num_levels=6):
    trimmer = cv2.bitwise_or(canvas, m) # to trim the blurry edges around the image after blending
    m[m == 255] = 1

    GA = A.copy()
    GB = B.copy()
    GM = m.copy()

    gpA = [GA]
    gpB = [GB]
    gpM = [GM]

    for i in range(num_levels):
        GA = cv2.pyrDown(GA)
        GB = cv2.pyrDown(GB)
        GM = cv2.pyrDown(GM)

        gpA.append(np.float32(GA))
        gpB.append(np.float32(GB))
        gpM.append(np.float32(GM))

    lpA = [gpA[num_levels - 1]]
    lpB = [gpB[num_levels - 1]]
    gpMr = [gpM[num_levels - 1]]

    for i in range(num_levels - 1, 0, -1):
        size = (gpA[i - 1].shape[1], gpA[i - 1].shape[0])

        LA = np.subtract(gpA[i - 1], cv2.pyrUp(gpA[i], dstsize=size))
        LB = np.subtract(gpB[i - 1], cv2.pyrUp(gpB[i], dstsize=size))

        lpA.append(LA)
        lpB.append(LB)

        gpMr.append(gpM[i - 1])

    LS = []
    for la, lb, gm in zip(lpA, lpB, gpMr):
        ls = la * gm + lb * (1.0 - gm)
        # ls = la + lb
        LS.append(ls)

    ls_ = LS[0]
    for i in range(1, num_levels):
        size = (LS[i].shape[1], LS[i].shape[0])
        ls_ = cv2.add(cv2.pyrUp(ls_, dstsize=size), np.float32(LS[i]))
        ls_[ls_ > 255] = 255; ls_[ls_ < 0] = 0
    
    ls_ = ls_.astype(np.uint8)

    cv2.imwrite("trimmer.jpg", trimmer)
    ls_ = cv2.bitwise_and(ls_, trimmer)

    return ls_

Canvas传递给函数(基本上是target/mosaic的掩码):

source/new 图片的遮罩:

我也乐于探索其他方法来无缝混合两个图像,以防 MBB 不是实现我的目标的最合适方法。请帮忙。

这是一个 C++ 答案,但算法很简单。

int main()
{
    std::string folder = "C:/Development/Projects/UNDIST_FISHEYE/OpenCV4_Experiments_VS2017/";
    cv::Mat mosaic_img = cv::imread(folder + "mosaic_img.jpg");
    cv::Mat newImage_img = cv::imread(folder + "newImage_img.jpg");

    //cv::Mat mosaic_mask = cv::imread(folder + "mosaic_mask.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat mosaic_mask = cv::imread(folder + "mosaic_mask_2.jpg", cv::IMREAD_GRAYSCALE);
    mosaic_mask = mosaic_mask > 230; // threshold because of jpeg artifacts

    cv::Mat newImage_mask_raw = cv::imread(folder + "newImage_mask.jpg", cv::IMREAD_GRAYSCALE);
    newImage_mask_raw = newImage_mask_raw > 230;
    // newImage_mask_raw is a few pixels too small...
    cv::Mat newImage_mask = cv::Mat::zeros(mosaic_mask.size(), mosaic_mask.type());

    newImage_mask_raw.copyTo(newImage_mask(cv::Rect(0,0, newImage_mask_raw.cols, newImage_mask_raw.rows)));

    cv::Mat mosaic_blending = cv::Mat::zeros(mosaic_mask.size(), CV_32FC1);
    cv::distanceTransform(mosaic_mask, mosaic_blending, cv::DIST_L2, cv::DIST_MASK_PRECISE);
    cv::Mat newImage_blending = cv::Mat::zeros(mosaic_mask.size(), CV_32FC1);
    cv::distanceTransform(newImage_mask, newImage_blending, cv::DIST_L2, cv::DIST_MASK_PRECISE);

    cv::imshow("mosaic blending", mosaic_blending/255);
    cv::imshow("newImage blending", newImage_blending/255);

    cv::Mat newMosaic = mosaic_img.clone();
    // now compose the mosaic:
    // for each pixel: mosaic=(m1*p1 + m2*p2)/(m1+m2)
    for (int y = 0; y < newMosaic.rows; ++y)
    {
        for (int x = 0; x < newMosaic.cols; ++x)
        {
            // for efficiency: only process pixels where the new image hits the mosaic canvas
            if (newImage_blending.at<float>(y, x) == 0) continue;

            float m1 = newImage_blending.at<float>(y, x);
            float m2 = mosaic_blending.at<float>(y, x);
            float ma = m1 + m2;

            m1 = m1 / ma;
            m2 = m2 / ma;

            cv::Vec3f mosaicPixel = m1 * newImage_img.at<cv::Vec3b>(y, x) + m2 * mosaic_img.at<cv::Vec3b>(y, x);
            newMosaic.at<cv::Vec3b>(y, x) = mosaicPixel; // maybe cast or round here
        }

    
    }

    cv::imwrite("mask1.png", mosaic_mask);
    cv::imwrite("mask2.png", newImage_mask);
    cv::imwrite("mosaic.jpg", newMosaic);

    cv::imshow("mosaic", newMosaic);


    cv::waitKey(0);
}

一般的想法是测量从蒙版边界到内部的距离,并假设边界处的像素质量较低(更有可能导致接缝),因此这些像素的混合应该更强。

如果在将蒙版变形为马赛克之前测量(甚至预先计算)这个距离,这可能会更好 canvas。

使用这些面具时

我得到这个结果:

如您所见,仍然存在接缝,但这是来自中间马赛克(输入图像之一)的接缝,如果之前的拼接使用相同的混合进行,则不会存在。

然后将这个蒙版用于中间马赛克(说明已经给定的接缝像素质量低)

我得到这个结果:

最好是使用先前混合蒙版的每像素最大值和新图像混合蒙版来组成马赛克混合蒙版。