fix(label): label result
This commit is contained in:
@@ -14,7 +14,12 @@ import { processVideo } from "@/app/component/label/video2image/processVideo"
|
|||||||
import { Box, Flex, LoadingOverlay, Stack } from "@mantine/core"
|
import { Box, Flex, LoadingOverlay, Stack } from "@mantine/core"
|
||||||
import { showNotification } from "@mantine/notifications"
|
import { showNotification } from "@mantine/notifications"
|
||||||
import { getLabelResult, getServerImage } from "./api/label"
|
import { getLabelResult, getServerImage } from "./api/label"
|
||||||
import { Comment, WorkLoad } from "./api/label/typing"
|
import {
|
||||||
|
Comment,
|
||||||
|
ImageObjects,
|
||||||
|
LabelResult,
|
||||||
|
WorkLoad,
|
||||||
|
} from "./api/label/typing"
|
||||||
import { getProjectDetail } from "./api/project"
|
import { getProjectDetail } from "./api/project"
|
||||||
import { Project } from "./api/project/typing"
|
import { Project } from "./api/project/typing"
|
||||||
import { getTaskList, getTaskWorkFlow } from "./api/task"
|
import { getTaskList, getTaskWorkFlow } from "./api/task"
|
||||||
@@ -91,6 +96,29 @@ const normalizeNamePrefix = (name: string) => {
|
|||||||
return value || "video"
|
return value || "video"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ResultImageEntry {
|
||||||
|
frameName: string
|
||||||
|
imageObjects: ImageObjects
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandLabelResultImages = (item: LabelResult): ResultImageEntry[] => {
|
||||||
|
if (item.image_objects) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
frameName: item.data_name,
|
||||||
|
imageObjects: item.image_objects,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (!item.video_objects) return []
|
||||||
|
return Object.entries(item.video_objects).map(([frameName, imageObjects]) => {
|
||||||
|
return {
|
||||||
|
frameName,
|
||||||
|
imageObjects,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
interface LabelProps {
|
interface LabelProps {
|
||||||
headerHeight: number
|
headerHeight: number
|
||||||
leftWidth: number
|
leftWidth: number
|
||||||
@@ -237,84 +265,95 @@ const LabelPage = ({
|
|||||||
|
|
||||||
const normalizeVideoTaskData = useCallback(
|
const normalizeVideoTaskData = useCallback(
|
||||||
async (detail: Task.DataProps) => {
|
async (detail: Task.DataProps) => {
|
||||||
try {
|
const sourceNames = detail.data_name
|
||||||
const sourceNames = detail.data_name
|
.map((item) => item?.name?.[0] || "")
|
||||||
.map((item) => item?.name?.[0] || "")
|
.filter((name) => !!name)
|
||||||
.filter((name) => !!name)
|
if (!sourceNames.length || !projectId || !taskId) return detail
|
||||||
if (!sourceNames.length || !projectId) return detail
|
|
||||||
|
|
||||||
const taskDataType = Number(
|
const taskDataType = Number(
|
||||||
(detail as { [key: string]: any })?.data_type ??
|
(detail as { [key: string]: any })?.data_type ??
|
||||||
(detail as { [key: string]: any })?.task_data_type ??
|
(detail as { [key: string]: any })?.task_data_type ??
|
||||||
-1
|
-1
|
||||||
)
|
)
|
||||||
const isVideoTask =
|
const isVideoTask =
|
||||||
taskDataType === 2 ||
|
taskDataType === 2 ||
|
||||||
sourceNames.some((name) => videoExtPattern.test(name))
|
taskDataType === 7 ||
|
||||||
if (!isVideoTask) return detail
|
sourceNames.some((name) => videoExtPattern.test(name))
|
||||||
|
if (!isVideoTask) return detail
|
||||||
|
|
||||||
const imagesMap = new Map(useImagesStore.getState().images)
|
const imagesMap = new Map(useImagesStore.getState().images)
|
||||||
let imageMapUpdated = false
|
const frameMapUpdates = new Map<string, string[]>()
|
||||||
const normalizedDataName: Task.DataProps["data_name"] = []
|
let imageMapUpdated = false
|
||||||
|
const normalizedDataName: Task.DataProps["data_name"] = []
|
||||||
|
|
||||||
for (const videoName of sourceNames) {
|
for (const videoName of sourceNames) {
|
||||||
const cacheKey = `${projectId}:${taskId}:${videoName}`
|
const cacheKey = `${projectId}:${taskId}:${videoName}`
|
||||||
let frameNames =
|
let frameNames =
|
||||||
useVideoFrameStore.getState().videoFrames.get(cacheKey) ?? []
|
useVideoFrameStore.getState().videoFrames.get(cacheKey) ?? []
|
||||||
|
|
||||||
const hasAllCachedFrames =
|
const hasAllCachedFrames =
|
||||||
frameNames.length > 0 &&
|
frameNames.length > 0 &&
|
||||||
frameNames.every((frameName) => {
|
frameNames.every((frameName) => {
|
||||||
const cache = imagesMap.get(frameName)
|
const cache = imagesMap.get(frameName)
|
||||||
return typeof cache === "string" && cache.length > 0
|
return typeof cache === "string" && cache.length > 0
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!hasAllCachedFrames) {
|
if (!hasAllCachedFrames) {
|
||||||
|
let frameData: Awaited<ReturnType<typeof processVideo>> = []
|
||||||
|
try {
|
||||||
const base64Video = await getServerImage({
|
const base64Video = await getServerImage({
|
||||||
data_names: [videoName],
|
data_names: [videoName],
|
||||||
data_type: 2,
|
data_type: 2,
|
||||||
project_id: projectId,
|
project_id: projectId,
|
||||||
})
|
})
|
||||||
const frameData = await processVideo({
|
frameData = await processVideo({
|
||||||
base64Data: String(base64Video || ""),
|
base64Data: String(base64Video || ""),
|
||||||
fileName: videoName,
|
fileName: videoName,
|
||||||
namePrefix: normalizeNamePrefix(
|
namePrefix: normalizeNamePrefix(
|
||||||
`${projectId}_${taskId}_${videoName.replace(/\.[^.]+$/, "")}`
|
`${projectId}_${taskId}_${videoName.replace(/\.[^.]+$/, "")}`
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
frameNames = frameData.map((frame) => frame.name)
|
} catch (error) {
|
||||||
frameData.forEach((frame) => {
|
const message = error instanceof Error ? error.message : "未知错误"
|
||||||
imagesMap.set(frame.name, frame.dataUrl)
|
throw new Error(`视频帧生成失败:${videoName}(${message})`)
|
||||||
})
|
|
||||||
imageMapUpdated = true
|
|
||||||
useVideoFrameStore.getState().setVideoFrames(cacheKey, frameNames)
|
|
||||||
}
|
}
|
||||||
|
frameNames = frameData.map((frame) => frame.name)
|
||||||
frameNames.forEach((frameName) => {
|
if (!frameNames.length) {
|
||||||
normalizedDataName.push({
|
throw new Error(`视频帧生成失败:${videoName}`)
|
||||||
name: [frameName] as [string],
|
}
|
||||||
related_images: [],
|
frameData.forEach((frame) => {
|
||||||
})
|
imagesMap.set(frame.name, frame.dataUrl)
|
||||||
})
|
})
|
||||||
|
imageMapUpdated = true
|
||||||
|
frameMapUpdates.set(cacheKey, frameNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageMapUpdated) {
|
if (!frameNames.length) {
|
||||||
useImagesStore.getState().setImages(imagesMap)
|
throw new Error(`视频帧映射为空:${videoName}`)
|
||||||
}
|
}
|
||||||
if (!normalizedDataName.length) return detail
|
|
||||||
|
|
||||||
return {
|
frameNames.forEach((frameName) => {
|
||||||
...detail,
|
normalizedDataName.push({
|
||||||
data_name: normalizedDataName,
|
name: [frameName] as [string],
|
||||||
}
|
related_images: [],
|
||||||
} catch (error) {
|
})
|
||||||
console.log(error)
|
|
||||||
showNotification({
|
|
||||||
color: "red",
|
|
||||||
title: "视频解码失败",
|
|
||||||
message: "视频帧生成失败,已回退到原始数据",
|
|
||||||
})
|
})
|
||||||
return detail
|
}
|
||||||
|
|
||||||
|
if (!normalizedDataName.length) {
|
||||||
|
throw new Error("未生成可用视频帧")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageMapUpdated) {
|
||||||
|
useImagesStore.getState().setImages(imagesMap)
|
||||||
|
}
|
||||||
|
frameMapUpdates.forEach((frameNames, cacheKey) => {
|
||||||
|
useVideoFrameStore.getState().setVideoFrames(cacheKey, frameNames)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...detail,
|
||||||
|
data_name: normalizedDataName,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[projectId, taskId]
|
[projectId, taskId]
|
||||||
@@ -385,6 +424,9 @@ const LabelPage = ({
|
|||||||
// 获取并处理当前任务标注结果数据
|
// 获取并处理当前任务标注结果数据
|
||||||
const dataRes = await getLabelResult(taskId)
|
const dataRes = await getLabelResult(taskId)
|
||||||
const { results } = dataRes
|
const { results } = dataRes
|
||||||
|
const resultImageEntries = results.flatMap((item) =>
|
||||||
|
expandLabelResultImages(item)
|
||||||
|
)
|
||||||
|
|
||||||
let currentWorkTime = 0
|
let currentWorkTime = 0
|
||||||
if (label_status === 2) {
|
if (label_status === 2) {
|
||||||
@@ -411,75 +453,75 @@ const LabelPage = ({
|
|||||||
review_key_frame_size: {},
|
review_key_frame_size: {},
|
||||||
question_size: {},
|
question_size: {},
|
||||||
}
|
}
|
||||||
results.forEach((item) => {
|
resultImageEntries.forEach(({ frameName, imageObjects }) => {
|
||||||
const { image_objects } = item
|
const { annotations, images, key_frame, desc } = imageObjects
|
||||||
if (image_objects) {
|
const rasterName = frameName || images.file_name
|
||||||
const { annotations, images, key_frame, desc } = image_objects
|
if (rasterName) {
|
||||||
usePaperStore
|
usePaperStore
|
||||||
.getState()
|
.getState()
|
||||||
.setRasterSize(images.file_name, [images.width, images.height])
|
.setRasterSize(rasterName, [images.width, images.height])
|
||||||
annotations.forEach((annotation) => {
|
}
|
||||||
let reviewed = false
|
annotations.forEach((annotation) => {
|
||||||
const { comment, prelabel, uid } = annotation
|
let reviewed = false
|
||||||
if (prelabel)
|
const { comment, prelabel, uid } = annotation
|
||||||
workLoad.prelabel_modify_size[uid]
|
if (prelabel)
|
||||||
? workLoad.prelabel_modify_size[uid]++
|
workLoad.prelabel_modify_size[uid]
|
||||||
: (workLoad.prelabel_modify_size[uid] = 1)
|
? workLoad.prelabel_modify_size[uid]++
|
||||||
else
|
: (workLoad.prelabel_modify_size[uid] = 1)
|
||||||
workLoad.object_size[uid]
|
else
|
||||||
? workLoad.object_size[uid]++
|
workLoad.object_size[uid]
|
||||||
: (workLoad.object_size[uid] = 1)
|
? workLoad.object_size[uid]++
|
||||||
if (comment) {
|
: (workLoad.object_size[uid] = 1)
|
||||||
comment.forEach((c) => {
|
if (comment) {
|
||||||
if (c.comment_type > 0 && c.uid === user_id) reviewed = true
|
comment.forEach((c) => {
|
||||||
if (c.comment_type === 2 && c.uid === user_id)
|
if (c.comment_type > 0 && c.uid === user_id) reviewed = true
|
||||||
workLoad.comment_size++
|
if (c.comment_type === 2 && c.uid === user_id)
|
||||||
})
|
workLoad.comment_size++
|
||||||
}
|
})
|
||||||
if (reviewed) workLoad.review_size++
|
|
||||||
})
|
|
||||||
if (key_frame) {
|
|
||||||
if (label_status === 2) {
|
|
||||||
let id = image_objects.label1_uid!
|
|
||||||
if (id)
|
|
||||||
workLoad.key_frame_size[id]
|
|
||||||
? workLoad.key_frame_size[id]++
|
|
||||||
: (workLoad.key_frame_size[id] = 1)
|
|
||||||
} else if (label_status === 4) {
|
|
||||||
let id = image_objects.review1_uid!
|
|
||||||
if (id)
|
|
||||||
workLoad.review_key_frame_size[id]
|
|
||||||
? workLoad.review_key_frame_size[id]++
|
|
||||||
: (workLoad.review_key_frame_size[id] = 1)
|
|
||||||
} else if (label_status === 6) {
|
|
||||||
let id = image_objects.review2_uid!
|
|
||||||
if (id)
|
|
||||||
workLoad.review_key_frame_size[id]
|
|
||||||
? workLoad.review_key_frame_size[id]++
|
|
||||||
: (workLoad.review_key_frame_size[id] = 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (useDescToolsStore.getState().qaOperations.length) {
|
if (reviewed) workLoad.review_size++
|
||||||
if (desc) {
|
})
|
||||||
const { other_desc } = desc
|
if (key_frame) {
|
||||||
const descData = other_desc ? JSON.parse(other_desc) : {}
|
if (label_status === 2) {
|
||||||
Object.values(descData).forEach((questionArr: any) => {
|
let id = imageObjects.label1_uid!
|
||||||
questionArr.forEach((q: any) => {
|
if (id)
|
||||||
const v: any = Object.values(q)[0]
|
workLoad.key_frame_size[id]
|
||||||
if (v.uid) {
|
? workLoad.key_frame_size[id]++
|
||||||
workLoad.question_size[v.uid]
|
: (workLoad.key_frame_size[id] = 1)
|
||||||
? workLoad.question_size[v.uid]++
|
} else if (label_status === 4) {
|
||||||
: (workLoad.question_size[v.uid] = 1)
|
let id = imageObjects.review1_uid!
|
||||||
}
|
if (id)
|
||||||
if (v.tag) {
|
workLoad.review_key_frame_size[id]
|
||||||
const uid = usePermissionStore.getState().user_id
|
? workLoad.review_key_frame_size[id]++
|
||||||
workLoad.review_question_size[uid]
|
: (workLoad.review_key_frame_size[id] = 1)
|
||||||
? workLoad.review_question_size[uid]++
|
} else if (label_status === 6) {
|
||||||
: (workLoad.review_question_size[uid] = 1)
|
let id = imageObjects.review2_uid!
|
||||||
}
|
if (id)
|
||||||
})
|
workLoad.review_key_frame_size[id]
|
||||||
|
? workLoad.review_key_frame_size[id]++
|
||||||
|
: (workLoad.review_key_frame_size[id] = 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useDescToolsStore.getState().qaOperations.length) {
|
||||||
|
if (desc) {
|
||||||
|
const { other_desc } = desc
|
||||||
|
const descData = other_desc ? JSON.parse(other_desc) : {}
|
||||||
|
Object.values(descData).forEach((questionArr: any) => {
|
||||||
|
questionArr.forEach((q: any) => {
|
||||||
|
const v: any = Object.values(q)[0]
|
||||||
|
if (v.uid) {
|
||||||
|
workLoad.question_size[v.uid]
|
||||||
|
? workLoad.question_size[v.uid]++
|
||||||
|
: (workLoad.question_size[v.uid] = 1)
|
||||||
|
}
|
||||||
|
if (v.tag) {
|
||||||
|
const uid = usePermissionStore.getState().user_id
|
||||||
|
workLoad.review_question_size[uid]
|
||||||
|
? workLoad.review_question_size[uid]++
|
||||||
|
: (workLoad.review_question_size[uid] = 1)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -492,180 +534,178 @@ const LabelPage = ({
|
|||||||
// 重置 PathGroupIds
|
// 重置 PathGroupIds
|
||||||
useRightToolsStore.getState().setPathGroupIds([])
|
useRightToolsStore.getState().setPathGroupIds([])
|
||||||
// 生成标注结果
|
// 生成标注结果
|
||||||
results.forEach((item) => {
|
resultImageEntries.forEach(({ frameName, imageObjects }) => {
|
||||||
const { data_name, image_objects } = item
|
const key = frameName
|
||||||
if (image_objects) {
|
if (!key) return
|
||||||
const categoryMap = new Map()
|
const categoryMap = new Map()
|
||||||
const { annotations, image_groups, desc } = image_objects
|
const { annotations, image_groups, desc } = imageObjects
|
||||||
// 赋值用过的 id
|
// 赋值用过的 id
|
||||||
annotations.map((annotation) => {
|
annotations.map((annotation) => {
|
||||||
useRightToolsStore.getState().pushPathId(annotation.id)
|
useRightToolsStore.getState().pushPathId(annotation.id)
|
||||||
|
})
|
||||||
|
// 赋值用过的 groupId
|
||||||
|
image_groups &&
|
||||||
|
Object.keys(image_groups)?.map((groupId) => {
|
||||||
|
useRightToolsStore.getState().pushPathGroupId(+groupId)
|
||||||
})
|
})
|
||||||
// 赋值用过的 groupId
|
annotations.forEach((annotation) => {
|
||||||
image_groups &&
|
const { id, category_id, comment } = annotation
|
||||||
Object.keys(image_groups)?.map((groupId) => {
|
let current_comment: Comment[] = []
|
||||||
useRightToolsStore.getState().pushPathGroupId(+groupId)
|
if (comment) {
|
||||||
})
|
current_comment = flowCommentArr.map((c) => {
|
||||||
annotations.forEach((annotation) => {
|
const { turn, times } = c
|
||||||
const { id, category_id, comment } = annotation
|
let modified_comment = null
|
||||||
let current_comment: Comment[] = []
|
comment.forEach((item) => {
|
||||||
if (comment) {
|
if (item.turn === turn && item.times === times)
|
||||||
current_comment = flowCommentArr.map((c) => {
|
modified_comment = item
|
||||||
const { turn, times } = c
|
|
||||||
let modified_comment = null
|
|
||||||
comment.forEach((item) => {
|
|
||||||
if (item.turn === turn && item.times === times)
|
|
||||||
modified_comment = item
|
|
||||||
})
|
|
||||||
return modified_comment ? modified_comment : c
|
|
||||||
})
|
})
|
||||||
} else {
|
return modified_comment ? modified_comment : c
|
||||||
current_comment = [...flowCommentArr]
|
})
|
||||||
}
|
} else {
|
||||||
let detail: any = {
|
current_comment = [...flowCommentArr]
|
||||||
...annotation,
|
|
||||||
comment: current_comment,
|
|
||||||
}
|
|
||||||
// group info
|
|
||||||
if (image_groups) {
|
|
||||||
const key = findGroupKey(image_groups as any, id)
|
|
||||||
if (key !== -1) detail.parentGroupId = key
|
|
||||||
}
|
|
||||||
const categoryKey = `${category_id}`
|
|
||||||
let arr = categoryMap.has(categoryKey)
|
|
||||||
? categoryMap.get(categoryKey)
|
|
||||||
: []
|
|
||||||
if (annotation.rect) {
|
|
||||||
// arr.push([id, getRectPoints(annotation.rect), detail, []]);
|
|
||||||
arr.push([id, [getRectPoints(annotation.rect)], detail, []])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (annotation.line_segment) {
|
|
||||||
arr.push([
|
|
||||||
id,
|
|
||||||
annotation.line_segment.map((item) => getSegmentPoints(item)),
|
|
||||||
detail,
|
|
||||||
[],
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (annotation.segmentation) {
|
|
||||||
arr.push([
|
|
||||||
id,
|
|
||||||
// getSegmentPoints(annotation.segmentation[0]),
|
|
||||||
annotation.segmentation.map((item) => getSegmentPoints(item)),
|
|
||||||
detail,
|
|
||||||
annotation.hollow_segmentation?.map((item) =>
|
|
||||||
getSegmentPoints(item)
|
|
||||||
) || [],
|
|
||||||
])
|
|
||||||
}
|
|
||||||
if (annotation.key_points) {
|
|
||||||
arr.push([
|
|
||||||
id,
|
|
||||||
// getSegmentPoints(annotation.segmentation[0]),
|
|
||||||
[
|
|
||||||
getSegmentPoints(
|
|
||||||
annotation.key_points.map((item) => item.point)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
Object.assign(detail, {
|
|
||||||
circles: annotation.key_points,
|
|
||||||
}),
|
|
||||||
[],
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
categoryMap.set(categoryKey, arr)
|
|
||||||
})
|
|
||||||
const key = data_name
|
|
||||||
taskLabelData.set(key, categoryMap)
|
|
||||||
// 关键帧信息
|
|
||||||
const keyFrame = {
|
|
||||||
key_frame: image_objects.key_frame || false,
|
|
||||||
label1_ts: image_objects.label1_ts || 0,
|
|
||||||
label1_uid: image_objects.label1_uid || 0,
|
|
||||||
review1_ts: image_objects.review1_ts || 0,
|
|
||||||
review1_uid: image_objects.review1_uid || 0,
|
|
||||||
review2_ts: image_objects.review2_ts || 0,
|
|
||||||
review2_uid: image_objects.review2_uid || 0,
|
|
||||||
}
|
}
|
||||||
useBottomToolsStore.getState().setKeyFrameData(key, keyFrame)
|
let detail: any = {
|
||||||
// 大语言模型 两种数据情况
|
...annotation,
|
||||||
if (useDescToolsStore.getState().qaOperations.length) {
|
comment: current_comment,
|
||||||
if (desc) {
|
|
||||||
const { other_desc } = desc
|
|
||||||
let current_data: any = {}
|
|
||||||
const descData = other_desc ? JSON.parse(other_desc) : {}
|
|
||||||
Object.entries(descData).forEach(
|
|
||||||
([label_class, questionArr]: any) => {
|
|
||||||
let arr = questionArr.map((question: any) => {
|
|
||||||
let [name, val]: any = Object.entries(question)[0]
|
|
||||||
return {
|
|
||||||
[name]: {
|
|
||||||
...val,
|
|
||||||
value: val.value
|
|
||||||
? `${val.value}${splitWord}${val.value}`
|
|
||||||
: undefined,
|
|
||||||
comment: val.comment
|
|
||||||
? `${val.comment}${splitWord}${val.comment}`
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
current_data[label_class] = arr
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const qaData = useDescToolsStore.getState().qaData
|
|
||||||
// 无文本描述时 赋予预设值
|
|
||||||
if (JSON.stringify(current_data) === "{}") {
|
|
||||||
current_data = qaData.get("init")
|
|
||||||
}
|
|
||||||
const qaMap = structuredClone(qaData)
|
|
||||||
qaMap.set(key, current_data)
|
|
||||||
useDescToolsStore.getState().setQaData(qaMap)
|
|
||||||
}
|
|
||||||
} else if (useDescToolsStore.getState().descOperations.length) {
|
|
||||||
if (desc) {
|
|
||||||
const { meta_operation, other_desc } = desc
|
|
||||||
const currentMetaMap = structuredClone(
|
|
||||||
useDescToolsStore.getState().metaData
|
|
||||||
)
|
|
||||||
currentMetaMap.set(key, meta_operation || [])
|
|
||||||
useDescToolsStore.getState().setMetaData(currentMetaMap)
|
|
||||||
const currentDescMap = structuredClone(
|
|
||||||
useDescToolsStore.getState().descData
|
|
||||||
)
|
|
||||||
let descMap = new Map()
|
|
||||||
const descData = other_desc ? JSON.parse(other_desc) : {}
|
|
||||||
console.log(descData)
|
|
||||||
Object.entries(descData).forEach(
|
|
||||||
([label_class, { value, is_correct, comment }]: any) => {
|
|
||||||
descMap.set(label_class, {
|
|
||||||
value:
|
|
||||||
(typeof value === "string"
|
|
||||||
? `${value}${splitWord}${value}`
|
|
||||||
: value) || undefined,
|
|
||||||
is_correct: is_correct || undefined,
|
|
||||||
comment: comment || undefined,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
currentDescMap.set(key, descMap)
|
|
||||||
useDescToolsStore.getState().setDescData(currentDescMap)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
image_groups &&
|
// group info
|
||||||
pathGroupData.set(
|
if (image_groups) {
|
||||||
key,
|
const key = findGroupKey(image_groups as any, id)
|
||||||
new Map(
|
if (key !== -1) detail.parentGroupId = key
|
||||||
Object.entries(image_groups).map(([key, value]: any) => [
|
}
|
||||||
key * 1,
|
const categoryKey = `${category_id}`
|
||||||
value,
|
let arr = categoryMap.has(categoryKey)
|
||||||
])
|
? categoryMap.get(categoryKey)
|
||||||
)
|
: []
|
||||||
)
|
if (annotation.rect) {
|
||||||
|
// arr.push([id, getRectPoints(annotation.rect), detail, []]);
|
||||||
|
arr.push([id, [getRectPoints(annotation.rect)], detail, []])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (annotation.line_segment) {
|
||||||
|
arr.push([
|
||||||
|
id,
|
||||||
|
annotation.line_segment.map((item) => getSegmentPoints(item)),
|
||||||
|
detail,
|
||||||
|
[],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (annotation.segmentation) {
|
||||||
|
arr.push([
|
||||||
|
id,
|
||||||
|
// getSegmentPoints(annotation.segmentation[0]),
|
||||||
|
annotation.segmentation.map((item) => getSegmentPoints(item)),
|
||||||
|
detail,
|
||||||
|
annotation.hollow_segmentation?.map((item) =>
|
||||||
|
getSegmentPoints(item)
|
||||||
|
) || [],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
if (annotation.key_points) {
|
||||||
|
arr.push([
|
||||||
|
id,
|
||||||
|
// getSegmentPoints(annotation.segmentation[0]),
|
||||||
|
[
|
||||||
|
getSegmentPoints(
|
||||||
|
annotation.key_points.map((item) => item.point)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Object.assign(detail, {
|
||||||
|
circles: annotation.key_points,
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryMap.set(categoryKey, arr)
|
||||||
|
})
|
||||||
|
taskLabelData.set(key, categoryMap)
|
||||||
|
// 关键帧信息
|
||||||
|
const keyFrame = {
|
||||||
|
key_frame: imageObjects.key_frame || false,
|
||||||
|
label1_ts: imageObjects.label1_ts || 0,
|
||||||
|
label1_uid: imageObjects.label1_uid || 0,
|
||||||
|
review1_ts: imageObjects.review1_ts || 0,
|
||||||
|
review1_uid: imageObjects.review1_uid || 0,
|
||||||
|
review2_ts: imageObjects.review2_ts || 0,
|
||||||
|
review2_uid: imageObjects.review2_uid || 0,
|
||||||
}
|
}
|
||||||
|
useBottomToolsStore.getState().setKeyFrameData(key, keyFrame)
|
||||||
|
// 大语言模型 两种数据情况
|
||||||
|
if (useDescToolsStore.getState().qaOperations.length) {
|
||||||
|
if (desc) {
|
||||||
|
const { other_desc } = desc
|
||||||
|
let current_data: any = {}
|
||||||
|
const descData = other_desc ? JSON.parse(other_desc) : {}
|
||||||
|
Object.entries(descData).forEach(
|
||||||
|
([label_class, questionArr]: any) => {
|
||||||
|
let arr = questionArr.map((question: any) => {
|
||||||
|
let [name, val]: any = Object.entries(question)[0]
|
||||||
|
return {
|
||||||
|
[name]: {
|
||||||
|
...val,
|
||||||
|
value: val.value
|
||||||
|
? `${val.value}${splitWord}${val.value}`
|
||||||
|
: undefined,
|
||||||
|
comment: val.comment
|
||||||
|
? `${val.comment}${splitWord}${val.comment}`
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
current_data[label_class] = arr
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const qaData = useDescToolsStore.getState().qaData
|
||||||
|
// 无文本描述时 赋予预设值
|
||||||
|
if (JSON.stringify(current_data) === "{}") {
|
||||||
|
current_data = qaData.get("init")
|
||||||
|
}
|
||||||
|
const qaMap = structuredClone(qaData)
|
||||||
|
qaMap.set(key, current_data)
|
||||||
|
useDescToolsStore.getState().setQaData(qaMap)
|
||||||
|
}
|
||||||
|
} else if (useDescToolsStore.getState().descOperations.length) {
|
||||||
|
if (desc) {
|
||||||
|
const { meta_operation, other_desc } = desc
|
||||||
|
const currentMetaMap = structuredClone(
|
||||||
|
useDescToolsStore.getState().metaData
|
||||||
|
)
|
||||||
|
currentMetaMap.set(key, meta_operation || [])
|
||||||
|
useDescToolsStore.getState().setMetaData(currentMetaMap)
|
||||||
|
const currentDescMap = structuredClone(
|
||||||
|
useDescToolsStore.getState().descData
|
||||||
|
)
|
||||||
|
let descMap = new Map()
|
||||||
|
const descData = other_desc ? JSON.parse(other_desc) : {}
|
||||||
|
console.log(descData)
|
||||||
|
Object.entries(descData).forEach(
|
||||||
|
([label_class, { value, is_correct, comment }]: any) => {
|
||||||
|
descMap.set(label_class, {
|
||||||
|
value:
|
||||||
|
(typeof value === "string"
|
||||||
|
? `${value}${splitWord}${value}`
|
||||||
|
: value) || undefined,
|
||||||
|
is_correct: is_correct || undefined,
|
||||||
|
comment: comment || undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
currentDescMap.set(key, descMap)
|
||||||
|
useDescToolsStore.getState().setDescData(currentDescMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
image_groups &&
|
||||||
|
pathGroupData.set(
|
||||||
|
key,
|
||||||
|
new Map(
|
||||||
|
Object.entries(image_groups).map(([key, value]: any) => [
|
||||||
|
key * 1,
|
||||||
|
value,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
setLabel(taskLabelData)
|
setLabel(taskLabelData)
|
||||||
// 获取任务详情时设置状态栈初始值
|
// 获取任务详情时设置状态栈初始值
|
||||||
@@ -675,9 +715,18 @@ const LabelPage = ({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
setOldWorkLoad(null)
|
setOldWorkLoad(null)
|
||||||
|
setTaskDetail(undefined)
|
||||||
|
useBottomToolsStore.getState().setAllItems([])
|
||||||
setLabel(new Map())
|
setLabel(new Map())
|
||||||
setStateStack([])
|
setStateStack([])
|
||||||
useRightToolsStore.getState().setPathGroupMap(new Map())
|
useRightToolsStore.getState().setPathGroupMap(new Map())
|
||||||
|
if (err instanceof Error && err.message.includes("视频")) {
|
||||||
|
showNotification({
|
||||||
|
color: "red",
|
||||||
|
title: "视频解码失败",
|
||||||
|
message: err.message,
|
||||||
|
})
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
usePaperStore.getState().setLoadingData(false)
|
usePaperStore.getState().setLoadingData(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
export interface LabelResult {
|
export interface LabelResult {
|
||||||
data_name: string
|
data_name: string
|
||||||
image_objects?: ImageObjects
|
image_objects?: ImageObjects
|
||||||
|
video_objects?: {
|
||||||
|
[key: string]: ImageObjects
|
||||||
|
}
|
||||||
pc_box_objects?: { [key: string]: any }[]
|
pc_box_objects?: { [key: string]: any }[]
|
||||||
pc_segment_objects?: { [key: string]: any }[]
|
pc_segment_objects?: { [key: string]: any }[]
|
||||||
pc_vegetation_objects?: { [key: string]: any }[]
|
pc_vegetation_objects?: { [key: string]: any }[]
|
||||||
@@ -129,6 +132,7 @@ export interface DataList {
|
|||||||
data_name: string
|
data_name: string
|
||||||
data_type: string
|
data_type: string
|
||||||
image_data?: string
|
image_data?: string
|
||||||
|
video_data?: string
|
||||||
pointcloud_data?: PointcloudDatum[]
|
pointcloud_data?: PointcloudDatum[]
|
||||||
[property: string]: any
|
[property: string]: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ export namespace Task {
|
|||||||
id: number
|
id: number
|
||||||
create_date: string
|
create_date: string
|
||||||
data_type?: number
|
data_type?: number
|
||||||
task_data_type?: number
|
|
||||||
data_name: {
|
data_name: {
|
||||||
name: [string]
|
name: [string]
|
||||||
related_images: []
|
related_images: []
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ import {
|
|||||||
useKeyEventStore,
|
useKeyEventStore,
|
||||||
useLabelStore,
|
useLabelStore,
|
||||||
useObjectStore,
|
useObjectStore,
|
||||||
|
useVideoFrameStore,
|
||||||
} from "../store"
|
} from "../store"
|
||||||
import { useBackUrlStore, usePermissionStore } from "../store/auth"
|
import { useBackUrlStore, usePermissionStore } from "../store/auth"
|
||||||
import { useBottomToolsStore } from "../useBottomToolsStore"
|
import { useBottomToolsStore } from "../useBottomToolsStore"
|
||||||
@@ -450,7 +451,7 @@ const TopTools = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { label_status, data_name } = taskDetail!
|
const { label_status, data_name } = taskDetail!
|
||||||
let results = data_name.map((item, imageId) => {
|
const frameResults = data_name.map((item, imageId) => {
|
||||||
const name = item.name?.[0]
|
const name = item.name?.[0]
|
||||||
let finalData: LabelResult = {
|
let finalData: LabelResult = {
|
||||||
data_name: name,
|
data_name: name,
|
||||||
@@ -637,6 +638,90 @@ const TopTools = (
|
|||||||
finalData.image_objects = image_objects
|
finalData.image_objects = image_objects
|
||||||
return finalData
|
return finalData
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let saveResults: LabelResult[] = frameResults
|
||||||
|
if (projectDetail?.label_type === 7) {
|
||||||
|
const frameNamesFromTask = data_name
|
||||||
|
.map((item) => item.name?.[0] || "")
|
||||||
|
.filter((name) => !!name)
|
||||||
|
const frameNameSet = new Set(frameNamesFromTask)
|
||||||
|
const frameStore = useVideoFrameStore.getState().videoFrames
|
||||||
|
const taskVideoPrefix = `${taskDetail!.project_id}:${taskDetail!.id}:`
|
||||||
|
const videoFrameEntries = Array.from(frameStore.entries()).filter(
|
||||||
|
([key]) => key.startsWith(taskVideoPrefix)
|
||||||
|
)
|
||||||
|
if (!videoFrameEntries.length) {
|
||||||
|
throw new Error("视频帧映射缺失,无法保存,请刷新任务后重试")
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoFrameMap = new Map<string, string[]>()
|
||||||
|
videoFrameEntries.forEach(([key, frameNames]) => {
|
||||||
|
const videoName = key.slice(taskVideoPrefix.length)
|
||||||
|
if (!videoName) return
|
||||||
|
videoFrameMap.set(
|
||||||
|
videoName,
|
||||||
|
(frameNames || []).filter((frameName) =>
|
||||||
|
frameNameSet.has(frameName)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
if (!videoFrameMap.size) {
|
||||||
|
throw new Error("视频帧映射无效,无法保存,请刷新任务后重试")
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoNames = Array.from(videoFrameMap.keys())
|
||||||
|
const frameToVideoMap = new Map<string, string>()
|
||||||
|
videoFrameMap.forEach((frameNames, videoName) => {
|
||||||
|
frameNames.forEach((frameName) => {
|
||||||
|
frameToVideoMap.set(frameName, videoName)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const unresolvedFrames = frameNamesFromTask.filter(
|
||||||
|
(frameName) => !frameToVideoMap.has(frameName)
|
||||||
|
)
|
||||||
|
if (unresolvedFrames.length) {
|
||||||
|
throw new Error(
|
||||||
|
`视频帧映射缺失,无法保存,请刷新任务后重试: ${unresolvedFrames.slice(0, 5).join(", ")}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const frameResultMap = new Map<string, ImageObjects>()
|
||||||
|
frameResults.forEach((item) => {
|
||||||
|
if (item.image_objects) {
|
||||||
|
frameResultMap.set(item.data_name, item.image_objects)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const groupedVideoObjects = new Map<
|
||||||
|
string,
|
||||||
|
{ [key: string]: ImageObjects }
|
||||||
|
>()
|
||||||
|
frameResultMap.forEach((imageObjects, frameName) => {
|
||||||
|
const videoName = frameToVideoMap.get(frameName)
|
||||||
|
if (!videoName) return
|
||||||
|
if (!groupedVideoObjects.has(videoName)) {
|
||||||
|
groupedVideoObjects.set(videoName, {})
|
||||||
|
}
|
||||||
|
groupedVideoObjects.get(videoName)![frameName] = imageObjects
|
||||||
|
})
|
||||||
|
|
||||||
|
saveResults = videoNames
|
||||||
|
.map((videoName) => {
|
||||||
|
const videoObjects = groupedVideoObjects.get(videoName)
|
||||||
|
if (!videoObjects || !Object.keys(videoObjects).length) return null
|
||||||
|
return {
|
||||||
|
data_name: videoName,
|
||||||
|
video_objects: videoObjects,
|
||||||
|
} as LabelResult
|
||||||
|
})
|
||||||
|
.filter((item): item is LabelResult => !!item)
|
||||||
|
|
||||||
|
if (!saveResults.length) {
|
||||||
|
throw new Error("视频任务保存结果为空")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const workTime = {
|
const workTime = {
|
||||||
label_work_time: labelTime.label,
|
label_work_time: labelTime.label,
|
||||||
first_review_work_time: labelTime.review1,
|
first_review_work_time: labelTime.review1,
|
||||||
@@ -653,7 +738,7 @@ const TopTools = (
|
|||||||
}
|
}
|
||||||
const workLoad: WorkLoad = JSON.parse(JSON.stringify(initialWorkLoad))
|
const workLoad: WorkLoad = JSON.parse(JSON.stringify(initialWorkLoad))
|
||||||
workLoad.work_time = time ? time - oldWorkLoad.work_time : 0
|
workLoad.work_time = time ? time - oldWorkLoad.work_time : 0
|
||||||
results.forEach((item) => {
|
frameResults.forEach((item) => {
|
||||||
const { image_objects } = item
|
const { image_objects } = item
|
||||||
if (image_objects) {
|
if (image_objects) {
|
||||||
const { annotations, key_frame, desc } = image_objects
|
const { annotations, key_frame, desc } = image_objects
|
||||||
@@ -744,12 +829,9 @@ const TopTools = (
|
|||||||
let params: RequestLabelResult = {
|
let params: RequestLabelResult = {
|
||||||
uid: user_id,
|
uid: user_id,
|
||||||
task_id: taskDetail?.id || 0,
|
task_id: taskDetail?.id || 0,
|
||||||
results: results,
|
results: saveResults,
|
||||||
...defaultObj,
|
...defaultObj,
|
||||||
}
|
}
|
||||||
results.forEach((item) => {
|
|
||||||
console.log(item.image_objects)
|
|
||||||
})
|
|
||||||
console.log(params, workLoad)
|
console.log(params, workLoad)
|
||||||
await saveLabelResult(params)
|
await saveLabelResult(params)
|
||||||
notifications.show({
|
notifications.show({
|
||||||
@@ -760,6 +842,11 @@ const TopTools = (
|
|||||||
updateOldWorkLoad(workLoad)
|
updateOldWorkLoad(workLoad)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
notifications.hide("save")
|
||||||
|
notifications.show({
|
||||||
|
color: "red",
|
||||||
|
message: error?.message || "保存失败",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
labelCategories,
|
labelCategories,
|
||||||
|
|||||||
Reference in New Issue
Block a user