实现一种特定的图片特效“幻影坦克”,以下是一个实现这一效果的示例代码。
1. 设置环境
首先,确保你已安装 Pillow
:
pip install Pillow
2. 原理说明
在计算机视觉和数字图像处理中,RGB和RGBA图像是常见的图像表示形式。RGB颜色模式 有三个通道,分别是红Red
、绿Green
和蓝Blue
。这种颜色模型通过将红、绿、蓝三原色的色光以不同的比例相加,以合成产生各种色彩光。RGBA颜色模式 则包含四个通道:红Red
、绿Green
、蓝Blue
和透明度Alpha
。
灰度值:表征单色的亮暗程度。数值在0-255之间,数值大小表示允许通过色彩多少,越大,相应色彩越深。
透明度:数值在0-255之间,0完全透明,255完全不透明。
RGB图像中的R、G、B三通道灰度值相等时,图片也会是黑白色的,即为灰度图。
三通道图可以是灰度图,单通道图只能是灰度图。
RGBA图像中,若R、G、B三通道的值相等,图像将呈现为带透明度的灰色。如果Alpha值为255,则图像显示为相应的灰色;如果Alpha值小于255,则图像的显示效果会有背景色的影响,可能呈现出灰色与背景色的混合效果。
RGBA
(4x8-bit pixels, true color with transparency)图像模式及其参数:
- 扩展RGB模式,增加一个8位的透明度通道。
- 适合需要透明效果的彩色图像。
- R: 红色通道,值范围为 0 - 255。
- G: 绿,0 - 255。
- B: 蓝,0 - 255。
- A: 透明度通道,0 - 255(0完全透明)。
逻辑:在保持/降低底层(interior.png)灰度影响的同时,提高上层图像(exterior.png)亮度,利用上层图像的亮度来调整整体的透明度。在合成图像时,我们用当前层图像的灰度值和透明度来决定最终的灰度值。如果 α \alpha α 较小(透明度越低),表示在合成中需要“放大”当前层的亮度,以确保合成后的效果能体现出底层和上层的真实关系。值得注意的是,实际情况下这可能需要更多的归一化处理,以确保输出值灰度值在有效的范围内(0 - 255)。
把RGBA图像视为由三张带有透明度的R、G、B通道图组成。
混合透明度 { α 1 = 255 − R 1 + R 2 α 2 = 255 − G 1 + G 2 α 3 = 255 − B 1 + B 2 混合透明度\begin{cases} \alpha_{1} = 255 - R_1 + R_2\\ \alpha_{2} = 255 - G_1 + G_2\\ \alpha_{3} = 255 - B_1 + B_2 \end{cases} 混合透明度⎩ ⎨ ⎧α1=255−R1+R2α2=255−G1+G2α3=255−B1+B2
将三者拉到近似相等的水平,提高视觉一致性和色彩的自然性,这对于图像的质量、表现以及后续的操作都是至关重要的。
α
=
255
−
R
1
+
R
2
\alpha = 255 - R_1 + R_2
α=255−R1+R2
灰度值 { R = R 2 / α G = G 2 / α B = B 2 / α 灰度值\begin{cases} R ={R_2} /{\alpha}\\ G = {G_2}/{\alpha}\\ B = {B_2} /{\alpha} \end{cases} 灰度值⎩ ⎨ ⎧R=R2/αG=G2/αB=B2/α
3. 代码实现
准备两张大小一样的图,一张作为上层图像exterior.png,另一张作为底层图像interior.png。
- 加载图片:使用
PIL
库打开图像,并将其转换为 RGBA 模式。 - 创建图像:新建一个大小一样的空白图像。
- 合成图像:遍历每一个像素点,计算出R、G、B、A后填充到空白图片。
- 保存结果:将生成的图像保存到指定路径。
from PIL import Image
# Load two footage images and convert them to RGBA images
img1 = Image.open('exterior.png').convert('RGBA')
img2 = Image.open('interior.png').convert('RGBA')
# Adjust the brightness of the picture
img1 = img1.point(lambda p: int((p + 255) / 2)) # Lighten
img2 = img2.point(lambda p: int(p / 2)) # Darken
def create_phantom_tank_effect():
img = Image.new('RGBA', img1.size) # New a blank picture
# Go through each pixel, calculate the transparency and R, G, B value
# Then fill in the blank picture
for x in range(img.width):
for y in range(img.height):
# alpha = 255 - R1 + R2
alpha = 255 - img1.getpixel((x, y))[0] + img2.getpixel((x, y))[0]
# R = R2 / alpha
R_value = int(img2.getpixel((x, y))[0] * 255 / alpha) if alpha else 127
# G = G2 / alpha
G_value = int(img2.getpixel((x, y))[1] * 255 / alpha) if alpha else 127
# B = B2 / alpha
B_value = int(img2.getpixel((x, y))[2] * 255 / alpha) if alpha else 127
img.putpixel((x, y), (R_value, G_value, B_value, alpha))
img.save('result.png')# Save it
# Example use
create_phantom_tank_effect()
4. 运行结果
▶ 替换 "exterior.png"
、 interior.png
以你的输入图像路径,并指定合适的输出路径 "result.png"
。eg:
exterior.png | interior.png |
▶ 运行代码后,查看输出的效果图像result.png。
浅色模式下 | 深色模式下 |
---|---|
5. 批量处理
import os
from PIL import Image
# input folder path
input_folder = './input'
# get files
files = os.listdir(input_folder)
# Create a list to store matching files
img1_files = sorted([f for f in files if f.startswith('exterior') and f.endswith('.png')])
img2_files = sorted([f for f in files if f.startswith('interior') and f.endswith('.png')])
# Ensure that both lists have the same length
num_images = min(len(img1_files), len(img2_files))
def create_phantom_tank_effect_batch():
for i in range(num_images):
img1_path = os.path.join(input_folder, img1_files[i])
img2_path = os.path.join(input_folder, img2_files[i])
# Load two footage images and convert them to RGBA images
img1 = Image.open(img1_path).convert('RGBA')
img2 = Image.open(img2_path).convert('RGBA')
img1 = img1.point(lambda p: int((p + 255) / 2)) # Lighten
img2 = img2.point(lambda p: int(p / 2)) # Darken
# New a blank picture
img = Image.new('RGBA', img1.size)
for x in range(img.width):
for y in range(img.height):
# alpha = 255 - R1 + R2
alpha = 255 - img1.getpixel((x, y))[0] + img2.getpixel((x, y))[0]
# R = R2 / alpha
R_value = int(img2.getpixel((x, y))[0] * 255 / alpha) if alpha else 127
# G = G2 / alpha
G_value = int(img2.getpixel((x, y))[1] * 255 / alpha) if alpha else 127
# B = B2 / alpha
B_value = int(img2.getpixel((x, y))[2] * 255 / alpha) if alpha else 127
img.putpixel((x, y), (R_value, G_value, B_value, alpha))
# Save
# output_folder = f'D:/coding/untitled/phantom_tank/output/result{i + 1}.png'
output_folder = f'../output/result{i + 1}.png'
result_path = os.path.join(input_folder, output_folder)
img.save(result_path)
print(f'Saved: {result_path}')
create_phantom_tank_effect_batch()