英文:
Png transparency with opencv in python
问题
我一直在使用OpenCV将一个PNG图像贴在背景上。我的目标是模拟水下的一种塑料袋效果。这样,我想应用Alpha值,使PNG图像在背景上呈半透明状态,但当我这样做时,PNG的黑色遮罩也变得透明。
y1, y2 = yoff, yoff + png.shape[0]
x1, x2 = xoff, xoff + png.shape[1]
alpha_png = png[:, :, 3] / 255.0
alpha_bg = 0.5 - alpha_png
bg_copy = bg.copy()
for c in range(0, 3):
bg_copy[y1:y2, x1:x2, c] = (alpha_png * png[:, :, c] + alpha_bg * bg_copy[y1:y2, x1:x2, c])
cv2.imwrite("result.png", bg_copy)
我在网上找到了这段代码,并改变了alpha_bg以使图像更或少透明。
结果是:
我需要移除黑色的半透明背景,但PNG的其余部分应保持半透明,另外袋子应该获得背景的绿色或水下颜色(但我稍后会处理这个)。
任何建议都非常感谢,也考虑使用PIL代替OpenCV。
编辑:
使用的图片是:
<details>
<summary>英文:</summary>
I've been using opencv to paste a png image over a background. My target is to simulate a sort of plastic bag underwater.
In that way, I would like to apply Alpha in order to have the png image semi-transparent over the background, but when I do, the black mask of the png becomes transparent too.
y1, y2 = yoff, yoff + png.shape[0]
x1, x2 = xoff, xoff + png.shape[1]
alpha_png = png[:, :, 3] / 255.0
alpha_bg = 0.5 - alpha_png
bg_copy = bg.copy()
for c in range(0, 3):
bg_copy[y1:y2, x1:x2, c] = (alpha_png * png[:, :, c] + alpha_bg * bg_copy[y1:y2, x1:x2, c])
cv2.imwrite("result.png", bg_copy)
I found this code online, and I changed the alpha_bg in order to make the image more or less transparent.
And the result is
[![png image on background][1]][1]
I need to remove the black semi-transparent background, but the rest of the png should remain semi-transparent, plus the bag should get the background green color or underwater color (But I'll work later on this).
Any suggestion is really appreciated, also considering to use PIL instead of opencv.
Edit:
The used images are:
Foreground
[![foreground][2]][2]
And Background:
[![background][3]][3]
[1]: https://i.stack.imgur.com/UNhSa.jpg
[2]: https://i.stack.imgur.com/bONUF.png
[3]: https://i.stack.imgur.com/75dWi.jpg
</details>
# 答案1
**得分**: 1
这是一种在Python/OpenCV中实现的方法。
- 读取背景(水)图像并获取其形状
- 读取前景(袋子)图像并获取其形状
- 分离袋子的BGR通道和alpha通道
- 用透明黑色扩展袋子的BGR图像作为掩码 - 将袋子插入到水图像大小的黑色水中心
- 同样,将袋子的alpha通道扩展到黑色的水图像大小中心(或任何您想要的位置)
- 定义混合因子
- 将掩码转换为浮点数,除以255并乘以混合量
- 将袋子的BGR转换为浮点数并使用掩码与水混合
- 裁剪结果并转换回uint8
- 保存结果
水图像:
![enter image description here](https://i.stack.imgur.com/Of7Wf.jpg)
袋子图像:
![enter image description here](https://i.stack.imgur.com/6JHqm.png)
```python
import cv2
import numpy as np
# 读取背景图像
water = cv2.imread('background.jpg')
h, w = water.shape[:2]
cx = w//2
cy = h//2
# 读取前景图像
bag = cv2.imread('bag.png', cv2.IMREAD_UNCHANGED)
hh, ww = bag.shape[:2]
# 分离袋子图像中的BGR和alpha通道
bag_bgr = bag[:,:,0:3]
bag_alpha = bag[:,:,3]
# 用黑色和透明度扩展袋子到水的大小,然后插入到水的中心
bag_ext = np.zeros((h,w,3), dtype=np.uint8)
bag_ext[cy:cy+hh, cx:cx+ww] = bag_bgr
mask = np.zeros((h,w), dtype=np.uint8)
mask[cy:cy+hh, cx:cx+ww] = bag_alpha
mask = cv2.merge([mask,mask,mask])
# 使用混合因子混合bag_bgr和水图像
blend = 0.5
mask = blend*mask.astype(np.float64)/255
result = bag_ext.astype(np.float64) * mask + water.astype(np.float64) * (1-mask)
result = result.clip(0,255).astype(np.uint8)
# 保存结果
cv2.imwrite('water_bag.jpg', result)
# 显示结果
cv2.imshow('result', result)
cv2.waitKey(0)
混合程度为0.5的结果:
英文:
Here is one way to do that in Python/OpenCV.
- Read the background (water) image and get its shape
- Read the foreground (bag) image and get its shape
- Separate the bag's BGR channels and its alpha channel
- Extend the bag BGR image with transparent black as a mask - insert the bag at the center of the water inside black the size of the water image
- Similarly extend the bag's alpha channel in black the size of the water image at the center (or where ever you want)
- Define the blend factor
- Convert the mask to float, divide by 255 and multiply by the blend amount
- Convert the bag's BGR to float and use the mask to blend with the water
- Clip the result and convert back to uint8
- Save the result
Water:
Bag:
import cv2
import numpy as np
# read the background image
water = cv2.imread('background.jpg')
h, w = water.shape[:2]
cx = w//2
cy = h//2
# read the foreground image
bag = cv2.imread('bag.png', cv2.IMREAD_UNCHANGED)
hh, ww = bag.shape[:2]
# separate bgr and alpha in bag image
bag_bgr = bag[:,:,0:3]
bag_alpha = bag[:,:,3]
# extend bag to size of water with added area black and transparent
# then insert bag at center of water
bag_ext = np.zeros((h,w,3), dtype=np.uint8)
bag_ext[cy:cy+hh, cx:cx+ww] = bag_bgr
mask = np.zeros((h,w), dtype=np.uint8)
mask[cy:cy+hh, cx:cx+ww] = bag_alpha
mask = cv2.merge([mask,mask,mask])
# blend bag_bgr with water using bag_alpha
blend = 0.5
mask = blend*mask.astype(np.float64)/255
result = bag_ext.astype(np.float64) * mask + water.astype(np.float64) * (1-mask)
result = result.clip(0,255).astype(np.uint8)
# save results
cv2.imwrite('water_bag.jpg', result)
# show results
cv2.imshow('result', result)
cv2.waitKey(0)
Result for blend=0.5:
答案2
得分: 1
与fmw42的回答等效的替代方法:
composite = water.copy()
roi = composite[cy:cy+hh, cx:cx+ww]
water_alpha = 0.5 # 它对下层对象的影响程度
# 将叠加物(水)的alpha的余数/补充部分烘焙到底层(包)的alpha中
# 同时除以255,以使值范围在0.0到1.0之间
# float32是一种更便宜的数据类型
alpha = bag_alpha * np.float32((1 - water_alpha) / 255)
# 添加一个维度以进行沿着颜色通道的广播
alpha = alpha[..., None]
# 凸组合,无需剪裁
roi[:] = bag_bgr * alpha + roi * (1 - alpha)
结果相同。
英文:
An equivalent alternative to fmw42's answer:
composite = water.copy()
roi = composite[cy:cy+hh, cx:cx+ww]
water_alpha = 0.5 # how much it affects underlaid objects
# bake the _remainder/complement_ of the overlay's (water) alpha into the underlay's (bag) alpha
# also /255 so the value range is 0.0 to 1.0
# float32 is a cheaper data type
alpha = bag_alpha * np.float32((1 - water_alpha) / 255)
# add a dimension for broadcasting along color channels
alpha = alpha[..., None]
# convex combination, no need to clip
roi[:] = bag_bgr * alpha + roi * (1 - alpha)
Result is the same.
答案3
得分: 1
根据Christoph Rackwitz的回答中提供的细节,我们首先从水图像中裁剪出所需位置的大小,然后使用来自alpha通道的掩码与包包图像(bgr)混合。然后将生成的合成图像插入到水图像的所需位置。
import cv2
import numpy as np
# 读取背景图像
water = cv2.imread('background.jpg')
h, w = water.shape[:2]
cx = w//2
cy = h//2
# 读取前景图像
bag = cv2.imread('bag.png', cv2.IMREAD_UNCHANGED)
hh, ww = bag.shape[:2]
# 将包包图像分成BGR和alpha通道
bag_bgr = bag[:,:,0:3]
bag_alpha = bag[:,:,3]
# 在中心裁剪水图像以适应包包大小
water2 = water.copy()
water_crop = water2[cy:cy+hh, cx:cx+ww]
blend = 0.5
mask = blend * bag_alpha.astype(np.float32) / 255
mask = mask[..., None]
composite = bag_bgr.astype(np.float32) * mask + water_crop.astype(np.float32) * (1 - mask)
composite = composite.astype(np.uint8)
# 将合成图像插入到水图像中
result = water.copy()
result[cy:cy+hh, cx:cx+ww] = composite
# 保存结果
cv2.imwrite('water_bag2.jpg', result)
# 显示结果
cv2.imshow('result', result)
cv2.waitKey(0)
结果图像:
英文:
Filling out the the details from the answer according Christoph Rackwitz, we crop the water image at the desired location to the size of the bag and blend with the bag image (bgr) using the mask from the alpha channel. Then we insert the resulting composite into a copy of the water image at the desired location.
import cv2
import numpy as np
# read the background image
water = cv2.imread('background.jpg')
h, w = water.shape[:2]
cx = w//2
cy = h//2
# read the foreground image
bag = cv2.imread('bag.png', cv2.IMREAD_UNCHANGED)
hh, ww = bag.shape[:2]
# separate bgr and alpha in bag image
bag_bgr = bag[:,:,0:3]
bag_alpha = bag[:,:,3]
# crop the water image at center to size of bag
water2 = water.copy()
water_crop = water2[cy:cy+hh, cx:cx+ww]
blend = 0.5
mask = blend*bag_alpha.astype(np.float32)/255
mask = mask[..., None]
composite = bag_bgr.astype(np.float32) * mask + water_crop.astype(np.float32) * (1-mask)
composite = composite.astype(np.uint8)
# insert composite into water
result = water.copy()
result[cy:cy+hh, cx:cx+ww] = composite
# save results
cv2.imwrite('water_bag2.jpg', result)
# show results
cv2.imshow('result', result)
cv2.waitKey(0)
Result:
答案4
得分: 0
以下是翻译好的内容:
正确的搜索术语是“alpha混合”,但以下是一个快速示例,将Python标志添加到您的图像中。
from PIL import Image
import numpy as np
with Image.open("python-logo-only.png") as img: # 您可能需要自行调整文件名以运行此代码
image_with_alpha = np.array(img)
with Image.open("UNhSa.jpg") as img:
background = np.array(img) # 没有alpha通道的图像
# 我们需要相同大小的图像,以便轻松将它们相加
foreground = np.zeros_like(background)
# 将图像颜色数据复制到前景层
w, h, _ = image_with_alpha.shape
foreground[:w, :h, :] = image_with_alpha[:,:,:3] # 复制颜色数据而不是alpha通道
# 制作透明度蒙版
transparency = np.zeros(background.shape[:2], dtype = float)
# 从前景图像复制透明度层数据
transparency[:w, :h] = image_with_alpha[:,:,3] / 255 # 将0-255范围转换为0-1
# 将透明度乘以颜色数据以获得前景在最终图像中的贡献
foreground_multiplied = foreground * transparency[:,:,None] # 添加 None (np.newaxis) 以获取正确的ndim
# 将反向透明度乘以背景以获得背景在最终图像中的贡献
background_multiplied = background * (1-transparency[:,:,None])
# 将两个图像相加
final_image = foreground_multiplied + background_multiplied
img = Image.fromarray(final_image.astype(np.uint8))
img.show()
最终图像:
PIL
还具有内置方法可执行大部分操作,如 PIL.Image.alpha_composite
和 PIL.Image.composite
,但仍然需要调整图像大小。Image.paste
是一个几乎一行代码的几乎完整的解决方案。
英文:
The correct term to search for is "alpha blending", but here's a quick example adding the python logo to your image.
source images: from here and from your post
from PIL import Image
import numpy as np
with Image.open(r"python-logo-only.png") as img: #you'll probably have to adjust file names to run this yourself
image_with_alpha = np.array(img)
with Image.open(r"UNhSa.jpg") as img:
background = np.array(img) #image without alpha
#we need images of the same size so we can add them together easily
foreground = np.zeros_like(background)
#copy image color data to forground layer
w, h, _ = image_with_alpha.shape
foreground[:w, :h, :] = image_with_alpha[:,:,:3] #copy color data not alpha layer
#make a transparency mask
transparency = np.zeros(background.shape[:2], dtype = float)
#copy transparency layer data from foreground image
transparency[:w, :h] = image_with_alpha[:,:,3] / 255 #convert 0-255 range to 0-1
#multiply transparency by color data to get forground contribution to final image
foreground_multiplied = foreground * transparency[:,:,None] #add None (np.newaxis) to get correct ndim
#multiply inverse transparency by background to get background contribution to final image
background_multiplied = background * (1-transparency[:,:,None])
#add the two images
final_image = foreground_multiplied + background_multiplied
img = Image.fromarray(final_image.astype(np.uint8))
img.show()
final image:
PIL
also has built-in methods to do much of this in PIL.Image.alpha_composite
and PIL.Image.composite
, but you will still need to adjust the images to the same dimensions. Image.paste
is a nearly complete solution in just one line.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论