File size: 4,473 Bytes
e40db0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
169
170
171
172
173
174
#!/usr/bin/env python3
"""
Florence Forge - 数据处理工具函数

提供数据处理相关的工具函数
"""

import torch
import numpy as np
from typing import List, Tuple
from PIL import Image, ImageDraw

def generate_mask_from_polygon(
    polygon: List[Tuple[float, float]], 
    image_size: Tuple[int, int]
) -> np.ndarray:
    """从多边形坐标生成掩码
    
    Args:
        polygon: 多边形顶点坐标列表 [(x1, y1), (x2, y2), ...]
        image_size: 图像尺寸 (width, height)
    
    Returns:
        二值掩码数组,形状为 (height, width)
    """
    width, height = image_size
    
    # 创建空白图像
    mask_image = Image.new('L', (width, height), 0)
    draw = ImageDraw.Draw(mask_image)
    
    # 绘制多边形
    if len(polygon) >= 3:
        draw.polygon(polygon, fill=255)
    
    # 转换为numpy数组
    mask = np.array(mask_image)
    
    return mask

def normalize_bbox(
    bbox: List[float], 
    image_size: Tuple[int, int]
) -> List[float]:
    """归一化边界框坐标
    
    Args:
        bbox: 边界框坐标 [x1, y1, x2, y2]
        image_size: 图像尺寸 (width, height)
    
    Returns:
        归一化后的边界框坐标
    """
    width, height = image_size
    x1, y1, x2, y2 = bbox
    
    return [
        x1 / width,
        y1 / height, 
        x2 / width,
        y2 / height
    ]

def denormalize_bbox(
    bbox: List[float],
    image_size: Tuple[int, int]
) -> List[float]:
    """反归一化边界框坐标
    
    Args:
        bbox: 归一化的边界框坐标 [x1, y1, x2, y2]
        image_size: 图像尺寸 (width, height)
    
    Returns:
        实际像素坐标的边界框
    """
    width, height = image_size
    x1, y1, x2, y2 = bbox
    
    return [
        x1 * width,
        y1 * height,
        x2 * width, 
        y2 * height
    ]

def resize_image_and_annotations(
    image: Image.Image,
    annotations: dict,
    target_size: Tuple[int, int]
) -> Tuple[Image.Image, dict]:
    """调整图像大小并相应调整标注
    
    Args:
        image: PIL图像
        annotations: 标注字典
        target_size: 目标尺寸 (width, height)
    
    Returns:
        调整后的图像和标注
    """
    original_size = image.size
    resized_image = image.resize(target_size, Image.Resampling.LANCZOS)
    
    # 计算缩放比例
    scale_x = target_size[0] / original_size[0]
    scale_y = target_size[1] / original_size[1]
    
    # 调整标注
    updated_annotations = annotations.copy()
    
    # 调整边界框
    if 'bboxes' in annotations:
        updated_bboxes = []
        for bbox in annotations['bboxes']:
            x1, y1, x2, y2 = bbox
            updated_bboxes.append([
                x1 * scale_x,
                y1 * scale_y,
                x2 * scale_x,
                y2 * scale_y
            ])
        updated_annotations['bboxes'] = updated_bboxes
    
    # 调整多边形
    if 'polygons' in annotations:
        updated_polygons = []
        for polygon in annotations['polygons']:
            updated_polygon = [
                (x * scale_x, y * scale_y) for x, y in polygon
            ]
            updated_polygons.append(updated_polygon)
        updated_annotations['polygons'] = updated_polygons
    
    return resized_image, updated_annotations

def collate_batch_data(batch_data: List[dict]) -> dict:
    """整理批次数据
    
    Args:
        batch_data: 批次数据列表
    
    Returns:
        整理后的批次数据字典
    """
    if not batch_data:
        return {}
    
    # 获取所有键
    keys = batch_data[0].keys()
    collated = {}
    
    for key in keys:
        values = [item[key] for item in batch_data]
        
        # 根据数据类型进行不同的处理
        if isinstance(values[0], torch.Tensor):
            # 对于张量,尝试堆叠
            try:
                collated[key] = torch.stack(values)
            except RuntimeError:
                # 如果无法堆叠,保持列表形式
                collated[key] = values
        elif isinstance(values[0], (list, tuple)):
            # 对于列表或元组,保持原样
            collated[key] = values
        else:
            # 对于其他类型,尝试转换为张量
            try:
                collated[key] = torch.tensor(values)
            except (ValueError, TypeError):
                collated[key] = values
    
    return collated