Files
nanocode/image-resize/resize_to_fixed.py
T
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

202 lines
6.6 KiB
Python
Raw 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.
#!/usr/bin/env python3
"""
图片尺寸处理工具 - 固定宽度,智能高度
目标尺寸: 宽1620px,高≤2880px(9:16比例)
处理逻辑:
1. 先按宽度1620px等比例压缩
2. 压缩后高度>2880px: 从中间裁剪多余部分
3. 压缩后高度<2880px: 保持实际高度
"""
import os
import sys
import subprocess
from pathlib import Path
# 配置
TARGET_WIDTH = 1620
MAX_HEIGHT = 2880
SOURCE_DIR = Path('/home/ubuntu/images')
OUTPUT_DIR = SOURCE_DIR / 'resized_output'
def get_image_size(filepath):
"""获取图片尺寸"""
try:
result = subprocess.run(
['identify', '-format', '%w %h', str(filepath)],
capture_output=True, text=True, check=True
)
width, height = map(int, result.stdout.strip().split())
return width, height
except Exception as e:
print(f" ❌ 无法获取图片尺寸: {e}")
return None, None
def process_image(input_path, output_path):
"""
处理图片:
1. 先按宽度1620px等比例压缩
2. 如果高度>2880,从中间裁剪
3. 如果高度<2880,保持原样
"""
try:
# 第一步: 按宽度1620px等比例压缩
# 使用 ^ 表示最小尺寸,确保宽度至少1620,然后裁剪
temp_path = str(output_path) + '.tmp.jpg'
cmd_resize = [
'convert', str(input_path),
'-resize', f'{TARGET_WIDTH}x', # 宽度固定1620,高度自适应
'-quality', '95',
temp_path
]
subprocess.run(cmd_resize, check=True, capture_output=True)
# 获取压缩后的尺寸
new_width, new_height = get_image_size(temp_path)
if not new_width:
os.remove(temp_path)
return False, "无法获取压缩后尺寸"
print(f" 等比压缩后: {new_width}x{new_height}")
# 第二步: 处理高度
if new_height > MAX_HEIGHT:
# 高度超过2880,从中间裁剪
offset_y = (new_height - MAX_HEIGHT) // 2
cmd_crop = [
'convert', temp_path,
'-crop', f'{TARGET_WIDTH}x{MAX_HEIGHT}+0+{offset_y}',
'+repage', # 重置页面信息
'-quality', '95',
str(output_path)
]
subprocess.run(cmd_crop, check=True, capture_output=True)
final_height = MAX_HEIGHT
action = f"居中裁剪至{MAX_HEIGHT}px"
else:
# 高度小于等于2880,保持原样,只复制
os.rename(temp_path, output_path)
final_height = new_height
action = f"保持实际高度{new_height}px"
# 清理临时文件
if os.path.exists(temp_path):
os.remove(temp_path)
return True, action
except Exception as e:
print(f" ❌ 处理失败: {e}")
return False, str(e)
def format_size(size_bytes):
"""格式化文件大小"""
if size_bytes < 1024:
return f"{size_bytes}B"
elif size_bytes < 1024 * 1024:
return f"{size_bytes/1024:.1f}KB"
else:
return f"{size_bytes/(1024*1024):.1f}MB"
def main():
print("=" * 70)
print(f"🖼️ 图片尺寸处理工具")
print(f" 目标宽度: {TARGET_WIDTH}px")
print(f" 最大高度: {MAX_HEIGHT}px (超出则居中裁剪)")
print(f" 输出目录: {OUTPUT_DIR}")
print("=" * 70)
# 检查ImageMagick
try:
subprocess.run(['identify', '--version'], capture_output=True, check=True)
except:
print("❌ 错误: 未安装 ImageMagick")
print(" 请安装: sudo apt install imagemagick")
sys.exit(1)
if not SOURCE_DIR.exists():
print(f"❌ 目录不存在: {SOURCE_DIR}")
sys.exit(1)
# 创建输出目录
OUTPUT_DIR.mkdir(exist_ok=True)
# 支持的图片格式
extensions = {'.jpg', '.jpeg', '.png', '.webp', '.gif', '.bmp', '.tiff'}
# 收集需要处理的图片
to_process = []
for file in SOURCE_DIR.iterdir():
if file.is_file() and file.suffix.lower() in extensions:
# 跳过输出目录和备份目录
if file.parent.name in ['resized_output', 'original_backup']:
continue
width, height = get_image_size(file)
if width:
to_process.append({
'file': file,
'orig_width': width,
'orig_height': height
})
if not to_process:
print(f"\n❌ 未找到需要处理的图片")
return
print(f"\n📋 找到 {len(to_process)} 张图片:\n")
for item in to_process:
ratio = item['orig_width'] / item['orig_height']
estimated_height = int(TARGET_WIDTH / ratio)
action = "需裁剪" if estimated_height > MAX_HEIGHT else "保持"
print(f" {item['file'].name}: {item['orig_width']}x{item['orig_height']} → {TARGET_WIDTH}x{estimated_height} ({action})")
# 确认
response = input(f"\n是否开始处理? [y/N]: ").strip().lower()
if response != 'y':
print("\n❌ 已取消")
return
# 处理图片
print("\n" + "=" * 70)
success_count = 0
for i, item in enumerate(to_process, 1):
file = item['file']
output_path = OUTPUT_DIR / f"{file.stem}_1620x{MAX_HEIGHT}{file.suffix}"
print(f"\n[{i}/{len(to_process)}] {file.name}")
print(f" 原图: {item['orig_width']}x{item['orig_height']}")
# 处理
success, action = process_image(file, output_path)
if success:
final_width, final_height = get_image_size(output_path)
orig_size = file.stat().st_size
new_size = output_path.stat().st_size
print(f" 结果: {final_width}x{final_height}")
print(f" 动作: {action}")
print(f" 大小: {format_size(orig_size)} → {format_size(new_size)}")
print(f" 保存: {output_path.name}")
success_count += 1
else:
print(f" ❌ 失败: {action}")
# 汇总
print("\n" + "=" * 70)
print(f"✅ 完成: {success_count}/{len(to_process)} 张图片已处理")
print(f"📁 输出目录: {OUTPUT_DIR}")
if success_count > 0:
print(f"\n💡 提示:")
print(f" - 处理后的图片在: {OUTPUT_DIR}/")
print(f" - 原图未改动,保留在: {SOURCE_DIR}/")
print(f" - 文件名格式: 原名_1620x2880.扩展名")
if __name__ == '__main__':
main()