Newer
Older
ss2502 / R1 / r1.py
@刘老老 刘老老 on 16 Oct 10 KB .gitignore setting
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()