File size: 5,755 Bytes
4d9b0d5
e5ad646
4d9b0d5
 
e5ad646
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d9b0d5
 
 
 
 
 
 
 
 
 
 
e5ad646
4d9b0d5
 
 
 
 
 
 
e5ad646
4d9b0d5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7347923
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d9b0d5
 
 
 
 
 
 
 
 
e5ad646
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
"""示例场景图片管理 — 优先使用真实图片,缺失时生成合成图片

真实图片位于 test_imgs/complex/ 目录(通过 Git LFS 推送)
文本场景示例由合成生成(模拟合同文档)
"""
import os
from PIL import Image, ImageDraw, ImageFont, ImageFilter


def _get_font(size):
    try:
        return ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", size)
    except Exception:
        try:
            return ImageFont.truetype("DejaVuSans.ttf", size)
        except Exception:
            return ImageFont.load_default()


def _generate_text_scene(path1, path2):
    """生成文本场景示例:模拟合同文档

    两张图内容相同,但图2做了亮度调整和水平偏移,模拟不同角度拍摄
    """
    w, h = 600, 800

    for path, brightness, offset_x in [(path1, 0, 0), (path2, 30, 5)]:
        img = Image.new('RGB', (w, h), (255, 255, 255))
        draw = ImageDraw.Draw(img)

        # 标题区域
        draw.rectangle([50 + offset_x, 40, 550 + offset_x, 80], fill=(30, 30, 30))
        # 正文行
        for i in range(25):
            y = 120 + i * 25
            x_start = 50 + offset_x
            x_end = 550 - (i % 5) * 20 + offset_x
            draw.rectangle([x_start, y, x_end, y + 12], fill=(60, 60, 60))

        # 模拟印章(红色圆形)
        draw.ellipse([400 + offset_x, 650, 500 + offset_x, 750],
                     outline=(180, 30, 30), width=3)
        draw.text((420 + offset_x, 680), "SEAL", fill=(180, 30, 30),
                  font=_get_font(20))

        # 亮度调整
        if brightness > 0:
            import numpy as np
            arr = np.array(img).astype(int) + brightness
            arr = np.clip(arr, 0, 255).astype('uint8')
            img = Image.fromarray(arr)

        img.save(path, 'JPEG', quality=85)


def _scan_real_images(directory):
    """扫描目录中的真实图片文件"""
    if not os.path.isdir(directory):
        return []
    exts = {'.jpg', '.jpeg', '.png', '.bmp', '.webp'}
    imgs = []
    for f in sorted(os.listdir(directory)):
        if os.path.splitext(f)[1].lower() in exts:
            imgs.append(os.path.join(directory, f))
    return imgs


def ensure_demo_images(base_dir):
    """确保示例图片存在,优先使用真实图片

    Args:
        base_dir: 项目根目录
    Returns:
        dict: {'text': [path1, path2], 'complex': [path1, path2]} 或 None
    """
    demo_dir = os.path.join(base_dir, 'test_imgs')
    os.makedirs(demo_dir, exist_ok=True)

    # === 复杂场景:优先使用真实图片 ===
    complex_dir = os.path.join(demo_dir, 'complex')
    real_complex = _scan_real_images(complex_dir)

    if len(real_complex) >= 2:
        complex_imgs = real_complex[:2]
    else:
        # Fallback:生成合成图片
        c1 = os.path.join(demo_dir, 'demo_complex_1.jpg')
        c2 = os.path.join(demo_dir, 'demo_complex_2.jpg')
        if not os.path.exists(c1):
            try:
                _generate_complex_scene(c1, c2)
                print("[示例] 已生成合成复杂场景图片")
            except Exception as e:
                print(f"[示例] 合成复杂场景生成失败: {e}")
                return None
        complex_imgs = [c1, c2]

    # === 文本场景:优先使用真实图片 ===
    text_dir = os.path.join(demo_dir, 'text')
    real_text = _scan_real_images(text_dir)

    if len(real_text) >= 2:
        text_imgs = real_text[:2]
    else:
        # Fallback:生成合成图片
        t1 = os.path.join(demo_dir, 'demo_text_1.jpg')
        t2 = os.path.join(demo_dir, 'demo_text_2.jpg')
        if not os.path.exists(t1):
            try:
                _generate_text_scene(t1, t2)
                print("[示例] 已生成合成文本场景图片")
            except Exception as e:
                print(f"[示例] 合成文本场景生成失败: {e}")
                return None
        text_imgs = [t1, t2]

    return {
        'text': text_imgs,
        'complex': complex_imgs,
    }


def _generate_complex_scene(path1, path2):
    """生成复杂场景示例:模拟实景照片(fallback 用)"""
    w, h = 600, 450

    for path, shift_x, color_shift in [(path1, 0, 0), (path2, 15, 20)]:
        img = Image.new('RGB', (w, h))
        draw = ImageDraw.Draw(img)

        # 天空渐变
        for y in range(h // 2):
            r = int(100 + y * 0.3 + color_shift)
            g = int(150 + y * 0.2)
            b = int(200 + y * 0.15)
            draw.line([(0, y), (w, y)], fill=(min(r, 255), min(g, 255), min(b, 255)))

        # 地面渐变
        for y in range(h // 2, h):
            r = int(80 + (y - h // 2) * 0.3)
            g = int(120 + (y - h // 2) * 0.2)
            b = int(60 + (y - h // 2) * 0.1)
            draw.line([(0, y), (w, y)], fill=(min(r, 255), min(g, 255), min(b, 255)))

        # 太阳
        sun_x = 450 + shift_x
        draw.ellipse([sun_x - 40, 50, sun_x + 40, 130],
                     fill=(255, 220, 100))

        # 山脉
        draw.polygon([(0, 225), (150 + shift_x, 150), (300 + shift_x, 200),
                      (450 + shift_x, 160), (600, 220), (600, 225)],
                     fill=(80, 100, 80))

        # 建筑
        draw.rectangle([100 + shift_x, 250, 200 + shift_x, 400],
                       fill=(120, 100, 90))
        draw.rectangle([250 + shift_x, 280, 350 + shift_x, 400],
                       fill=(100, 110, 100))
        draw.rectangle([400 + shift_x, 260, 480 + shift_x, 400],
                       fill=(90, 80, 100))

        # 轻微模糊模拟不同焦距
        if shift_x > 0:
            img = img.filter(ImageFilter.GaussianBlur(radius=0.8))

        img.save(path, 'JPEG', quality=85)