Search code examples
pythonopencvimage-processingcomputer-visionblending

How to get rid of black lines caused of blending images?


I recently worked on a project that required blending multiple images together. I successfully completed all the necessary steps and successfully blended the images. However, I noticed that when I blended the images, black lines appeared on the borders of each piece of the final image, indicating where each image was placed. You can see an example of the final image with these artifacts in the following link:

final image

These artifacts are noticeable even with a quick glance at the image.

This is my code for blending the images:

def normalize_piece(img):
    return cv2.normalize(img.astype('float'), None, 0.0, 1.0, cv2.NORM_MINMAX)


def blend_pieces(piece1, piece2, transformation_matrix, width, height):
    piece1_norm = normalize_piece(piece1)
    piece2_norm = normalize_piece(piece2)

    piece1_warped = cv2.warpPerspective(src=piece1_norm, M=transformation_matrix, dsize=(width, height))

    black_rgb_pixel = np.zeros(3)
    mask_left = np.all(piece1_warped != black_rgb_pixel, axis=-1)
    mask_right = np.all(piece2_norm != black_rgb_pixel, axis=-1)

    mask_overlap = mask_left & mask_right
    mask_left_only = mask_left & ~mask_right
    mask_right_only = mask_right & ~mask_left

    piece1_warped[mask_left_only] = piece1_warped[mask_left_only]
    piece1_warped[mask_right_only] = piece2_norm[mask_right_only]
    piece1_warped[mask_overlap] = (piece1_warped[mask_overlap] + piece2_norm[mask_overlap]) / 2

    return piece1_warped

Edit

I have these 3 parts of the final image for example and I want to merge them together:

part 1, part 2, part 3

I need to blend part 1 with part 2. And then, I need to blend the blended part with part 3.


Solution

  • I was able to achieve an answer to my question.

    Here is a simple code to merge the parts that were provided in the question:

    import numpy as np
    import cv2
    
    width = 808
    height = 457
    
    
    def blend_images(image1, image2):
        # Create the canvas with the desired shape
        # Note that when debugging it shows that the canvas is ndarray of shape (457, 808, 3)
        # The 457 here represents the height, the 808 represents the width and the 3 represents the channels (RGB)
        canvas = np.zeros((height, width, 3), dtype=np.uint8)
    
        # Iterate over the canvas and the two images while taking the maximum RGB value of the two images for each pixel
        for y in range(height):
            for x in range(width):
                canvas[y, x] = np.maximum(image1[y, x], image2[y, x])
    
        return canvas
    
    
    def main():
        # Change the images paths to your images paths
        image1 = cv2.imread('part1.jpg')
        image2 = cv2.imread('part2.jpg')
        image3 = cv2.imread('part3.jpg')
    
        # Blend the first two images
        canvas1 = blend_images(image1, image2)
    
        # Blend the blended images with the third image to get the final result
        canvas2 = blend_images(canvas1, image3)
    
        # Show the final result
        cv2.imshow('Canvas', canvas2)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    
    
    if __name__ == '__main__':
        main()
    
    

    The blend_images function creates a canvas of the specified shape (height, width) and merges the two input images using an element-wise maximum operation. Specifically, the function iterates over each pixel of the canvas and selects the maximum RGB value from the corresponding pixels in the two input images. The resulting pixel values are then assigned to the corresponding pixel in the new image.

    Update

    Thanks to a helpful comment from 'Christoph Rackwitz', it was pointed out that the merging operation in the blend_images function can be performed more efficiently using a Numpy routine.

    Here's an updated version of the code:

    import numpy as np
    import cv2
    
    
    def blend_images(image1, image2):
        # Ensure that the input images have the same shape
        assert image1.shape == image2.shape
        return np.maximum(image1, image2)
    
    
    def main():
        # Change the images paths to your images paths
        image1 = cv2.imread('part1.jpg')
        image2 = cv2.imread('part2.jpg')
        image3 = cv2.imread('part3.jpg')
    
        # Blend the first two images
        canvas = blend_images(image1, image2)
    
        # Blend the blended images with the third image to get the final result
        canvas = blend_images(canvas, image3)
    
        # Show the final result
        cv2.imshow('Canvas', canvas)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    
    
    if __name__ == '__main__':
        main()
    
    

    In this updated code, the blend_images function is modified to use the Numpy maximum function instead of nested loops to perform the merging operation. The function also includes an assertion to ensure that the input images have the same shape.