#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 福建水务营收系统 - 高质量图片尺寸调整工具 支持多种调整方式,保持图片清晰度 限制图片高度不超过23公分(约870像素) """ import argparse import os import sys from typing import Optional from PIL import Image, ImageEnhance from PIL.Image import Resampling def resize_image_pixels_high_quality( image_path: str, max_height_px: int, max_width_px: Optional[int] = None, quality: int = 95, enhance_sharpness: bool = True, resampling_method: str = 'LANCZOS' ) -> bool: """ 高质量像素级图片缩放,保持清晰度 Args: image_path (str): 图片文件路径 max_height_px (int): 最大高度(像素) max_width_px (Optional[int]): 最大宽度(像素),None表示按比例缩放 quality (int): 保存质量 (1-100) enhance_sharpness (bool): 是否进行锐化增强 resampling_method (str): 重采样方法 ('LANCZOS', 'BICUBIC', 'HAMMING') Returns: bool: 缩放是否成功 """ try: if not os.path.exists(image_path): print(f"❌ 错误: 文件不存在 {image_path}") return False # 重采样方法映射 resampling_map = { 'LANCZOS': Resampling.LANCZOS, # 最高质量,适合缩小 'BICUBIC': Resampling.BICUBIC, # 高质量,适合缩放 'HAMMING': Resampling.HAMMING, # 高质量,适合缩小 'BILINEAR': Resampling.BILINEAR, # 中等质量,速度快 } resampling_filter = resampling_map.get(resampling_method.upper(), Resampling.LANCZOS) with Image.open(image_path) as img: original_width, original_height = img.size print("📏 原始图片信息:") print(f" 像素尺寸: {original_width}x{original_height}px") print(f" 图片模式: {img.mode}") # 检查是否需要缩放 need_resize = False if original_height > max_height_px: need_resize = True target_height = max_height_px else: target_height = original_height if max_width_px and original_width > max_width_px: need_resize = True target_width = max_width_px else: # 按比例计算宽度 if need_resize and original_height > max_height_px: scale_ratio = max_height_px / original_height target_width = int(original_width * scale_ratio) else: target_width = original_width # 如果设置了最大宽度,再次检查 if max_width_px and target_width > max_width_px: scale_ratio = max_width_px / target_width target_width = max_width_px target_height = int(target_height * scale_ratio) if not need_resize: print(f"✅ 图片尺寸 {original_width}x{original_height}px 符合要求,无需调整") return True print("🔧 高质量像素缩放:") print(f" 目标尺寸: {target_width}x{target_height}px") print(f" 缩放比例: {target_width/original_width:.3f}x{target_height/original_height:.3f}") print(f" 重采样方法: {resampling_method}") print(f" 锐化增强: {'启用' if enhance_sharpness else '禁用'}") # 执行高质量缩放 resized_img = img.resize((target_width, target_height), resampling_filter) # 可选的锐化增强 if enhance_sharpness and (target_width < original_width or target_height < original_height): enhancer = ImageEnhance.Sharpness(resized_img) # 轻微锐化,避免过度锐化 sharpness_factor = 1.1 + (0.3 * min(original_width/target_width, original_height/target_height) - 0.3) sharpness_factor = max(1.0, min(1.5, sharpness_factor)) # 限制在1.0-1.5范围 resized_img = enhancer.enhance(sharpness_factor) print(f" 锐化系数: {sharpness_factor:.2f}") # 保存图片,保持原有元数据 save_kwargs = { 'optimize': True, 'quality': quality, } # 保持DPI信息 if 'dpi' in img.info: save_kwargs['dpi'] = img.info['dpi'] # 对于PNG保持透明度 if img.format == 'PNG' and img.mode in ('RGBA', 'LA'): save_kwargs['optimize'] = False # PNG优化可能影响透明度 resized_img.save(image_path, **save_kwargs) print(f"✅ 高质量像素缩放完成: {image_path}") print(f" 最终尺寸: {target_width}x{target_height}px") return True except Exception as e: print(f"❌ 图片缩放失败: {str(e)}") return False def resize_image_height_dpi(image_path, max_height_cm=23.0, dpi=96): """ 通过调整DPI元数据控制图片打印高度,限制在指定厘米范围内 Args: image_path (str): 图片文件路径 max_height_cm (float): 最大高度(厘米) dpi (int): 默认DPI设置,用于参考 Returns: bool: 调整是否成功 """ try: # 检查文件是否存在 if not os.path.exists(image_path): print(f"❌ 错误: 文件不存在 {image_path}") return False # 打开图片 with Image.open(image_path) as img: original_width, original_height = img.size # 获取当前DPI(如果存在) current_dpi = img.info.get("dpi", (dpi, dpi)) if isinstance(current_dpi, (list, tuple)): current_dpi_value = current_dpi[0] else: current_dpi_value = current_dpi # 计算当前图片的物理高度(厘米) current_height_cm = original_height / current_dpi_value * 2.54 print("📏 图片信息:") print(f" 像素尺寸: {original_width}x{original_height}px") print(f" 当前DPI: {current_dpi_value}") print(f" 当前打印高度: {current_height_cm:.2f}cm") # 检查是否需要调整 if current_height_cm <= max_height_cm: print(f"✅ 图片打印高度 {current_height_cm:.2f}cm 符合要求,无需调整") return True # 计算新的DPI以满足高度要求 # 新DPI = 原始像素高度 / (目标高度厘米 / 2.54) required_dpi = original_height / (max_height_cm / 2.54) print("🔧 调整DPI元数据:") print(f" 原始DPI: {current_dpi_value}") print(f" 调整后DPI: {required_dpi:.0f}") print(f" 目标打印高度: {max_height_cm}cm") print(f" 像素尺寸保持不变: {original_width}x{original_height}px") # 创建新图片并设置DPI元数据 new_img = img.copy() # 保存图片时设置新的DPI new_img.save( image_path, dpi=(required_dpi, required_dpi), optimize=True, quality=95 ) print(f"✅ 图片DPI元数据调整完成: {image_path}") print(f" 现在图片将以 {max_height_cm}cm 高度打印") return True except Exception as e: print(f"❌ 图片处理失败: {str(e)}") return False def smart_resize_image( image_path: str, method: str = 'dpi', max_height_cm: float = 23.0, max_height_px: int = 870, max_width_px: Optional[int] = None, quality: int = 95, dpi: int = 96, resampling_method: str = 'LANCZOS', enhance_sharpness: bool = True ) -> bool: """ 智能图片缩放,根据方法选择最佳缩放策略 Args: image_path (str): 图片文件路径 method (str): 缩放方法 ('dpi', 'pixel', 'auto') max_height_cm (float): DPI方法的最大高度(厘米) max_height_px (int): 像素方法的最大高度(像素) max_width_px (Optional[int]): 像素方法的最大宽度(像素) quality (int): 保存质量 dpi (int): DPI设置 resampling_method (str): 重采样方法 enhance_sharpness (bool): 是否启用锐化增强 Returns: bool: 处理是否成功 """ try: if method == 'dpi': print("🎯 使用DPI元数据调整方法(保持像素数据不变)") return resize_image_height_dpi(image_path, max_height_cm, dpi) elif method == 'pixel': print("🎯 使用高质量像素缩放方法") return resize_image_pixels_high_quality( image_path, max_height_px, max_width_px, quality, enhance_sharpness, resampling_method ) elif method == 'auto': print("🎯 使用智能自动选择方法") # 先尝试DPI方法,如果不够则使用像素缩放 success_dpi = resize_image_height_dpi(image_path, max_height_cm, dpi) if success_dpi: # 检查DPI调整后的效果 with Image.open(image_path) as img: width, height = img.size current_dpi = img.info.get("dpi", (dpi, dpi)) if isinstance(current_dpi, (list, tuple)): current_dpi_value = current_dpi[0] else: current_dpi_value = current_dpi # 计算实际显示高度(像素) display_height_px = height * 96 / current_dpi_value if display_height_px > max_height_px: print("⚠️ DPI调整后仍然过大,继续进行像素缩放") return resize_image_pixels_high_quality( image_path, max_height_px, max_width_px, quality, enhance_sharpness, resampling_method ) return success_dpi else: print(f"❌ 错误: 不支持的缩放方法 '{method}'") return False except Exception as e: print(f"❌ 智能缩放失败: {str(e)}") return False def main(): """主函数""" parser = argparse.ArgumentParser( description="高质量图片尺寸调整工具,保持清晰度", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 缩放方法: dpi - DPI元数据调整(默认,保持像素数据不变) pixel - 高质量像素缩放(实际改变图片尺寸) auto - 智能自动选择(先DPI后像素) 示例: # DPI调整方法(保持原始像素,推荐) python resize_image.py image.png python resize_image.py image.png --method dpi --max-height-cm 20 # 高质量像素缩放方法 python resize_image.py image.png --method pixel --max-height-px 600 python resize_image.py image.png --method pixel --max-height-px 600 --max-width-px 800 # 智能自动选择方法 python resize_image.py image.png --method auto --quality 98 """, ) parser.add_argument("image_path", help="要处理的图片文件路径") # 缩放方法选择 parser.add_argument( "--method", choices=['dpi', 'pixel', 'auto'], default='dpi', help="缩放方法: dpi(DPI调整), pixel(像素缩放), auto(智能选择). 默认: dpi" ) # DPI相关参数 parser.add_argument( "--max-height-cm", type=float, default=23.0, help="DPI方法的最大高度(厘米),默认23厘米" ) parser.add_argument( "--dpi", type=int, default=96, help="DPI设置,默认96" ) # 像素缩放相关参数 parser.add_argument( "--max-height-px", type=int, default=870, help="像素方法的最大高度(像素),默认870像素" ) parser.add_argument( "--max-width-px", type=int, default=None, help="像素方法的最大宽度(像素),不设置则按比例缩放" ) parser.add_argument( "--quality", type=int, default=95, help="像素缩放的保存质量 (1-100),默认95" ) parser.add_argument( "--resampling", choices=['LANCZOS', 'BICUBIC', 'HAMMING', 'BILINEAR'], default='LANCZOS', help="重采样方法,默认LANCZOS(最高质量)" ) parser.add_argument( "--no-sharpen", action="store_true", help="禁用锐化增强(像素缩放时)" ) parser.add_argument("--verbose", action="store_true", help="显示详细信息") args = parser.parse_args() # 处理图片 if args.verbose: print("🔧 图片处理参数:") print(f" 文件: {args.image_path}") print(f" 方法: {args.method}") if args.method in ['dpi', 'auto']: print(f" 最大高度(cm): {args.max_height_cm}") print(f" DPI: {args.dpi}") if args.method in ['pixel', 'auto']: print(f" 最大高度(px): {args.max_height_px}") print(f" 最大宽度(px): {args.max_width_px or '按比例'}") print(f" 质量: {args.quality}") print(f" 重采样: {args.resampling}") print(f" 锐化: {'禁用' if args.no_sharpen else '启用'}") print() # 使用智能缩放函数 success = smart_resize_image( image_path=args.image_path, method=args.method, max_height_cm=args.max_height_cm, max_height_px=args.max_height_px, max_width_px=args.max_width_px, quality=args.quality, dpi=args.dpi, resampling_method=args.resampling, enhance_sharpness=not args.no_sharpen ) # 返回适当的退出代码 sys.exit(0 if success else 1) if __name__ == "__main__": main()