pythonopencv

Subtract two images using alpha channel in python OpenCV


I want to subtract one image from the other using the first one's alpha channel.

The problem is really hard for me to explain by words, so please bear with me and my art.

Imagine that I have the following image. Let's call it image "A":

Image A

I also have another one, image "B":

Image B

B is an image of a terrifyingly complex shape. It also has some transparency, as depicted by the checkered background. Now, imagine that after I place B on top of A, I get the following:

B on top of A

Here's the problem; I want to get the following output:

Expected Output

What's happening here is that after placing B on top of A, I want to preserve the area form image A where it intersects with image B's INNER transparent area, but remove parts of A where it intersects with B's OUTER transparent area. Also, B's RGB must be preserved.

How can I achieve this in python?


Solution

  • So as outlined in the comments, you can do this in 2 steps

    1. find the outer part of B and make that white (instead of transparent)
    2. overlay B on top of A as you normally would. Now since the outer part of B isn't white, it stays that way.

    You can find the "outer part" of B (ie. the background) as opposed to other transparent areas in B by using the cv.connectedComponents() function (documentation here).

    transparent_areas = B[:, :, 3] == 0 # mask of B outlining transparent areas. replace "== 0" with a threshold if those areas aren't completely transparent
    
    n_components, transparent_connected_components = cv2.connectedComponents(transparent_areas.astype(np.uint8))
    
    

    No we have the 2 connected components of the transparent area:

    connected components

    The non-transparent parts are always going to be labeled 0.

    We just have to find which one(s) is/are the outer one(s) (here there is only one, but in a more complicated case, there could be several.)

    That is simple: a connected component is a border one iff it contains a pixel at the border. So we just have to check which connected components the border pixels are in:

    border_connected_components = np.concat([transparent_connected_components[0, :], 
       transparent_connected_components[-1, :] , 
       transparent_connected_components[:, 0], 
       transparent_connected_components[:, -1]])
    
    border_connected_components = np.unique(border_connected_components)
    
    # don't forget to remove 0 which represents non-transparent pixels
    border_connected_components = border_connected_components[border_connected_components!=0]
    
    

    Now we only have to set the border connected components to white

    
    B[np.isin(transparent_connected_components, border_connected_components)] = (255, 255, 255, 255)
    
    

    B with white background

    And to add them as you usually would