fujian_water_biz_doc/scripts/resize_image.py

401 lines
14 KiB
Python
Executable File
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
# -*- 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()