Python 使用 openCV 将透明图像叠加到另一个图像上

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40895785/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 00:09:58  来源:igfitidea点击:

Using openCV to overlay transparent image onto another image

pythonpython-2.7opencv

提问by Anthony Budd

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

如何在 python 中使用 openCV 将透明 PNG 覆盖到另一个图像上而不会失去它的透明度?

import cv2

background = cv2.imread('field.jpg')
overlay = cv2.imread('dice.png')

# Help please

cv2.imwrite('combined.png', background)

Desired output: enter image description here

期望的输出: 在此处输入图片说明

Sources:

资料来源:

Background Image

背景图片

Overlay

覆盖

回答by Manivannan Murugavel

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)

回答by Cristian Garcia

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

以下代码将使用叠加图像的 Alpha 通道将其正确混合到背景图像中,使用xy设置叠加图像的左上角。

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.

此代码将改变背景,因此如果您希望保留原始背景图像,请创建一个副本。

回答by GodIsAnAstronaut

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

忽略 ROI 矩形

enter image description here

在此处输入图片说明

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.

请注意,我使用了大小为 400x300 的背景图像和大小为 32x32 的叠加图像,根据我为其设置的坐标显示在背景图像的 x[0-32] 和 y[250-282] 部分,以首先计算混合,然后将计算出的混合放入我想要的图像部分。

(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)

(覆盖从磁盘加载,而不是从背景图像本身加载,不幸的是覆盖图像有自己的白色背景,所以你也可以在结果中看到)

回答by Mala

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

这个问题的正确答案太难了,所以即使这个问题真的很老,我还是发布了这个答案。您正在寻找的是“过度”合成,可以在维基百科上找到该​​算法: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:

我远不是 OpenCV 的专家,但经过一些实验,这是我发现完成任务的最有效方法:

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)

回答by Derzu

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

您需要使用标志 IMREAD_UNCHANGED 打开透明 png 图像

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:

然后拆分通道,将 RGB 分组并使用透明通道作为蒙版,这样做:

/**
 * @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);

回答by Saurabh yadav

To overlay png image watermark over normal 3 channel jpeg image

在普通 3 通道 jpeg 图像上覆盖 png 图像水印

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)

回答by Jeru Luke

Since you want to work with transparency, be sure to check THIS ANSWERI provided a month ago. I would have taken reference from another blog post also mentioned there.

由于您想使用透明度,请务必查看我一个月前提供的这个答案。我会参考那里也提到的另一篇博文。

Feel free to leave a comment if you find it useful or in case you have other issues.

如果您觉得有用或有其他问题,请随时发表评论。