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

时间:2016-11-30 18:38:15

标签: python python-2.7 opencv

如何将透明PNG叠加到另一张图像上,而不会在python中使用openCV来消除它的透明度?

import cv2

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

# Help please

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

期望的输出: enter image description here

来源:

Background Image

Overlay

7 个答案:

答案 0 :(得分:6)

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)

答案 1 :(得分:3)

自从这个问题出现以来已经有一段时间了,但我相信这是一个正确的简单答案,它仍然可以帮助某些人。

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

这会将图像叠加在背景图像上,如下所示:

  

忽略ROI矩形

enter image description here

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

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

答案 2 :(得分:3)

以下代码将使用覆盖图像的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

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

答案 3 :(得分:1)

对此的正确答案实在太难了,因此即使问题确实很老,我也要发布此答案。您正在寻找的是“过度”合成,可以在Wikipedia上找到算法:https://en.wikipedia.org/wiki/Alpha_compositing

我还不是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)

答案 4 :(得分:0)

由于您希望使用透明度,请务必查看我在一个月前提供的THIS ANSWER。我会参考另一篇也在那里提到的博客文章。

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

答案 5 :(得分:0)

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

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

然后拆分通道,将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);
}

可以这样称呼:

drawTransparency(background, overlay, 10, 10);

答案 6 :(得分:0)

要将png图像水印叠加在正常的3通道jpeg图像上

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

用法:

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)