diff --git a/frontend/image-tool/src/businessNew/api/model.ts b/frontend/image-tool/src/businessNew/api/model.ts index 1e94be3e..ba0be086 100644 --- a/frontend/image-tool/src/businessNew/api/model.ts +++ b/frontend/image-tool/src/businessNew/api/model.ts @@ -43,6 +43,10 @@ export async function getModelResult(dataIds: string[], recordId: string) { args.push(`serialNo=${recordId}`); return await get(`${url}?${args.join('&')}`); } +export async function clearModel(dataIds: number[], recordId: string) { + let url = `/api/data/removeModelDataResult`; + let data = await post(url, { serialNo: recordId, dataIds }); +} export async function runModel(config: any) { const url = `${Api.DATA}/modelAnnotate`; return await post(url, config); diff --git a/frontend/image-tool/src/businessNew/common/Editor.ts b/frontend/image-tool/src/businessNew/common/Editor.ts index 80de562b..8ec67811 100644 --- a/frontend/image-tool/src/businessNew/common/Editor.ts +++ b/frontend/image-tool/src/businessNew/common/Editor.ts @@ -9,6 +9,7 @@ import { IModel, DataTypeEnum, LoadStatus, + ModelCodeEnum, } from 'image-editor'; import { getDefault } from '../state'; import { IBSState, ISaveResp } from '../types'; @@ -58,7 +59,7 @@ export default class Editor extends BaseEditor { if (saveFrames.length == 0) return; const { saveDatas } = utils.getDataFlowSaveData(this, saveFrames); - console.log('========> saveDataFlow saveDatas: ', saveDatas); + // console.log('========> saveDataFlow saveDatas: ', saveDatas); if (saveDatas.length === 0) return; bsState.doing.saving = true; @@ -116,6 +117,75 @@ export default class Editor extends BaseEditor { return !!(validSource && validClass); }; } + handleModel() { + const frame = this.getCurrentFrame(); + const model = frame.model; + if (!model || !model.code || model.state !== LoadStatus.COMPLETE) return; + const result = this.dataManager.hasModelResult(model.code, frame); + if (result) { + // api.clearModel([+frame.id], model.recordId); + this.addModelData(model.code); + } else { + this.runModel(); + } + } + async addModelData(modelCode: ModelCodeEnum) { + const frame = this.getCurrentFrame(); + if (!frame || !frame.model) return; + const { state } = this; + const data = this.dataManager.getModelResult(modelCode, frame); + if (!data) return; + const subTitle = this.lang('Add Results?'); + + const ok = await this.showConfirm({ + title: this.lang('Warning'), + subTitle, + okText: this.lang('confirm'), + cancelText: this.lang('cancel'), + okDanger: true, + }) + .then( + () => true, + () => false, + ) + .catch(() => false); + if (!ok) return; + await this.addInstanceResult(data); + frame.model = undefined; + frame.needSave = true; + this.dataManager.removeModelResult(frame.id); + this.emit(Event.MODEL_RESULT_ADD); + } + async addInstanceResult(data: any) { + const { objects, segmentFileUrl } = data; + if (!objects) { + const errTips = segmentFileUrl + ? '检测到有分割模型结果,若想要添加分割模型结果, 请先切换到分割模式' + : '无模型结果数据'; + this.handleErr(errTips); + return; + } + const annotates = utils.convertObject2Annotate( + this, + objects.map((o: any) => { + return { + classAttributes: Object.assign({ contour: o }, o.classAttributes || o || {}), + ...o, + }; + }), + ); + annotates.forEach((e) => { + this.initIDInfo(e); + }); + if (annotates.length > 0) { + this.cmdManager.withGroup(() => { + if (this.state.isSeriesFrame) { + this.cmdManager.execute('add-track', this.createTrackObj(annotates)); + } + this.cmdManager.execute('add-object', annotates); + }); + } + } async runModel() { const modelConfig = this.state.modelConfig; if (!modelConfig.model) { diff --git a/frontend/image-tool/src/businessNew/components/Operation/Classification/index.vue b/frontend/image-tool/src/businessNew/components/Operation/Classification/index.vue index 02f34f6a..9fb5ca87 100644 --- a/frontend/image-tool/src/businessNew/components/Operation/Classification/index.vue +++ b/frontend/image-tool/src/businessNew/components/Operation/Classification/index.vue @@ -44,7 +44,6 @@ }); function onAttChange(name: string, value: any) { - console.log(name, value); let dataInfo = editor.getCurrentFrame(); dataInfo.needSave = true; } diff --git a/frontend/image-tool/src/businessNew/utils/class.ts b/frontend/image-tool/src/businessNew/utils/class.ts index 56a87f8c..0ec0f978 100644 --- a/frontend/image-tool/src/businessNew/utils/class.ts +++ b/frontend/image-tool/src/businessNew/utils/class.ts @@ -124,3 +124,53 @@ export function classificationAssign(baseArr: IClassification[], values: any[]) }); return returnArr; } + +export function classificationToSave(classification: IClassification) { + let attrMap = {} as Record; + classification.attrs.forEach((attr) => { + attrMap[attr.id] = attr; + }); + let attrs = classification.attrs.filter((e) => isAttrVisible(e, attrMap) && isAttrHasValue(e)); + + // find leaf + attrs.forEach((e) => (e.leafFlag = true)); + attrs.forEach((e) => { + let parent = e.parent && attrMap[e.parent] ? attrMap[e.parent] : null; + if (parent) parent.leafFlag = false; + }); + let data = attrs.map((e) => { + const isParentMulti = e.parent && attrMap[e.parent]?.type === AttrType.MULTI_SELECTION; + return { + id: e.id, + pid: e.parent ? e.parent : null, + name: e.name, + value: e.value, + alias: e.label, + pvalue: isParentMulti ? e.parentValue : undefined, + type: e.type, + isLeaf: !!e.leafFlag, + }; + }); + + return data; +} +export function isAttrVisible( + attr: IClassificationAttr, + attrMap: Record, +): boolean { + if (!attr.parent) return true; + let parentAttr = attrMap[attr.parent]; + let visible = + parentAttr.type !== AttrType.MULTI_SELECTION + ? parentAttr.value === attr.parentValue + : (parentAttr.value as any[]).indexOf(attr.parentValue) >= 0; + + return visible && isAttrVisible(parentAttr, attrMap); +} +export function isAttrHasValue(attr: IClassificationAttr) { + if (attr.type === AttrType.MULTI_SELECTION) { + return Array.isArray(attr.value) && attr.value.length > 0; + } else { + return !!attr.value; + } +} diff --git a/frontend/image-tool/src/businessNew/utils/result-request.ts b/frontend/image-tool/src/businessNew/utils/result-request.ts index 538db497..155e665b 100644 --- a/frontend/image-tool/src/businessNew/utils/result-request.ts +++ b/frontend/image-tool/src/businessNew/utils/result-request.ts @@ -17,7 +17,7 @@ import { checkPoints } from './common'; export function convertObject2Annotate(editor: Editor, objects: IObjectInfo[]) { const annotates = [] as AnnotateObject[]; - + console.log(objects); objects.forEach((e: IObjectInfo) => { const obj = e.classAttributes; const contour = (obj.contour || {}) as IContour; diff --git a/frontend/image-tool/src/businessNew/utils/result-save.ts b/frontend/image-tool/src/businessNew/utils/result-save.ts index aee5b307..59aabfc2 100644 --- a/frontend/image-tool/src/businessNew/utils/result-save.ts +++ b/frontend/image-tool/src/businessNew/utils/result-save.ts @@ -2,7 +2,7 @@ * */ import Editor from '../common/Editor'; -import { IContour, IObject, IObjectBasicInfo, ISaveFormat } from '../types'; +import { IClassificationAttr, IContour, IObject, IObjectBasicInfo, ISaveFormat } from '../types'; import { AnnotateObject, IFrame, @@ -16,6 +16,7 @@ import { SourceType, } from 'image-editor'; import { checkPoints, empty, fixed, validNumber } from './common'; +import { classificationToSave } from './class'; function objToArray(obj: Record = {}, attrMap: Map) { const data = [] as any[]; @@ -134,9 +135,19 @@ export function getDataFlowSaveData(editor: Editor, frames?: IFrame[]) { }); }); }); - + const dataAnnotations: any[] = []; + frame.classifications.forEach((classification) => { + let values = classificationToSave(classification); + dataAnnotations.push({ + classificationId: classification.id, + classificationAttributes: { + id: classification.id, + values: values, + }, + }); + }); dataMap[id] = { - dataAnnotations: [], + dataAnnotations: dataAnnotations, dataId: id, objects: objectInfos, }; diff --git a/frontend/image-tool/src/package/image-editor/configs/tools/config.ts b/frontend/image-tool/src/package/image-editor/configs/tools/config.ts index fc1ec7b4..96626dad 100644 --- a/frontend/image-tool/src/package/image-editor/configs/tools/config.ts +++ b/frontend/image-tool/src/package/image-editor/configs/tools/config.ts @@ -15,7 +15,7 @@ export const toolMap: Record = { [ToolName.polygon]: polygonTool, [ToolName.polyline]: lineTool, [ToolName['key-point']]: keyPointTool, - // [ToolName.model]: modelTool, + [ToolName.model]: modelTool, }; // fixed tools @@ -30,7 +30,7 @@ const tools_graph: IToolItemConfig[] = [ // segment const tools_segment: IToolItemConfig[] = []; // model -const tools_model: IToolItemConfig[] = []; +const tools_model: IToolItemConfig[] = [toolMap[ToolName.model]]; // segment model const tools_model_seg: IToolItemConfig[] = []; // instance tools diff --git a/frontend/image-tool/src/package/image-editor/configs/tools/tool-model.ts b/frontend/image-tool/src/package/image-editor/configs/tools/tool-model.ts index 733b1962..f27dd078 100644 --- a/frontend/image-tool/src/package/image-editor/configs/tools/tool-model.ts +++ b/frontend/image-tool/src/package/image-editor/configs/tools/tool-model.ts @@ -1,14 +1,8 @@ -import { Editor, IToolItemConfig } from '../..'; -import { ToolName } from '../../types/enum'; +import { Editor, IToolItemConfig, UIType } from '../..'; +import { LoadStatus, ToolName } from '../../types/enum'; import ModelConfig from 'image-ui/components/Tools/components/ModelConfig.vue'; const hasMsg = (editor: Editor) => { - return false; - // const list = editor - // .getModelsByType(ModelTypeEnum.DETECTION) - // .filter((e) => e.code !== ModelCodeEnum.IMAGE_SAM_EMBEDDING); - // const result = list.find((e) => editor.modelManager.hasModelResult(e.code)); - // if (result) return true; // return false; }; /** @@ -19,12 +13,16 @@ export const modelTool: IToolItemConfig = { name: 'Smart Tool', hotkey: 'E', title: 'modelTips', - hasMsg, + hasMsg: (editor: Editor) => { + const frame = editor.getCurrentFrame(); + return frame?.model?.state === LoadStatus.COMPLETE; + }, extra: () => ModelConfig, extraClass: true, - getIcon: (editor?: Editor) => { + getIcon: (editor: Editor) => { const icon = ToolName.model; - return icon; + const frame = editor.getCurrentFrame(); + return frame?.model?.state === LoadStatus.LOADING ? 'loading' : icon; // if (!editor) return icon; // const frame = editor.getCurrentFrame(); // if (!frame || !frame.model) return icon; @@ -33,13 +31,14 @@ export const modelTool: IToolItemConfig = { // return isLoading && codes.includes(frame.model.code) ? 'loading' : icon; }, isDisplay: function (editor: Editor) { - return true; + return editor.state.modeConfig.ui[UIType.model]; // return ( // editor.state.modeConfig.ui[UIType.model] && // editor.getModelsByType(ModelTypeEnum.DETECTION, AnnotateModeEnum.INSTANCE).length > 0 // ); }, - isActive: function () { - return false; + isActive: function (editor: Editor) { + const frame = editor.getCurrentFrame(); + return frame?.model?.state === LoadStatus.LOADING; }, }; diff --git a/frontend/image-tool/src/package/image-editor/types/common.ts b/frontend/image-tool/src/package/image-editor/types/common.ts index 3d1b059e..cc5f7090 100644 --- a/frontend/image-tool/src/package/image-editor/types/common.ts +++ b/frontend/image-tool/src/package/image-editor/types/common.ts @@ -1,3 +1,4 @@ +import { IClassification } from '@/businessNew/types'; import { AnnotateModeEnum, LoadStatus, @@ -57,6 +58,7 @@ export interface IFrame { model?: IModelRunningState; // save needSave?: boolean; + classifications: IClassification[]; // other any [k: string]: any; } diff --git a/frontend/image-tool/src/package/image-editor/types/enum.ts b/frontend/image-tool/src/package/image-editor/types/enum.ts index e5ba222c..9c289467 100644 --- a/frontend/image-tool/src/package/image-editor/types/enum.ts +++ b/frontend/image-tool/src/package/image-editor/types/enum.ts @@ -23,7 +23,7 @@ export enum ToolName { polyline = 'polyline', // polyline tool, ToolType.POLYLINE 'key-point' = 'key-point', // key-point tool, ToolType.KEY_POINT // model - // model = 'model', + model = 'model', } export enum AnnotateModeEnum { INSTANCE = 'INSTANCE', diff --git a/frontend/image-tool/src/package/image-ui/components/Tools/useTool.ts b/frontend/image-tool/src/package/image-ui/components/Tools/useTool.ts index 163c4051..85aa928f 100644 --- a/frontend/image-tool/src/package/image-ui/components/Tools/useTool.ts +++ b/frontend/image-tool/src/package/image-ui/components/Tools/useTool.ts @@ -1,6 +1,13 @@ import { ref, watchEffect } from 'vue'; import { useInjectEditor } from '../../context'; -import { Event, ToolName, toolMap, IToolItemConfig, tools } from '../../../image-editor'; +import { + Event, + ToolName, + toolMap, + IToolItemConfig, + tools, + LoadStatus, +} from '../../../image-editor'; interface IToolConfig { toolItems: IToolItemConfig[]; fixedItems: IToolItemConfig[]; @@ -100,12 +107,10 @@ export default function useTool() { break; } case 'model': { - // if (editor.mainView.currentDrawTool?.doing()) { - // return editor.showMsg('warning', editor.lang('resultNotComplete')); - // } + const frame = editor.getCurrentFrame(); // const code = editor.state.modelConfig.code; - // if (!code) return; - // editor.handleModel(code); + if (frame?.model?.state !== LoadStatus.COMPLETE) return; + editor.handleModel(); break; } } diff --git a/frontend/image-tool/src/style/index.less b/frontend/image-tool/src/style/index.less index 98eaa870..95bcd67a 100644 --- a/frontend/image-tool/src/style/index.less +++ b/frontend/image-tool/src/style/index.less @@ -63,5 +63,6 @@ body { } .loading { + display: inline-block; animation: loading-360 0.8s infinite linear; }