Skill: hgta23/picture-edit (version 1.0.0)

name: picture-edit version: 1.0.0 description: 创意图片处理技能包，以"P一下图片"为核心理念，提供图片基础操作、增强、滤镜、拼接、背景移除、文字添加等强大功能 *** # picture-edit 创意图片处理技能包 🎨 强大的图片处理工具包，让你轻松 "P一下图片"！p 字母代表 Power（强力）、Productivity（生产力）、Precision（精准）、Playful（有趣）。 ## 功能特性 ### 核心基础操作 - **图片加载与保存**：支持 JPEG、PNG、WebP 等常见格式 - **尺寸调整**：按比例缩放或指定尺寸，保持图片质量 - **图片裁剪**：精确裁剪指定区域 - **格式转换**：多种图片格式互转 ### 智能图片增强 - **亮度调整**：自由调节图片明暗 - **对比度调整**：增强画面层次感 - **清晰度增强**：让图片更锐利 - **智能增强**：一键优化，自动调整各项参数 ### 创意滤镜特效 - **黑白滤镜**：经典黑白效果 - **复古滤镜**：怀旧 sepia 色调 - **模糊滤镜**：柔化画面，支持自定义模糊半径 - **轮廓滤镜**：提取边缘，艺术效果 - **反色滤镜**：颜色反转 - **像素化滤镜**：马赛克效果，支持自定义像素大小 ### 图片拼接合成 - **水平拼接**：多张图片横向无缝拼接 - **垂直拼接**：多张图片纵向无缝拼接 - **网格拼接**：指定行列数，智能排列 - **智能对齐**：支持多种对齐方式（居中、左/右、上/下） ### 背景移除功能 - **简单背景移除**：自动检测白色/浅色背景并移除 - **指定颜色移除**：自定义背景颜色，支持容差调节 - **边缘平滑**：高斯模糊柔化边缘，更自然 ### 文字添加功能 - **多种位置预设**：9种预设位置（左上、上中、右上、左中、居中、右中、左下、下中、右下） - **自定义坐标**：精确 x,y 定位 - **字体样式**：支持自定义字体、字号、颜色 - **旋转效果**：任意角度旋转文字 - **中文字体**：自动检测并使用系统中文字体 ## 快速开始 ### 安装 ```bash pip install -r requirements.txt ``` ### 基础使用示例 ```python from p_skill import ( load_image, save_image, resize_image, crop_image, smart_enhance, grayscale, sepia, stitch_horizontal, remove_background_simple, add_text ) # 加载图片 image = load_image("input.jpg") # 调整尺寸 resized = resize_image(image, scale=0.5) # 智能增强 enhanced = smart_enhance(resized) # 添加复古滤镜 filtered = sepia(enhanced) # 添加文字 with_text = add_text(filtered, "Hello p-skill!", position="bottom-center") # 保存结果 save_image(with_text, "output.jpg") ``` ## API 文档 ### 核心模块 (core) #### load\_image(file\_path: str) -> Image.Image 加载图片文件。 **参数:** - `file_path`: 图片文件路径 **返回:** PIL Image 对象 #### save\_image(image: Image.Image, output\_path: str, quality: int = 95) -> None 保存图片到文件。 **参数:** - `image`: PIL Image 对象 - `output_path`: 输出文件路径 - `quality`: 图片质量 (1-100)，默认 95 #### resize\_image(image: Image.Image, size: tuple\[int, int] = None, scale: float = None) -> Image.Image 调整图片尺寸。 **参数:** - `image`: PIL Image 对象 - `size`: 目标尺寸 (width, height) - `scale`: 缩放比例 **返回:** 调整后的图片 #### crop\_image(image: Image.Image, box: tuple\[int, int, int, int]) -> Image.Image 裁剪图片。 **参数:** - `image`: PIL Image 对象 - `box`: 裁剪区域 (left, upper, right, lower) **返回:** 裁剪后的图片 #### convert\_format(image: Image.Image, format: str) -> Image.Image 转换图片格式。 **参数:** - `image`: PIL Image 对象 - `format`: 目标格式 ('JPEG', 'PNG', 'WEBP' 等) **返回:** 格式转换后的图片 ### 增强模块 (enhance) #### adjust\_brightness(image: Image.Image, factor: float) -> Image.Image 调整图片亮度。 **参数:** - `image`: PIL Image 对象 - `factor`: 亮度因子 (1.0 为原图，<1.0 变暗，>1.0 变亮) #### adjust\_contrast(image: Image.Image, factor: float) -> Image.Image 调整图片对比度。 **参数:** - `image`: PIL Image 对象 - `factor`: 对比度因子 (1.0 为原图) #### adjust\_sharpness(image: Image.Image, factor: float) -> Image.Image 调整图片清晰度。 **参数:** - `image`: PIL Image 对象 - `factor`: 清晰度因子 (1.0 为原图，>1.0 更锐利) #### smart\_enhance(image: Image.Image, brightness\_factor: float = 1.1, contrast\_factor: float = 1.1, sharpness\_factor: float = 1.2) -> Image.Image 智能增强，一键优化图片。 ### 滤镜模块 (filters) #### grayscale(image: Image.Image) -> Image.Image 黑白滤镜。 #### sepia(image: Image.Image) -> Image.Image 复古滤镜。 #### blur(image: Image.Image, radius: int = 2) -> Image.Image 模糊滤镜。 **参数:** - `radius`: 模糊半径，默认 2 #### contour(image: Image.Image) -> Image.Image 轮廓滤镜。 #### invert(image: Image.Image) -> Image.Image 反色滤镜。 #### pixelate(image: Image.Image, pixel\_size: int = 10) -> Image.Image 像素化滤镜。 **参数:** - `pixel_size`: 像素块大小，默认 10 ### 拼接模块 (composite) #### stitch\_horizontal(images: list\[Image.Image], align: str = 'center') -> Image.Image 水平拼接多张图片。 **参数:** - `images`: 图片列表 - `align`: 对齐方式 ('top', 'bottom', 'center') #### stitch\_vertical(images: list\[Image.Image], align: str = 'center') -> Image.Image 垂直拼接多张图片。 **参数:** - `images`: 图片列表 - `align`: 对齐方式 ('left', 'right', 'center') #### stitch\_grid(images: list\[Image.Image], cols: int, rows: int = None, align: str = 'center') -> Image.Image 网格拼接多张图片。 **参数:** - `images`: 图片列表 - `cols`: 列数 - `rows`: 行数（可选，自动计算） - `align`: 对齐方式 ### 背景移除模块 (background) #### remove\_background\_simple(image: Image.Image, threshold: int = 240, smooth\_radius: int = 2) -> Image.Image 简单背景移除（适用于白色/浅色背景）。 **参数:** - `threshold`: 颜色阈值 (0-255)，默认 240 - `smooth_radius`: 边缘平滑半径，默认 2 **返回:** 透明背景的 RGBA 图片 #### remove\_background\_color(image: Image.Image, target\_color: tuple\[int, int, int], tolerance: int = 30, smooth\_radius: int = 2) -> Image.Image 指定颜色背景移除。 **参数:** - `target_color`: 目标背景颜色 (R, G, B) - `tolerance`: 颜色容差，默认 30 - `smooth_radius`: 边缘平滑半径，默认 2 **返回:** 透明背景的 RGBA 图片 ### 文字添加模块 (text) #### add\_text(image: Image.Image, text: str, position: str | tuple\[int, int] = 'center', font\_size: int = 36, color: tuple\[int, int, int] | tuple\[int, int, int, int] = (0, 0, 0), font\_path: str = None, rotation: float = 0, offset: tuple\[int, int] = (0, 0)) -> Image.Image 向图片添加文字。 **参数:** - `image`: PIL Image 对象 - `text`: 要添加的文字内容 - `position`: 位置 ('top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right') 或 (x, y) 坐标 - `font_size`: 字体大小，默认 36 - `color`: 文字颜色 (R, G, B) 或 (R, G, B, A) - `font_path`: 字体文件路径（可选，使用系统默认） - `rotation`: 旋转角度，默认 0 - `offset`: 相对于位置的偏移 (x, y)，默认 (0, 0) **返回:** 添加文字后的图片 ## 性能说明 - 单张 1080p 图片处理时间通常 < 5 秒 - 支持最大分辨率取决于系统内存 - 推荐使用 PNG 格式保存需要透明背景的图片 ## 依赖 - Pillow >= 10.0.0

path: example.py
content: #!/usr/bin/env python3
"""
p-skill 综合使用示例
演示所有功能的使用方法
"""

import os
from PIL import Image
from p_skill import (
    load_image, save_image,
    resize_image, crop_image, convert_format,
    adjust_brightness, adjust_contrast, adjust_sharpness, smart_enhance,
    grayscale, sepia, blur, contour, invert, pixelate,
    stitch_horizontal, stitch_vertical, stitch_grid,
    remove_background_simple, remove_background_color,
    add_text
)


def create_sample_images():
    """创建示例图片用于演示"""
    print("创建示例图片...")
    
    # 创建简单的彩色测试图片
    img1 = Image.new('RGB', (200, 200), color='red')
    img2 = Image.new('RGB', (200, 200), color='green')
    img3 = Image.new('RGB', (200, 200), color='blue')
    img4 = Image.new('RGB', (200, 200), color='yellow')
    
    # 创建白色背景的测试图片（用于背景移除演示）
    img_white_bg = Image.new('RGB', (300, 300), color='white')
    draw = ImageDraw.Draw(img_white_bg)
    draw.ellipse([50, 50, 250, 250], fill='blue')
    
    # 保存示例图片
    os.makedirs('output', exist_ok=True)
    save_image(img1, 'output/sample_red.jpg')
    save_image(img2, 'output/sample_green.jpg')
    save_image(img3, 'output/sample_blue.jpg')
    save_image(img4, 'output/sample_yellow.jpg')
    save_image(img_white_bg, 'output/sample_white_bg.jpg')
    
    return img1, img2, img3, img4, img_white_bg


def demo_core_functions():
    """演示核心功能"""
    print("\n=== 核心功能演示 ===")
    
    # 创建测试图片
    test_img = Image.new('RGB', (400, 300), color='lightblue')
    draw = ImageDraw.Draw(test_img)
    draw.rectangle([50, 50, 350, 250], fill='orange')
    draw.text((100, 140), "Test Image", fill='black')
    
    save_image(test_img, 'output/core_original.jpg')
    
    # 调整尺寸
    resized = resize_image(test_img, scale=0.5)
    save_image(resized, 'output/core_resized.jpg')
    print("✓ 调整尺寸完成")
    
    # 裁剪
    cropped = crop_image(test_img, (50, 50, 350, 250))
    save_image(cropped, 'output/core_cropped.jpg')
    print("✓ 裁剪完成")
    
    # 格式转换
    converted_png = convert_format(test_img, 'PNG')
    save_image(converted_png, 'output/core_converted.png')
    print("✓ 格式转换完成")


def demo_enhance_functions():
    """演示增强功能"""
    print("\n=== 增强功能演示 ===")
    
    # 创建测试图片
    test_img = Image.new('RGB', (400, 300), color='gray')
    draw = ImageDraw.Draw(test_img)
    draw.rectangle([100, 100, 300, 200], fill='darkgray')
    
    # 亮度调整
    bright = adjust_brightness(test_img, 1.5)
    save_image(bright, 'output/enhance_bright.jpg')
    print("✓ 亮度调整完成")
    
    # 对比度调整
    contrast = adjust_contrast(test_img, 1.5)
    save_image(contrast, 'output/enhance_contrast.jpg')
    print("✓ 对比度调整完成")
    
    # 清晰度调整
    sharp = adjust_sharpness(test_img, 2.0)
    save_image(sharp, 'output/enhance_sharp.jpg')
    print("✓ 清晰度调整完成")
    
    # 智能增强
    enhanced = smart_enhance(test_img)
    save_image(enhanced, 'output/enhance_smart.jpg')
    print("✓ 智能增强完成")


def demo_filters():
    """演示滤镜功能"""
    print("\n=== 滤镜功能演示 ===")
    
    # 创建彩色测试图片
    test_img = Image.new('RGB', (400, 300), color='white')
    draw = ImageDraw.Draw(test_img)
    draw.rectangle([50, 50, 150, 250], fill='red')
    draw.rectangle([150, 50, 250, 250], fill='green')
    draw.rectangle([250, 50, 350, 250], fill='blue')
    
    # 黑白滤镜
    gray = grayscale(test_img)
    save_image(gray, 'output/filter_grayscale.jpg')
    print("✓ 黑白滤镜完成")
    
    # 复古滤镜
    sepia_img = sepia(test_img)
    save_image(sepia_img, 'output/filter_sepia.jpg')
    print("✓ 复古滤镜完成")
    
    # 模糊滤镜
    blurred = blur(test_img, radius=10)
    save_image(blurred, 'output/filter_blur.jpg')
    print("✓ 模糊滤镜完成")
    
    # 轮廓滤镜
    contour_img = contour(test_img)
    save_image(contour_img, 'output/filter_contour.jpg')
    print("✓ 轮廓滤镜完成")
    
    # 反色滤镜
    inverted = invert(test_img)
    save_image(inverted, 'output/filter_invert.jpg')
    print("✓ 反色滤镜完成")
    
    # 像素化滤镜
    pixelated = pixelate(test_img, pixel_size=20)
    save_image(pixelated, 'output/filter_pixelate.jpg')
    print("✓ 像素化滤镜完成")


def demo_composite_functions():
    """演示拼接功能"""
    print("\n=== 拼接功能演示 ===")
    
    # 加载示例图片
    img1 = load_image('output/sample_red.jpg')
    img2 = load_image('output/sample_green.jpg')
    img3 = load_image('output/sample_blue.jpg')
    img4 = load_image('output/sample_yellow.jpg')
    
    # 水平拼接
    horizontal = stitch_horizontal([img1, img2, img3])
    save_image(horizontal, 'output/composite_horizontal.jpg')
    print("✓ 水平拼接完成")
    
    # 垂直拼接
    vertical = stitch_vertical([img1, img2, img3])
    save_image(vertical, 'output/composite_vertical.jpg')
    print("✓ 垂直拼接完成")
    
    # 网格拼接
    grid = stitch_grid([img1, img2, img3, img4], cols=2)
    save_image(grid, 'output/composite_grid.jpg')
    print("✓ 网格拼接完成")


def demo_background_functions():
    """演示背景移除功能"""
    print("\n=== 背景移除功能演示 ===")
    
    # 加载白色背景的测试图片
    img_white_bg = load_image('output/sample_white_bg.jpg')
    
    # 简单背景移除
    removed_simple = remove_background_simple(img_white_bg, threshold=240)
    save_image(removed_simple, 'output/background_removed_simple.png')
    print("✓ 简单背景移除完成")
    
    # 指定颜色背景移除
    removed_color = remove_background_color(img_white_bg, target_color=(255, 255, 255), tolerance=50)
    save_image(removed_color, 'output/background_removed_color.png')
    print("✓ 指定颜色背景移除完成")


def demo_text_functions():
    """演示文字添加功能"""
    print("\n=== 文字添加功能演示 ===")
    
    # 创建测试图片
    test_img = Image.new('RGB', (500, 400), color='lightblue')
    
    # 中心位置添加文字
    text_center = add_text(test_img, "Center Text", position="center", font_size=40, color=(0, 0, 128))
    save_image(text_center, 'output/text_center.jpg')
    print("✓ 中心文字添加完成")
    
    # 底部添加文字
    text_bottom = add_text(test_img, "Bottom Text", position="bottom-center", font_size=30, color=(128, 0, 0))
    save_image(text_bottom, 'output/text_bottom.jpg')
    print("✓ 底部文字添加完成")
    
    # 旋转文字
    text_rotated = add_text(test_img, "Rotated Text", position="center", font_size=40, color=(0, 100, 0), rotation=30)
    save_image(text_rotated, 'output/text_rotated.jpg')
    print("✓ 旋转文字添加完成")
    
    # 多个位置添加文字
    img_multi = test_img.copy()
    img_multi = add_text(img_multi, "Top-Left", position="top-left", font_size=20, color=(0, 0, 0))
    img_multi = add_text(img_multi, "Top-Right", position="top-right", font_size=20, color=(0, 0, 0))
    img_multi = add_text(img_multi, "Bottom-Left", position="bottom-left", font_size=20, color=(0, 0, 0))
    img_multi = add_text(img_multi, "Bottom-Right", position="bottom-right", font_size=20, color=(0, 0, 0))
    save_image(img_multi, 'output/text_multi.jpg')
    print("✓ 多位置文字添加完成")


def demo_complete_workflow():
    """演示完整工作流"""
    print("\n=== 完整工作流演示 ===")
    
    # 创建基础图片
    base_img = Image.new('RGB', (600, 400), color='white')
    draw = ImageDraw.Draw(base_img)
    draw.rectangle([100, 100, 500, 300], fill='skyblue')
    draw.ellipse([200, 150, 400, 250], fill='yellow')
    
    # 保存原图
    save_image(base_img, 'output/workflow_original.jpg')
    
    # 完整处理流程
    result = base_img
    result = smart_enhance(result)
    result = sepia(result)
    result = add_text(result, "p-skill Demo", position="top-center", font_size=36, color=(128, 64, 0))
    result = add_text(result, "Creative Image Processing", position="bottom-center", font_size=24, color=(128, 64, 0))
    
    save_image(result, 'output/workflow_final.jpg')
    print("✓ 完整工作流完成")


def main():
    """主函数"""
    print("=" * 50)
    print("p-skill 综合使用示例")
    print("=" * 50)
    
    try:
        # 导入 ImageDraw
        from PIL import ImageDraw
        
        # 创建示例图片
        create_sample_images()
        
        # 演示各个功能
        demo_core_functions()
        demo_enhance_functions()
        demo_filters()
        demo_composite_functions()
        demo_background_functions()
        demo_text_functions()
        demo_complete_workflow()
        
        print("\n" + "=" * 50)
        print("所有演示完成！")
        print("生成的图片保存在 'output' 目录中。")
        print("=" * 50)
        
    except Exception as e:
        print(f"\n错误: {e}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    main()


path: README.md
content: # p-skill

创意图片处理技能包

## 版本

1.0.0

## 安装

```bash
pip install -r requirements.txt
```

## 简介

p-skill 是一个强大的创意图片处理技能包，以 "P一下图片" 为核心理念，提供高效、精准且有趣的图片处理能力。p 字母代表 Power（强力）、Productivity（生产力）、Precision（精准）、Playful（有趣）。

## 功能特性

- **图片基础操作**: 加载、保存、调整尺寸、裁剪、格式转换
- **图片增强**: 亮度、对比度、清晰度调整，智能增强
- **创意滤镜**: 黑白、复古、模糊、轮廓、反色、像素化
- **图片拼接**: 水平拼接、垂直拼接、网格拼接
- **背景移除**: 简单背景移除、指定颜色背景移除
- **文字添加**: 支持多种位置、字体、颜色、旋转角度的文字添加

## 快速开始

### 基础使用

```python
from p_skill import (
    load_image, save_image,
    resize_image, crop_image, convert_format,
    smart_enhance,
    grayscale, sepia,
    stitch_horizontal,
    remove_background_simple,
    add_text
)

# 加载图片
image = load_image("input.jpg")

# 调整尺寸
resized = resize_image(image, scale=0.5)

# 智能增强
enhanced = smart_enhance(resized)

# 添加滤镜
filtered = sepia(enhanced)

# 添加文字
with_text = add_text(filtered, "Hello p-skill!", position="bottom-center")

# 保存图片
save_image(with_text, "output.jpg")
```

## API 文档

### 核心模块 (core)

#### load_image(file_path: str) -> Image.Image
加载图片文件。

**参数:**
- `file_path`: 图片文件路径

**返回:** PIL Image 对象

#### save_image(image: Image.Image, output_path: str, quality: int = 95) -> None
保存图片到文件。

**参数:**
- `image`: PIL Image 对象
- `output_path`: 输出文件路径
- `quality`: 图片质量 (1-100)，默认 95

#### resize_image(image: Image.Image, size: tuple[int, int] = None, scale: float = None) -> Image.Image
调整图片尺寸。

**参数:**
- `image`: PIL Image 对象
- `size`: 目标尺寸 (width, height)
- `scale`: 缩放比例

**返回:** 调整后的图片

#### crop_image(image: Image.Image, box: tuple[int, int, int, int]) -> Image.Image
裁剪图片。

**参数:**
- `image`: PIL Image 对象
- `box`: 裁剪区域 (left, upper, right, lower)

**返回:** 裁剪后的图片

#### convert_format(image: Image.Image, format: str) -> Image.Image
转换图片格式。

**参数:**
- `image`: PIL Image 对象
- `format`: 目标格式 ('JPEG', 'PNG', 'WEBP' 等)

**返回:** 格式转换后的图片

### 增强模块 (enhance)

#### adjust_brightness(image: Image.Image, factor: float) -> Image.Image
调整亮度。

**参数:**
- `image`: PIL Image 对象
- `factor`: 亮度因子 (1.0 为原始亮度)

**返回:** 调整后的图片

#### adjust_contrast(image: Image.Image, factor: float) -> Image.Image
调整对比度。

**参数:**
- `image`: PIL Image 对象
- `factor`: 对比度因子 (1.0 为原始对比度)

**返回:** 调整后的图片

#### adjust_sharpness(image: Image.Image, factor: float) -> Image.Image
调整清晰度。

**参数:**
- `image`: PIL Image 对象
- `factor`: 清晰度因子 (1.0 为原始清晰度)

**返回:** 调整后的图片

#### smart_enhance(image: Image.Image, brightness_factor: float = 1.1, contrast_factor: float = 1.1, sharpness_factor: float = 1.2) -> Image.Image
智能增强图片。

**参数:**
- `image`: PIL Image 对象
- `brightness_factor`: 亮度因子
- `contrast_factor`: 对比度因子
- `sharpness_factor`: 清晰度因子

**返回:** 增强后的图片

### 滤镜模块 (filters)

#### grayscale(image: Image.Image) -> Image.Image
转换为黑白图片。

#### sepia(image: Image.Image) -> Image.Image
应用复古滤镜。

#### blur(image: Image.Image, radius: int = 5) -> Image.Image
应用模糊滤镜。

**参数:**
- `radius`: 模糊半径，默认 5

#### contour(image: Image.Image) -> Image.Image
应用轮廓滤镜。

#### invert(image: Image.Image) -> Image.Image
反色滤镜。

#### pixelate(image: Image.Image, pixel_size: int = 10) -> Image.Image
像素化滤镜。

**参数:**
- `pixel_size`: 像素块大小，默认 10

### 拼接模块 (composite)

#### stitch_horizontal(images: List[Image.Image], align: str = 'center') -> Image.Image
水平拼接图片。

**参数:**
- `images`: 图片列表
- `align`: 对齐方式 ('top', 'center', 'bottom')

#### stitch_vertical(images: List[Image.Image], align: str = 'center') -> Image.Image
垂直拼接图片。

**参数:**
- `images`: 图片列表
- `align`: 对齐方式 ('left', 'center', 'right')

#### stitch_grid(images: List[Image.Image], cols: int, rows: int = None, align: str = 'center') -> Image.Image
网格拼接图片。

**参数:**
- `images`: 图片列表
- `cols`: 列数
- `rows`: 行数（可选，自动计算）
- `align`: 对齐方式

### 背景模块 (background)

#### remove_background_simple(image: Image.Image, threshold: int = 240, smooth_radius: int = 2) -> Image.Image
简单背景移除（基于白色阈值）。

**参数:**
- `image`: PIL Image 对象
- `threshold`: 白色阈值 (0-255)，默认 240
- `smooth_radius`: 边缘平滑半径，默认 2

#### remove_background_color(image: Image.Image, target_color: tuple[int, int, int], tolerance: int = 30, smooth_radius: int = 2) -> Image.Image
指定颜色背景移除。

**参数:**
- `image`: PIL Image 对象
- `target_color`: 目标颜色 (R, G, B)
- `tolerance`: 颜色容差，默认 30
- `smooth_radius`: 边缘平滑半径，默认 2

### 文字模块 (text)

#### add_text(image: Image.Image, text: str, position: str | tuple[int, int] = "center", font_path: str | None = None, font_size: int = 40, color: tuple[int, int, int] | tuple[int, int, int, int] = (0, 0, 0), rotation: float = 0.0, offset: tuple[int, int] = (0, 0)) -> Image.Image
在图片上添加文字。

**参数:**
- `image`: PIL Image 对象
- `text`: 要添加的文字
- `position`: 位置（可以是坐标或预设位置）
  - 预设位置: 'top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'
- `font_path`: 字体文件路径（可选）
- `font_size`: 字体大小，默认 40
- `color`: 文字颜色 (R, G, B) 或 (R, G, B, A)
- `rotation`: 旋转角度，默认 0
- `offset`: 位置偏移 (x, y)

**返回:** 添加文字后的图片

## 完整示例

查看 `example.py` 文件获取完整的使用示例。

## 支持的格式

- 输入: JPEG, PNG, WebP, BMP, GIF 等
- 输出: JPEG, PNG, WebP

## 性能

- 单张 1080p 图片处理时间 < 5 秒

## 测试

运行所有测试：

```bash
python test_core.py
python test_enhance.py
python test_filters.py
python test_composite.py
python test_background.py
python test_text.py
```

## 许可证

MIT License


path: requirements.txt
content: pillow>=10.0.0


path: src/p_skill/background.py
content: from PIL import Image, ImageFilter


def _smooth_edges(mask: Image.Image, blur_radius: int = 2) -> Image.Image:
    mask = mask.filter(ImageFilter.GaussianBlur(radius=blur_radius))
    return mask


def remove_background_simple(
    image: Image.Image,
    threshold: int = 240,
    smooth_radius: int = 2
) -> Image.Image:
    if image.mode != 'RGBA':
        image = image.convert('RGBA')
    
    width, height = image.size
    mask = Image.new('L', (width, height), 255)
    
    pixels = image.load()
    mask_pixels = mask.load()
    
    for x in range(width):
        for y in range(height):
            r, g, b, a = pixels[x, y]
            if r >= threshold and g >= threshold and b >= threshold:
                mask_pixels[x, y] = 0
    
    if smooth_radius > 0:
        mask = _smooth_edges(mask, smooth_radius)
    
    result = image.copy()
    result.putalpha(mask)
    
    return result


def remove_background_color(
    image: Image.Image,
    target_color: tuple[int, int, int],
    tolerance: int = 30,
    smooth_radius: int = 2
) -> Image.Image:
    if image.mode != 'RGBA':
        image = image.convert('RGBA')
    
    width, height = image.size
    mask = Image.new('L', (width, height), 255)
    
    pixels = image.load()
    mask_pixels = mask.load()
    
    target_r, target_g, target_b = target_color
    
    for x in range(width):
        for y in range(height):
            r, g, b, a = pixels[x, y]
            color_dist = ((r - target_r) ** 2 + (g - target_g) ** 2 + (b - target_b) ** 2) ** 0.5
            if color_dist <= tolerance:
                mask_pixels[x, y] = 0
    
    if smooth_radius > 0:
        mask = _smooth_edges(mask, smooth_radius)
    
    result = image.copy()
    result.putalpha(mask)
    
    return result


path: src/p_skill/composite.py
content: from PIL import Image
from typing import List, Tuple


def stitch_horizontal(images: List[Image.Image], align: str = 'center') -> Image.Image:
    if not images:
        raise ValueError("No images provided")

    max_height = max(img.height for img in images)

    total_width = sum(img.width for img in images)

    mode = images[0].mode
    result = Image.new(mode, (total_width, max_height))

    current_x = 0
    for img in images:
        if img.mode != mode:
            img = img.convert(mode)

        if align == 'top':
            y_offset = 0
        elif align == 'bottom':
            y_offset = max_height - img.height
        else:
            y_offset = (max_height - img.height) // 2

        result.paste(img, (current_x, y_offset))
        current_x += img.width

    return result


def stitch_vertical(images: List[Image.Image], align: str = 'center') -> Image.Image:
    if not images:
        raise ValueError("No images provided")

    max_width = max(img.width for img in images)

    total_height = sum(img.height for img in images)

    mode = images[0].mode
    result = Image.new(mode, (max_width, total_height))

    current_y = 0
    for img in images:
        if img.mode != mode:
            img = img.convert(mode)

        if align == 'left':
            x_offset = 0
        elif align == 'right':
            x_offset = max_width - img.width
        else:
            x_offset = (max_width - img.width) // 2

        result.paste(img, (x_offset, current_y))
        current_y += img.height

    return result


def stitch_grid(images: List[Image.Image], cols: int, rows: int = None, align: str = 'center') -> Image.Image:
    if not images:
        raise ValueError("No images provided")

    if cols <= 0:
        raise ValueError("Number of columns must be positive")

    if rows is None:
        rows = (len(images) + cols - 1) // cols

    if cols * rows < len(images):
        raise ValueError("Grid size is too small for the number of images")

    max_width = max(img.width for img in images)
    max_height = max(img.height for img in images)

    mode = images[0].mode
    total_width = cols * max_width
    total_height = rows * max_height
    result = Image.new(mode, (total_width, total_height))

    for idx, img in enumerate(images):
        row = idx // cols
        col = idx % cols

        if img.mode != mode:
            img = img.convert(mode)

        x_offset = col * max_width
        y_offset = row * max_height

        if align == 'top':
            img_y = y_offset
        elif align == 'bottom':
            img_y = y_offset + max_height - img.height
        else:
            img_y = y_offset + (max_height - img.height) // 2

        if align == 'left':
            img_x = x_offset
        elif align == 'right':
            img_x = x_offset + max_width - img.width
        else:
            img_x = x_offset + (max_width - img.width) // 2

        result.paste(img, (img_x, img_y))

    return result


path: src/p_skill/core.py
content: from PIL import Image
import os


def load_image(file_path: str) -> Image.Image:
    image = Image.open(file_path)
    if image.mode in ('RGBA', 'LA', 'P'):
        image = image.convert('RGBA')
    return image


def save_image(image: Image.Image, output_path: str, quality: int = 95) -> None:
    ext = os.path.splitext(output_path)[1].lower()
    if ext in ('.jpg', '.jpeg'):
        if image.mode == 'RGBA':
            background = Image.new('RGB', image.size, (255, 255, 255))
            background.paste(image, mask=image.split()[3])
            image = background
        image.save(output_path, 'JPEG', quality=quality, optimize=True)
    elif ext == '.png':
        image.save(output_path, 'PNG', optimize=True)
    elif ext == '.webp':
        image.save(output_path, 'WEBP', quality=quality, method=6)
    else:
        image.save(output_path)


def resize_image(image: Image.Image, size: tuple[int, int] = None, scale: float = None) -> Image.Image:
    if scale is not None:
        if scale <= 0:
            raise ValueError("Scale must be a positive number")
        new_width = int(image.width * scale)
        new_height = int(image.height * scale)
        return image.resize((new_width, new_height), Image.Resampling.LANCZOS)
    elif size is not None:
        return image.resize(size, Image.Resampling.LANCZOS)
    else:
        raise ValueError("Either size or scale must be provided")


def crop_image(image: Image.Image, box: tuple[int, int, int, int]) -> Image.Image:
    return image.crop(box)


def convert_format(image: Image.Image, format: str) -> Image.Image:
    format = format.upper()
    if format in ('JPEG', 'JPG'):
        if image.mode == 'RGBA':
            background = Image.new('RGB', image.size, (255, 255, 255))
            background.paste(image, mask=image.split()[3])
            return background
        elif image.mode != 'RGB':
            return image.convert('RGB')
    elif format == 'PNG':
        if image.mode != 'RGBA' and image.mode != 'RGB':
            return image.convert('RGBA')
    return image


path: src/p_skill/enhance.py
content: from PIL import Image, ImageEnhance


def adjust_brightness(image: Image.Image, factor: float) -> Image.Image:
    enhancer = ImageEnhance.Brightness(image)
    return enhancer.enhance(factor)


def adjust_contrast(image: Image.Image, factor: float) -> Image.Image:
    enhancer = ImageEnhance.Contrast(image)
    return enhancer.enhance(factor)


def adjust_sharpness(image: Image.Image, factor: float) -> Image.Image:
    enhancer = ImageEnhance.Sharpness(image)
    return enhancer.enhance(factor)


def smart_enhance(
    image: Image.Image,
    brightness_factor: float = 1.1,
    contrast_factor: float = 1.1,
    sharpness_factor: float = 1.2
) -> Image.Image:
    result = image
    result = adjust_brightness(result, brightness_factor)
    result = adjust_contrast(result, contrast_factor)
    result = adjust_sharpness(result, sharpness_factor)
    return result


path: src/p_skill/filters.py
content: from PIL import Image, ImageFilter, ImageOps


def grayscale(image: Image.Image) -> Image.Image:
    return ImageOps.grayscale(image)


def sepia(image: Image.Image) -> Image.Image:
    sepia_img = image.convert('RGB')
    width, height = sepia_img.size
    pixels = sepia_img.load()
    
    for x in range(width):
        for y in range(height):
            r, g, b = pixels[x, y]
            tr = int(0.393 * r + 0.769 * g + 0.189 * b)
            tg = int(0.349 * r + 0.686 * g + 0.168 * b)
            tb = int(0.272 * r + 0.534 * g + 0.131 * b)
            tr = min(255, tr)
            tg = min(255, tg)
            tb = min(255, tb)
            pixels[x, y] = (tr, tg, tb)
    
    return sepia_img


def blur(image: Image.Image, radius: int = 5) -> Image.Image:
    return image.filter(ImageFilter.GaussianBlur(radius=radius))


def contour(image: Image.Image) -> Image.Image:
    return image.filter(ImageFilter.CONTOUR)


def invert(image: Image.Image) -> Image.Image:
    if image.mode == 'RGBA':
        r, g, b, a = image.split()
        rgb = Image.merge('RGB', (r, g, b))
        rgb_inverted = ImageOps.invert(rgb)
        r2, g2, b2 = rgb_inverted.split()
        return Image.merge('RGBA', (r2, g2, b2, a))
    else:
        return ImageOps.invert(image)


def pixelate(image: Image.Image, pixel_size: int = 10) -> Image.Image:
    if pixel_size < 1:
        pixel_size = 1
    width, height = image.size
    small = image.resize((width // pixel_size, height // pixel_size), Image.Resampling.NEAREST)
    return small.resize((width, height), Image.Resampling.NEAREST)


path: src/p_skill/text.py
content: from PIL import Image, ImageDraw, ImageFont
import platform
import os


def _get_system_font(size: int) -> ImageFont.FreeTypeFont:
    system = platform.system()
    font_paths = []
    
    if system == "Windows":
        font_paths = [
            "C:\\Windows\\Fonts\\msyh.ttc",
            "C:\\Windows\\Fonts\\simhei.ttf",
            "C:\\Windows\\Fonts\\simsun.ttc"
        ]
    elif system == "Darwin":
        font_paths = [
            "/System/Library/Fonts/PingFang.ttc",
            "/System/Library/Fonts/STHeiti Light.ttc",
            "/System/Library/Fonts/Arial.ttf"
        ]
    elif system == "Linux":
        font_paths = [
            "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
            "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
            "/usr/share/fonts/truetype/freefont/FreeSans.ttf"
        ]
    
    for font_path in font_paths:
        if os.path.exists(font_path):
            try:
                return ImageFont.truetype(font_path, size)
            except:
                continue
    
    try:
        return ImageFont.load_default()
    except:
        return ImageFont.truetype("arial.ttf", size)


def _calculate_position(
    image: Image.Image,
    text: str,
    font: ImageFont.FreeTypeFont,
    position: str | tuple[int, int],
    offset: tuple[int, int] = (0, 0)
) -> tuple[int, int]:
    bbox = font.getbbox(text)
    text_width = bbox[2] - bbox[0]
    text_height = bbox[3] - bbox[1]
    img_width, img_height = image.size
    
    if isinstance(position, str):
        position = position.lower()
        if position == "top-left":
            x, y = 0, 0
        elif position == "top-center":
            x, y = (img_width - text_width) // 2, 0
        elif position == "top-right":
            x, y = img_width - text_width, 0
        elif position == "center-left":
            x, y = 0, (img_height - text_height) // 2
        elif position == "center":
            x, y = (img_width - text_width) // 2, (img_height - text_height) // 2
        elif position == "center-right":
            x, y = img_width - text_width, (img_height - text_height) // 2
        elif position == "bottom-left":
            x, y = 0, img_height - text_height
        elif position == "bottom-center":
            x, y = (img_width - text_width) // 2, img_height - text_height
        elif position == "bottom-right":
            x, y = img_width - text_width, img_height - text_height
        else:
            raise ValueError(f"Unknown position: {position}")
    else:
        x, y = position
    
    return (x + offset[0], y + offset[1])


def add_text(
    image: Image.Image,
    text: str,
    position: str | tuple[int, int] = "center",
    font_path: str | None = None,
    font_size: int = 40,
    color: tuple[int, int, int] | tuple[int, int, int, int] = (0, 0, 0),
    rotation: float = 0.0,
    offset: tuple[int, int] = (0, 0)
) -> Image.Image:
    result = image.copy()
    
    if font_path and os.path.exists(font_path):
        font = ImageFont.truetype(font_path, font_size)
    else:
        font = _get_system_font(font_size)
    
    text_image = Image.new("RGBA", (1, 1), (255, 255, 255, 0))
    draw = ImageDraw.Draw(text_image)
    bbox = font.getbbox(text)
    text_width = bbox[2] - bbox[0]
    text_height = bbox[3] - bbox[1]
    
    text_image = Image.new("RGBA", (text_width + 20, text_height + 20), (255, 255, 255, 0))
    draw = ImageDraw.Draw(text_image)
    draw.text((10, 10), text, font=font, fill=color)
    
    if rotation != 0:
        text_image = text_image.rotate(rotation, expand=True, resample=Image.Resampling.BICUBIC)
    
    x, y = _calculate_position(result, text, font, position, offset)
    
    result.paste(text_image, (x, y), text_image)
    
    return result


path: src/p_skill/__init__.py
content: __version__ = "1.0.0"

from .core import (
    load_image,
    save_image,
    resize_image,
    crop_image,
    convert_format
)
from .enhance import (
    adjust_brightness,
    adjust_contrast,
    adjust_sharpness,
    smart_enhance
)
from .filters import (
    grayscale,
    sepia,
    blur,
    contour,
    invert,
    pixelate
)
from .composite import (
    stitch_horizontal,
    stitch_vertical,
    stitch_grid
)
from .background import (
    remove_background_simple,
    remove_background_color
)
from .text import (
    add_text
)



Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal:  | SkillSpector: suspicious (HIGH)