概述
这个Python脚本旨在处理图片文件,将它们转换为WebP格式,组织到特定的文件夹中,并保留它们的元数据。它还处理文件重命名,以去除空格,并确保在转换后的文件中保留原始图片的元数据。
使用说明
要使用此脚本,请在命令行中运行并提供以下参数:
input_dir
: 包含要处理的图片文件的目录路径。output_dir
: 转换后的图片将被保存的目录路径。--ext
: (可选) 要处理的图片文件扩展名列表。默认为["all"]
。--subdir
: (可选) 一个标志,表示是否要在子目录中递归搜索图片。默认为False
。
示例命令:
python image_processing_script.py /path/to/input /path/to/output --ext jpg,png --subdir
功能特点
- 使用
cwebp
命令将图片转换为WebP格式。 - 根据转换选项将转换后的图片组织到不同的文件夹中。
- 重命名文件以去除空格。
- 使用
exiftool
保留原始图片的元数据到转换后的文件中。 - 提供带有时间戳和日志级别的日志信息。
函数说明
process_single_image
: 转换单个图片并将其Markdown链接写入文件。process_images
: 使用指定的扩展名处理输入目录中的所有图片文件。expand_image_extensions
: 扩展图片扩展名列表以包含常见变体。find_image_files
: 在给定目录中查找具有指定扩展名的所有图片文件。create_output_directories
: 在基础目录中创建必要的输出目录。convert_image_with_cwebp
: 使用cwebp
转换图片并保存到输出目录。build_converted_path
: 构建转换后图片文件的路径。
运行要求
- Python 3.x
- 必须安装并配置
cwebp
命令行工具,并在系统PATH中可用。 - 必须安装并配置
exiftool
命令行工具,并在系统PATH中可用。
注意事项
- 确保您有权限在指定目录中读取和写入文件。
- 在运行脚本之前,请始终备份您的原始图片以防止数据丢失。
- 脚本假设
cwebp
和exiftool
工具已正确安装并配置在您的系统上。
代码
import argparse
import subprocess
from pathlib import Path
import logging
# Enable logger, print to console with timestamp and log level
logging.basicConfig(
level=logging.INFO, format="\n%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
def process_single_image(
image_path: Path,
output_dir: Path,
) -> None:
"""
Processes a single image file: converts it, uploads it, and writes the markdown link to a file.
Args:
image_path (Path): The path to the input image file.
output_dir (Path): The directory where the converted images will be saved.
Returns:
None
"""
folder_options = [
# ("Lossless", ["-z", "8", "-mt"]),
# ("6MB", ["-size", "6291456", "-pass", "10", "-qrange", "80", "100", "-mt"]),
("Web", ["-resize", "1280", "0", "-mt"]),
]
for folder_name, options in folder_options:
folder_path = output_dir / folder_name
create_output_directories(output_dir, folder_name)
convert_image_with_cwebp(image_path, folder_path, options)
def process_images(
input_dir: Path,
output_dir: Path,
extensions: list[str],
subdir: bool,
) -> None:
"""
Processes all image files in the input directory with the specified extensions.
Args:
input_dir (Path): The directory containing the input image files.
output_dir (Path): The directory where the converted images and markdown links will be saved.
extensions (list[str]): A space-separated list of image extensions.
subdir (bool): Whether to search recursively in subdirectories.
md_links_file_name (str): The name of the file where the markdown links will be written.
"""
image_files = find_image_files(input_dir, extensions, subdir)
for image_path in image_files:
image_path.rename(image_path.parent / image_path.name.replace(" ", "-"))
process_single_image(image_path, output_dir)
def expand_image_extensions(extensions: list[str]) -> list[str]:
"""
Expands a comma-separated list of image extensions to include common variants.
Args:
extensions (list[str]): A list of image extensions.
Returns:
list[str]: A list of expanded image extensions.
"""
supported_extensions = ["jpg", "jpeg", "png", "tif", "tiff", "webp"]
expanded_extensions = []
for ext in [s.lower() for s in extensions]:
if ext == "all":
expanded_extensions = supported_extensions
break
elif ext in ["jpg", "jpeg"]:
expanded_extensions.extend(["jpg", "jpeg"])
elif ext in ["tif", "tiff"]:
expanded_extensions.extend(["tif", "tiff"])
else:
expanded_extensions.append(ext)
return [s.lower() for s in expanded_extensions] + [
s.upper() for s in expanded_extensions
]
def find_image_files(
directory: Path, extensions: list[str], subdir: bool
) -> list[Path]:
"""
Finds all image files in the given directory with the specified extensions.
Args:
directory (Path): The directory to search for image files.
extensions (list[str]): A list of image file extensions.
subdir (bool): Whether to search recursively in subdirectories.
Returns:
list[Path]: A list of paths to the found image files.
"""
return sorted(
[
file
for ext in extensions
for file in directory.rglob(f"*.{ext}")
if not file.name.startswith(".")
]
if subdir
else [
file
for ext in extensions
for file in directory.glob(f"*.{ext}")
if not file.name.startswith(".")
]
)
def create_output_directories(base_dir: Path, *folder_names: str) -> None:
"""
Creates the necessary output directories within the base directory.
Args:
base_dir (Path): The base directory where the output directories will be created.
*folder_names (str): Variable number of folder names to create.
"""
for folder_name in folder_names:
Path(base_dir, folder_name).mkdir(parents=True, exist_ok=True)
def convert_image_with_cwebp(input_path: Path, output_dir: Path, options: list) -> None:
"""
Converts an image using cwebp and saves it to the output directory.
Args:
input_path (Path): The path to the input image file.
output_dir (Path): The directory where the converted image will be saved.
options (list): A list of options to pass to the cwebp command.
"""
output_path = build_converted_path(input_path, output_dir)
cweb_cmd = [
"cwebp",
*options,
"-metadata",
"all",
str(input_path),
"-o",
str(output_path),
]
try:
subprocess.run(cweb_cmd, check=True)
subprocess.run(
[
"exiftool",
"-TagsFromFile",
str(input_path),
"-overwrite_original",
str(output_path),
],
check=True,
)
except subprocess.CalledProcessError as e:
logger.error(f"Failed to convert {input_path}: {e}")
def build_converted_path(input_path: Path, output_dir: Path) -> Path:
"""
Builds the path for the converted image file.
Args:
input_path (Path): The path to the input image file.
output_dir (Path): The directory where the converted image will be saved.
Returns:
Path: The path to the converted image file.
"""
return output_dir / (input_path.stem + ".webp")
def main() -> None:
"""
The main function that parses command line arguments and processes images.
"""
parser = argparse.ArgumentParser(description="Convert and upload images.")
parser.add_argument("input_dir", type=str, help="Input directory path")
parser.add_argument("output_dir", type=str, help="Output directory path")
parser.add_argument(
"--ext",
nargs="+",
default=["all"],
help="List of input image extensions",
)
parser.add_argument(
"--subdir",
action="store_false",
help="Search for images recursively in subdirectories",
)
args = parser.parse_args()
input_dir = Path(args.input_dir)
output_dir = Path(args.output_dir)
process_images(
input_dir, output_dir, expand_image_extensions(args.ext), args.subdir
)
if __name__ == "__main__":
main()