Using openCV to overlay transparent image onto another image

后端 未结 6 2112
庸人自扰
庸人自扰 2020-12-01 12:18

How can I overlay a transparent PNG onto another image without loosing it\'s transparency using openCV in python?

import cv2

background = cv2.imread(\'field         


        
相关标签:
6条回答
  • 2020-12-01 12:24

    The correct answer to this was far too hard to come by, so I'm posting this answer even though the question is really old. What you are looking for is "over" compositing, and the algorithm for this can be found on Wikipedia: https://en.wikipedia.org/wiki/Alpha_compositing

    I am far from an expert with OpenCV, but after some experimentation this is the most efficient way I have found to accomplish the task:

    import cv2
    
    background = cv2.imread("background.png", cv2.IMREAD_UNCHANGED)
    foreground = cv2.imread("overlay.png", cv2.IMREAD_UNCHANGED)
    
    # normalize alpha channels from 0-255 to 0-1
    alpha_background = background[:,:,3] / 255.0
    alpha_foreground = foreground[:,:,3] / 255.0
    
    # set adjusted colors
    for color in range(0, 3):
        background[:,:,color] = alpha_foreground * foreground[:,:,color] + \
            alpha_background * background[:,:,color] * (1 - alpha_foreground)
    
    # set adjusted alpha and denormalize back to 0-255
    background[:,:,3] = (1 - (1 - alpha_foreground) * (1 - alpha_background)) * 255
    
    # display the image
    cv2.imshow("Composited image", background)
    cv2.waitKey(0)
    
    0 讨论(0)
  • 2020-12-01 12:25

    You need to open the transparent png image using the flag IMREAD_UNCHANGED

    Mat overlay = cv::imread("dice.png", IMREAD_UNCHANGED);
    

    Then split the channels, group the RGB and use the transparent channel as an mask, do like that:

    /**
     * @brief Draws a transparent image over a frame Mat.
     * 
     * @param frame the frame where the transparent image will be drawn
     * @param transp the Mat image with transparency, read from a PNG image, with the IMREAD_UNCHANGED flag
     * @param xPos x position of the frame image where the image will start.
     * @param yPos y position of the frame image where the image will start.
     */
    void drawTransparency(Mat frame, Mat transp, int xPos, int yPos) {
        Mat mask;
        vector<Mat> layers;
    
        split(transp, layers); // seperate channels
        Mat rgb[3] = { layers[0],layers[1],layers[2] };
        mask = layers[3]; // png's alpha channel used as mask
        merge(rgb, 3, transp);  // put together the RGB channels, now transp insn't transparent 
        transp.copyTo(frame.rowRange(yPos, yPos + transp.rows).colRange(xPos, xPos + transp.cols), mask);
    }
    

    Can be called like that:

    drawTransparency(background, overlay, 10, 10);
    
    0 讨论(0)
  • 2020-12-01 12:26

    To overlay png image watermark over normal 3 channel jpeg image

    import cv2
    import numpy as np
    ​
    def logoOverlay(image,logo,alpha=1.0,x=0, y=0, scale=1.0):
        (h, w) = image.shape[:2]
        image = np.dstack([image, np.ones((h, w), dtype="uint8") * 255])
    ​
        overlay = cv2.resize(logo, None,fx=scale,fy=scale)
        (wH, wW) = overlay.shape[:2]
        output = image.copy()
        # blend the two images together using transparent overlays
        try:
            if x<0 : x = w+x
            if y<0 : y = h+y
            if x+wW > w: wW = w-x  
            if y+wH > h: wH = h-y
            print(x,y,wW,wH)
            overlay=cv2.addWeighted(output[y:y+wH, x:x+wW],alpha,overlay[:wH,:wW],1.0,0)
            output[y:y+wH, x:x+wW ] = overlay
        except Exception as e:
            print("Error: Logo position is overshooting image!")
            print(e)
    ​
        output= output[:,:,:3]
        return output
    
    

    Usage:

    background = cv2.imread('image.jpeg')
    overlay = cv2.imread('logo.png', cv2.IMREAD_UNCHANGED)
    ​
    print(overlay.shape) # must be (x,y,4)
    print(background.shape) # must be (x,y,3)
    
    # downscale logo by half and position on bottom right reference
    out = logoOverlay(background,overlay,scale=0.5,y=-100,x=-100) 
    ​
    cv2.imshow("test",out)
    cv2.waitKey(0)
    
    0 讨论(0)
  • 2020-12-01 12:30
    import cv2
    
    background = cv2.imread('field.jpg')
    overlay = cv2.imread('dice.png')
    
    added_image = cv2.addWeighted(background,0.4,overlay,0.1,0)
    
    cv2.imwrite('combined.png', added_image)
    
    0 讨论(0)
  • 2020-12-01 12:31

    Been a while since this question appeared, but I believe this is the right simple answer, which could still help somebody.

    background = cv2.imread('road.jpg')
    overlay = cv2.imread('traffic sign.png')
    
    rows,cols,channels = overlay.shape
    
    overlay=cv2.addWeighted(background[250:250+rows, 0:0+cols],0.5,overlay,0.5,0)
    
    background[250:250+rows, 0:0+cols ] = overlay
    

    This will overlay the image over the background image such as shown here:

    Ignore the ROI rectangles

    Note that I used a background image of size 400x300 and the overlay image of size 32x32, is shown in the x[0-32] and y[250-282] part of the background image according to the coordinates I set for it, to first calculate the blend and then put the calculated blend in the part of the image where I want to have it.

    (overlay is loaded from disk, not from the background image itself,unfortunately the overlay image has its own white background, so you can see that too in the result)

    0 讨论(0)
  • 2020-12-01 12:43

    The following code will use the alpha channels of the overlay image to correctly blend it into the background image, use x and y to set the top-left corner of the overlay image.

    import cv2
    import numpy as np
    
    def overlay_transparent(background, overlay, x, y):
    
        background_width = background.shape[1]
        background_height = background.shape[0]
    
        if x >= background_width or y >= background_height:
            return background
    
        h, w = overlay.shape[0], overlay.shape[1]
    
        if x + w > background_width:
            w = background_width - x
            overlay = overlay[:, :w]
    
        if y + h > background_height:
            h = background_height - y
            overlay = overlay[:h]
    
        if overlay.shape[2] < 4:
            overlay = np.concatenate(
                [
                    overlay,
                    np.ones((overlay.shape[0], overlay.shape[1], 1), dtype = overlay.dtype) * 255
                ],
                axis = 2,
            )
    
        overlay_image = overlay[..., :3]
        mask = overlay[..., 3:] / 255.0
    
        background[y:y+h, x:x+w] = (1.0 - mask) * background[y:y+h, x:x+w] + mask * overlay_image
    
        return background
    

    This code will mutate background so create a copy if you wish to preserve the original background image.

    0 讨论(0)
提交回复
热议问题