import argparse
import numpy as np
from PIL import Image
import os
import sys
def load_image(image_path):
"""
加载图像文件并转换为numpy数组
Args:
image_path (str): 输入图像路径
Returns:
numpy.ndarray: 图像数据 (H, W, C)
Raises:
FileNotFoundError: 当图像文件不存在时
ValueError: 当图像格式不支持时
"""
try:
# 检查文件是否存在
if not os.path.exists(image_path):
raise FileNotFoundError(f"图像文件不存在: {image_path}")
image = Image.open(image_path)
image_array = np.array(image)
print(f"✅ 成功加载图像: {image_path}")
print(f"📐 原始图像尺寸: {image_array.shape} (高度, 宽度, 通道数)")
return image_array
except FileNotFoundError as e:
raise e
except Exception as e:
raise ValueError(f"❌ 无法加载图像 '{image_path}': {e}")
def save_image(image_array, output_path):
"""
将numpy数组保存为图像文件
Args:
image_array (numpy.ndarray): 图像数据
output_path (str): 输出图像路径
"""
try:
# 确保输出目录存在
os.makedirs(os.path.dirname(output_path) if os.path.dirname(output_path) else '.', exist_ok=True)
image = Image.fromarray(image_array.astype(np.uint8))
image.save(output_path)
print(f"✅ 成功保存图像: {output_path}")
print(f"📐 输出图像尺寸: {image_array.shape}")
except Exception as e:
raise ValueError(f"❌ 无法保存图像 '{output_path}': {e}")
def process_image(input_array, output_size, anchor_point):
"""
核心处理函数:根据输出尺寸和基准点进行裁剪或填充
Args:
input_array (numpy.ndarray): 输入图像数组 (H, W, C)
output_size (int): 输出图像尺寸(正方形)
anchor_point (int): 基准点 (0-8)
Returns:
numpy.ndarray: 处理后的图像数组
Raises:
ValueError: 当参数无效时
"""
# 输入验证
if len(input_array.shape) != 3 or input_array.shape[2] not in [3, 4]:
raise ValueError("❌ 输入必须是3通道(RGB)或4通道(RGBA)彩色图像")
if output_size <= 0:
raise ValueError("❌ 输出尺寸必须为正整数")
if anchor_point not in range(0, 9):
raise ValueError("❌ 基准点必须在0-8范围内")
input_height, input_width, channels = input_array.shape
print(f"📥 输入尺寸: {input_width}x{input_height}, 通道数: {channels}")
print(f"🎯 目标尺寸: {output_size}x{output_size}, 基准点: {anchor_point}")
# 创建输出数组(初始为黑色)
output_array = np.zeros((output_size, output_size, channels), dtype=input_array.dtype)
# 计算裁剪或填充的区域
crop_height = min(input_height, output_size)
crop_width = min(input_width, output_size)
print(f"✂️ 实际复制区域: {crop_width}x{crop_height}")
# 根据基准点计算源和目标坐标
src_x, src_y, dst_x, dst_y = calculate_coordinates(
input_width, input_height, output_size, crop_width, crop_height, anchor_point
)
# 执行裁剪或填充操作
output_array[dst_y:dst_y + crop_height, dst_x:dst_x + crop_width] = \
input_array[src_y:src_y + crop_height, src_x:src_x + crop_width]
# 判断是裁剪还是填充
if output_size > input_width or output_size > input_height:
operation = "填充(Padding)"
elif output_size < input_width or output_size < input_height:
operation = "裁剪(Cropping)"
else:
operation = "尺寸不变"
print(f"🔄 操作类型: {operation}")
return output_array
def calculate_coordinates(input_width, input_height, output_size, crop_width, crop_height, anchor_point):
"""
根据基准点计算源图像和目标图像的坐标
Args:
input_width (int): 输入图像宽度
input_height (int): 输入图像高度
output_size (int): 输出图像尺寸
crop_width (int): 裁剪宽度
crop_height (int): 裁剪高度
anchor_point (int): 基准点
Returns:
tuple: (src_x, src_y, dst_x, dst_y)
"""
# 基准点映射说明:
anchor_names = {
0: "左上(Top-Left)", 1: "中上(Top-Center)", 2: "右上(Top-Right)",
3: "左中(Middle-Left)", 4: "中心(Center)", 5: "右中(Middle-Right)",
6: "左下(Bottom-Left)", 7: "中下(Bottom-Center)", 8: "右下(Bottom-Right)"
}
print(f"📍 基准点: {anchor_point} - {anchor_names[anchor_point]}")
# 源图像起始坐标(从输入图像中裁剪的部分)
if anchor_point in [0, 3, 6]: # 左对齐
src_x = 0
elif anchor_point in [1, 4, 7]: # 水平居中
src_x = (input_width - crop_width) // 2
else: # 右对齐 [2, 5, 8]
src_x = input_width - crop_width
if anchor_point in [0, 1, 2]: # 上对齐
src_y = 0
elif anchor_point in [3, 4, 5]: # 垂直居中
src_y = (input_height - crop_height) // 2
else: # 下对齐 [6, 7, 8]
src_y = input_height - crop_height
# 目标图像起始坐标(在输出图像中放置的位置)
if anchor_point in [0, 3, 6]: # 左对齐
dst_x = 0
elif anchor_point in [1, 4, 7]: # 水平居中
dst_x = (output_size - crop_width) // 2
else: # 右对齐 [2, 5, 8]
dst_x = output_size - crop_width
if anchor_point in [0, 1, 2]: # 上对齐
dst_y = 0
elif anchor_point in [3, 4, 5]: # 垂直居中
dst_y = (output_size - crop_height) // 2
else: # 下对齐 [6, 7, 8]
dst_y = output_size - crop_height
print(f"🎯 源坐标: ({src_x}, {src_y}), 目标坐标: ({dst_x}, {dst_y})")
return src_x, src_y, dst_x, dst_y
def setup_argument_parser():
"""
设置命令行参数解析器
Returns:
argparse.ArgumentParser: 配置好的参数解析器
"""
parser = argparse.ArgumentParser(
description="🎨 彩色图像裁剪与填充处理器",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
📋 基准点选项 (0-8):
0: 左上(Top-Left) 1: 中上(Top-Center) 2: 右上(Top-Right)
3: 左中(Middle-Left) 4: 中心(Center) 5: 右中(Middle-Right)
6: 左下(Bottom-Left) 7: 中下(Bottom-Center) 8: 右下(Bottom-Right)
💡 使用示例:
python image_processor.py input.jpg output.jpg 512 4
python image_processor.py photo.png result.png 256 0
🚀 完整演示:
python image_processor.py "/Users/liulaolao/SkillSemi2025/pepper.png" "output_512_center.jpg" 512 4
"""
)
parser.add_argument(
"input_image",
help="📥 输入图像文件路径"
)
parser.add_argument(
"output_image",
help="📤 输出图像文件路径"
)
parser.add_argument(
"output_size",
type=int,
help="📐 输出图像尺寸(正方形边长)"
)
parser.add_argument(
"anchor_point",
type=int,
choices=range(0, 9), # 0-8
help="📍 裁剪/填充基准点 (0-8)"
)
return parser
def demo_with_fixed_path():
"""
使用固定路径进行演示的函数
"""
print("🚀 开始使用固定路径演示图像处理...")
# 固定参数设置
input_path = "/Users/liulaolao/SkillSemi2025/pepper.png"
output_sizes = [300, 512, 800] # 测试不同尺寸
anchor_points = [0, 4, 8] # 测试不同基准点:左上、中心、右下
try:
# 加载图像
original_image = load_image(input_path)
original_height, original_width, _ = original_image.shape
print(f"\n📊 原始图像信息:")
print(f" 宽度: {original_width}px")
print(f" 高度: {original_height}px")
for size in output_sizes:
for anchor in anchor_points:
print(f"\n{'=' * 60}")
print(f"🔄 处理中: 尺寸={size}, 基准点={anchor}")
print(f"{'=' * 60}")
# 处理图像
processed_image = process_image(original_image, size, anchor)
# 生成输出文件名
output_filename = f"pepper_{size}px_anchor{anchor}.jpg"
output_path = os.path.join("/Users/liulaolao/SkillSemi2025/", output_filename)
# 保存结果
save_image(processed_image, output_path)
print(f"\n🎉 所有演示完成!生成的图像已保存到 SkillSemi2025 目录")
except Exception as e:
print(f"❌ 演示过程中发生错误: {e}")
return False
return True
def main():
"""主函数"""
try:
# 设置参数解析器
parser = setup_argument_parser()
# 检查是否有命令行参数
if len(sys.argv) == 1:
# 没有参数时,运行固定路径演示
print("🎮 未提供命令行参数,运行固定路径演示模式...")
demo_with_fixed_path()
else:
# 有参数时,正常解析并运行
args = parser.parse_args()
print("=" * 60)
print("🎨 彩色图像裁剪与填充处理器")
print("=" * 60)
# 显示参数信息
print(f"📥 输入文件: {args.input_image}")
print(f"📤 输出文件: {args.output_image}")
print(f"📐 输出尺寸: {args.output_size}")
print(f"📍 基准点: {args.anchor_point}")
print("-" * 60)
# 加载图像
input_array = load_image(args.input_image)
# 处理图像
output_array = process_image(input_array, args.output_size, args.anchor_point)
# 保存结果
save_image(output_array, args.output_image)
print("-" * 60)
print("✅ 处理完成!")
print("=" * 60)
except FileNotFoundError as e:
print(f"❌ 文件错误: {e}")
sys.exit(1)
except ValueError as e:
print(f"❌ 参数错误: {e}")
sys.exit(1)
except KeyboardInterrupt:
print("\n⏹️ 程序被用户中断")
sys.exit(1)
except Exception as e:
print(f"❌ 未知错误: {e}")
sys.exit(1)
if __name__ == "__main__":
main()