#!/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()