Files
2025-12-06 15:52:28 +08:00

128 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import numpy as np
from PIL import Image, ImageFilter, ImageChops
def split_cloud_smart():
input_path = os.path.join(os.path.dirname(__file__), 'origin', 'cloud.jpg')
output_dir = os.path.join(os.path.dirname(__file__), 'clouds')
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Processing {input_path}...")
try:
img = Image.open(input_path).convert("RGBA")
except Exception as e:
print(f"Error opening image: {e}")
return
# 1. 智能采样背景色 (Smart Sampling)
# 取图像四周边缘的像素来计算背景特征,比只取一个角更稳健
width, height = img.size
# 提取上、下、左、右四条边的像素
top_edge = np.array(img.crop((0, 0, width, 1)))
bottom_edge = np.array(img.crop((0, height-1, width, height)))
left_edge = np.array(img.crop((0, 0, 1, height)))
right_edge = np.array(img.crop((width-1, 0, width, height)))
# 合并边缘像素
edges = np.concatenate([
top_edge.reshape(-1, 4),
bottom_edge.reshape(-1, 4),
left_edge.reshape(-1, 4),
right_edge.reshape(-1, 4)
])
# 计算背景色的平均值和标准差,用于确定容差范围
bg_mean = np.mean(edges, axis=0)
print(f"Smart sampled background color (RGBA): {bg_mean}")
# 2. HSV 色彩空间分离 (HSV Separation)
# 将图片转为 HSV利用饱和度(S)和亮度(V)来区分云(通常S低V高)和深色背景(通常S高V低)
hsv_img = img.convert("HSV")
hsv_data = np.array(hsv_img)
rgb_data = np.array(img)
# 提取通道
H, S, V = hsv_data[:,:,0], hsv_data[:,:,1], hsv_data[:,:,2]
R, G, B = rgb_data[:,:,0], rgb_data[:,:,1], rgb_data[:,:,2]
# 计算 RGB 欧氏距离 (针对平均背景色)
# 只比较 RGB 前三个通道
diff_r = R.astype(float) - bg_mean[0]
diff_g = G.astype(float) - bg_mean[1]
diff_b = B.astype(float) - bg_mean[2]
rgb_distance = np.sqrt(diff_r**2 + diff_g**2 + diff_b**2)
# 定义阈值
# RGB 容差:允许背景有一定的颜色波动
rgb_tolerance = 60.0
# HSV 辅助判断:
# 背景通常是深紫色:需要保护云朵(白色/灰色),云朵的特征是低饱和度(Low S)
# 如果一个像素离背景色有点远,但它饱和度很高且偏紫,那它可能还是背景(渐变区)
# 如果一个像素离背景色很近,但它饱和度极低(它是灰色的云边缘),那应该保留
# 创建 Alpha Mask (0 为完全透明/背景255 为完全不透明/云)
# 初始 Mask距离背景色越近Alpha 越小
alpha_mask = np.zeros_like(H, dtype=np.float32)
# 核心逻辑:
# 1. 主要是背景RGB 距离 < 容差
# 2. 渐变增强:对于边缘区域,使用 Sigmoid 函数做软过渡,而不是硬切
# 归一化距离,距离越小越接近背景
normalized_dist = np.clip(rgb_distance / rgb_tolerance, 0, 1)
# 简单的线性映射翻转:距离越小(背景)Alpha越小(透明)
# 使用平滑函数 (Smoothstep) 让过渡更自然: 3x^2 - 2x^3
alpha_mask = np.clip((normalized_dist - 0.2) / 0.6, 0, 1) # 0.2到0.8之间过渡
alpha_mask = alpha_mask * alpha_mask * (3 - 2 * alpha_mask)
# 3. 保护云朵核心 (Cloud Core Protection)
# 如果像素很亮(V高)且饱和度很低(S低),强制认为是云,设为不透明
# 假设云是白色的,背景是深色的
is_cloud_core = (V > 150) & (S < 60)
alpha_mask[is_cloud_core] = 1.0
# 4. 转换回 0-255 并应用羽化
final_alpha = (alpha_mask * 255).astype(np.uint8)
# 创建蒙版图像
mask_img = Image.fromarray(final_alpha, mode='L')
# 边缘羽化 (Matte Refinement)
# 对蒙版进行轻微模糊,消除锯齿
mask_img = mask_img.filter(ImageFilter.GaussianBlur(radius=1.5))
# 将处理好的 Alpha 通道应用回原图
r, g, b, a = img.split()
img_transparent = Image.merge('RGBA', (r, g, b, mask_img))
# 切割逻辑保持不变
width, height = img.size
cell_width = width // 3
cell_height = height // 3
count = 0
for r in range(3):
for c in range(3):
left = c * cell_width
top = r * cell_height
right = left + cell_width
bottom = top + cell_height
cell = img_transparent.crop((left, top, right, bottom))
output_filename = f"cloud_{count}.png"
output_path = os.path.join(output_dir, output_filename)
cell.save(output_path)
print(f"Saved {output_path}")
count += 1
print("Smart split done!")
if __name__ == "__main__":
split_cloud_smart()