from PIL import Image import os from config import Config from logger import logger class ImageProcessor: # 格式映射:将常见的小写格式名转换为 Pillow 可识别的格式名 FORMAT_MAP = { 'jpg': 'JPEG', 'jpeg': 'JPEG', 'png': 'PNG', 'webp': 'WEBP', 'gif': 'GIF', 'bmp': 'BMP', 'tiff': 'TIFF', } @staticmethod def resize_by_ratio(img, ratio_width, ratio_height): """等比例缩放至目标比例(最长边适配)""" w, h = img.size target_ratio = ratio_width / ratio_height current_ratio = w / h if current_ratio > target_ratio: new_w = int(h * target_ratio) new_h = h else: new_w = w new_h = int(w / target_ratio) return img.resize((new_w, new_h), Image.Resampling.LANCZOS) @staticmethod def resize_by_pixel(img, target_width, target_height): """等比例缩放至不超过目标像素(保持比例,以较长边为准)""" w, h = img.size ratio_w = target_width / w ratio_h = target_height / h ratio = min(ratio_w, ratio_h) new_w = int(w * ratio) new_h = int(h * ratio) return img.resize((new_w, new_h), Image.Resampling.LANCZOS) @staticmethod def convert_image(input_path, output_path, target_format, quality=None): """ 转换图片格式 target_format: 小写字符串,如 'jpg', 'png', 'webp' """ try: with Image.open(input_path) as img: # 获取 Pillow 标准格式名 pillow_format = ImageProcessor.FORMAT_MAP.get(target_format.lower()) if pillow_format is None: logger.error(f"不支持的输出格式: {target_format}") return False # 对于 JPEG 格式,需要处理透明通道 if pillow_format == 'JPEG' and img.mode in ('RGBA', 'P'): rgb_img = Image.new('RGB', img.size, (255, 255, 255)) rgb_img.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None) img = rgb_img save_kwargs = {} if pillow_format in ('JPEG', 'WEBP'): save_kwargs['quality'] = quality if quality is not None else Config.DEFAULT_QUALITY elif pillow_format == 'PNG': save_kwargs['compress_level'] = 6 img.save(output_path, format=pillow_format, **save_kwargs) logger.info(f"转换成功: {input_path} -> {output_path}") return True except Exception as e: logger.error(f"转换失败 {input_path}: {e}") return False