import os import pathlib import subprocess from typing import Union import matplotlib import matplotlib.pyplot as plt class Week1: def __init__(self): # 避免覆盖内置名 list self.qualities = [2, 7, 12, 17, 22, 27, 31] def _ffmpeg_path(self) -> pathlib.Path: """ 返回项目 bin 目录下 ffmpeg 的可执行路径。 Week1/Week1.py 位于项目的 Week1 目录内,bin 与 Week1 处于同级。 """ here = pathlib.Path(__file__).resolve() project_root = here.parent.parent # /Week1/Week1.py -> bin_dir = project_root / "bin" exe_name = "ffmpeg.exe" if os.name == "nt" else "ffmpeg" ffmpeg = bin_dir / exe_name if not ffmpeg.exists(): raise FileNotFoundError(f"未找到 ffmpeg 可执行文件: {ffmpeg}") return ffmpeg def compress(self, image_src: Union[str, pathlib.Path]): """ 使用不同 q:v 质量参数将 PNG 转为 JPEG。 输出文件与输入文件位于同目录,命名为 <原名>_.jpg """ image_path = pathlib.Path(image_src).resolve() if not image_path.exists(): raise FileNotFoundError(f"输入图像不存在: {image_path}") ffmpeg = self._ffmpeg_path() stem = image_path.stem parent = image_path.parent for q in self.qualities: out_path = parent / f"{stem}_{q}.jpg" # -y 覆盖已存在文件;参数以列表方式传入 cmd = [ str(ffmpeg), "-y", "-i", str(image_path), "-q:v", str(q), str(out_path), ] # 失败时抛异常,便于定位问题 subprocess.run(cmd, check=True) def render(self, image_src: Union[str, pathlib.Path]): """ 读取生成的 JPEG 文件大小,并绘制质量(q) vs 文件大小(bytes) 的折线图。 """ image_path = pathlib.Path(image_src).resolve() stem = image_path.stem parent = image_path.parent sizes = [] qualities = [] for q in self.qualities: jpg_path = parent / f"{stem}_{q}.jpg" if not jpg_path.exists(): # 若缺失,给出清晰异常,提示先运行 compress raise FileNotFoundError(f"缺少压缩输出文件,请先运行 compress: {jpg_path}") qualities.append(q) sizes.append(os.path.getsize(jpg_path)/8) # 使用 pyplot 正确保存图像 plt.figure(figsize=(6, 4)) plt.plot(qualities, sizes, marker="o") plt.xlabel("JPEG quality (q:v)") plt.ylabel("File size (Kbytes)") plt.title("Quality vs File Size") plt.grid(True) project_root = pathlib.Path(__file__).resolve().parent.parent out_png = project_root / "data" / "render.png" out_png.parent.mkdir(parents=True, exist_ok=True) plt.tight_layout() plt.savefig(str(out_png)) plt.close()