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