1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
import os
from pathlib import Path
from PIL import Image
# 支持读取的常见图片格式(不限制输入类型)
IMAGE_EXTS = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff', '.webp', '.jfif', '.gif'}
# 外观设计专利规范常量
TARGET_DPI = 300 # 统一输出分辨率 (DPI)
CM_PER_INCH = 2.54 # 1英寸 = 2.54厘米
MAX_W_CM = 15.0 # 宽度上限阈值
TARGET_W_CM = 14.9 # 宽度超限后的目标宽度
MAX_H_CM = 22.0 # 高度上限阈值
TARGET_H_CM = 21.9 # 高度超限后的目标高度
def get_image_info(img_path: Path):
"""读取图片像素尺寸,并基于 300 DPI 计算物理尺寸 (厘米)"""
with Image.open(img_path) as img:
w_px, h_px = img.size
# 在 300 DPI 下:厘米 = (像素 / 300) * 2.54
w_cm = (w_px / TARGET_DPI) * CM_PER_INCH
h_cm = (h_px / TARGET_DPI) * CM_PER_INCH
return {
'path': img_path,
'stem': img_path.stem, # 文件名(不含后缀)
'filename': img_path.name, # 原完整文件名
'w_px': w_px,
'h_px': h_px,
'w_cm': w_cm,
'h_cm': h_cm
}
def convert_to_rgb(img: Image.Image) -> Image.Image:
"""将任意色彩模式的图像转换为适用于 JPG 的 RGB 模式,透明区域填充为纯白色"""
if img.mode in ('RGBA', 'LA') or (img.mode == 'P' and 'transparency' in img.info):
# 转换为 RGBA 提取透明通道
rgba_img = img.convert('RGBA')
# 创建纯白底图
white_bg = Image.new('RGB', rgba_img.size, (255, 255, 255))
# 按照 Alpha 遮罩将图像贴到白底上
white_bg.paste(rgba_img, mask=rgba_img.split()[3])
return white_bg
elif img.mode != 'RGB':
return img.convert('RGB')
return img
def process_image_group(input_group_dir: Path, output_group_dir: Path):
"""处理单个子目录下的同组外观设计图片"""
image_files = [
f for f in input_group_dir.iterdir()
if f.is_file() and f.suffix.lower() in IMAGE_EXTS
]
if not image_files:
return
# 1. 收集该目录下所有图片在 300 DPI 下的尺寸信息
images_info = []
for f in image_files:
try:
images_info.append(get_image_info(f))
except Exception as e:
print(f"⚠️ 无法读取文件 {f.name},已跳过: {e}")
if not images_info:
return
# ---------------- 第一次遍历:宽度检查 ----------------
max_w_cm = max(info['w_cm'] for info in images_info)
scale_w = 1.0
if max_w_cm >= MAX_W_CM:
scale_w = TARGET_W_CM / max_w_cm
# ---------------- 第二次遍历:高度检查 ----------------
max_h_cm_after_w = max(info['h_cm'] * scale_w for info in images_info)
scale_h = 1.0
if max_h_cm_after_w >= MAX_H_CM:
scale_h = TARGET_H_CM / max_h_cm_after_w
# 最终综合缩放比例
final_scale = scale_w * scale_h
# 确保目标输出目录存在
output_group_dir.mkdir(parents=True, exist_ok=True)
print(f"\n📂 正在处理目录: {input_group_dir}")
print(f" - 图片数量: {len(images_info)} 张")
print(f" - 原始最大尺寸 (300 DPI下): {max_w_cm:.2f}cm × {max(i['h_cm'] for i in images_info):.2f}cm")
if final_scale < 1.0:
print(f" - 需等比缩小,最终缩放比例: {final_scale * 100:.2f}%")
else:
print(f" - 尺寸均在规范内 (≤15cm × ≤22cm),保持原尺寸并转为 300 DPI JPG")
# ---------------- 执行转换、转RGB并强制另存为 JPG ----------------
for info in images_info:
target_w_px = max(1, int(round(info['w_px'] * final_scale)))
target_h_px = max(1, int(round(info['h_px'] * final_scale)))
# 强制将输出文件后缀设为 .jpg
target_path = output_group_dir / f"{info['stem']}.jpg"
with Image.open(info['path']) as img:
# 1. 尺寸调整(高质量 Lanczos 抗锯齿插值)
if target_w_px != info['w_px'] or target_h_px != info['h_px']:
processed_img = img.resize((target_w_px, target_h_px), Image.Resampling.LANCZOS)
else:
processed_img = img.copy()
# 2. 格式与透明通道处理(转为标准 RGB 白底)
processed_img = convert_to_rgb(processed_img)
# 3. 保存为高质量 300 DPI 的 JPEG 图像
processed_img.save(
target_path,
format='JPEG',
dpi=(TARGET_DPI, TARGET_DPI),
quality=95, # 保持极高清晰度
subsampling=0 # 保持色彩无色度抽样损失
)
out_w_cm = (target_w_px / TARGET_DPI) * CM_PER_INCH
out_h_cm = (target_h_px / TARGET_DPI) * CM_PER_INCH
print(f" ✓ {info['filename']} -> {target_path.name} ({target_w_px}x{target_h_px}px, {out_w_cm:.2f}cm × {out_h_cm:.2f}cm)")
def batch_process(input_root_dir: str, output_root_dir: str):
"""递归遍历输入根目录下的所有子目录,并将结果镜像保存到输出根目录"""
in_root = Path(input_root_dir).resolve()
out_root = Path(output_root_dir).resolve()
if not in_root.exists():
print(f"❌ 输入目录不存在: {in_root}")
return
if in_root == out_root:
print("❌ 输出目录不能与输入目录相同,请指定一个独立的输出目录!")
return
print("=" * 60)
print(f"输入源根目录: {in_root}")
print(f"输出目标目录: {out_root}")
print("=" * 60)
for current_dir, _, _ in os.walk(in_root):
curr_in_path = Path(current_dir)
rel_path = curr_in_path.relative_to(in_root)
curr_out_path = out_root / rel_path
process_image_group(curr_in_path, curr_out_path)
print("\n🎉 全部处理完成!所有图片已标准化转为 300 DPI 的 JPG 格式。")
if __name__ == '__main__':
# ------------------ 请在此处配置路径 ------------------
# 1. 存放原始图片的根目录
INPUT_DIR = r"D:\Images_Raw"
# 2. 输出目录(会自动镜像创建相同结构的子文件夹)
OUTPUT_DIR = r"D:\Images_Export_JPG"
# -----------------------------------------------------
batch_process(INPUT_DIR, OUTPUT_DIR)
|