hejianhao
2026-04-02 0940d56748a73525790c350c393a5be960aab62c
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
 
/**
 * 图片辅助工具函数集合
 * 整合了原有的imageHelper和gallery/imageUtils功能
 */
 
/**
 * 将十六进制颜色转换为rgba
 * @param {string} hex - 十六进制颜色  
 * @param {number} alpha - 透明度 (0-1)
 * @returns {string} rgba颜色字符串
 */
export function hexToRgba(hex, alpha = 0.2) {
    if (!hex || !hex.startsWith('#')) return `rgba(0, 0, 0, ${alpha})`
    const r = parseInt(hex.slice(1, 3), 16)
    const g = parseInt(hex.slice(3, 5), 16)
    const b = parseInt(hex.slice(5, 7), 16)
    return `rgba(${r}, ${g}, ${b}, ${alpha})`
}
 
/**
 * 获取占位图像
 */
export function getPlaceholderImage() {
  return new URL("../../static/common/icon_loading.png", import.meta.url).href
}
 
/**
 * 图像加载错误处理
 */
export function handleImageError(event) {
  console.warn("[ImageHelper] 图像加载失败:", event.target?.src)
 
  // 防止无限循环:如果已经是占位图还失败,就不再尝试替换
  const placeholderImage = getPlaceholderImage()
  if (event.target && event.target.src === placeholderImage) {
    console.error("[ImageHelper] 占位图像也加载失败,停止重试")
    event.target.onerror = null
    return
  }
 
  // 设置默认占位图
  if (event.target) {
    event.target.src = placeholderImage
  }
}
 
/**
 * 获取完整图片路径
 */
export function getFullImagePath(imagePath, projectPath) {
  if (!imagePath || !projectPath) {
    return ""
  }
  // 如果已经是绝对路径,直接返回
  if (imagePath.startsWith('/')) {
    return imagePath
  }
  return `${projectPath}/${imagePath}`
}
 
/**
 * 获取图片的拆分分配类型(用于tooltip)
 */
export function getImageSplitTypeForTooltip(image, selectedSplit) {
  if (!image || !selectedSplit) {
    return "未选择拆分"
  }
 
  const assignments = selectedSplit.assignments || {}
  const splitType = assignments[image.id]
 
  if (!splitType) {
    return "未分配"
  }
 
  switch (splitType) {
    case "train":
      return "训练集"
    case "val":
      return "验证集"
    case "test":
      return "测试集"
    default:
      return "未分配"
  }
}
 
/**
 * 获取图片的所有类别信息(用于显示类别标签)
 */
export function getImageCategories(image, projectTypes) {
  if (!image?.annotations || image.annotations.length === 0) {
    return []
  }
  if (!projectTypes || projectTypes.length === 0) {
    return []
  }
  
  // 收集所有标注的类别ID(去重)
  const categoryIds = new Set()
  image.annotations.forEach(ann => {
    if (ann.categoryId !== undefined && ann.categoryId !== null) {
      categoryIds.add(ann.categoryId)
    }
  })
  
  // 从项目类别中查找对应的类别信息
  const categories = []
  categoryIds.forEach(categoryId => {
    const category = projectTypes.find(t => {
      // 类型转换以处理可能的字符串和数字比较
      return t.key === categoryId || String(t.key) === String(categoryId)
    })
    if (category) {
      categories.push(category)
    }
  })
  
  return categories
}
 
/**
 * 获取项目显示名称,对已删除项目添加标记
 */
export function getProjectDisplayName(project) {
  const baseName = project.project?.name || project.name || "未命名项目"
  if (project.status === "removed" || project.exists === false) {
    return `${baseName} (已删除)`
  }
  return baseName
}
 
/**
 * 计算多边形面积
 * @param {Array} points - 多边形顶点数组
 * @returns {number} 面积
 */
export function calculatePolygonArea(points) {
  if (!points || points.length < 3) return 0;
  
  let area = 0;
  for (let i = 0; i < points.length; i++) {
    const j = (i + 1) % points.length;
    area += points[i].x * points[j].y;
    area -= points[j].x * points[i].y;
  }
  
  return Math.abs(area / 2);
}
 
/**
 * 清理重复图像数据和冗余字段
 */
export async function cleanDuplicateImages(images) {
  if (!images) return { images: [], hasChanges: false }
 
  const uniqueImages = []
  const seenIds = new Set()
  const seenPaths = new Set()
  let hasChanges = false
 
  images.forEach((img) => {
    // 清理重复的image字段(如果与path相同)
    let cleanedImg = { ...img }
    if (
      cleanedImg.image &&
      cleanedImg.path &&
      cleanedImg.image === cleanedImg.path
    ) {
      delete cleanedImg.image // 删除重复的字段
      hasChanges = true
      console.log(`[ImageHelper] 清理重复字段: ${img.name}`)
    }
 
    // 确保必要字段存在
    if (!cleanedImg.annotations) {
      cleanedImg.annotations = []
      hasChanges = true
    }
 
    // 检查重复图像
    if (!seenIds.has(img.id) && !seenPaths.has(img.path)) {
      uniqueImages.push(cleanedImg)
      seenIds.add(img.id)
      seenPaths.add(img.path)
    } else {
      console.log(`[ImageHelper] 移除重复图像: ${img.name}`)
      hasChanges = true
    }
  })
 
  return {
    images: uniqueImages,
    hasChanges: hasChanges || uniqueImages.length !== images.length
  }
}