feat(label): add label page
This commit is contained in:
49
components/label/utils/clone.ts
Normal file
49
components/label/utils/clone.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
const deepCloneFallback = <T>(value: T, cache = new WeakMap()): T => {
|
||||
if (value === null || typeof value !== "object") return value
|
||||
if (cache.has(value as object)) return cache.get(value as object)
|
||||
|
||||
if (value instanceof Date) return new Date(value.getTime()) as T
|
||||
if (value instanceof Map) {
|
||||
const clonedMap = new Map()
|
||||
cache.set(value as object, clonedMap)
|
||||
value.forEach((mapValue, mapKey) => {
|
||||
clonedMap.set(
|
||||
deepCloneFallback(mapKey, cache),
|
||||
deepCloneFallback(mapValue, cache)
|
||||
)
|
||||
})
|
||||
return clonedMap as T
|
||||
}
|
||||
if (value instanceof Set) {
|
||||
const clonedSet = new Set()
|
||||
cache.set(value as object, clonedSet)
|
||||
value.forEach((setValue) => {
|
||||
clonedSet.add(deepCloneFallback(setValue, cache))
|
||||
})
|
||||
return clonedSet as T
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const clonedArr: any[] = []
|
||||
cache.set(value as object, clonedArr)
|
||||
value.forEach((item, index) => {
|
||||
clonedArr[index] = deepCloneFallback(item, cache)
|
||||
})
|
||||
return clonedArr as T
|
||||
}
|
||||
|
||||
const clonedObj: Record<string, any> = {}
|
||||
cache.set(value as object, clonedObj)
|
||||
Object.keys(value as object).forEach((key) => {
|
||||
clonedObj[key] = deepCloneFallback((value as any)[key], cache)
|
||||
})
|
||||
return clonedObj as T
|
||||
}
|
||||
|
||||
export const safeClone = <T>(value: T): T => {
|
||||
try {
|
||||
return structuredClone(value)
|
||||
} catch (error) {
|
||||
console.warn("structuredClone failed, fallback to deep clone", error)
|
||||
return deepCloneFallback(value)
|
||||
}
|
||||
}
|
||||
248
components/label/utils/constants.ts
Normal file
248
components/label/utils/constants.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
"use client"
|
||||
|
||||
export const dateFormat = "YYYY-MM-DD HH:mm:ss"
|
||||
export const weekDayText = (data: number) => {
|
||||
switch (data) {
|
||||
case 0:
|
||||
return "星期日"
|
||||
case 1:
|
||||
return "星期一"
|
||||
case 2:
|
||||
return "星期二"
|
||||
case 3:
|
||||
return "星期三"
|
||||
case 4:
|
||||
return "星期四"
|
||||
case 5:
|
||||
return "星期五"
|
||||
case 6:
|
||||
return "星期六"
|
||||
default:
|
||||
return "-"
|
||||
}
|
||||
}
|
||||
|
||||
// 标注任务工序
|
||||
export const processOpts = [
|
||||
{ label: "标注", value: "标注" },
|
||||
{ label: "审核1", value: "审核1" },
|
||||
{ label: "审核2", value: "审核2" },
|
||||
]
|
||||
|
||||
// 状态码
|
||||
export const STATUS_CODE = new Map([
|
||||
[1, "已创建"],
|
||||
[101, "已创建"], // 审核
|
||||
[102, "已创建"], // 复审
|
||||
[103, "已创建"], // 审核3
|
||||
[180, "数据同步中"],
|
||||
[190, "Embedding生成中"],
|
||||
[191, "Embedding生成失败"],
|
||||
[199, "审核失败"],
|
||||
[201, "试标阶段"], // 试标中
|
||||
[202, "试标阶段"], // 试标完成
|
||||
[203, "试标阶段"], // 试标确认
|
||||
[500, "标注中"],
|
||||
[580, "待验收"],
|
||||
[600, "已完成"],
|
||||
[700, "已终止"],
|
||||
])
|
||||
|
||||
// 状态码
|
||||
export const projectStatusOpts = [
|
||||
{ label: "试标阶段", value: "201,202,203" },
|
||||
{ label: "标注中", value: 500 },
|
||||
{ label: "待验收", value: 580 },
|
||||
{ label: "已完成", value: 600 },
|
||||
// { label: "审核失败", value: 199 },
|
||||
{ label: "已终止", value: 700 },
|
||||
]
|
||||
|
||||
export const CREATED_CODE = [1, 101, 102, 103, 190, 191, 199] // 已创建的状态码数组
|
||||
export const ALL_CODE = [201, 202, 203, 500, 580, 600, 700] // 其他的状态码数组
|
||||
export const PRELABEL_CODE = [201, 202, 203]
|
||||
export const LABEL_CODE = [500, 580]
|
||||
|
||||
export const projectLabelTypeOpts = [
|
||||
{ label: "点云框", value: 1 },
|
||||
{ label: "点云分割", value: 2 },
|
||||
{ label: "点云分类", value: 3 },
|
||||
{ label: "图片", value: 4 },
|
||||
{ label: "大语言基础", value: 5 },
|
||||
{ label: "大语言QA", value: 6 },
|
||||
{ label: "视频", value: 7 },
|
||||
]
|
||||
|
||||
// 项目列表中状态码对应值及style
|
||||
export const statusStyles = {
|
||||
1: {
|
||||
label: "已创建",
|
||||
styleText: "text-[#1874FF] bg-[#E8F0FC]",
|
||||
},
|
||||
101: {
|
||||
label: "已创建",
|
||||
styleText: "text-[#1874FF] bg-[#E8F0FC]",
|
||||
},
|
||||
102: {
|
||||
label: "已创建",
|
||||
styleText: "text-[#1874FF] bg-[#E8F0FC]",
|
||||
},
|
||||
103: {
|
||||
label: "已创建",
|
||||
styleText: "text-[#1874FF] bg-[#E8F0FC]",
|
||||
},
|
||||
190: {
|
||||
label: "Embedding生成中",
|
||||
styleText: "text-[#1874FF] bg-[#E8F0FC]",
|
||||
},
|
||||
191: {
|
||||
label: "Embedding生成失败",
|
||||
styleText: "text-[#D9333E] bg-[#FBEBEC]",
|
||||
},
|
||||
199: {
|
||||
label: "审核失败",
|
||||
styleText: "text-[#D9333E] bg-[#FBEBEC]",
|
||||
},
|
||||
201: {
|
||||
label: "试标阶段",
|
||||
styleText: "text-[#21D3ED] bg-[#EEFDFF]",
|
||||
},
|
||||
202: {
|
||||
label: "试标阶段",
|
||||
styleText: "text-[#21D3ED] bg-[#EEFDFF]",
|
||||
},
|
||||
203: {
|
||||
label: "试标阶段",
|
||||
styleText: "text-[#21D3ED] bg-[#EEFDFF]",
|
||||
},
|
||||
500: {
|
||||
label: "标注中",
|
||||
styleText: "text-[#1874FF] bg-[#E8F0FC]",
|
||||
},
|
||||
580: {
|
||||
label: "待验收",
|
||||
styleText: "text-[#FFB84D] bg-[#FEF5E7]",
|
||||
},
|
||||
600: {
|
||||
label: "已完成",
|
||||
styleText: "text-[#4FD796] bg-[#E7F9F0]",
|
||||
},
|
||||
700: {
|
||||
label: "已终止",
|
||||
styleText: "text-[#34425F] bg-[#F7F8F9]",
|
||||
},
|
||||
}
|
||||
|
||||
export const staticsColor = [
|
||||
{ "8_151_156": 1 },
|
||||
{ "9_109_217": 2 },
|
||||
{ "15_15_118": 3 },
|
||||
{ "28_27_175": 4 },
|
||||
{ "35_84_116": 5 },
|
||||
{ "45_69_235": 6 },
|
||||
{ "53_127_127": 7 },
|
||||
{ "54_124_171": 8 },
|
||||
{ "54_207_201": 9 },
|
||||
{ "56_158_13": 10 },
|
||||
{ "63_18_119": 11 },
|
||||
{ "69_151_223": 12 },
|
||||
{ "81_188_187": 13 },
|
||||
{ "83_29_171": 14 },
|
||||
{ "84_119_48": 15 },
|
||||
{ "95_33_176": 16 },
|
||||
{ "100_16_23": 17 },
|
||||
{ "113_249_251": 18 },
|
||||
{ "116_78_41": 19 },
|
||||
{ "125_179_75": 20 },
|
||||
{ "127_48_236": 21 },
|
||||
{ "127_127_54": 22 },
|
||||
{ "132_133_239": 23 },
|
||||
{ "147_210_239": 24 },
|
||||
{ "151_29_36": 25 },
|
||||
{ "155_125_62": 26 },
|
||||
{ "156_239_102": 27 },
|
||||
{ "161_98_48": 28 },
|
||||
{ "178_253_253": 29 },
|
||||
{ "188_136_242": 30 },
|
||||
{ "189_191_84": 31 },
|
||||
{ "191_49_47": 32 },
|
||||
{ "196_29_127": 33 },
|
||||
{ "202_247_138": 34 },
|
||||
{ "203_164_81": 35 },
|
||||
{ "207_19_34": 36 },
|
||||
{ "212_107_8": 37 },
|
||||
{ "212_177_6": 38 },
|
||||
{ "223_134_144": 39 },
|
||||
{ "233_157_84": 40 },
|
||||
{ "244_206_159": 41 },
|
||||
{ "252_202_0": 42 },
|
||||
{ "252_250_147": 43 },
|
||||
{ "253_247_87": 44 },
|
||||
{ "255_191_120": 45 },
|
||||
{ "255_236_61": 46 },
|
||||
]
|
||||
|
||||
export const labelTypeOpts = [
|
||||
{ label: "点云框", value: 1 },
|
||||
{ label: "点云分割", value: 2 },
|
||||
// { label: '点云分类', value: '点云分类' },
|
||||
{ label: "2D框", value: 101 },
|
||||
{ label: "多边形", value: 102 },
|
||||
{ label: "关键点", value: 103 },
|
||||
{ label: "多线段", value: 104 },
|
||||
{ label: "文本描述", value: 201 },
|
||||
]
|
||||
|
||||
export const labelTypeMap = new Map([
|
||||
[1, "点云框"],
|
||||
[2, "点云分割"],
|
||||
[101, "2D框"],
|
||||
[102, "多边形"],
|
||||
[103, "关键点"],
|
||||
[104, "多线段"],
|
||||
[201, "文本描述"],
|
||||
])
|
||||
|
||||
export const selectModeOpts = [
|
||||
{ label: "输入", value: 0 },
|
||||
{ label: "单选", value: 1 },
|
||||
{ label: "多选", value: 2 },
|
||||
]
|
||||
|
||||
export const selectModeMap = new Map([
|
||||
[0, "输入"],
|
||||
[1, "单选"],
|
||||
[2, "多选"],
|
||||
])
|
||||
|
||||
// 标注任务状态码
|
||||
export const TaskStatusEnum = new Map([
|
||||
// [0, "None"],
|
||||
[1, "待领取"],
|
||||
[2, "标注中"],
|
||||
[3, "标注完成"],
|
||||
[4, "审核中"],
|
||||
[5, "审核完成"],
|
||||
[6, "复审中"],
|
||||
[7, "复审完成"],
|
||||
[8, "无法标注"],
|
||||
])
|
||||
|
||||
export const OwnTaskStatusOpts = {
|
||||
0: "全部",
|
||||
2: "标注中",
|
||||
4: "审核中",
|
||||
6: "复审中",
|
||||
}
|
||||
|
||||
export const TaskStatusOpts = {
|
||||
0: "全部",
|
||||
1: "待领取",
|
||||
2: "标注中",
|
||||
3: "标注完成",
|
||||
4: "审核中",
|
||||
5: "审核完成",
|
||||
6: "复审中",
|
||||
7: "复审完成",
|
||||
8: "无法标注",
|
||||
}
|
||||
164
components/label/utils/paperjs.ts
Normal file
164
components/label/utils/paperjs.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import paper from "paper"
|
||||
|
||||
const scaleFlatPoint = (point: any, scale: number): [number, number] => {
|
||||
return [point[0] * scale, point[1] * scale]
|
||||
}
|
||||
|
||||
const scaleSegmentToPoint = (seg: any, scale: number): any => {
|
||||
if (seg instanceof paper.Point) {
|
||||
return [seg.x * scale, seg.y * scale]
|
||||
}
|
||||
if (seg instanceof paper.Segment) {
|
||||
return [seg.point.x * scale, seg.point.y * scale]
|
||||
}
|
||||
if (
|
||||
Array.isArray(seg) &&
|
||||
seg.length >= 2 &&
|
||||
typeof seg[0] === "number" &&
|
||||
typeof seg[1] === "number"
|
||||
) {
|
||||
return scaleFlatPoint(seg, scale)
|
||||
}
|
||||
if (
|
||||
Array.isArray(seg) &&
|
||||
Array.isArray(seg[0]) &&
|
||||
seg[0].length >= 2 &&
|
||||
typeof seg[0][0] === "number" &&
|
||||
typeof seg[0][1] === "number"
|
||||
) {
|
||||
// Support paper JSON segment format: [[x,y], [handleInX, handleInY], [handleOutX, handleOutY]]
|
||||
return scaleFlatPoint(seg[0], scale)
|
||||
}
|
||||
if (
|
||||
seg?.point &&
|
||||
typeof seg.point.x === "number" &&
|
||||
typeof seg.point.y === "number"
|
||||
) {
|
||||
return [seg.point.x * scale, seg.point.y * scale]
|
||||
}
|
||||
if (
|
||||
seg?.point &&
|
||||
Array.isArray(seg.point) &&
|
||||
seg.point.length >= 2 &&
|
||||
typeof seg.point[0] === "number" &&
|
||||
typeof seg.point[1] === "number"
|
||||
) {
|
||||
return [seg.point[0] * scale, seg.point[1] * scale]
|
||||
}
|
||||
return seg
|
||||
}
|
||||
|
||||
// 调整原始比例点位到当前图片的缩放比例
|
||||
export const adjustPoints: (
|
||||
id: string,
|
||||
nestedMap: Map<string, Map<string, [number, any[], any, any[]][]>>,
|
||||
scale: number
|
||||
) => Map<string, Map<string, [number, any[], any, any[]][]>> = (
|
||||
id,
|
||||
nestedMap,
|
||||
scale
|
||||
) => {
|
||||
const adjustedMap = new Map<
|
||||
string,
|
||||
Map<string, [number, any[], any, any[]][]>
|
||||
>()
|
||||
nestedMap.forEach((innerMap, outerKey) => {
|
||||
if (id === outerKey) {
|
||||
const adjustedInnerMap = new Map<
|
||||
string,
|
||||
Array<[number, any[], any, any[]]>
|
||||
>()
|
||||
innerMap.forEach((array, innerKey) => {
|
||||
const adjustedArray = array.map(
|
||||
([id, points, detail, hollowPoints]) => {
|
||||
// let adjustedPoints = points.map((point) => {
|
||||
// let flag = point instanceof paper.Point;
|
||||
// if (flag) {
|
||||
// return [point.x * scale, point.y * scale];
|
||||
// } else {
|
||||
// return point.map((item: any) => item * scale);
|
||||
// }
|
||||
// });
|
||||
|
||||
let adjustedPoints = points.map((child) => {
|
||||
return child.map((seg: any) => {
|
||||
return scaleSegmentToPoint(seg, scale)
|
||||
})
|
||||
})
|
||||
|
||||
let adjustedHollowPoints = hollowPoints.map((child) => {
|
||||
return child.map((seg: any) => {
|
||||
return scaleSegmentToPoint(seg, scale)
|
||||
})
|
||||
})
|
||||
|
||||
return [id, adjustedPoints, detail, adjustedHollowPoints]
|
||||
}
|
||||
)
|
||||
adjustedInnerMap.set(innerKey, adjustedArray as any)
|
||||
})
|
||||
adjustedMap.set(outerKey, adjustedInnerMap)
|
||||
} else {
|
||||
adjustedMap.set(outerKey, innerMap)
|
||||
}
|
||||
})
|
||||
|
||||
return adjustedMap
|
||||
}
|
||||
|
||||
// 根据比例调整不同图片的点位数据
|
||||
export const adjustAllPoints: (
|
||||
nestedMap: Map<string, Map<string, [number, any[], any, any[]][]>>,
|
||||
allScale: { [x: string]: number }
|
||||
) => Map<string, Map<string, [number, any[], any, any[]][]>> = (
|
||||
nestedMap,
|
||||
allScale
|
||||
) => {
|
||||
const adjustedMap = new Map<
|
||||
string,
|
||||
Map<string, [number, any[], any, any[]][]>
|
||||
>()
|
||||
nestedMap.forEach((innerMap, outerKey) => {
|
||||
const scale = allScale[outerKey]
|
||||
if (scale) {
|
||||
const adjustedInnerMap = new Map<
|
||||
string,
|
||||
Array<[number, any[], any, any[]]>
|
||||
>()
|
||||
innerMap.forEach((array, innerKey) => {
|
||||
const adjustedArray = array.map(
|
||||
([id, points, detail, hollowPoints]) => {
|
||||
// let adjustedPoints = points.map((point) => {
|
||||
// let flag = point instanceof paper.Point;
|
||||
// if (flag) {
|
||||
// return [point.x * scale, point.y * scale];
|
||||
// } else {
|
||||
// return point.map((item: any) => item * scale);
|
||||
// }
|
||||
// });
|
||||
|
||||
let adjustedPoints = points.map((child) => {
|
||||
return child.map((seg: any) => {
|
||||
return scaleSegmentToPoint(seg, scale)
|
||||
})
|
||||
})
|
||||
|
||||
let adjustedHollowPoints = hollowPoints.map((child) => {
|
||||
return child.map((seg: any) => {
|
||||
return scaleSegmentToPoint(seg, scale)
|
||||
})
|
||||
})
|
||||
|
||||
return [id, adjustedPoints, detail, adjustedHollowPoints]
|
||||
}
|
||||
)
|
||||
adjustedInnerMap.set(innerKey, adjustedArray as any)
|
||||
})
|
||||
adjustedMap.set(outerKey, adjustedInnerMap)
|
||||
} else {
|
||||
adjustedMap.set(outerKey, innerMap)
|
||||
}
|
||||
})
|
||||
|
||||
return adjustedMap
|
||||
}
|
||||
15
components/label/utils/util.ts
Normal file
15
components/label/utils/util.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export const enterFullscreen = (element: any) => {
|
||||
if (element?.requestFullscreen) {
|
||||
element.requestFullscreen()
|
||||
} else if (element?.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen()
|
||||
} else if (element?.msRequestFullscreen) {
|
||||
element.msRequestFullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
export const exitFullscreen = () => {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user