From a49e51a6ab818c0d872545fe9ca65ef8cf45cc87 Mon Sep 17 00:00:00 2001 From: lijingchi Date: Tue, 30 Jan 2024 11:21:36 +0800 Subject: [PATCH 01/43] ci: Support beta version release --- .github/workflows/Js-sdk-release.github-ci.yml | 5 +++-- .releaserc.json | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Js-sdk-release.github-ci.yml b/.github/workflows/Js-sdk-release.github-ci.yml index ccb8fcfef..475846707 100644 --- a/.github/workflows/Js-sdk-release.github-ci.yml +++ b/.github/workflows/Js-sdk-release.github-ci.yml @@ -9,6 +9,7 @@ on: branches: - main - alpha + - beta workflow_dispatch: inputs: @@ -24,7 +25,7 @@ jobs: - name: install node js uses: actions/setup-node@v2 with: - node-version: '18.9.0' + node-version: "18.9.0" - name: npm install run: npm install - name: lerna bootstrap @@ -34,4 +35,4 @@ jobs: - name: semantic publish run: npm run semantic env: - WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }} \ No newline at end of file + WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }} diff --git a/.releaserc.json b/.releaserc.json index 7f9e96ab0..6c873bbb2 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -9,6 +9,10 @@ "name": "alpha", "prerelease": true }, + { + "name": "beta", + "prerelease": true + }, { "name": "dev", "prerelease": true From 3f020d8bfecefdab84f4f18e422ff6671ee2ec7c Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Wed, 24 Jan 2024 15:41:13 +0800 Subject: [PATCH 02/43] feat: NLP tool --- packages/lb-annotation/src/constant/tool.ts | 4 + .../src/components/NLPToolView/index.tsx | 105 ++++++++ .../NLPToolView/textContent/index.module.scss | 0 .../NLPToolView/textContent/index.tsx | 51 ++++ .../src/components/NLPToolView/types.ts | 30 +++ .../lb-components/src/data/enums/ToolType.ts | 1 + .../src/store/annotation/reducer.ts | 9 +- packages/lb-components/src/store/ctx.ts | 9 + .../src/views/MainView/NLPLayout/index.tsx | 54 ++++ .../src/views/MainView/index.tsx | 37 +-- .../MainView/sidebar/NLPSidebar/index.tsx | 35 +++ .../src/views/MainView/sidebar/index.tsx | 6 + packages/lb-demo/src/App.js | 13 +- packages/lb-demo/src/mock/NLPTool.js | 23 ++ packages/lb-demo/src/mock/index.js | 5 + packages/lb-demo/src/mock/taskConfig.js | 237 ++++++++++-------- 16 files changed, 499 insertions(+), 120 deletions(-) create mode 100644 packages/lb-components/src/components/NLPToolView/index.tsx create mode 100644 packages/lb-components/src/components/NLPToolView/textContent/index.module.scss create mode 100644 packages/lb-components/src/components/NLPToolView/textContent/index.tsx create mode 100644 packages/lb-components/src/components/NLPToolView/types.ts create mode 100644 packages/lb-components/src/views/MainView/NLPLayout/index.tsx create mode 100644 packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx create mode 100644 packages/lb-demo/src/mock/NLPTool.js diff --git a/packages/lb-annotation/src/constant/tool.ts b/packages/lb-annotation/src/constant/tool.ts index b5abb689c..48dab6014 100644 --- a/packages/lb-annotation/src/constant/tool.ts +++ b/packages/lb-annotation/src/constant/tool.ts @@ -69,6 +69,8 @@ export enum EToolName { PointCloudPolygon = 'pointCloudPolygon', /** LLM标注工具-大模型 */ LLM = 'LLMTool', + /** NLP标注工具-大模型 */ + NLP = 'NLPTool', } export enum ECheckModel { @@ -107,6 +109,7 @@ export const TOOL_NAME: { [a: string]: string } = { [EPointCloudName.PointCloud]: '点云', [EToolName.Cuboid]: '立体框', [EToolName.LLM]: '大模型', + [EToolName.NLP]: 'NLP标注', }; export const TOOL_NAME_EN: { [a: string]: string } = { @@ -132,6 +135,7 @@ export const TOOL_NAME_EN: { [a: string]: string } = { [EPointCloudName.PointCloud]: 'PointCloud', [EToolName.Cuboid]: 'Cuboid', [EToolName.LLM]: 'LLM', + [EToolName.NLP]: 'NLP', }; export enum EDependPattern { diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx new file mode 100644 index 000000000..1253a8883 --- /dev/null +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -0,0 +1,105 @@ +/* + * @file NLP tool view + * @Author: lixinghua lixinghua@sensetime.com + * @Date: 2024-01-24 + */ + +import React, { useContext, useEffect, useState } from 'react'; +import { AppState } from '@/store'; +import { connect } from 'react-redux'; +import { LabelBeeContext, NLPContext } from '@/store/ctx'; +import { message } from 'antd'; +import { ELLMDataType, prefix } from '@/constant'; +import { Layout } from 'antd/es'; +import TextContent from './textContent'; +import { useTranslation } from 'react-i18next'; +import { ITextList, INLPToolConfig, ITextData } from './types'; +import AnnotationTips from '@/views/MainView/annotationTips'; +import { getStepConfig } from '@/store/annotation/reducer'; +import { jsonParser } from '@/utils'; +import { getCurrentResultFromResultList } from '../LLMToolView/utils/data'; + +interface IProps { + checkMode?: boolean; + annotation?: any; + showTips?: boolean; + tips?: string; +} +const LLMViewCls = `${prefix}-LLMView`; +const NLPToolView: React.FC = (props) => { + const { annotation, checkMode = true, tips, showTips } = props; + const { imgIndex, imgList, stepList, step } = annotation; + const { highlightKey } = useContext(NLPContext); + + const [NLPConfig, setNLPConfig] = useState(); + const [textData, setTextData] = useState([ + { + content: '', + }, + ]); + + const { t } = useTranslation(); + + useEffect(() => { + let interval: undefined | ReturnType; + + if (!checkMode) { + interval = setInterval(() => { + message.info(t('EfficientListening')); + }, 1000 * 60); + + return () => { + if (interval) { + clearInterval(interval); + } + }; + } + }, []); + + useEffect(() => { + if (!imgList[imgIndex]) { + return; + } + const textData = imgList[imgIndex]?.textData; + setTextData(textData); + }, [imgIndex, NLPConfig]); + + useEffect(() => { + if (!imgList[imgIndex]) { + return; + } + const currentData = imgList[imgIndex] ?? {}; + const result = getCurrentResultFromResultList(currentData?.result); + const currentResult = result?.length > 0 ? result[0] : result; + }, [imgIndex]); + + useEffect(() => { + if (stepList && step) { + const NLPStepConfig = getStepConfig(stepList, step)?.config; + setNLPConfig(jsonParser(NLPStepConfig)); + } + }, [stepList, step]); + + return ( + +
+ {showTips === true && } + +
+
+ ); +}; + +const mapStateToProps = (state: AppState) => { + return { + annotation: state.annotation, + }; +}; + +export default connect(mapStateToProps, null, null, { context: LabelBeeContext })(NLPToolView); diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.module.scss b/packages/lb-components/src/components/NLPToolView/textContent/index.module.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx new file mode 100644 index 000000000..c86d83c96 --- /dev/null +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -0,0 +1,51 @@ +/* + * @file text view + * @Author: lixinghua lixinghua@sensetime.com + * @Date: 2024-01-24 + */ + +import React, { useEffect, useState } from 'react'; +import { Tag } from 'antd'; +import classNames from 'classnames'; +import { useTranslation, I18nextProvider } from 'react-i18next'; +import { i18n } from '@labelbee/lb-utils'; +import { INLPToolConfig, ITextData } from '../types'; + +interface IProps { + highlightKey?: string; + textData: ITextData[]; + lang?: string; + checkMode?: boolean; + annotation?: any; + NLPConfig?: INLPToolConfig; + answerHeaderSlot?: React.ReactDOM | string; +} + +const TextContent: React.FC = (props) => { + const { highlightKey, textData, lang, checkMode = true, NLPConfig, answerHeaderSlot } = props; + + const { t } = useTranslation(); + + useEffect(() => { + if (lang) { + i18n?.changeLanguage(lang); + } + }, []); + + return ( +
+ {t('textTool')} +
{textData?.[0]?.content}
+
+ ); +}; + +const WrapQuestionView = (props: IProps) => { + return ( + + + + ); +}; + +export default WrapQuestionView; diff --git a/packages/lb-components/src/components/NLPToolView/types.ts b/packages/lb-components/src/components/NLPToolView/types.ts new file mode 100644 index 000000000..a5bee0a78 --- /dev/null +++ b/packages/lb-components/src/components/NLPToolView/types.ts @@ -0,0 +1,30 @@ +import { IInfoList } from '@labelbee/lb-utils'; + +export interface IndicatorDetermine { + label: string; + value: string; +} + +export interface INLPToolConfig { + indicatorDetermine?: IndicatorDetermine[]; // 指标判断 + isTextEdit: boolean; + textEdit: ITextList[]; + attributeConfigurable: boolean; + attributeList: IInfoList[]; +} + +export interface ITextList { + textId?: string; + title?: string | number; + tip?: string; + min?: number; + max?: number; + value?: string; + isFillAnswer?: boolean; + textControl?: boolean; + isLaText?: boolean; +} + +export interface ITextData { + content: string; +} diff --git a/packages/lb-components/src/data/enums/ToolType.ts b/packages/lb-components/src/data/enums/ToolType.ts index e5f5d42b5..17c6d906b 100644 --- a/packages/lb-components/src/data/enums/ToolType.ts +++ b/packages/lb-components/src/data/enums/ToolType.ts @@ -45,6 +45,7 @@ export enum EToolName { /** 点云多边形工具 */ PointCloudPolygon = 'pointCloudPolygon', LLM = 'LLMTool', + NLP = 'NLPTool', } // 文本标注类型 diff --git a/packages/lb-components/src/store/annotation/reducer.ts b/packages/lb-components/src/store/annotation/reducer.ts index 5bd2cf5ff..63f7f8fa4 100644 --- a/packages/lb-components/src/store/annotation/reducer.ts +++ b/packages/lb-components/src/store/annotation/reducer.ts @@ -78,13 +78,12 @@ const updateToolInstance = (annotation: AnnotationState, imgNode: HTMLImageEleme return; } - // TODO: 点云实例化对接 if (ToolUtils.isPointCloudTool(stepConfig?.tool)) { return; } - if (EToolName.LLM === stepConfig?.tool) { + if ([EToolName.LLM, EToolName.NLP].includes(stepConfig?.tool)) { return; } @@ -119,7 +118,8 @@ export const LoadFileAndFileData = const currentIsVideo = StepUtils.currentToolIsVideo(step, stepList); const currentIsPointCloud = StepUtils.currentToolIsPointCloud(step, stepList); const currentIsLLM = StepUtils.getCurrentStepInfo(step, stepList)?.tool === EToolName.LLM; - const currentIsAudio = StepUtils.currentToolIsAudio(step, stepList) + const currentIsNLP = StepUtils.getCurrentStepInfo(step, stepList)?.tool === EToolName.NLP; + const currentIsAudio = StepUtils.currentToolIsAudio(step, stepList); SetAnnotationLoading(dispatch, true); @@ -129,7 +129,7 @@ export const LoadFileAndFileData = dispatch(AfterVideoLoaded(nextIndex)); return; } - if (currentIsPointCloud || currentIsLLM || currentIsAudio) { + if (currentIsPointCloud || currentIsLLM || currentIsAudio || currentIsNLP) { dispatch(AfterCommonLoaded(nextIndex)); return; } @@ -451,7 +451,6 @@ export const annotationReducer = ( return { ...state, imgIndex: action.payload.nextIndex }; } - const currentStepInfo = StepUtils.getCurrentStepInfo(step, stepList); const { nextIndex, imgNode, nextBasicIndex, imgError } = action.payload; diff --git a/packages/lb-components/src/store/ctx.ts b/packages/lb-components/src/store/ctx.ts index 651fd56bc..39f77a68b 100644 --- a/packages/lb-components/src/store/ctx.ts +++ b/packages/lb-components/src/store/ctx.ts @@ -10,6 +10,10 @@ interface ILLMContext { setModelAPIResponse: React.Dispatch>; setNewAnswerList: (value: IAnswerList[]) => void; } +interface INLPContext { + highlightKey: string; + setHighlightKey: (value: string) => void; +} export const LabelBeeContext = React.createContext(undefined) as any; export const useDispatch = createDispatchHook(LabelBeeContext) as () => Dispatch; // TODO, Any need to be updated. export const useSelector = createSelectorHook(LabelBeeContext); @@ -21,3 +25,8 @@ export const LLMContext = React.createContext({ setModelAPIResponse: () => {}, setNewAnswerList: () => {}, }); + +export const NLPContext = React.createContext({ + highlightKey: '', + setHighlightKey: () => {}, +}); diff --git a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx new file mode 100644 index 000000000..98222cc29 --- /dev/null +++ b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx @@ -0,0 +1,54 @@ +import { AppProps } from '@/App'; +import { prefix } from '@/constant'; +import { Layout } from 'antd/es'; +import _ from 'lodash'; +import React, { useState, useMemo } from 'react'; +import Sidebar from '../sidebar'; +import ToolFooter from '../toolFooter'; +import { getClassName } from '@/utils/dom'; +import { classnames } from '@/utils'; +import { NLPContext } from '@/store/ctx'; +import NLPToolView from '@/components/NLPToolView'; + +interface IProps { + path: string; + loading: boolean; +} + +const { Sider, Content } = Layout; +const layoutCls = `${prefix}-layout`; + +const NLPLayout: React.FC = (props) => { + const [highlightKey, setHighlightKey] = useState(''); + + return ( + + { + return { + highlightKey , + setHighlightKey , + }; + }, [highlightKey])} + > + {props?.leftSider} + + + + + + + + {props.drawLayerSlot?.({})} + + + + ); +}; + +export default NLPLayout; diff --git a/packages/lb-components/src/views/MainView/index.tsx b/packages/lb-components/src/views/MainView/index.tsx index 40591ad3f..0c709f3b2 100644 --- a/packages/lb-components/src/views/MainView/index.tsx +++ b/packages/lb-components/src/views/MainView/index.tsx @@ -23,7 +23,8 @@ import PreviewResult from '@/components/predictTracking/previewResult'; import { LabelBeeContext } from '@/store/ctx'; import { EToolName } from '@/data/enums/ToolType'; import LLMLayout from './LLMLayout'; -import AudioAnnotate from '@/components/audioAnnotate' +import NLPLayout from './NLPLayout'; +import AudioAnnotate from '@/components/audioAnnotate'; import { LoadingOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; import { EPointCloudName } from '@labelbee/lb-annotation'; @@ -57,11 +58,7 @@ const PointCloudAnnotate: React.FC = (props) => { intelligentFit={props.intelligentFit} measureVisible={props.measureVisible} /> - + ); }; @@ -77,10 +74,7 @@ const AnnotatedArea: React.FC = (props) => { } if (isVideoTool) { - return + return ; } return ; @@ -90,9 +84,9 @@ const ViewportProviderLayout = (props: AppProps & IProps & { children: any }) => const { t } = useTranslation(); const { stepList, step } = props; const currentToolName = getStepConfig(stepList, step)?.tool; - const hasLangNode = ![EToolName.LLM].includes(currentToolName) - const hasHeaderOption = ![EToolName.LLM].includes(currentToolName) - const hasPredictTrackingIcon = [EPointCloudName.PointCloud].includes(currentToolName) + const hasLangNode = ![EToolName.LLM].includes(currentToolName); + const hasHeaderOption = ![EToolName.LLM].includes(currentToolName); + const hasPredictTrackingIcon = [EPointCloudName.PointCloud].includes(currentToolName); return ( = (props) => { const { stepList, step } = props; const currentToolName = getStepConfig(stepList, step)?.tool; const isLLMTool = EToolName.LLM === currentToolName; + const isNLPTool = EToolName.NLP === currentToolName; const isAudioTool = ToolUtils.isAudioTool(currentToolName); if (isLLMTool) { @@ -136,10 +131,20 @@ const MainView: React.FC = (props) => { ); } + if (isNLPTool) { + return ( + + + + ); + } + if (isAudioTool) { - return - - + return ( + + + + ); } return ( diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx new file mode 100644 index 000000000..d87e30af6 --- /dev/null +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx @@ -0,0 +1,35 @@ +import { Collapse } from 'antd/es'; +import React from 'react'; +import { TagOperation } from '@labelbee/lb-annotation'; +import { connect } from 'react-redux'; +import { AppState } from '@/store'; +import { LabelBeeContext } from '@/store/ctx'; +import { prefix } from '@/constant'; +import SwitchAttributeList from '../SwitchAttributeList'; +import GeneralOperation from '../GeneralOperation'; + +interface IProps { + imgIndex: number; + toolInstance: TagOperation; + checkMode?: boolean; +} + +const { Panel } = Collapse; +export const sidebarCls = `${prefix}-sidebar`; +const NLPSidebar: React.FC = ({ toolInstance, imgIndex }) => { + const attributeList = ; + const operation = ; + + return ( +
+
{attributeList}
+ {operation} +
+ ); +}; + +function mapStateToProps(state: AppState) { + return { toolInstance: state.annotation.toolInstance, imgIndex: state.annotation.imgIndex }; +} + +export default connect(mapStateToProps, null, null, { context: LabelBeeContext })(NLPSidebar); diff --git a/packages/lb-components/src/views/MainView/sidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/index.tsx index e1b247f79..ca8024b0c 100644 --- a/packages/lb-components/src/views/MainView/sidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/index.tsx @@ -23,6 +23,7 @@ import { Tabs } from 'antd'; import { classnames } from '@/utils'; import menuFoldSvg from '@/assets/annotation/common/icon_menu_fold.svg'; import LLMToolSidebar from '@/components/LLMToolView/sidebar'; +import NLPToolSidebar from './NLPSidebar' import VideoClipAnnotatedList from '@/components/videoAnnotate/videoClipTool/components/annotatedList' const { EVideoToolName, EPointCloudName } = cTool; @@ -130,6 +131,7 @@ const Sidebar: React.FC = ({ ) const LLMSidebar = ; + const NLPSidebar = ; const horizontal =
; @@ -332,6 +334,10 @@ const Sidebar: React.FC = ({ return LLMSidebar; } + if (toolName === EToolName.NLP) { + return NLPSidebar; + } + return null; }; diff --git a/packages/lb-demo/src/App.js b/packages/lb-demo/src/App.js index a9efcbee5..b47c75f94 100644 --- a/packages/lb-demo/src/App.js +++ b/packages/lb-demo/src/App.js @@ -24,6 +24,7 @@ import { getDependStepList, getStepList } from './mock/taskConfig'; import car1 from './mock/cuboidImages/1.png'; import { EToolName } from '@labelbee/lb-annotation'; import { LLMToolQa, LLMToolResult } from './mock/LLMTool'; +import { textData, NLPToolResult } from './mock/NLPTool'; const App = () => { const tool = qs.parse(window.location.search, { @@ -61,7 +62,7 @@ const App = () => { ...extraData, id: i + 1, url, - result: JSON.stringify(LLMToolResult.step_1.result.map((item) => i + 1 + item.answer)), + result: JSON.stringify(NLPToolResult.step_1.result.map((item) => i + 1 + item.answer)), questionList: { ...LLMToolQa, question: `${i + 1}-${LLMToolQa.question}`, @@ -73,6 +74,16 @@ const App = () => { })); } + if (EToolName.NLP === tool) { + return srcList.map((url, i) => ({ + ...extraData, + id: i + 1, + url, + result: JSON.stringify(LLMToolResult.step_1.result.map((item) => i + 1 + item.answer)), + textData, + })); + } + return srcList.map((url, i) => ({ ...extraData, id: i + 1, diff --git a/packages/lb-demo/src/mock/NLPTool.js b/packages/lb-demo/src/mock/NLPTool.js new file mode 100644 index 000000000..f997e4263 --- /dev/null +++ b/packages/lb-demo/src/mock/NLPTool.js @@ -0,0 +1,23 @@ +export const NLPToolResult = { + step_1: { + dataSourceStep: 0, + result: [ + { + id: 1, + newText: '', + indicatorDetermine: {}, + textAnotation: [], + }, + ], + toolName: 'NLPTool', + }, +}; + +export const textData = [ + { + id: 1, + content: `云计算、物联网、社交网络等新兴服务促使人类社会的数据种类和规模正以前所未有的速度增长,大数据时代正式到来.数据从简单的处理对象开始转变为一种基础性资源,如何更好地管理和利用大数据已经成为普遍关注的话题.大数据的规模效应给数据存储、管理以及数据分析带来了极大的挑战,数据管理方式上的变革正在酝酿和发生.对大数据的基本概念进行剖析,并对大数据的主要应用作简单对比.在此基础上,阐述大数据处理的基本框架,并就云计算技术对于大数据时代数据管理所产生的作用进行分析.最后归纳总结大数据时代所面临的新挑战. + 关键词: 大数据 数据分析 云计算云计算、物联网、社交网络等新兴服务促使人类社会的数据种类和规模正以前所未有的速度增长,大数据时代正式到来.数据从简单的处理对象开始转变为一种基础性资源,如何更好地管理和利用大数据已经成为普遍关注的话题.大数据的规模效应给数据存储、管理以及数据分析带来了极大的挑战,数据管理方式上的变革正在酝酿和发生.对大数据的基本概念进行剖析,并对大数据的主要应用作简单对比.在此基础上,阐述大数据处理的基本框架,并就云计算技术对于大数据时代数据管理所产生的作用进行分析.最后归纳总结大数据时代所面临的新挑战. + 关键词: 大数据 数据分析 云计算`, + }, +]; diff --git a/packages/lb-demo/src/mock/index.js b/packages/lb-demo/src/mock/index.js index 3d432ad4c..ff893d1e8 100644 --- a/packages/lb-demo/src/mock/index.js +++ b/packages/lb-demo/src/mock/index.js @@ -4,6 +4,7 @@ import img3 from './images/20.jpg'; import img4 from './images/66.jpg'; import { pointCloudResult1 } from './pointCloud'; import { LLMToolResult } from './LLMTool'; +import { NLPToolResult } from './NLPTool'; // audios import audio1 from './audios/audio1.mp3' @@ -133,6 +134,10 @@ export const getMockResult = (tool) => { return LLMToolResult; } + if (tool === 'NLPTool') { + return NLPToolResult; + } + if (tool === 'pointCloudTool') { return '{}'; // return pointCloudResult; diff --git a/packages/lb-demo/src/mock/taskConfig.js b/packages/lb-demo/src/mock/taskConfig.js index 6feb4fecc..55e5b8d1c 100644 --- a/packages/lb-demo/src/mock/taskConfig.js +++ b/packages/lb-demo/src/mock/taskConfig.js @@ -86,37 +86,34 @@ const tagToolConfig = { }; const textToolConfig = { - "showConfirm": true, - "showDirection": false, - "skipWhileNoDependencies": false, - "configList": [ - { - "label": "文本", - "key": "text", - "required": false, - "default": "", - "maxLength": 1000 - }, - { - "label": "文本2", - "key": "text2", - "required": false, - "default": "", - "maxLength": 1000 - }, - { - "label": "文本3", - "key": "text3", - "required": false, - "default": "", - "maxLength": 1000 - } + showConfirm: true, + showDirection: false, + skipWhileNoDependencies: false, + configList: [ + { + label: '文本', + key: 'text', + required: false, + default: '', + maxLength: 1000, + }, + { + label: '文本2', + key: 'text2', + required: false, + default: '', + maxLength: 1000, + }, + { + label: '文本3', + key: 'text3', + required: false, + default: '', + maxLength: 1000, + }, ], - "filterData": [ - "valid", - "invalid" - ] -} + filterData: ['valid', 'invalid'], +}; const lineToolConfig = { lineType: 0, @@ -293,112 +290,152 @@ const LLMToolConfig = { max: 1000, isFillAnswer: true, // 是否填充答案 isLaText: true, // 是否打开LaTex编辑 - textControl: true // 文本对照 + textControl: true, // 文本对照 }, { title: 2, min: 10, isFillAnswer: false, // 是否填充答案 - textControl: true // 文本对照 + textControl: true, // 文本对照 }, { title: 3, max: 100, isFillAnswer: true, // 是否填充答案 - textControl: false // 文本对照 + textControl: false, // 文本对照 }, { title: 4, isFillAnswer: false, // 是否填充答案 - textControl: false // 文本对照 + textControl: false, // 文本对照 }, ], }; +const NLPToolConfig = { + indicatorDetermine: [ + { + label: '指标判断-Mm', + value: 'indicatorDetermine-Mm', + }, + { + label: '指标判断-Y8', + value: 'indicatorDetermine-Y8', + }, + ], + attributeConfigurable: true, + attributeList: [ + { + key: '类别1', + value: '类别1', + }, + { + key: '类别bk', + value: 'class-bk', + color: 'rgba(0, 255, 48, 1)', + }, + { + key: '类别e4', + value: 'class-e4', + color: 'rgba(255, 136, 247, 1)', + }, + ], + isTextEdit: true, + textEdit: [ + { + title: '标题', + isFillAnswer: true, + isLaText: true, + textControl: true, + min: 1, + max: 222, + }, + ], + showConfirm: true, + showDirection: false, + skipWhileNoDependencies: false, +}; + const audioToolConfig = { - "showConfirm": true, - "showDirection": false, - "skipWhileNoDependencies": false, - "configList": [ - { - "label": "测试文本", - "key": "text", - "required": false, - "default": "", - "maxLength": 1000 - } + showConfirm: true, + showDirection: false, + skipWhileNoDependencies: false, + configList: [ + { + label: '测试文本', + key: 'text', + required: false, + default: '', + maxLength: 1000, + }, ], - "inputList": [ + inputList: [ { - "key": "类别1", - "value": "class1", - "isMulti": false, - "subSelected": [ + key: '类别1', + value: 'class1', + isMulti: false, + subSelected: [ { - "key": "选项1", - "value": "option1", - "isDefault": false - } - ] + key: '选项1', + value: 'option1', + isDefault: false, + }, + ], }, { - "key": "类别xl", - "value": "class-xl", - "isMulti": false, - "subSelected": [ + key: '类别xl', + value: 'class-xl', + isMulti: false, + subSelected: [ { - "key": "选项2-1", - "value": "option2-1", - "isMulti": false - } - ] + key: '选项2-1', + value: 'option2-1', + isMulti: false, + }, + ], }, { - "key": "类别dg", - "value": "class-dg", - "isMulti": false, - "subSelected": [ + key: '类别dg', + value: 'class-dg', + isMulti: false, + subSelected: [ { - "key": "选项3-1", - "value": "option3-1", - "isMulti": false - } - ] - } - ], - "tagConfigurable": true, - "textConfigurable": true, - "filterData": [ - "valid", - "invalid" + key: '选项3-1', + value: 'option3-1', + isMulti: false, + }, + ], + }, ], - "clipConfigurable": true, - "clipTextConfigurable": true, - "clipAttributeList": [ + tagConfigurable: true, + textConfigurable: true, + filterData: ['valid', 'invalid'], + clipConfigurable: true, + clipTextConfigurable: true, + clipAttributeList: [ { - "key": "类别1", - "value": "类别1" + key: '类别1', + value: '类别1', }, { - "key": "类别Jy", - "value": "class-Jy" + key: '类别Jy', + value: 'class-Jy', }, { - "key": "类别JU", - "value": "class-JU" + key: '类别JU', + value: 'class-JU', }, { - "key": "类别EZ", - "value": "class-EZ" + key: '类别EZ', + value: 'class-EZ', }, { - "key": "类别4E", - "value": "class-4E" - } + key: '类别4E', + value: 'class-4E', + }, ], - "clipAttributeConfigurable": true -} - + clipAttributeConfigurable: true, +}; export const getConfig = (tool) => { if (tool === EToolName.Line) { @@ -445,8 +482,12 @@ export const getConfig = (tool) => { return LLMToolConfig; } + if (tool === EToolName.NLP) { + return NLPToolConfig; + } + if (tool === EAudioToolName.AudioTextTool) { - return audioToolConfig + return audioToolConfig; } return rectToolConfig; From ab3299fbd68598eb07bef9376be88b4d840d8c24 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Thu, 25 Jan 2024 18:10:28 +0800 Subject: [PATCH 03/43] feat: Annotation core func in NLP --- .../src/components/NLPToolView/index.tsx | 69 ++++++++++-- .../NLPToolView/textContent/index.tsx | 75 +++++++++++-- .../src/components/NLPToolView/types.ts | 14 +++ .../lb-components/src/hooks/annotation.ts | 27 +++++ packages/lb-components/src/index.scss | 39 +++++++ .../src/store/annotation/reducer.ts | 2 +- packages/lb-components/src/types/main.d.ts | 2 + .../src/views/MainView/NLPLayout/index.tsx | 2 +- .../src/views/MainView/sidebar/index.tsx | 1 + packages/lb-demo/package.json | 102 +++++++++--------- packages/lb-demo/src/App.js | 4 +- 11 files changed, 269 insertions(+), 68 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index 1253a8883..fe1d7f652 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -9,15 +9,18 @@ import { AppState } from '@/store'; import { connect } from 'react-redux'; import { LabelBeeContext, NLPContext } from '@/store/ctx'; import { message } from 'antd'; -import { ELLMDataType, prefix } from '@/constant'; +import { prefix } from '@/constant'; import { Layout } from 'antd/es'; import TextContent from './textContent'; import { useTranslation } from 'react-i18next'; -import { ITextList, INLPToolConfig, ITextData } from './types'; +import { ITextList, INLPToolConfig, ITextData, INLPTextAnnotation, INLPResult } from './types'; import AnnotationTips from '@/views/MainView/annotationTips'; import { getStepConfig } from '@/store/annotation/reducer'; import { jsonParser } from '@/utils'; import { getCurrentResultFromResultList } from '../LLMToolView/utils/data'; +import { useCustomToolInstance } from '@/hooks/annotation'; +import { toolStyleConverter } from '@labelbee/lb-utils'; +import { uuid } from '@labelbee/lb-annotation'; interface IProps { checkMode?: boolean; @@ -25,18 +28,26 @@ interface IProps { showTips?: boolean; tips?: string; } -const LLMViewCls = `${prefix}-LLMView`; +const NLPViewCls = `${prefix}-NLPView`; const NLPToolView: React.FC = (props) => { const { annotation, checkMode = true, tips, showTips } = props; const { imgIndex, imgList, stepList, step } = annotation; const { highlightKey } = useContext(NLPContext); + const { toolInstanceRef } = useCustomToolInstance(); const [NLPConfig, setNLPConfig] = useState(); + const [selectedAttribute, setSelectedAttribute] = useState('') const [textData, setTextData] = useState([ { content: '', }, ]); + const [result, setResult] = useState({ + id: 1, + newText: '', + indicatorDetermine: {}, + textAnnotation: [], + }) const { t } = useTranslation(); @@ -70,7 +81,7 @@ const NLPToolView: React.FC = (props) => { } const currentData = imgList[imgIndex] ?? {}; const result = getCurrentResultFromResultList(currentData?.result); - const currentResult = result?.length > 0 ? result[0] : result; + setResult(result) }, [imgIndex]); useEffect(() => { @@ -80,9 +91,51 @@ const NLPToolView: React.FC = (props) => { } }, [stepList, step]); + useEffect(() => { + toolInstanceRef.current.exportData = () => { + return [[result], { }]; + }; + + toolInstanceRef.current.setResult = () => {} + toolInstanceRef.current.clearResult = () => {} + toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute + }, [result]); + + const updateSidebar = () => { + toolInstanceRef.current.emit('changeAttributeSidebar'); + } + const setDefaultAttribute = (attribute: string) => { + toolInstanceRef.current.defaultAttribute = attribute + setSelectedAttribute(attribute) + updateSidebar() + } + + const onSelectionChange = (text: string) => { + if (text === '') return + let selection = window.getSelection() + const { anchorOffset, focusOffset, anchorNode, focusNode } = selection + console.log(selection) + if (anchorNode === focusNode) { + // ignore the order of selection + let start = Math.min(anchorOffset, focusOffset) + let end = Math.max(anchorOffset, focusOffset) + setResult({ + ...result, + textAnnotation: [...(result?.textAnnotation || []), { + id: uuid(8, 62), + start, + end, + attribute: selectedAttribute, + text, + }] + }) + window.getSelection().empty() + } + } + return ( - -
+
+
{showTips === true && } = (props) => { checkMode={checkMode} annotation={annotation} NLPConfig={NLPConfig} + textAnnotation={result.textAnnotation ?? []} + onSelectionChange={onSelectionChange} />
- +
); }; diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index c86d83c96..fe602b942 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -4,27 +4,61 @@ * @Date: 2024-01-24 */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef, useMemo } from 'react'; import { Tag } from 'antd'; import classNames from 'classnames'; import { useTranslation, I18nextProvider } from 'react-i18next'; -import { i18n } from '@labelbee/lb-utils'; -import { INLPToolConfig, ITextData } from '../types'; +import { i18n, toolStyleConverter } from '@labelbee/lb-utils'; +import { INLPToolConfig, ITextData, INLPTextAnnotation } from '../types'; +import { prefix } from '@/constant'; +import { useTextSelection } from 'ahooks'; +import _ from 'lodash' interface IProps { highlightKey?: string; textData: ITextData[]; + textAnnotation: INLPTextAnnotation[]; lang?: string; checkMode?: boolean; annotation?: any; NLPConfig?: INLPToolConfig; answerHeaderSlot?: React.ReactDOM | string; + onSelectionChange?: (text: string) => void; } +const NLPViewCls = `${prefix}-NLPView`; + const TextContent: React.FC = (props) => { - const { highlightKey, textData, lang, checkMode = true, NLPConfig, answerHeaderSlot } = props; + const { highlightKey, textData, lang, checkMode = true, NLPConfig, answerHeaderSlot, onSelectionChange, textAnnotation } = props; + + const content = useMemo(() => { + return textData?.[0]?.content + }, [textData]) + + const splitIntervals = useMemo(() => { + const splitPoints = _.uniq(_.concat(0, ...textAnnotation.map((range: INLPTextAnnotation) => [range.start, range.end]), content.length)).sort((a, b) => a - b) + let intervals = [] + for (let i = 0; i < splitPoints.length - 1; i++){ + let start = splitPoints[i] + let end = splitPoints[i + 1] + let annotations = textAnnotation.filter((range: INLPTextAnnotation) => range.start >= start && range.end <= end) + intervals.push({ + start, + end, + annotations, + text: content.slice(start, end) + }) + } + return intervals + }, [textAnnotation]) const { t } = useTranslation(); + const contentRef = useRef(null) + const selection = useTextSelection(contentRef) + + const getColor = (attribute = '') => { + return toolStyleConverter.getColorByConfig({ attribute, config: NLPConfig }); + } useEffect(() => { if (lang) { @@ -32,10 +66,39 @@ const TextContent: React.FC = (props) => { } }, []); + useEffect(() => { + onSelectionChange?.(selection.text) + }, [selection.text]) + + const renderContent = () => { + return
+ { + splitIntervals.map((interval: any) => { + const annotation = _.last(interval.annotations) + if (annotation) { + const color = getColor(annotation.attribute) + return {interval.text} + } else { + return {interval.text} + } + }) + } + { + renderMask() + } +
+ } + + const renderMask = () => { + return
{content}
+ } + return (
- {t('textTool')} -
{textData?.[0]?.content}
+
{t('textTool')}
+
+ {renderContent()} +
); }; diff --git a/packages/lb-components/src/components/NLPToolView/types.ts b/packages/lb-components/src/components/NLPToolView/types.ts index a5bee0a78..a538e2085 100644 --- a/packages/lb-components/src/components/NLPToolView/types.ts +++ b/packages/lb-components/src/components/NLPToolView/types.ts @@ -28,3 +28,17 @@ export interface ITextList { export interface ITextData { content: string; } + +export interface INLPResult { + id: number; + newText: string; + indicatorDetermine: any; + textAnnotation: INLPTextAnnotation[]; +} +export interface INLPTextAnnotation { + id: string; + start: number; + end: number; + attribute: string; + text: string; +} diff --git a/packages/lb-components/src/hooks/annotation.ts b/packages/lb-components/src/hooks/annotation.ts index 35f689964..277edceeb 100644 --- a/packages/lb-components/src/hooks/annotation.ts +++ b/packages/lb-components/src/hooks/annotation.ts @@ -105,8 +105,34 @@ const useCustomToolInstance = ({ basicInfo }: ICustomToolInstanceProps = {}) => Object.assign(toolInstanceRef.current, initialCustomToolInstance); }; + const initEventEmitter = () => { + toolInstanceRef.current.emit = (event: string) => { + const listener = toolInstanceRef.current.fns.get(event); + if (listener) { + listener.forEach((fn: any) => { + if (fn) { + fn?.(); + } + }); + } + } + toolInstanceRef.current.fns = new Map() + toolInstanceRef.current.singleOn = (event: string, func: () => void) => { + toolInstanceRef.current.fns.set(event, [func]); + }; + + toolInstanceRef.current.on = (event: string, func: () => void) => { + toolInstanceRef.current.singleOn(event, func); + }; + + toolInstanceRef.current.unbindAll = (eventName: string) => { + toolInstanceRef.current.fns.delete(eventName); + }; + } + useEffect(() => { // Initial toolInstance + initEventEmitter() onMounted(toolInstanceRef.current); return () => { onUnmounted(); @@ -116,6 +142,7 @@ const useCustomToolInstance = ({ basicInfo }: ICustomToolInstanceProps = {}) => return { toolInstanceRef, clearToolInstance, + initEventEmitter, }; }; diff --git a/packages/lb-components/src/index.scss b/packages/lb-components/src/index.scss index e52f63926..56a25f1ad 100644 --- a/packages/lb-components/src/index.scss +++ b/packages/lb-components/src/index.scss @@ -2175,6 +2175,45 @@ $ptToolHeight: 40px; } } +/** NLP style */ + +.#{$prefix}-NLPLayout { + overflow: hidden; +} +.#{$prefix}-NLPView { + height: 100%; + background-color: #ffffff; + overflow-y: auto; + flex: auto; + padding: 26px 32px; +} + +.#{$prefix}-NLPView-question { + flex: 1; + overflow: hidden; + &-title{ + font-weight: 500; + font-size: 18px; + margin-bottom: 24px; + } + &-content{ + font-size: 14px; + font-weight: 400; + background-color: #f5f5f5; + border-radius: 4px; + padding: 8px; + &-mask{ + position: absolute; + left: 0px; + top: 0px; + background-color: transparent; + color: transparent; + z-index: 10; + opacity: 1; + } + } +} + /** longText style */ .#{$prefix}-longText { display: flex; diff --git a/packages/lb-components/src/store/annotation/reducer.ts b/packages/lb-components/src/store/annotation/reducer.ts index 63f7f8fa4..4467b3831 100644 --- a/packages/lb-components/src/store/annotation/reducer.ts +++ b/packages/lb-components/src/store/annotation/reducer.ts @@ -83,7 +83,7 @@ const updateToolInstance = (annotation: AnnotationState, imgNode: HTMLImageEleme return; } - if ([EToolName.LLM, EToolName.NLP].includes(stepConfig?.tool)) { + if ([EToolName.LLM as string, EToolName.NLP as string].includes(stepConfig?.tool)) { return; } diff --git a/packages/lb-components/src/types/main.d.ts b/packages/lb-components/src/types/main.d.ts index dfe18beab..840dff958 100644 --- a/packages/lb-components/src/types/main.d.ts +++ b/packages/lb-components/src/types/main.d.ts @@ -31,6 +31,7 @@ export type Sider = ({ horizontal, scribbleSidebar, LLMSidebar, + NLPSidebar, videoClipSidebar, }: { toolIcon: React.ReactNode; @@ -45,6 +46,7 @@ export type Sider = ({ scribbleSidebar: React.ReactNode; LLMSidebar: React.ReactNode; videoClipSidebar: React.ReactNode; + NLPSidebar: React.ReactNode; // PointCloud pointCloudToolSidebar: React.ReactNode; pointCloudOperation: React.ReactNode; diff --git a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx index 98222cc29..efd0d9647 100644 --- a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx +++ b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx @@ -35,7 +35,7 @@ const NLPLayout: React.FC = (props) => { diff --git a/packages/lb-components/src/views/MainView/sidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/index.tsx index ca8024b0c..1353aea56 100644 --- a/packages/lb-components/src/views/MainView/sidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/index.tsx @@ -159,6 +159,7 @@ const Sidebar: React.FC = ({ scribbleSidebar, LLMSidebar, videoClipSidebar, + NLPSidebar })}
); diff --git a/packages/lb-demo/package.json b/packages/lb-demo/package.json index ebaec44ad..8d8566709 100644 --- a/packages/lb-demo/package.json +++ b/packages/lb-demo/package.json @@ -1,53 +1,53 @@ { - "name": "lb-demo", - "version": "1.11.0-alpha.2", - "private": true, - "publishConfig": { - "access": "public" - }, - "license": "Apache-2.0", - "dependencies": { - "@labelbee/lb-annotation": "*", - "@labelbee/lb-components": "*", - "@labelbee/lb-utils": "*", - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^11.1.0", - "@testing-library/user-event": "^12.1.10", - "@types/three": ">=0.141.0", - "qs": "^6.10.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-error-overlay": "6.0.9", - "react-scripts": "4.0.3", - "three": ">=0.141.0", - "web-vitals": "^1.0.2" - }, - "scripts": { - "start": "react-scripts start", - "dev": "npm run start", - "build:frontend": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "antd": "^4.16.13" - } + "name": "lb-demo", + "version": "1.11.0-alpha.2", + "private": true, + "publishConfig": { + "access": "public" + }, + "license": "Apache-2.0", + "dependencies": { + "@labelbee/lb-annotation": "*", + "@labelbee/lb-components": "*", + "@labelbee/lb-utils": "*", + "@testing-library/jest-dom": "^5.11.4", + "@testing-library/react": "^11.1.0", + "@testing-library/user-event": "^12.1.10", + "@types/three": ">=0.141.0", + "qs": "^6.10.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-error-overlay": "6.0.9", + "react-scripts": "4.0.3", + "three": ">=0.141.0", + "web-vitals": "^1.0.2" + }, + "scripts": { + "start": "react-scripts start", + "dev": "npm run start", + "build:frontend": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "antd": "^4.16.13" + } } diff --git a/packages/lb-demo/src/App.js b/packages/lb-demo/src/App.js index b47c75f94..889b36320 100644 --- a/packages/lb-demo/src/App.js +++ b/packages/lb-demo/src/App.js @@ -62,7 +62,7 @@ const App = () => { ...extraData, id: i + 1, url, - result: JSON.stringify(NLPToolResult.step_1.result.map((item) => i + 1 + item.answer)), + result: JSON.stringify(LLMToolResult.step_1.result.map((item) => i + 1 + item.answer)), questionList: { ...LLMToolQa, question: `${i + 1}-${LLMToolQa.question}`, @@ -79,7 +79,7 @@ const App = () => { ...extraData, id: i + 1, url, - result: JSON.stringify(LLMToolResult.step_1.result.map((item) => i + 1 + item.answer)), + result: JSON.stringify(NLPToolResult.step_1.result), textData, })); } From ef8ba08a84c73b1b3f260520f5c44ca080df2bfc Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Thu, 25 Jan 2024 18:27:55 +0800 Subject: [PATCH 04/43] fix: Fix cover problem --- packages/lb-components/src/components/NLPToolView/index.tsx | 1 - .../src/components/NLPToolView/textContent/index.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index fe1d7f652..6522b936d 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -114,7 +114,6 @@ const NLPToolView: React.FC = (props) => { if (text === '') return let selection = window.getSelection() const { anchorOffset, focusOffset, anchorNode, focusNode } = selection - console.log(selection) if (anchorNode === focusNode) { // ignore the order of selection let start = Math.min(anchorOffset, focusOffset) diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index fe602b942..0ee9d9647 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -41,7 +41,7 @@ const TextContent: React.FC = (props) => { for (let i = 0; i < splitPoints.length - 1; i++){ let start = splitPoints[i] let end = splitPoints[i + 1] - let annotations = textAnnotation.filter((range: INLPTextAnnotation) => range.start >= start && range.end <= end) + let annotations = textAnnotation.filter((range: INLPTextAnnotation) => (range.start >= start && range.end <= end) || (start >= range.start && end <= range.end)) intervals.push({ start, end, From 3165abf5f68aeaff5f2c7fe9d52f5995c420c4c3 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Thu, 25 Jan 2024 19:58:51 +0800 Subject: [PATCH 05/43] feat: Support highlight in nlp tool --- .../src/components/NLPToolView/index.tsx | 10 ++- .../NLPToolView/textContent/index.tsx | 14 +++- packages/lb-components/src/index.scss | 19 +++++ .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 79 +++++++++++++++++++ .../MainView/sidebar/NLPSidebar/index.tsx | 7 +- packages/lb-components/tsconfig.json | 2 +- 6 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index 6522b936d..c807494a3 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -32,16 +32,18 @@ const NLPViewCls = `${prefix}-NLPView`; const NLPToolView: React.FC = (props) => { const { annotation, checkMode = true, tips, showTips } = props; const { imgIndex, imgList, stepList, step } = annotation; - const { highlightKey } = useContext(NLPContext); + const { highlightKey, setHighlightKey } = useContext(NLPContext); const { toolInstanceRef } = useCustomToolInstance(); const [NLPConfig, setNLPConfig] = useState(); const [selectedAttribute, setSelectedAttribute] = useState('') + const [textData, setTextData] = useState([ { content: '', }, ]); + const [result, setResult] = useState({ id: 1, newText: '', @@ -99,6 +101,8 @@ const NLPToolView: React.FC = (props) => { toolInstanceRef.current.setResult = () => {} toolInstanceRef.current.clearResult = () => {} toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute + toolInstanceRef.current.setHighlightKey = setHighlightKey + updateSidebar() }, [result]); const updateSidebar = () => { @@ -113,7 +117,7 @@ const NLPToolView: React.FC = (props) => { const onSelectionChange = (text: string) => { if (text === '') return let selection = window.getSelection() - const { anchorOffset, focusOffset, anchorNode, focusNode } = selection + const { anchorOffset = 0, focusOffset = 0, anchorNode, focusNode } = selection || {} if (anchorNode === focusNode) { // ignore the order of selection let start = Math.min(anchorOffset, focusOffset) @@ -128,7 +132,7 @@ const NLPToolView: React.FC = (props) => { text, }] }) - window.getSelection().empty() + window.getSelection()?.empty() } } diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index 0ee9d9647..948b4450b 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -26,6 +26,13 @@ interface IProps { onSelectionChange?: (text: string) => void; } +interface INLPInterval { + start: number; + end: number; + annotations: INLPTextAnnotation[]; + text: string; +} + const NLPViewCls = `${prefix}-NLPView`; const TextContent: React.FC = (props) => { @@ -35,7 +42,7 @@ const TextContent: React.FC = (props) => { return textData?.[0]?.content }, [textData]) - const splitIntervals = useMemo(() => { + const splitIntervals: INLPInterval[] = useMemo(() => { const splitPoints = _.uniq(_.concat(0, ...textAnnotation.map((range: INLPTextAnnotation) => [range.start, range.end]), content.length)).sort((a, b) => a - b) let intervals = [] for (let i = 0; i < splitPoints.length - 1; i++){ @@ -73,11 +80,12 @@ const TextContent: React.FC = (props) => { const renderContent = () => { return
{ - splitIntervals.map((interval: any) => { + splitIntervals.map((interval: INLPInterval) => { const annotation = _.last(interval.annotations) if (annotation) { const color = getColor(annotation.attribute) - return {interval.text} + const highlight = annotation.id === highlightKey + return {interval.text} } else { return {interval.text} } diff --git a/packages/lb-components/src/index.scss b/packages/lb-components/src/index.scss index 56a25f1ad..a86cf5b1c 100644 --- a/packages/lb-components/src/index.scss +++ b/packages/lb-components/src/index.scss @@ -2214,6 +2214,25 @@ $ptToolHeight: 40px; } } +.#{$prefix}-sidebar__content__NLPList{ + display:flex; + flex-direction:column; + &__item{ + padding: 16px; + font-weight: 400; + font-size: 14px; + line-height: 20px; + color: #666666; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor:pointer; + &__active{ + background-color: #eeefff; + } + } +} + /** longText style */ .#{$prefix}-longText { display: flex; diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx new file mode 100644 index 000000000..6ac6fc9e6 --- /dev/null +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -0,0 +1,79 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { connect } from 'react-redux'; +import { AppState } from 'src/store'; +import StepUtils from '@/utils/StepUtils'; +import { IStepInfo } from '@/types/step'; +import { useTranslation } from 'react-i18next'; +import { LabelBeeContext } from '@/store/ctx'; +import { ICustomToolInstance } from '@/hooks/annotation'; +import { prefix } from '@/constant'; +import { classnames } from '@/utils'; +import { INLPTextAnnotation } from '@/components/NLPToolView/types' + +interface IProps { + toolInstance: ICustomToolInstance; + stepInfo: IStepInfo; +} + +export const sidebarCls = `${prefix}-sidebar`; + +const NLPAnnotatedList: React.FC = (props) => { + + const [_, forceRender] = useState(0); + const listRef = useRef(null); + const { toolInstance } = props; + const { t } = useTranslation(); + + const [highlight, setHighlight] = useState('') + + useEffect(() => { + if (toolInstance) { + toolInstance.singleOn('changeAttributeSidebar', (index: number) => { + forceRender((s) => s + 1); + }); + } + return () => { + toolInstance?.unbindAll('changeAttributeSidebar'); + }; + }, [toolInstance, listRef]); + + const [result] = toolInstance.exportData() + const annotatedList = result?.[0]?.textAnnotation ?? [] + + const setHighlightKey = (key: string) => { + toolInstance?.setHighlightKey(key) + setHighlight(key) + } + + if (annotatedList?.length === 0) return null + return
+ { + annotatedList.map((v: INLPTextAnnotation, k: number) => { + const active = highlight === v.id + return
setHighlightKey(v.id)} + > + {`${k + 1}、${v.text}`} +
+ }) + } +
+}; + +const mapStateToProps = (state: AppState) => { + const stepInfo = StepUtils.getCurrentStepInfo(state.annotation?.step, state.annotation?.stepList); + + return { + toolInstance: state.annotation.toolInstance, + stepInfo, + }; +}; + +export default connect(mapStateToProps, null, null, { context: LabelBeeContext })( + NLPAnnotatedList, +); diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx index d87e30af6..d15541b96 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx @@ -7,6 +7,7 @@ import { LabelBeeContext } from '@/store/ctx'; import { prefix } from '@/constant'; import SwitchAttributeList from '../SwitchAttributeList'; import GeneralOperation from '../GeneralOperation'; +import NLPAnnotatedList from './NLPAnnotatedList'; interface IProps { imgIndex: number; @@ -19,10 +20,14 @@ export const sidebarCls = `${prefix}-sidebar`; const NLPSidebar: React.FC = ({ toolInstance, imgIndex }) => { const attributeList = ; const operation = ; + const annotatedList = ; return (
-
{attributeList}
+
+ {attributeList} + {annotatedList} +
{operation}
); diff --git a/packages/lb-components/tsconfig.json b/packages/lb-components/tsconfig.json index ee53d023f..b19c90cfe 100644 --- a/packages/lb-components/tsconfig.json +++ b/packages/lb-components/tsconfig.json @@ -8,7 +8,7 @@ "moduleResolution": "node", "allowJs": false, // 是否编辑js文件 "strict": true, // 严格模式 - "noUnusedLocals": true, // 未使用变量报错 + "noUnusedLocals": false, // 未使用变量报错 "experimentalDecorators": true, // 启动装饰器 "resolveJsonModule": true, // 加载json "esModuleInterop": true, From 24f1d2037e04860ed214f0adcdda8ac2490eb94d Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Thu, 25 Jan 2024 20:06:16 +0800 Subject: [PATCH 06/43] feat: Support clear result in nlp tool --- .../src/components/NLPToolView/index.tsx | 12 +++++++++++- .../src/components/NLPToolView/textContent/index.tsx | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index c807494a3..cb9a9b69c 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -99,7 +99,7 @@ const NLPToolView: React.FC = (props) => { }; toolInstanceRef.current.setResult = () => {} - toolInstanceRef.current.clearResult = () => {} + toolInstanceRef.current.clearResult = clearResult toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute toolInstanceRef.current.setHighlightKey = setHighlightKey updateSidebar() @@ -114,6 +114,16 @@ const NLPToolView: React.FC = (props) => { updateSidebar() } + const clearResult = () => { + setResult({ + id: 1, + newText: '', + indicatorDetermine: {}, + textAnnotation: [], + }) + updateSidebar() + } + const onSelectionChange = (text: string) => { if (text === '') return let selection = window.getSelection() diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index 948b4450b..02bbddec2 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -84,7 +84,7 @@ const TextContent: React.FC = (props) => { const annotation = _.last(interval.annotations) if (annotation) { const color = getColor(annotation.attribute) - const highlight = annotation.id === highlightKey + const highlight = interval.annotations.find((v: INLPTextAnnotation) => v.id === highlightKey) return {interval.text} } else { return {interval.text} From dbfdf820110c0375b5ae772398c25a44403f8ae8 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Fri, 26 Jan 2024 17:16:41 +0800 Subject: [PATCH 07/43] feat: NLPTool: Support delete in siderbar --- .../src/components/NLPToolView/index.tsx | 8 ++++++ packages/lb-components/src/index.scss | 24 ++++++++++++------ .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 25 +++++++++++++++++-- packages/lb-utils/src/i18n/resources.json | 2 ++ 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index cb9a9b69c..4069d0a38 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -102,6 +102,7 @@ const NLPToolView: React.FC = (props) => { toolInstanceRef.current.clearResult = clearResult toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute toolInstanceRef.current.setHighlightKey = setHighlightKey + toolInstanceRef.current.deleteTextAnnotation = deleteTextAnnotation updateSidebar() }, [result]); @@ -124,6 +125,13 @@ const NLPToolView: React.FC = (props) => { updateSidebar() } + const deleteTextAnnotation = (key: string) => { + setResult((origin: INLPResult) => ({ + ...origin, + textAnnotation: origin.textAnnotation.filter((item: INLPTextAnnotation) => item.id !== key), + })) + } + const onSelectionChange = (text: string) => { if (text === '') return let selection = window.getSelection() diff --git a/packages/lb-components/src/index.scss b/packages/lb-components/src/index.scss index a86cf5b1c..5e70e5871 100644 --- a/packages/lb-components/src/index.scss +++ b/packages/lb-components/src/index.scss @@ -2218,17 +2218,25 @@ $ptToolHeight: 40px; display:flex; flex-direction:column; &__item{ - padding: 16px; - font-weight: 400; - font-size: 14px; - line-height: 20px; - color: #666666; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + display: flex; + align-items: center; cursor:pointer; + &__text{ + flex: 1 1; + padding: 16px; + font-weight: 400; + font-size: 12px; + line-height: 20px; + color: #666666; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } &__active{ background-color: #eeefff; + &__text{ + color: #666fff; + } } } } diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx index 6ac6fc9e6..2a9312ebd 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -8,7 +8,9 @@ import { LabelBeeContext } from '@/store/ctx'; import { ICustomToolInstance } from '@/hooks/annotation'; import { prefix } from '@/constant'; import { classnames } from '@/utils'; -import { INLPTextAnnotation } from '@/components/NLPToolView/types' +import { INLPTextAnnotation } from '@/components/NLPToolView/types'; +import { Popconfirm, Tooltip } from 'antd'; +import { CloseOutlined } from '@ant-design/icons'; interface IProps { toolInstance: ICustomToolInstance; @@ -45,6 +47,10 @@ const NLPAnnotatedList: React.FC = (props) => { setHighlight(key) } + const onDeleteTextAnnotation = (item: INLPTextAnnotation) => { + toolInstance?.deleteTextAnnotation(item.id) + } + if (annotatedList?.length === 0) return null return
{ @@ -58,7 +64,22 @@ const NLPAnnotatedList: React.FC = (props) => { })} onClick={() => setHighlightKey(v.id)} > - {`${k + 1}、${v.text}`} + + {`${v.attribute || t('NoAttribute')},${t('textTool')}:${v.text}`} + + { + active && trigger.parentElement} + onConfirm={() => onDeleteTextAnnotation(v)} + overlayClassName={`${prefix}-pop-confirm`} + > + + + }
}) } diff --git a/packages/lb-utils/src/i18n/resources.json b/packages/lb-utils/src/i18n/resources.json index 54debdab2..40cb37b94 100644 --- a/packages/lb-utils/src/i18n/resources.json +++ b/packages/lb-utils/src/i18n/resources.json @@ -285,6 +285,7 @@ "PreviousInterval": "Previous interval", "NextInterval": "Next interval", "DeleteComment": "Delete Comments", + "DeleteCommentConfirm": "Are you sure you want to delete this annotation?", "PreviousComment": "Previous comment", "NextComment": "Next comment", "ClipSelect": "Select", @@ -642,6 +643,7 @@ "PreviousInterval": "上一区间", "NextInterval": "下一区间", "DeleteComment": "删除批注", + "DeleteCommentConfirm": "确定删除该条批注吗", "PreviousComment": "上一条批注", "NextComment": "下一条批注", "ClipSelect": "截取选中", From 7e566c2ed26de73987da5e9bd2a1255c1530b3fe Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Mon, 29 Jan 2024 11:28:07 +0800 Subject: [PATCH 08/43] feat: Support lock and uncheck attr in nlp tool --- .../src/components/NLPToolView/index.tsx | 19 +++++++++++++++++-- .../src/components/attributeList/index.tsx | 2 +- .../audioSide/clipSidebar/index.tsx | 2 +- .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 5 +++-- .../MainView/sidebar/NLPSidebar/index.tsx | 7 ++++++- .../sidebar/SwitchAttributeList/index.tsx | 4 +++- 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index 4069d0a38..e3926a42c 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -4,7 +4,7 @@ * @Date: 2024-01-24 */ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState, useMemo } from 'react'; import { AppState } from '@/store'; import { connect } from 'react-redux'; import { LabelBeeContext, NLPContext } from '@/store/ctx'; @@ -37,6 +37,7 @@ const NLPToolView: React.FC = (props) => { const [NLPConfig, setNLPConfig] = useState(); const [selectedAttribute, setSelectedAttribute] = useState('') + const [lockList, setLockList] = useState([]) const [textData, setTextData] = useState([ { @@ -51,6 +52,15 @@ const NLPToolView: React.FC = (props) => { textAnnotation: [], }) + const displayAnnotation = useMemo(() => { + if (!result?.textAnnotation) { + return [] + } + return result.textAnnotation.filter((item: INLPTextAnnotation) => { + return lockList.length === 0 || lockList.includes(item.attribute) + }) + }, [result, lockList]) + const { t } = useTranslation(); useEffect(() => { @@ -103,6 +113,7 @@ const NLPToolView: React.FC = (props) => { toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute toolInstanceRef.current.setHighlightKey = setHighlightKey toolInstanceRef.current.deleteTextAnnotation = deleteTextAnnotation + toolInstanceRef.current.setAttributeLockList = setAttributeLockList updateSidebar() }, [result]); @@ -115,6 +126,10 @@ const NLPToolView: React.FC = (props) => { updateSidebar() } + const setAttributeLockList = (list: string[]) => { + setLockList(list) + } + const clearResult = () => { setResult({ id: 1, @@ -164,7 +179,7 @@ const NLPToolView: React.FC = (props) => { checkMode={checkMode} annotation={annotation} NLPConfig={NLPConfig} - textAnnotation={result.textAnnotation ?? []} + textAnnotation={displayAnnotation} onSelectionChange={onSelectionChange} />
diff --git a/packages/lb-components/src/components/attributeList/index.tsx b/packages/lb-components/src/components/attributeList/index.tsx index 0a4c99d09..0e4f47266 100644 --- a/packages/lb-components/src/components/attributeList/index.tsx +++ b/packages/lb-components/src/components/attributeList/index.tsx @@ -32,7 +32,7 @@ interface IProps { enableColorPicker?: boolean; updateColorConfig?: (value: string, color: string) => void; updateSize?: (size: IDefaultSize) => void; - attributeLockChange?: (list: any) => void; + attributeLockChange?: (list: string[]) => void; forbidShowLimitPopover?: boolean; } diff --git a/packages/lb-components/src/components/audioAnnotate/audioSide/clipSidebar/index.tsx b/packages/lb-components/src/components/audioAnnotate/audioSide/clipSidebar/index.tsx index 122eb3253..a0f890298 100644 --- a/packages/lb-components/src/components/audioAnnotate/audioSide/clipSidebar/index.tsx +++ b/packages/lb-components/src/components/audioAnnotate/audioSide/clipSidebar/index.tsx @@ -114,7 +114,7 @@ const ClipSidebar = (props: IClipSidebarProps) => { list={list} attributeChanged={attributeChanged} selectedAttribute={selectedAttribute} - attributeLockChange={(list: any) => { + attributeLockChange={(list: string[]) => { setAudioClipState({ attributeLockList: list, }); diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx index 2a9312ebd..2161f642b 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -43,8 +43,9 @@ const NLPAnnotatedList: React.FC = (props) => { const annotatedList = result?.[0]?.textAnnotation ?? [] const setHighlightKey = (key: string) => { - toolInstance?.setHighlightKey(key) - setHighlight(key) + let chooseKey = key === highlight ? '' : key + toolInstance?.setHighlightKey(chooseKey) + setHighlight(chooseKey) } const onDeleteTextAnnotation = (item: INLPTextAnnotation) => { diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx index d15541b96..28de803b8 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx @@ -18,7 +18,12 @@ interface IProps { const { Panel } = Collapse; export const sidebarCls = `${prefix}-sidebar`; const NLPSidebar: React.FC = ({ toolInstance, imgIndex }) => { - const attributeList = ; + + const setAttributeLockList = (list: string[]) => { + toolInstance?.setAttributeLockList(list) + } + + const attributeList = ; const operation = ; const annotatedList = ; diff --git a/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx b/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx index 970c138fa..906b379f5 100644 --- a/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx @@ -13,13 +13,14 @@ import { LabelBeeContext } from '@/store/ctx'; interface IProps { toolInstance: GraphToolInstance; stepInfo: IStepInfo; + attributeLockChange: (list: string[]) => void; } const SwitchAttributeList: React.FC = (props) => { const [_, forceRender] = useState(0); const listRef = useRef(null); - const { toolInstance } = props; + const { toolInstance, attributeLockChange } = props; const { t } = useTranslation(); useEffect(() => { @@ -73,6 +74,7 @@ const SwitchAttributeList: React.FC = (props) => { selectedAttribute={toolInstance?.defaultAttribute ?? ''} ref={listRef} forbidDefault={isScribbleTool} + attributeLockChange={attributeLockChange} /> ); } From 61266ed6dc2dede094ab33b7621be2f51f9187bd Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Mon, 29 Jan 2024 17:27:59 +0800 Subject: [PATCH 09/43] feat: NLP tool view --- .../src/components/NLPToolView/index.tsx | 109 ++++++++++-------- .../src/components/NLPToolView/utils/index.ts | 31 +++++ packages/lb-components/src/index.tsx | 2 + .../src/views/MainView/NLPLayout/index.tsx | 16 ++- 4 files changed, 106 insertions(+), 52 deletions(-) create mode 100644 packages/lb-components/src/components/NLPToolView/utils/index.ts diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index e3926a42c..e89d8f0df 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -10,16 +10,20 @@ import { connect } from 'react-redux'; import { LabelBeeContext, NLPContext } from '@/store/ctx'; import { message } from 'antd'; import { prefix } from '@/constant'; -import { Layout } from 'antd/es'; import TextContent from './textContent'; import { useTranslation } from 'react-i18next'; -import { ITextList, INLPToolConfig, ITextData, INLPTextAnnotation, INLPResult } from './types'; +import { + INLPToolConfig, + ITextData, + INLPTextAnnotation, + INLPResult, + IRemarkLayer, +} from './types'; import AnnotationTips from '@/views/MainView/annotationTips'; import { getStepConfig } from '@/store/annotation/reducer'; import { jsonParser } from '@/utils'; import { getCurrentResultFromResultList } from '../LLMToolView/utils/data'; import { useCustomToolInstance } from '@/hooks/annotation'; -import { toolStyleConverter } from '@labelbee/lb-utils'; import { uuid } from '@labelbee/lb-annotation'; interface IProps { @@ -27,17 +31,20 @@ interface IProps { annotation?: any; showTips?: boolean; tips?: string; + remarkLayer?: (value: IRemarkLayer) => void; + remark: any; } const NLPViewCls = `${prefix}-NLPView`; const NLPToolView: React.FC = (props) => { - const { annotation, checkMode = true, tips, showTips } = props; + const { annotation, checkMode, tips, showTips, remarkLayer, remark } = props; + const { imgIndex, imgList, stepList, step } = annotation; const { highlightKey, setHighlightKey } = useContext(NLPContext); const { toolInstanceRef } = useCustomToolInstance(); const [NLPConfig, setNLPConfig] = useState(); - const [selectedAttribute, setSelectedAttribute] = useState('') - const [lockList, setLockList] = useState([]) + const [selectedAttribute, setSelectedAttribute] = useState(''); + const [lockList, setLockList] = useState([]); const [textData, setTextData] = useState([ { @@ -50,16 +57,16 @@ const NLPToolView: React.FC = (props) => { newText: '', indicatorDetermine: {}, textAnnotation: [], - }) + }); const displayAnnotation = useMemo(() => { if (!result?.textAnnotation) { - return [] + return []; } return result.textAnnotation.filter((item: INLPTextAnnotation) => { - return lockList.length === 0 || lockList.includes(item.attribute) - }) - }, [result, lockList]) + return lockList.length === 0 || lockList.includes(item.attribute); + }); + }, [result, lockList]); const { t } = useTranslation(); @@ -93,7 +100,7 @@ const NLPToolView: React.FC = (props) => { } const currentData = imgList[imgIndex] ?? {}; const result = getCurrentResultFromResultList(currentData?.result); - setResult(result) + setResult(result); }, [imgIndex]); useEffect(() => { @@ -105,30 +112,30 @@ const NLPToolView: React.FC = (props) => { useEffect(() => { toolInstanceRef.current.exportData = () => { - return [[result], { }]; + return [[result], {}]; }; - toolInstanceRef.current.setResult = () => {} - toolInstanceRef.current.clearResult = clearResult - toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute - toolInstanceRef.current.setHighlightKey = setHighlightKey - toolInstanceRef.current.deleteTextAnnotation = deleteTextAnnotation - toolInstanceRef.current.setAttributeLockList = setAttributeLockList - updateSidebar() + toolInstanceRef.current.setResult = () => {}; + toolInstanceRef.current.clearResult = clearResult; + toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute; + toolInstanceRef.current.setHighlightKey = setHighlightKey; + toolInstanceRef.current.deleteTextAnnotation = deleteTextAnnotation; + toolInstanceRef.current.setAttributeLockList = setAttributeLockList; + updateSidebar(); }, [result]); const updateSidebar = () => { toolInstanceRef.current.emit('changeAttributeSidebar'); - } + }; const setDefaultAttribute = (attribute: string) => { - toolInstanceRef.current.defaultAttribute = attribute - setSelectedAttribute(attribute) - updateSidebar() - } + toolInstanceRef.current.defaultAttribute = attribute; + setSelectedAttribute(attribute); + updateSidebar(); + }; const setAttributeLockList = (list: string[]) => { - setLockList(list) - } + setLockList(list); + }; const clearResult = () => { setResult({ @@ -136,38 +143,43 @@ const NLPToolView: React.FC = (props) => { newText: '', indicatorDetermine: {}, textAnnotation: [], - }) - updateSidebar() - } + }); + updateSidebar(); + }; const deleteTextAnnotation = (key: string) => { setResult((origin: INLPResult) => ({ ...origin, textAnnotation: origin.textAnnotation.filter((item: INLPTextAnnotation) => item.id !== key), - })) - } + })); + }; const onSelectionChange = (text: string) => { - if (text === '') return - let selection = window.getSelection() - const { anchorOffset = 0, focusOffset = 0, anchorNode, focusNode } = selection || {} + if (text === '' || checkMode) return; + let selection = window.getSelection(); + + const { anchorOffset = 0, focusOffset = 0, anchorNode, focusNode } = selection || {}; if (anchorNode === focusNode) { // ignore the order of selection - let start = Math.min(anchorOffset, focusOffset) - let end = Math.max(anchorOffset, focusOffset) + let start = Math.min(anchorOffset, focusOffset); + let end = Math.max(anchorOffset, focusOffset); setResult({ ...result, - textAnnotation: [...(result?.textAnnotation || []), { - id: uuid(8, 62), - start, - end, - attribute: selectedAttribute, - text, - }] - }) - window.getSelection()?.empty() + textAnnotation: [ + ...(result?.textAnnotation || []), + { + id: uuid(8, 62), + start, + end, + attribute: selectedAttribute, + text, + }, + ], + }); + + window.getSelection()?.empty(); } - } + }; return (
@@ -177,10 +189,11 @@ const NLPToolView: React.FC = (props) => { highlightKey={highlightKey} textData={textData} checkMode={checkMode} - annotation={annotation} NLPConfig={NLPConfig} textAnnotation={displayAnnotation} onSelectionChange={onSelectionChange} + remarkLayer={remarkLayer} + remark={remark} />
diff --git a/packages/lb-components/src/components/NLPToolView/utils/index.ts b/packages/lb-components/src/components/NLPToolView/utils/index.ts new file mode 100644 index 000000000..564692319 --- /dev/null +++ b/packages/lb-components/src/components/NLPToolView/utils/index.ts @@ -0,0 +1,31 @@ +import _ from 'lodash'; +import { INLPInterval } from '../types'; + +export const getIntervals = (content: string, textAnnotation: INLPInterval[], key: string) => { + if (!content?.length || !textAnnotation?.length) { + return []; + } + const splitPoints = _.uniq( + _.concat( + 0, + ...textAnnotation.map((range: INLPInterval) => [range.start, range.end]), + content.length, + ), + ).sort((a, b) => a - b); + const intervals: INLPInterval[] = []; + for (let i = 0; i < splitPoints.length - 1; i++) { + const start = splitPoints[i]; + const end = splitPoints[i + 1]; + const annotations: INLPInterval[] = textAnnotation.filter( + (range: INLPInterval) => + (range.start >= start && range.end <= end) || (start >= range.start && end <= range.end), + ); + intervals.push({ + start, + end, + [key]: annotations, + text: content.slice(start, end), + }); + } + return intervals; +}; diff --git a/packages/lb-components/src/index.tsx b/packages/lb-components/src/index.tsx index 98938d85b..cbc8161e7 100644 --- a/packages/lb-components/src/index.tsx +++ b/packages/lb-components/src/index.tsx @@ -2,6 +2,7 @@ import { AppProps } from '@/App'; import AnnotationView from '@/components/AnnotationView'; import PointCloudAnnotationView from '@/components/AnnotationView/pointCloudAnnotationView'; import QuestionView from '@/components/LLMToolView/questionView'; +import TextContent from '@/components/NLPToolView/textContent'; import { i18n } from '@labelbee/lb-utils'; import React, { useImperativeHandle, useState } from 'react'; import { I18nextProvider } from 'react-i18next'; @@ -69,6 +70,7 @@ export { AnnotationView, PointCloudAnnotationView, QuestionView, + TextContent, LLMToolView, i18n, VideoTagTool, diff --git a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx index efd0d9647..2131cc514 100644 --- a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx +++ b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx @@ -13,6 +13,8 @@ import NLPToolView from '@/components/NLPToolView'; interface IProps { path: string; loading: boolean; + remarkLayer?: any; + remark: any; } const { Sider, Content } = Layout; @@ -26,8 +28,8 @@ const NLPLayout: React.FC = (props) => { { return { - highlightKey , - setHighlightKey , + highlightKey, + setHighlightKey, }; }, [highlightKey])} > @@ -38,11 +40,17 @@ const NLPLayout: React.FC = (props) => { [`${prefix}-NLPLayout`]: true, })} > - + - + {props.drawLayerSlot?.({})} From 14ac2a8a736752c5e13e31923bde144e9d92621e Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Mon, 29 Jan 2024 17:30:25 +0800 Subject: [PATCH 10/43] feat: NLP tool support remark --- .../NLPToolView/textContent/index.module.scss | 22 ++ .../NLPToolView/textContent/index.tsx | 271 ++++++++++++++---- 2 files changed, 238 insertions(+), 55 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.module.scss b/packages/lb-components/src/components/NLPToolView/textContent/index.module.scss index e69de29bb..acec4cf74 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.module.scss +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.module.scss @@ -0,0 +1,22 @@ +.textContain { + padding: 26px 32px; + flex: 1; + display: flex; + flex-flow: column; + background-color: #fff; +} + +.Header { + margin-bottom: 24px; + height: 32px; + font-size: 18px; + font-weight: 500; +} + +.text { + background-color: #f5f5f5; + padding: 12px; + font-size: 14px; + line-height: 22px; + flex: 1; +} diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index 02bbddec2..e568cea93 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -5,14 +5,24 @@ */ import React, { useEffect, useState, useRef, useMemo } from 'react'; -import { Tag } from 'antd'; -import classNames from 'classnames'; import { useTranslation, I18nextProvider } from 'react-i18next'; import { i18n, toolStyleConverter } from '@labelbee/lb-utils'; -import { INLPToolConfig, ITextData, INLPTextAnnotation } from '../types'; +import { + INLPToolConfig, + ITextData, + INLPTextAnnotation, + INLPInterval, + ISelectText, + IRemarkLayer, + IRemarkInterval, + IRemarkAnnotation, +} from '../types'; import { prefix } from '@/constant'; import { useTextSelection } from 'ahooks'; -import _ from 'lodash' +import _ from 'lodash'; +import { CommonToolUtils, uuid } from '@labelbee/lb-annotation'; +import styleString from '@/constant/styleString'; +import { getIntervals } from '../utils'; interface IProps { highlightKey?: string; @@ -20,52 +30,60 @@ interface IProps { textAnnotation: INLPTextAnnotation[]; lang?: string; checkMode?: boolean; - annotation?: any; NLPConfig?: INLPToolConfig; answerHeaderSlot?: React.ReactDOM | string; onSelectionChange?: (text: string) => void; -} - -interface INLPInterval { - start: number; - end: number; - annotations: INLPTextAnnotation[]; - text: string; + remarkLayer?: (values: IRemarkLayer) => void; + remark?: any; + isSourceView?: boolean; } const NLPViewCls = `${prefix}-NLPView`; const TextContent: React.FC = (props) => { - const { highlightKey, textData, lang, checkMode = true, NLPConfig, answerHeaderSlot, onSelectionChange, textAnnotation } = props; + const { + highlightKey, + textData, + lang, + checkMode = true, + NLPConfig, + onSelectionChange, + textAnnotation, + remark, + isSourceView, + } = props; + const { enableRemark, displayRemarkList } = remark || {}; + + const { t } = useTranslation(); + const contentRef = useRef(null); + const selection = useTextSelection(contentRef); + const [remarkResut, setRemarkResut] = useState({}); + const [remarkStyle, setRemarkStyle] = useState(undefined); const content = useMemo(() => { - return textData?.[0]?.content - }, [textData]) - - const splitIntervals: INLPInterval[] = useMemo(() => { - const splitPoints = _.uniq(_.concat(0, ...textAnnotation.map((range: INLPTextAnnotation) => [range.start, range.end]), content.length)).sort((a, b) => a - b) - let intervals = [] - for (let i = 0; i < splitPoints.length - 1; i++){ - let start = splitPoints[i] - let end = splitPoints[i + 1] - let annotations = textAnnotation.filter((range: INLPTextAnnotation) => (range.start >= start && range.end <= end) || (start >= range.start && end <= range.end)) - intervals.push({ - start, - end, - annotations, - text: content.slice(start, end) - }) + return textData?.[0]?.content; + }, [textData]); + + // remark split intervals + const remarkSplitIntervals: IRemarkInterval[] = useMemo(() => { + const remarkAnnotation = _.clone(displayRemarkList); + if (remarkResut?.start) { + remarkAnnotation.push(remarkResut); } - return intervals - }, [textAnnotation]) - const { t } = useTranslation(); - const contentRef = useRef(null) - const selection = useTextSelection(contentRef) + return getIntervals(content, remarkAnnotation, 'remarkAnnotations'); + }, [displayRemarkList, remarkResut, content]); + + // annotation split intervals + const splitIntervals: INLPInterval[] = useMemo( + () => getIntervals(content, textAnnotation, 'annotations'), + [textAnnotation], + ); const getColor = (attribute = '') => { - return toolStyleConverter.getColorByConfig({ attribute, config: NLPConfig }); - } + const style = CommonToolUtils.jsonParser(styleString); + return toolStyleConverter.getColorByConfig({ attribute, config: NLPConfig, style }); + }; useEffect(() => { if (lang) { @@ -74,37 +92,180 @@ const TextContent: React.FC = (props) => { }, []); useEffect(() => { - onSelectionChange?.(selection.text) - }, [selection.text]) + if (enableRemark) { + onSelectiRemark(selection.text); + } else { + onSelectionChange?.(selection.text); + } + }, [selection.text]); + + // When replying to comments, the pop-up window expands to the corresponding position. + useEffect(() => { + if (!remark?.editAuditID) { + return; + } + const remarkItem = displayRemarkList.filter( + (item: IRemarkAnnotation) => item.auditID === remark.editAuditID, + )[0]; + const id = remarkItem?.id; + const element = document.getElementById(id); + if (element && contentRef.current) { + const elementRect = element.getBoundingClientRect(); + const parentRect = contentRef.current.getBoundingClientRect(); + const relativeLeft = elementRect.right - parentRect.left; + const relativeTop = elementRect.bottom - parentRect.top - element.offsetHeight; + if (relativeLeft && relativeTop) { + setRemarkStyle({ left: relativeLeft, top: relativeTop }); + } + } + }, [remark?.editAuditID]); + + const onSelectiRemark = (text: string) => { + if (text === '') return; + let curSelection = window.getSelection(); + + const { anchorOffset = 0, focusOffset = 0, anchorNode, focusNode } = curSelection || {}; + + if (anchorNode === focusNode) { + // ignore the order of selection + let start = Math.min(anchorOffset, focusOffset); + let end = Math.max(anchorOffset, focusOffset); + + if (selection && contentRef?.current && curSelection) { + const contentRect = contentRef.current?.getBoundingClientRect(); + const range = curSelection.getRangeAt(0); + const rangeRect = range.getBoundingClientRect(); + const endPosition = { + left: rangeRect.right - contentRect.left, + top: rangeRect.top - contentRect.top, + }; + if (endPosition.left && endPosition.left) { + setRemarkStyle(endPosition); + } + } + const value = { + id: uuid(8, 62), + start, + end, + text, + }; + setRemarkResut(value); + } + }; + + const randerRemark = () => { + if (props?.remarkLayer) { + return props.remarkLayer({ + style: remarkStyle, + onClose: () => { + setRemarkStyle(undefined); + setRemarkResut({}); + window.getSelection()?.empty(); + }, + submitData: remarkResut, + }); + } + return null; + }; + + const renderRemarkMask = () => { + return ( +
+ {remarkSplitIntervals.map((interval: IRemarkInterval, index: number) => { + const remarkAnnotation = _.last(interval.remarkAnnotations); + const highlight = interval?.remarkAnnotations?.find( + (i) => i?.auditID === remark.hoverAuditID, + ); + const color = highlight ? '#ffc60a' : '#fcdf7e'; + if (remarkAnnotation) { + return ( + + {interval.text} + + ); + } + return {interval.text}; + })} +
+ ); + }; const renderContent = () => { - return
- { - splitIntervals.map((interval: INLPInterval) => { - const annotation = _.last(interval.annotations) + return ( +
+ {splitIntervals.map((interval: INLPInterval, index: number) => { + const annotation = _.last(interval.annotations); if (annotation) { - const color = getColor(annotation.attribute) - const highlight = interval.annotations.find((v: INLPTextAnnotation) => v.id === highlightKey) - return {interval.text} + const color = getColor(annotation.attribute); + const highlight = interval?.annotations?.find( + (v: INLPTextAnnotation) => v.id === highlightKey, + ); + return ( + + {interval.text} + + ); } else { - return {interval.text} + return {interval.text}; } - }) - } - { - renderMask() - } -
- } + })} + {displayRemarkList?.length > 0 && renderRemarkMask()} + {renderMask()} + {randerRemark()} +
+ ); + }; const renderMask = () => { - return
{content}
+ return ( +
+ {content} +
+ ); + }; + + // Unlabeled data + if (isSourceView) { + return ( +
+
{t('textTool')}
+
{content}
+
+ ); } return (
{t('textTool')}
-
+
{renderContent()}
From f760487359d478d72fda7b3f572c25fa73570083 Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Mon, 29 Jan 2024 17:31:45 +0800 Subject: [PATCH 11/43] feat: NLP tool type --- .../src/components/NLPToolView/types.ts | 42 +++++++++++++++++++ packages/lb-components/src/types/main.d.ts | 1 + 2 files changed, 43 insertions(+) diff --git a/packages/lb-components/src/components/NLPToolView/types.ts b/packages/lb-components/src/components/NLPToolView/types.ts index a538e2085..e873ac9ea 100644 --- a/packages/lb-components/src/components/NLPToolView/types.ts +++ b/packages/lb-components/src/components/NLPToolView/types.ts @@ -1,4 +1,5 @@ import { IInfoList } from '@labelbee/lb-utils'; +import React from 'react'; export interface IndicatorDetermine { label: string; @@ -42,3 +43,44 @@ export interface INLPTextAnnotation { attribute: string; text: string; } + +export interface INLPInterval { + start: number; + end: number; + annotations?: INLPTextAnnotation[]; + text: string; + attribute?: string; + id?: string; +} + +export interface ISelectText { + id?: string; + start?: number; + end?: number; + text?: string; +} + +export interface IRemarkLayer { + style: React.CSSProperties | undefined; + onClose: () => void; + submitData: ISelectText; +} + +export interface IRemarkInterval { + start: number; + end: number; + remarkAnnotations?: IRemarkAnnotation[]; + text: string; + attribute?: string; + id?: string; +} + + +export interface IRemarkAnnotation{ + auditID: number, + text: string, + id: string, + start: number, + end: number, + uuid: string, +} diff --git a/packages/lb-components/src/types/main.d.ts b/packages/lb-components/src/types/main.d.ts index 840dff958..4abe29f2d 100644 --- a/packages/lb-components/src/types/main.d.ts +++ b/packages/lb-components/src/types/main.d.ts @@ -45,6 +45,7 @@ export type Sider = ({ horizontal: React.ReactNode; scribbleSidebar: React.ReactNode; LLMSidebar: React.ReactNode; + NLPSidebar: React.ReactNode; videoClipSidebar: React.ReactNode; NLPSidebar: React.ReactNode; // PointCloud From 4ba510c8d9c901b0186b03e51f2bdeedd943839a Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Mon, 29 Jan 2024 17:33:03 +0800 Subject: [PATCH 12/43] feat: NLP tool sidebar and hotkey --- .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 1 + .../src/views/MainView/sidebar/NLPSidebar/index.tsx | 13 +++++-------- .../toolFooter/FooterTips/ToolHotKey/NLP/index.tsx | 12 ++++++++++++ .../toolFooter/FooterTips/ToolHotKey/index.tsx | 2 ++ 4 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/NLP/index.tsx diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx index 2161f642b..49e51004b 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -74,6 +74,7 @@ const NLPAnnotatedList: React.FC = (props) => { placement='topRight' okText={t('Confirm')} cancelText={t('Cancel')} + // @ts-ignore getPopupContainer={(trigger) => trigger.parentElement} onConfirm={() => onDeleteTextAnnotation(v)} overlayClassName={`${prefix}-pop-confirm`} diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx index 28de803b8..20a1402c7 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx @@ -1,4 +1,3 @@ -import { Collapse } from 'antd/es'; import React from 'react'; import { TagOperation } from '@labelbee/lb-annotation'; import { connect } from 'react-redux'; @@ -15,15 +14,13 @@ interface IProps { checkMode?: boolean; } -const { Panel } = Collapse; export const sidebarCls = `${prefix}-sidebar`; -const NLPSidebar: React.FC = ({ toolInstance, imgIndex }) => { - +const NLPSidebar: React.FC = ({ toolInstance, checkMode }) => { const setAttributeLockList = (list: string[]) => { - toolInstance?.setAttributeLockList(list) - } + toolInstance?.setAttributeLockList(list); + }; - const attributeList = ; + const attributeList = ; const operation = ; const annotatedList = ; @@ -33,7 +30,7 @@ const NLPSidebar: React.FC = ({ toolInstance, imgIndex }) => { {attributeList} {annotatedList}
- {operation} + {!checkMode && operation} ); }; diff --git a/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/NLP/index.tsx b/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/NLP/index.tsx new file mode 100644 index 000000000..8b13a57e2 --- /dev/null +++ b/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/NLP/index.tsx @@ -0,0 +1,12 @@ +import { backward, forward } from '../common'; +import ForwardSvg from '@/assets/annotation/toolHotKeyIcon/icon_next_kj.svg'; + +export const forwardWithEnter = { + name: 'Save', + icon: ForwardSvg, + shortCut: ['Ctrl', 'Enter'], +}; + +const NLPShortCutTable = [backward, forward, forwardWithEnter]; + +export default NLPShortCutTable; diff --git a/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/index.tsx b/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/index.tsx index ffbb8536e..c42c8b34c 100644 --- a/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/index.tsx +++ b/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/index.tsx @@ -18,6 +18,7 @@ import pointCloudShortCutTable, { import scribbleShortCutTable from './scribble'; import cuboidShortCutTable from './cuboid'; import LLMShortCutTable from './LLM'; +import NLPShortCutTable from './NLP'; import audioTextShortCutTable from './audioText' import { footerCls } from '../../index'; @@ -62,6 +63,7 @@ export const shortCutTable: { [a: string]: IShortCutInfo[] } = { [EToolName.ScribbleTool]: scribbleShortCutTable, [EToolName.Cuboid]: cuboidShortCutTable, [EToolName.LLM]: LLMShortCutTable, + [EToolName.NLP]: NLPShortCutTable, [EAudioToolName.AudioTextTool]: audioTextShortCutTable, }; From 443733f9aaaa5fd2bfb46d72dd5411dacb0d8b3a Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Mon, 29 Jan 2024 21:36:20 +0800 Subject: [PATCH 13/43] feat: NLP tool support indicatorDetermine --- .../src/components/NLPToolView/index.tsx | 10 +-- .../NLPToolView/textContent/index.tsx | 17 +++- packages/lb-components/src/index.scss | 1 + .../src/views/MainView/NLPLayout/index.tsx | 2 +- .../MainView/sidebar/NLPSidebar/index.tsx | 8 +- .../sidebar/NLPSidebar/indicatorDetermine.tsx | 86 +++++++++++++++++++ 6 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index e89d8f0df..2cda0e976 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -12,13 +12,7 @@ import { message } from 'antd'; import { prefix } from '@/constant'; import TextContent from './textContent'; import { useTranslation } from 'react-i18next'; -import { - INLPToolConfig, - ITextData, - INLPTextAnnotation, - INLPResult, - IRemarkLayer, -} from './types'; +import { INLPToolConfig, ITextData, INLPTextAnnotation, INLPResult, IRemarkLayer } from './types'; import AnnotationTips from '@/views/MainView/annotationTips'; import { getStepConfig } from '@/store/annotation/reducer'; import { jsonParser } from '@/utils'; @@ -115,7 +109,7 @@ const NLPToolView: React.FC = (props) => { return [[result], {}]; }; - toolInstanceRef.current.setResult = () => {}; + toolInstanceRef.current.setResult = (value) => setResult(value); toolInstanceRef.current.clearResult = clearResult; toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute; toolInstanceRef.current.setHighlightKey = setHighlightKey; diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index e568cea93..3b66e8e9d 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -23,6 +23,7 @@ import _ from 'lodash'; import { CommonToolUtils, uuid } from '@labelbee/lb-annotation'; import styleString from '@/constant/styleString'; import { getIntervals } from '../utils'; +import { classnames } from '@/utils'; interface IProps { highlightKey?: string; @@ -190,6 +191,8 @@ const TextContent: React.FC = (props) => { = (props) => { style={{ backgroundColor: color.valid.stroke, color: highlight ? 'white' : undefined, + display: 'inline-block', + lineHeight: '22px', }} key={index} > @@ -244,16 +249,17 @@ const TextContent: React.FC = (props) => { ); }; + const noSplitIntervals = !(splitIntervals?.length > 0 || displayRemarkList?.length > 0); // Unlabeled data - if (isSourceView) { + if (isSourceView || noSplitIntervals) { return (
{t('textTool')}
@@ -265,7 +271,12 @@ const TextContent: React.FC = (props) => { return (
{t('textTool')}
-
+
{renderContent()}
diff --git a/packages/lb-components/src/index.scss b/packages/lb-components/src/index.scss index 5e70e5871..9685ad547 100644 --- a/packages/lb-components/src/index.scss +++ b/packages/lb-components/src/index.scss @@ -2186,6 +2186,7 @@ $ptToolHeight: 40px; overflow-y: auto; flex: auto; padding: 26px 32px; + line-height: 26px; } .#{$prefix}-NLPView-question { diff --git a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx index 2131cc514..975de989d 100644 --- a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx +++ b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx @@ -14,7 +14,7 @@ interface IProps { path: string; loading: boolean; remarkLayer?: any; - remark: any; + remark?: any; } const { Sider, Content } = Layout; diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx index 20a1402c7..9798727b7 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx @@ -7,6 +7,7 @@ import { prefix } from '@/constant'; import SwitchAttributeList from '../SwitchAttributeList'; import GeneralOperation from '../GeneralOperation'; import NLPAnnotatedList from './NLPAnnotatedList'; +import IndicatorDetermineList from './indicatorDetermine'; interface IProps { imgIndex: number; @@ -23,10 +24,12 @@ const NLPSidebar: React.FC = ({ toolInstance, checkMode }) => { const attributeList = ; const operation = ; const annotatedList = ; + const indicatorDetermineList = ; return (
+ {indicatorDetermineList} {attributeList} {annotatedList}
@@ -36,7 +39,10 @@ const NLPSidebar: React.FC = ({ toolInstance, checkMode }) => { }; function mapStateToProps(state: AppState) { - return { toolInstance: state.annotation.toolInstance, imgIndex: state.annotation.imgIndex }; + return { + toolInstance: state.annotation.toolInstance, + imgIndex: state.annotation.imgIndex, + }; } export default connect(mapStateToProps, null, null, { context: LabelBeeContext })(NLPSidebar); diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx new file mode 100644 index 000000000..848651b5b --- /dev/null +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx @@ -0,0 +1,86 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { connect } from 'react-redux'; +import { AppState } from 'src/store'; +import StepUtils from '@/utils/StepUtils'; +import { IStepInfo } from '@/types/step'; +import { useTranslation } from 'react-i18next'; +import { LabelBeeContext } from '@/store/ctx'; +import { ICustomToolInstance } from '@/hooks/annotation'; +import { jsonParser } from '@/utils'; +import { IndicatorDetermine } from '@/components/NLPToolView/types'; +import DetermineGroup from '@/components/LLMToolView/sidebar/components/determineGroup'; + +interface IProps { + toolInstance: ICustomToolInstance; + stepInfo: IStepInfo; + checkMode?: boolean; + imgIndex: number; +} + +const IndicatorDetermineList = (props: IProps) => { + const { toolInstance, stepInfo, checkMode, imgIndex } = props; + const [result] = toolInstance.exportData(); + const [currentResult, setCurrentResult] = useState(result?.[0]); + const { t } = useTranslation(); + + useEffect(() => { + const [result] = toolInstance.exportData(); + setCurrentResult(result?.[0]); + }, [imgIndex]); + const config = jsonParser(stepInfo.config); + const { indicatorDetermine } = config; + + const updateIndicatorDetermine = (params: { key: string; value: boolean }) => { + const { value, key } = params; + if (key) { + const selected = { [key]: value }; + const originData = currentResult?.indicatorDetermine ?? {}; + const newResult = { ...currentResult, indicatorDetermine: { ...originData, ...selected } }; + setCurrentResult(newResult); + toolInstance.setResult(newResult); + } + }; + if (indicatorDetermine?.length > 0) { + return ( +
+
+ {t('IndicatorJudgment')} +
+
+ {indicatorDetermine.map((item: IndicatorDetermine, index: number) => { + const { label, value } = item; + return label ? ( + { + const values = { + key: value, + value: changeValue, + }; + updateIndicatorDetermine(values); + }} + key={index} + isDisableAll={checkMode} + /> + ) : null; + })} +
+
+ ); + } + return null; +}; +const mapStateToProps = (state: AppState) => { + const stepInfo = StepUtils.getCurrentStepInfo(state.annotation?.step, state.annotation?.stepList); + + return { + toolInstance: state.annotation.toolInstance, + stepInfo, + imgIndex: state.annotation.imgIndex, + }; +}; + +export default connect(mapStateToProps, null, null, { context: LabelBeeContext })( + IndicatorDetermineList, +); From a6d0c8f97fe9bd102c0e3b65f538429e12f897bc Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Mon, 29 Jan 2024 21:51:21 +0800 Subject: [PATCH 14/43] feat: Type deal with --- .../views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx index 848651b5b..612f29798 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx @@ -13,8 +13,8 @@ import DetermineGroup from '@/components/LLMToolView/sidebar/components/determin interface IProps { toolInstance: ICustomToolInstance; stepInfo: IStepInfo; - checkMode?: boolean; imgIndex: number; + checkMode?: boolean; } const IndicatorDetermineList = (props: IProps) => { From f222ab8324d4c7d5d30f837777528fa839af71de Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Tue, 30 Jan 2024 14:16:54 +0800 Subject: [PATCH 15/43] feat: Code adjustment --- .../src/components/NLPToolView/index.tsx | 1 + .../NLPToolView/textContent/index.tsx | 31 +----- .../src/components/NLPToolView/utils/index.ts | 2 +- .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 95 ++++++++++--------- .../sidebar/NLPSidebar/indicatorDetermine.tsx | 19 +++- 5 files changed, 69 insertions(+), 79 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index 2cda0e976..de229b6f6 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -120,6 +120,7 @@ const NLPToolView: React.FC = (props) => { const updateSidebar = () => { toolInstanceRef.current.emit('changeAttributeSidebar'); + toolInstanceRef.current.emit('changeIndicatorDetermine'); }; const setDefaultAttribute = (attribute: string) => { toolInstanceRef.current.defaultAttribute = attribute; diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index 3b66e8e9d..a895a3ae5 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -72,13 +72,13 @@ const TextContent: React.FC = (props) => { remarkAnnotation.push(remarkResut); } - return getIntervals(content, remarkAnnotation, 'remarkAnnotations'); + return getIntervals(content, remarkAnnotation ?? [], 'remarkAnnotations'); }, [displayRemarkList, remarkResut, content]); // annotation split intervals const splitIntervals: INLPInterval[] = useMemo( - () => getIntervals(content, textAnnotation, 'annotations'), - [textAnnotation], + () => getIntervals(content, textAnnotation ?? [], 'annotations'), + [textAnnotation, content], ); const getColor = (attribute = '') => { @@ -191,8 +191,7 @@ const TextContent: React.FC = (props) => { = (props) => { style={{ backgroundColor: color.valid.stroke, color: highlight ? 'white' : undefined, - display: 'inline-block', - lineHeight: '22px', + padding:'2px 0px' }} key={index} > @@ -249,25 +247,6 @@ const TextContent: React.FC = (props) => { ); }; - const noSplitIntervals = !(splitIntervals?.length > 0 || displayRemarkList?.length > 0); - // Unlabeled data - if (isSourceView || noSplitIntervals) { - return ( -
-
{t('textTool')}
-
{content}
-
- ); - } - return (
{t('textTool')}
diff --git a/packages/lb-components/src/components/NLPToolView/utils/index.ts b/packages/lb-components/src/components/NLPToolView/utils/index.ts index 564692319..ad30fc411 100644 --- a/packages/lb-components/src/components/NLPToolView/utils/index.ts +++ b/packages/lb-components/src/components/NLPToolView/utils/index.ts @@ -2,7 +2,7 @@ import _ from 'lodash'; import { INLPInterval } from '../types'; export const getIntervals = (content: string, textAnnotation: INLPInterval[], key: string) => { - if (!content?.length || !textAnnotation?.length) { + if (!content?.length) { return []; } const splitPoints = _.uniq( diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx index 49e51004b..bda0e0e7e 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -20,13 +20,12 @@ interface IProps { export const sidebarCls = `${prefix}-sidebar`; const NLPAnnotatedList: React.FC = (props) => { - const [_, forceRender] = useState(0); const listRef = useRef(null); const { toolInstance } = props; const { t } = useTranslation(); - const [highlight, setHighlight] = useState('') + const [highlight, setHighlight] = useState(''); useEffect(() => { if (toolInstance) { @@ -39,53 +38,57 @@ const NLPAnnotatedList: React.FC = (props) => { }; }, [toolInstance, listRef]); - const [result] = toolInstance.exportData() - const annotatedList = result?.[0]?.textAnnotation ?? [] + const [result] = toolInstance.exportData(); + const annotatedList = result?.[0]?.textAnnotation ?? []; const setHighlightKey = (key: string) => { - let chooseKey = key === highlight ? '' : key - toolInstance?.setHighlightKey(chooseKey) - setHighlight(chooseKey) - } + let chooseKey = key === highlight ? '' : key; + toolInstance?.setHighlightKey(chooseKey); + setHighlight(chooseKey); + }; const onDeleteTextAnnotation = (item: INLPTextAnnotation) => { - toolInstance?.deleteTextAnnotation(item.id) - } + toolInstance?.deleteTextAnnotation(item.id); + }; - if (annotatedList?.length === 0) return null - return
- { - annotatedList.map((v: INLPTextAnnotation, k: number) => { - const active = highlight === v.id - return
setHighlightKey(v.id)} - > - - {`${v.attribute || t('NoAttribute')},${t('textTool')}:${v.text}`} - - { - active && trigger.parentElement} - onConfirm={() => onDeleteTextAnnotation(v)} - overlayClassName={`${prefix}-pop-confirm`} - > - - - } -
- }) - } -
+ if (annotatedList?.length === 0) return null; + return ( +
+ {annotatedList.map((v: INLPTextAnnotation, k: number) => { + const active = highlight === v.id; + return ( +
setHighlightKey(v.id)} + > + + {`${ + v.attribute || t('NoAttribute') + },${t('textTool')}:${v.text}`} + + {active && ( + trigger.parentElement} + onConfirm={() => onDeleteTextAnnotation(v)} + overlayClassName={`${prefix}-pop-confirm`} + > + {e.stopPropagation()}}/> + + )} +
+ ); + })} +
+ ); }; const mapStateToProps = (state: AppState) => { @@ -97,6 +100,4 @@ const mapStateToProps = (state: AppState) => { }; }; -export default connect(mapStateToProps, null, null, { context: LabelBeeContext })( - NLPAnnotatedList, -); +export default connect(mapStateToProps, null, null, { context: LabelBeeContext })(NLPAnnotatedList); diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx index 612f29798..c463a6142 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx @@ -20,13 +20,22 @@ interface IProps { const IndicatorDetermineList = (props: IProps) => { const { toolInstance, stepInfo, checkMode, imgIndex } = props; const [result] = toolInstance.exportData(); - const [currentResult, setCurrentResult] = useState(result?.[0]); + const currentResult = result?.[0]; const { t } = useTranslation(); + const [_, forceRender] = useState(0); + useEffect(() => { - const [result] = toolInstance.exportData(); - setCurrentResult(result?.[0]); - }, [imgIndex]); + if (toolInstance) { + toolInstance.on('changeIndicatorDetermine', (index: number) => { + forceRender((s) => s + 1); + }); + } + return () => { + toolInstance?.unbindAll('changeIndicatorDetermine'); + }; + }, [toolInstance, imgIndex]); + const config = jsonParser(stepInfo.config); const { indicatorDetermine } = config; @@ -36,10 +45,10 @@ const IndicatorDetermineList = (props: IProps) => { const selected = { [key]: value }; const originData = currentResult?.indicatorDetermine ?? {}; const newResult = { ...currentResult, indicatorDetermine: { ...originData, ...selected } }; - setCurrentResult(newResult); toolInstance.setResult(newResult); } }; + if (indicatorDetermine?.length > 0) { return (
From d3bbfed939b2c068c83a1c36abfa6e8b1fe4c031 Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Tue, 30 Jan 2024 15:13:10 +0800 Subject: [PATCH 16/43] feat: Code review --- .../src/components/NLPToolView/index.tsx | 16 ++- .../NLPToolView/textContent/index.tsx | 4 +- .../sidebar/NLPSidebar/indicatorDetermine.tsx | 7 +- .../sidebar/SwitchAttributeList/index.tsx | 13 +-- packages/lb-demo/package.json | 102 +++++++++--------- 5 files changed, 71 insertions(+), 71 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index de229b6f6..9c9475b05 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -35,8 +35,6 @@ const NLPToolView: React.FC = (props) => { const { imgIndex, imgList, stepList, step } = annotation; const { highlightKey, setHighlightKey } = useContext(NLPContext); const { toolInstanceRef } = useCustomToolInstance(); - - const [NLPConfig, setNLPConfig] = useState(); const [selectedAttribute, setSelectedAttribute] = useState(''); const [lockList, setLockList] = useState([]); @@ -63,6 +61,13 @@ const NLPToolView: React.FC = (props) => { }, [result, lockList]); const { t } = useTranslation(); + const NLPConfig = useMemo(() => { + if (stepList && step) { + const NLPStepConfig = getStepConfig(stepList, step)?.config; + return jsonParser(NLPStepConfig); + } + return undefined + }, [stepList, step]); useEffect(() => { let interval: undefined | ReturnType; @@ -97,13 +102,6 @@ const NLPToolView: React.FC = (props) => { setResult(result); }, [imgIndex]); - useEffect(() => { - if (stepList && step) { - const NLPStepConfig = getStepConfig(stepList, step)?.config; - setNLPConfig(jsonParser(NLPStepConfig)); - } - }, [stepList, step]); - useEffect(() => { toolInstanceRef.current.exportData = () => { return [[result], {}]; diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index a895a3ae5..74239db5d 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -94,7 +94,7 @@ const TextContent: React.FC = (props) => { useEffect(() => { if (enableRemark) { - onSelectiRemark(selection.text); + onSelectionRemark(selection.text); } else { onSelectionChange?.(selection.text); } @@ -121,7 +121,7 @@ const TextContent: React.FC = (props) => { } }, [remark?.editAuditID]); - const onSelectiRemark = (text: string) => { + const onSelectionRemark = (text: string) => { if (text === '') return; let curSelection = window.getSelection(); diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx index c463a6142..e66fd7c7d 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx @@ -50,15 +50,16 @@ const IndicatorDetermineList = (props: IProps) => { }; if (indicatorDetermine?.length > 0) { + const displayList = indicatorDetermine.filter((i: IndicatorDetermine) => i?.label); return (
{t('IndicatorJudgment')}
- {indicatorDetermine.map((item: IndicatorDetermine, index: number) => { + {displayList.map((item: IndicatorDetermine, index: number) => { const { label, value } = item; - return label ? ( + return ( { key={index} isDisableAll={checkMode} /> - ) : null; + ); })}
diff --git a/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx b/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx index 906b379f5..52020d59f 100644 --- a/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx @@ -11,13 +11,12 @@ import { EToolName } from '@/data/enums/ToolType'; import { LabelBeeContext } from '@/store/ctx'; interface IProps { - toolInstance: GraphToolInstance; - stepInfo: IStepInfo; - attributeLockChange: (list: string[]) => void; + toolInstance?: GraphToolInstance; + stepInfo?: IStepInfo; + attributeLockChange?: (list: string[]) => void; } const SwitchAttributeList: React.FC = (props) => { - const [_, forceRender] = useState(0); const listRef = useRef(null); const { toolInstance, attributeLockChange } = props; @@ -63,8 +62,10 @@ const SwitchAttributeList: React.FC = (props) => { } const attributeChanged = (v: string) => { - toolInstance.setDefaultAttribute(v); - forceRender((s) => s + 1); + if (toolInstance) { + toolInstance.setDefaultAttribute(v); + forceRender((s) => s + 1); + } }; return ( diff --git a/packages/lb-demo/package.json b/packages/lb-demo/package.json index 8d8566709..ebaec44ad 100644 --- a/packages/lb-demo/package.json +++ b/packages/lb-demo/package.json @@ -1,53 +1,53 @@ { - "name": "lb-demo", - "version": "1.11.0-alpha.2", - "private": true, - "publishConfig": { - "access": "public" - }, - "license": "Apache-2.0", - "dependencies": { - "@labelbee/lb-annotation": "*", - "@labelbee/lb-components": "*", - "@labelbee/lb-utils": "*", - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^11.1.0", - "@testing-library/user-event": "^12.1.10", - "@types/three": ">=0.141.0", - "qs": "^6.10.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-error-overlay": "6.0.9", - "react-scripts": "4.0.3", - "three": ">=0.141.0", - "web-vitals": "^1.0.2" - }, - "scripts": { - "start": "react-scripts start", - "dev": "npm run start", - "build:frontend": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "antd": "^4.16.13" - } + "name": "lb-demo", + "version": "1.11.0-alpha.2", + "private": true, + "publishConfig": { + "access": "public" + }, + "license": "Apache-2.0", + "dependencies": { + "@labelbee/lb-annotation": "*", + "@labelbee/lb-components": "*", + "@labelbee/lb-utils": "*", + "@testing-library/jest-dom": "^5.11.4", + "@testing-library/react": "^11.1.0", + "@testing-library/user-event": "^12.1.10", + "@types/three": ">=0.141.0", + "qs": "^6.10.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-error-overlay": "6.0.9", + "react-scripts": "4.0.3", + "three": ">=0.141.0", + "web-vitals": "^1.0.2" + }, + "scripts": { + "start": "react-scripts start", + "dev": "npm run start", + "build:frontend": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "antd": "^4.16.13" + } } From 58d44762dafd8a90afdfb01a44026589f13f53c0 Mon Sep 17 00:00:00 2001 From: lijingchi Date: Tue, 30 Jan 2024 17:47:30 +0800 Subject: [PATCH 17/43] feat(nlp): Hide attribute list on checkMode --- .../MainView/sidebar/NLPSidebar/index.tsx | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx index 9798727b7..e253c548f 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx @@ -15,25 +15,39 @@ interface IProps { checkMode?: boolean; } +/** + * nlp tool attribute option list + * @param param0 + * @returns + */ +const NlpAttributeList = ({ + setAttributeLockList, + checkMode, +}: { + setAttributeLockList: (list: string[]) => void; + checkMode?: boolean; +}) => { + if (checkMode) { + return null; + } + + return ; +}; + export const sidebarCls = `${prefix}-sidebar`; const NLPSidebar: React.FC = ({ toolInstance, checkMode }) => { const setAttributeLockList = (list: string[]) => { toolInstance?.setAttributeLockList(list); }; - const attributeList = ; - const operation = ; - const annotatedList = ; - const indicatorDetermineList = ; - return (
- {indicatorDetermineList} - {attributeList} - {annotatedList} + + + ;
- {!checkMode && operation} + {!checkMode && }
); }; From a11eecf7239dd00f8fd1fa71f10d8dcd53f44d7c Mon Sep 17 00:00:00 2001 From: lijingchi Date: Tue, 30 Jan 2024 17:50:14 +0800 Subject: [PATCH 18/43] feat(nlp): Remove warning on annotation page --- .../src/components/NLPToolView/index.tsx | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index 9c9475b05..5321a4e9f 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -66,25 +66,9 @@ const NLPToolView: React.FC = (props) => { const NLPStepConfig = getStepConfig(stepList, step)?.config; return jsonParser(NLPStepConfig); } - return undefined + return undefined; }, [stepList, step]); - useEffect(() => { - let interval: undefined | ReturnType; - - if (!checkMode) { - interval = setInterval(() => { - message.info(t('EfficientListening')); - }, 1000 * 60); - - return () => { - if (interval) { - clearInterval(interval); - } - }; - } - }, []); - useEffect(() => { if (!imgList[imgIndex]) { return; From 6810792d51f79463a417ea73f6cb355ee941135b Mon Sep 17 00:00:00 2001 From: lijingchi Date: Tue, 30 Jan 2024 17:51:33 +0800 Subject: [PATCH 19/43] feat(nlp): Remove useless shortcut --- .../toolFooter/FooterTips/ToolHotKey/NLP/index.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/NLP/index.tsx b/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/NLP/index.tsx index 8b13a57e2..ffc98f6eb 100644 --- a/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/NLP/index.tsx +++ b/packages/lb-components/src/views/MainView/toolFooter/FooterTips/ToolHotKey/NLP/index.tsx @@ -1,12 +1,5 @@ import { backward, forward } from '../common'; -import ForwardSvg from '@/assets/annotation/toolHotKeyIcon/icon_next_kj.svg'; -export const forwardWithEnter = { - name: 'Save', - icon: ForwardSvg, - shortCut: ['Ctrl', 'Enter'], -}; - -const NLPShortCutTable = [backward, forward, forwardWithEnter]; +const NLPShortCutTable = [backward, forward]; export default NLPShortCutTable; From f25009662e509659a7789153f51a38ea1d97ea7a Mon Sep 17 00:00:00 2001 From: lijingchi Date: Tue, 30 Jan 2024 17:55:51 +0800 Subject: [PATCH 20/43] feat(nlp): Remove dbl default event on nlp conatiner --- .../src/components/NLPToolView/textContent/index.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index 74239db5d..35ddb3702 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -191,7 +191,7 @@ const TextContent: React.FC = (props) => { = (props) => { style={{ backgroundColor: color.valid.stroke, color: highlight ? 'white' : undefined, - padding:'2px 0px' + padding: '2px 0px', }} key={index} > @@ -255,6 +255,9 @@ const TextContent: React.FC = (props) => { [`${NLPViewCls}-question-content`]: true, })} style={{ position: 'relative' }} + onDoubleClick={(e) => { + e.preventDefault(); + }} > {renderContent()}
From c79bcbeb1413a23f29978ef64484d2e1ba3f41c5 Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Tue, 30 Jan 2024 17:58:42 +0800 Subject: [PATCH 21/43] feat: Code review --- .../NLPToolView/textContent/index.tsx | 117 ++++++------------ .../NLPToolView/textContent/remarkMask.tsx | 51 ++++++++ 2 files changed, 88 insertions(+), 80 deletions(-) create mode 100644 packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index 35ddb3702..39b207b0b 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -24,6 +24,7 @@ import { CommonToolUtils, uuid } from '@labelbee/lb-annotation'; import styleString from '@/constant/styleString'; import { getIntervals } from '../utils'; import { classnames } from '@/utils'; +import RemarkMask from './remarkMask'; interface IProps { highlightKey?: string; @@ -51,7 +52,6 @@ const TextContent: React.FC = (props) => { onSelectionChange, textAnnotation, remark, - isSourceView, } = props; const { enableRemark, displayRemarkList } = remark || {}; @@ -154,7 +154,7 @@ const TextContent: React.FC = (props) => { } }; - const randerRemark = () => { + const RemarkModal = () => { if (props?.remarkLayer) { return props.remarkLayer({ style: remarkStyle, @@ -169,83 +169,11 @@ const TextContent: React.FC = (props) => { return null; }; - const renderRemarkMask = () => { - return ( -
- {remarkSplitIntervals.map((interval: IRemarkInterval, index: number) => { - const remarkAnnotation = _.last(interval.remarkAnnotations); - const highlight = interval?.remarkAnnotations?.find( - (i) => i?.auditID === remark.hoverAuditID, - ); - const color = highlight ? '#ffc60a' : '#fcdf7e'; - if (remarkAnnotation) { - return ( - - {interval.text} - - ); - } - return {interval.text}; - })} -
- ); - }; - - const renderContent = () => { - return ( -
- {splitIntervals.map((interval: INLPInterval, index: number) => { - const annotation = _.last(interval.annotations); - if (annotation) { - const color = getColor(annotation.attribute); - const highlight = interval?.annotations?.find( - (v: INLPTextAnnotation) => v.id === highlightKey, - ); - return ( - - {interval.text} - - ); - } else { - return {interval.text}; - } - })} - {displayRemarkList?.length > 0 && renderRemarkMask()} - {renderMask()} - {randerRemark()} -
- ); - }; - - const renderMask = () => { - return ( -
- {content} -
- ); - }; + const renderMask = ( +
+ {content} +
+ ); return (
@@ -259,7 +187,36 @@ const TextContent: React.FC = (props) => { e.preventDefault(); }} > - {renderContent()} +
+ {splitIntervals.map((interval: INLPInterval, index: number) => { + const annotation = _.last(interval.annotations); + if (annotation) { + const color = getColor(annotation.attribute); + const highlight = interval?.annotations?.find( + (v: INLPTextAnnotation) => v.id === highlightKey, + ); + return ( + + {interval.text} + + ); + } else { + return {interval.text}; + } + })} + {displayRemarkList?.length > 0 && ( + + )} + {renderMask} + {RemarkModal()} +
); diff --git a/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx b/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx new file mode 100644 index 000000000..5bd39fff8 --- /dev/null +++ b/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx @@ -0,0 +1,51 @@ +/** + * @file remark mask + * @author lixinghua + * @date 2024.01.30 + */ + +import React from 'react'; +import { IRemarkInterval } from '../types'; + +export default ({ + remarkSplitIntervals, + remark, +}: { + remarkSplitIntervals: IRemarkInterval[]; + remark?: any; +}) => { + return ( +
+ {remarkSplitIntervals.map((interval: IRemarkInterval, index: number) => { + const remarkAnnotation = _.last(interval.remarkAnnotations); + const highlight = interval?.remarkAnnotations?.find( + (i) => i?.auditID === remark.hoverAuditID, + ); + const color = highlight ? '#ffc60a' : '#fcdf7e'; + if (remarkAnnotation) { + return ( + + {interval.text} + + ); + } + return {interval.text}; + })} +
+ ); +}; From 6807e2179b1071d027f6e2e38af425761b3e74f1 Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Tue, 30 Jan 2024 18:00:20 +0800 Subject: [PATCH 22/43] feat: Hide option icon in header --- packages/lb-components/src/views/MainView/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lb-components/src/views/MainView/index.tsx b/packages/lb-components/src/views/MainView/index.tsx index 0c709f3b2..2630a70e5 100644 --- a/packages/lb-components/src/views/MainView/index.tsx +++ b/packages/lb-components/src/views/MainView/index.tsx @@ -84,8 +84,8 @@ const ViewportProviderLayout = (props: AppProps & IProps & { children: any }) => const { t } = useTranslation(); const { stepList, step } = props; const currentToolName = getStepConfig(stepList, step)?.tool; - const hasLangNode = ![EToolName.LLM].includes(currentToolName); - const hasHeaderOption = ![EToolName.LLM].includes(currentToolName); + const hasLangNode = ![EToolName.LLM, EToolName.NLP].includes(currentToolName); + const hasHeaderOption = ![EToolName.LLM, EToolName.NLP].includes(currentToolName); const hasPredictTrackingIcon = [EPointCloudName.PointCloud].includes(currentToolName); return ( From 1006f96ce222db97b704248746a8d8359991eaa5 Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Tue, 30 Jan 2024 18:01:16 +0800 Subject: [PATCH 23/43] feat: Hover delete icon --- .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx index bda0e0e7e..b4c88320b 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -26,6 +26,7 @@ const NLPAnnotatedList: React.FC = (props) => { const { t } = useTranslation(); const [highlight, setHighlight] = useState(''); + const [hoverId, setHoverId] = useState(''); useEffect(() => { if (toolInstance) { @@ -56,6 +57,7 @@ const NLPAnnotatedList: React.FC = (props) => {
{annotatedList.map((v: INLPTextAnnotation, k: number) => { const active = highlight === v.id; + const isRemove = hoverId === v.id; return (
= (props) => { [`${sidebarCls}__content__NLPList__item__active`]: active, })} onClick={() => setHighlightKey(v.id)} + onMouseEnter={() => setHoverId(v.id)} + onMouseLeave={() => setHoverId('')} > {`${ v.attribute || t('NoAttribute') },${t('textTool')}:${v.text}`} - {active && ( + {isRemove && ( = (props) => { onConfirm={() => onDeleteTextAnnotation(v)} overlayClassName={`${prefix}-pop-confirm`} > - {e.stopPropagation()}}/> + { + e.stopPropagation(); + }} + /> )}
From 1eae95e850ed6c95477c8515a8c94a34b9e265a1 Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Tue, 30 Jan 2024 18:02:32 +0800 Subject: [PATCH 24/43] feat: Copywriting and style adjustment --- .../views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx | 2 +- packages/lb-utils/src/i18n/resources.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx index e66fd7c7d..3806dcb22 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx @@ -52,7 +52,7 @@ const IndicatorDetermineList = (props: IProps) => { if (indicatorDetermine?.length > 0) { const displayList = indicatorDetermine.filter((i: IndicatorDetermine) => i?.label); return ( -
+
{t('IndicatorJudgment')}
diff --git a/packages/lb-utils/src/i18n/resources.json b/packages/lb-utils/src/i18n/resources.json index 40cb37b94..355c94b5f 100644 --- a/packages/lb-utils/src/i18n/resources.json +++ b/packages/lb-utils/src/i18n/resources.json @@ -643,7 +643,7 @@ "PreviousInterval": "上一区间", "NextInterval": "下一区间", "DeleteComment": "删除批注", - "DeleteCommentConfirm": "确定删除该条批注吗", + "DeleteCommentConfirm": "确定删除该条标注吗", "PreviousComment": "上一条批注", "NextComment": "下一条批注", "ClipSelect": "截取选中", From 94b8e70cda4d847faaabb4cb7a34a117bb406e9e Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Tue, 30 Jan 2024 18:10:47 +0800 Subject: [PATCH 25/43] feat: NLP hide set validity --- .../NLPToolView/textContent/index.tsx | 59 +++++++++++-------- .../NLPToolView/textContent/remarkMask.tsx | 1 + packages/lb-components/src/index.scss | 12 ++++ .../sidebar/GeneralOperation/index.tsx | 5 +- .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 41 ++++++------- .../MainView/sidebar/NLPSidebar/index.tsx | 2 +- 6 files changed, 71 insertions(+), 49 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index 39b207b0b..beb6120aa 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -42,6 +42,31 @@ interface IProps { const NLPViewCls = `${prefix}-NLPView`; +const renderRemarkModal = ({ + remarkLayer, + remarkStyle, + setRemarkStyle, + setRemarkResut, + remarkResut, +}: { + setRemarkStyle: (value?: React.CSSProperties) => void; + remarkLayer?: (values: IRemarkLayer) => void; + remarkStyle: React.CSSProperties | undefined; + setRemarkResut: (value: ISelectText) => void; + remarkResut: ISelectText; +}) => { + if (remarkLayer) { + remarkLayer({ + style: remarkStyle, + onClose: () => { + setRemarkStyle(undefined); + setRemarkResut({}); + window.getSelection()?.empty(); + }, + submitData: remarkResut, + }); + } +}; const TextContent: React.FC = (props) => { const { highlightKey, @@ -154,27 +179,6 @@ const TextContent: React.FC = (props) => { } }; - const RemarkModal = () => { - if (props?.remarkLayer) { - return props.remarkLayer({ - style: remarkStyle, - onClose: () => { - setRemarkStyle(undefined); - setRemarkResut({}); - window.getSelection()?.empty(); - }, - submitData: remarkResut, - }); - } - return null; - }; - - const renderMask = ( -
- {content} -
- ); - return (
{t('textTool')}
@@ -214,8 +218,17 @@ const TextContent: React.FC = (props) => { {displayRemarkList?.length > 0 && ( )} - {renderMask} - {RemarkModal()} + {renderRemarkModal({ + setRemarkStyle, + remarkLayer: props?.remarkLayer, + remarkStyle, + remarkResut, + setRemarkResut, + })} + +
+ {content} +
diff --git a/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx b/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx index 5bd39fff8..47446964a 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx @@ -4,6 +4,7 @@ * @date 2024.01.30 */ +import _ from 'lodash'; import React from 'react'; import { IRemarkInterval } from '../types'; diff --git a/packages/lb-components/src/index.scss b/packages/lb-components/src/index.scss index 9685ad547..dfb2dc111 100644 --- a/packages/lb-components/src/index.scss +++ b/packages/lb-components/src/index.scss @@ -2222,6 +2222,18 @@ $ptToolHeight: 40px; display: flex; align-items: center; cursor:pointer; + + .#{$prefix}-sidebar-pop-remove{ + margin: 0px 16px; + display: none; + } + + &:hover{ + .#{$prefix}-sidebar-pop-remove{ + display: block; + } + } + &__text{ flex: 1 1; padding: 16px; diff --git a/packages/lb-components/src/views/MainView/sidebar/GeneralOperation/index.tsx b/packages/lb-components/src/views/MainView/sidebar/GeneralOperation/index.tsx index aacd121be..b8202c26d 100644 --- a/packages/lb-components/src/views/MainView/sidebar/GeneralOperation/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/GeneralOperation/index.tsx @@ -35,14 +35,15 @@ interface IProps { imgList: AnnotationFileList; imgIndex: number; stepList: IStepInfo[]; + hideValidity?: boolean; } -const GeneralOperation: React.FC = ({ toolInstance, stepInfo }) => { +const GeneralOperation: React.FC = ({ toolInstance, stepInfo, hideValidity }) => { const operationList = useOperationList(toolInstance); const config = jsonParser(stepInfo?.config); const allOperation: IOperationConfig[] = [operationList.empty]; - if (stepInfo?.dataSourceStep === 0) { + if (stepInfo?.dataSourceStep === 0 && !hideValidity) { allOperation.push(operationList.setValidity); } diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx index b4c88320b..857621eec 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -26,7 +26,6 @@ const NLPAnnotatedList: React.FC = (props) => { const { t } = useTranslation(); const [highlight, setHighlight] = useState(''); - const [hoverId, setHoverId] = useState(''); useEffect(() => { if (toolInstance) { @@ -57,7 +56,6 @@ const NLPAnnotatedList: React.FC = (props) => {
{annotatedList.map((v: INLPTextAnnotation, k: number) => { const active = highlight === v.id; - const isRemove = hoverId === v.id; return (
= (props) => { [`${sidebarCls}__content__NLPList__item__active`]: active, })} onClick={() => setHighlightKey(v.id)} - onMouseEnter={() => setHoverId(v.id)} - onMouseLeave={() => setHoverId('')} > {`${ v.attribute || t('NoAttribute') },${t('textTool')}:${v.text}`} - {isRemove && ( - trigger.parentElement} - onConfirm={() => onDeleteTextAnnotation(v)} - overlayClassName={`${prefix}-pop-confirm`} - > - { - e.stopPropagation(); - }} - /> - - )} + + trigger.parentElement} + onConfirm={() => onDeleteTextAnnotation(v)} + overlayClassName={`${prefix}-pop-confirm`} + > + { + e.stopPropagation(); + }} + /> +
); })} diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx index e253c548f..8b8993a0d 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx @@ -47,7 +47,7 @@ const NLPSidebar: React.FC = ({ toolInstance, checkMode }) => { ;
- {!checkMode && } + {!checkMode && }
); }; From d70bcc6c341f1a2b24b20addc4a1e24c123f542e Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Wed, 31 Jan 2024 10:08:50 +0800 Subject: [PATCH 26/43] fix: NLP tool view mode cannot delete annotations --- .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 39 ++++++++++--------- .../MainView/sidebar/NLPSidebar/index.tsx | 4 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx index 857621eec..44cf5547a 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -15,6 +15,7 @@ import { CloseOutlined } from '@ant-design/icons'; interface IProps { toolInstance: ICustomToolInstance; stepInfo: IStepInfo; + checkMode?: boolean; } export const sidebarCls = `${prefix}-sidebar`; @@ -22,7 +23,7 @@ export const sidebarCls = `${prefix}-sidebar`; const NLPAnnotatedList: React.FC = (props) => { const [_, forceRender] = useState(0); const listRef = useRef(null); - const { toolInstance } = props; + const { toolInstance, checkMode } = props; const { t } = useTranslation(); const [highlight, setHighlight] = useState(''); @@ -71,23 +72,25 @@ const NLPAnnotatedList: React.FC = (props) => { },${t('textTool')}:${v.text}`}
- trigger.parentElement} - onConfirm={() => onDeleteTextAnnotation(v)} - overlayClassName={`${prefix}-pop-confirm`} - > - { - e.stopPropagation(); - }} - /> - + {!checkMode && ( + trigger.parentElement} + onConfirm={() => onDeleteTextAnnotation(v)} + overlayClassName={`${prefix}-pop-confirm`} + > + { + e.stopPropagation(); + }} + /> + + )}
); })} diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx index 8b8993a0d..20efa8aed 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/index.tsx @@ -45,9 +45,9 @@ const NLPSidebar: React.FC = ({ toolInstance, checkMode }) => {
- ; + ;
- {!checkMode && } + {!checkMode && }
); }; From 92b50df30a68d017df79ed534709eef45aec184a Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Wed, 31 Jan 2024 10:34:57 +0800 Subject: [PATCH 27/43] feat: Code review --- .../src/components/NLPToolView/textContent/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index beb6120aa..75e322cbc 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -56,7 +56,7 @@ const renderRemarkModal = ({ remarkResut: ISelectText; }) => { if (remarkLayer) { - remarkLayer({ + return remarkLayer({ style: remarkStyle, onClose: () => { setRemarkStyle(undefined); @@ -218,9 +218,10 @@ const TextContent: React.FC = (props) => { {displayRemarkList?.length > 0 && ( )} + {renderRemarkModal({ - setRemarkStyle, remarkLayer: props?.remarkLayer, + setRemarkStyle, remarkStyle, remarkResut, setRemarkResut, From 521483be3e99995da9d51a478c7831ebe9eedc4a Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Wed, 31 Jan 2024 11:41:09 +0800 Subject: [PATCH 28/43] fix: NLP tool hides comment lines --- .../src/components/NLPToolView/textContent/remarkMask.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx b/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx index 47446964a..993754e26 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/remarkMask.tsx @@ -31,11 +31,15 @@ export default ({ (i) => i?.auditID === remark.hoverAuditID, ); const color = highlight ? '#ffc60a' : '#fcdf7e'; + let borderStyle = `2px solid ${color}`; + if (!remark?.isShowRemark) { + borderStyle = ''; + } if (remarkAnnotation) { return ( Date: Wed, 31 Jan 2024 18:43:47 +0800 Subject: [PATCH 29/43] fix: NLP tool does not produce results when labeling some --- .../src/components/NLPToolView/index.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index 5321a4e9f..e3a851202 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -140,6 +140,9 @@ const NLPToolView: React.FC = (props) => { // ignore the order of selection let start = Math.min(anchorOffset, focusOffset); let end = Math.max(anchorOffset, focusOffset); + if (checkSameByOneAttribute(start, end, selectedAttribute, result?.textAnnotation)) { + return; + } setResult({ ...result, textAnnotation: [ @@ -158,6 +161,17 @@ const NLPToolView: React.FC = (props) => { } }; + const checkSameByOneAttribute = ( + start: number, + end: number, + selectedAttribute: string, + textAnnotation: INLPTextAnnotation[], + ) => { + return textAnnotation?.some( + (i) => i?.start === start && i?.end === end && i?.attribute === selectedAttribute, + ); + }; + return (
From 7f49aa056bbea536bc384b220b19f5d64674ca73 Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Wed, 31 Jan 2024 20:31:49 +0800 Subject: [PATCH 30/43] fix: NLP tool indicators determine data anomalies --- .../src/components/NLPToolView/index.tsx | 12 +++++- .../src/components/NLPToolView/types.ts | 15 ++++---- .../sidebar/NLPSidebar/NLPAnnotatedList.tsx | 37 +++++++++++++------ .../MainView/sidebar/NLPSidebar/index.tsx | 2 +- .../sidebar/NLPSidebar/indicatorDetermine.tsx | 28 ++++++++++---- 5 files changed, 64 insertions(+), 30 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index e3a851202..cab520fb2 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -12,7 +12,12 @@ import { message } from 'antd'; import { prefix } from '@/constant'; import TextContent from './textContent'; import { useTranslation } from 'react-i18next'; -import { INLPToolConfig, ITextData, INLPTextAnnotation, INLPResult, IRemarkLayer } from './types'; +import { + ITextData, + INLPTextAnnotation, + INLPResult, + IRemarkLayer, +} from './types'; import AnnotationTips from '@/views/MainView/annotationTips'; import { getStepConfig } from '@/store/annotation/reducer'; import { jsonParser } from '@/utils'; @@ -91,7 +96,10 @@ const NLPToolView: React.FC = (props) => { return [[result], {}]; }; - toolInstanceRef.current.setResult = (value) => setResult(value); + toolInstanceRef.current.setResult = () => {}; + toolInstanceRef.current.updateResult = (value: INLPResult) => { + setResult(value); + }; toolInstanceRef.current.clearResult = clearResult; toolInstanceRef.current.setDefaultAttribute = setDefaultAttribute; toolInstanceRef.current.setHighlightKey = setHighlightKey; diff --git a/packages/lb-components/src/components/NLPToolView/types.ts b/packages/lb-components/src/components/NLPToolView/types.ts index e873ac9ea..fda651e91 100644 --- a/packages/lb-components/src/components/NLPToolView/types.ts +++ b/packages/lb-components/src/components/NLPToolView/types.ts @@ -75,12 +75,11 @@ export interface IRemarkInterval { id?: string; } - -export interface IRemarkAnnotation{ - auditID: number, - text: string, - id: string, - start: number, - end: number, - uuid: string, +export interface IRemarkAnnotation { + auditID: number; + text: string; + id: string; + start: number; + end: number; + uuid: string; } diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx index 44cf5547a..2f998cec6 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/NLPAnnotatedList.tsx @@ -7,30 +7,42 @@ import { useTranslation } from 'react-i18next'; import { LabelBeeContext } from '@/store/ctx'; import { ICustomToolInstance } from '@/hooks/annotation'; import { prefix } from '@/constant'; -import { classnames } from '@/utils'; -import { INLPTextAnnotation } from '@/components/NLPToolView/types'; +import { classnames, jsonParser } from '@/utils'; +import { INLPTextAnnotation, INLPToolConfig } from '@/components/NLPToolView/types'; import { Popconfirm, Tooltip } from 'antd'; import { CloseOutlined } from '@ant-design/icons'; - interface IProps { toolInstance: ICustomToolInstance; stepInfo: IStepInfo; checkMode?: boolean; } +interface INLPTextAnnotationList extends INLPTextAnnotation { + label: string; +} + export const sidebarCls = `${prefix}-sidebar`; +const getAnnotatedList = (config: INLPToolConfig, textAnnotation: INLPTextAnnotation[]) => { + const attributeList = config?.attributeList || []; + return textAnnotation.map((i) => { + const label = attributeList.filter((item) => item.value === i.attribute)?.[0]?.key || ''; + return { ...i, label }; + }); +}; + const NLPAnnotatedList: React.FC = (props) => { const [_, forceRender] = useState(0); const listRef = useRef(null); - const { toolInstance, checkMode } = props; + const { toolInstance, checkMode, stepInfo } = props; const { t } = useTranslation(); + const config = jsonParser(stepInfo.config); const [highlight, setHighlight] = useState(''); useEffect(() => { if (toolInstance) { - toolInstance.singleOn('changeAttributeSidebar', (index: number) => { + toolInstance.on('changeAttributeSidebar', (index: number) => { forceRender((s) => s + 1); }); } @@ -40,7 +52,8 @@ const NLPAnnotatedList: React.FC = (props) => { }, [toolInstance, listRef]); const [result] = toolInstance.exportData(); - const annotatedList = result?.[0]?.textAnnotation ?? []; + const list = result?.[0]?.textAnnotation ?? []; + const annotatedList = getAnnotatedList(config, list); const setHighlightKey = (key: string) => { let chooseKey = key === highlight ? '' : key; @@ -55,7 +68,7 @@ const NLPAnnotatedList: React.FC = (props) => { if (annotatedList?.length === 0) return null; return (
- {annotatedList.map((v: INLPTextAnnotation, k: number) => { + {annotatedList.map((v: INLPTextAnnotationList, k: number) => { const active = highlight === v.id; return (
= (props) => { })} onClick={() => setHighlightKey(v.id)} > - - {`${ - v.attribute || t('NoAttribute') - },${t('textTool')}:${v.text}`} - + + + {`${v.label || t('NoAttribute')},${t('textTool')}:${v.text}`} + + {!checkMode && ( = ({ toolInstance, checkMode }) => {
- ; +
{!checkMode && }
diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx index 3806dcb22..02504e41e 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useMemo } from 'react'; import { connect } from 'react-redux'; import { AppState } from 'src/store'; import StepUtils from '@/utils/StepUtils'; @@ -7,24 +7,38 @@ import { useTranslation } from 'react-i18next'; import { LabelBeeContext } from '@/store/ctx'; import { ICustomToolInstance } from '@/hooks/annotation'; import { jsonParser } from '@/utils'; -import { IndicatorDetermine } from '@/components/NLPToolView/types'; +import { IndicatorDetermine, INLPResult } from '@/components/NLPToolView/types'; import DetermineGroup from '@/components/LLMToolView/sidebar/components/determineGroup'; +import { getCurrentResultFromResultList } from '@/components/LLMToolView/utils/data'; +import { IFileItem } from '@/types/data'; interface IProps { toolInstance: ICustomToolInstance; stepInfo: IStepInfo; imgIndex: number; checkMode?: boolean; + imgList: IFileItem[]; } const IndicatorDetermineList = (props: IProps) => { - const { toolInstance, stepInfo, checkMode, imgIndex } = props; + const { toolInstance, stepInfo, checkMode, imgIndex, imgList } = props; + const [result] = toolInstance.exportData(); - const currentResult = result?.[0]; + const [currentResult, setCurrentResult] = useState(result?.[0]); + const { t } = useTranslation(); const [_, forceRender] = useState(0); + useEffect(() => { + if (!imgList[imgIndex]) { + return; + } + const currentData = imgList[imgIndex] ?? {}; + const result = getCurrentResultFromResultList(currentData?.result || ''); + setCurrentResult(result); + }, [imgIndex, imgList]); + useEffect(() => { if (toolInstance) { toolInstance.on('changeIndicatorDetermine', (index: number) => { @@ -34,7 +48,7 @@ const IndicatorDetermineList = (props: IProps) => { return () => { toolInstance?.unbindAll('changeIndicatorDetermine'); }; - }, [toolInstance, imgIndex]); + }, [toolInstance]); const config = jsonParser(stepInfo.config); const { indicatorDetermine } = config; @@ -45,7 +59,7 @@ const IndicatorDetermineList = (props: IProps) => { const selected = { [key]: value }; const originData = currentResult?.indicatorDetermine ?? {}; const newResult = { ...currentResult, indicatorDetermine: { ...originData, ...selected } }; - toolInstance.setResult(newResult); + toolInstance.updateResult(newResult); } }; @@ -83,11 +97,11 @@ const IndicatorDetermineList = (props: IProps) => { }; const mapStateToProps = (state: AppState) => { const stepInfo = StepUtils.getCurrentStepInfo(state.annotation?.step, state.annotation?.stepList); - return { toolInstance: state.annotation.toolInstance, stepInfo, imgIndex: state.annotation.imgIndex, + imgList: state.annotation.imgList, }; }; From 5413448dcebf2eff2b19be480bfee4639fda66f6 Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Wed, 31 Jan 2024 21:43:47 +0800 Subject: [PATCH 31/43] fix: NLP Tools Switch Panel Hide Annotations --- .../src/components/NLPToolView/index.tsx | 11 +++----- .../NLPToolView/textContent/index.tsx | 26 +++++++++++-------- packages/lb-components/src/constant/index.tsx | 6 +++++ .../src/views/MainView/NLPLayout/index.tsx | 2 ++ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/lb-components/src/components/NLPToolView/index.tsx b/packages/lb-components/src/components/NLPToolView/index.tsx index cab520fb2..48a2d72d2 100644 --- a/packages/lb-components/src/components/NLPToolView/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/index.tsx @@ -12,12 +12,7 @@ import { message } from 'antd'; import { prefix } from '@/constant'; import TextContent from './textContent'; import { useTranslation } from 'react-i18next'; -import { - ITextData, - INLPTextAnnotation, - INLPResult, - IRemarkLayer, -} from './types'; +import { ITextData, INLPTextAnnotation, INLPResult, IRemarkLayer } from './types'; import AnnotationTips from '@/views/MainView/annotationTips'; import { getStepConfig } from '@/store/annotation/reducer'; import { jsonParser } from '@/utils'; @@ -26,6 +21,7 @@ import { useCustomToolInstance } from '@/hooks/annotation'; import { uuid } from '@labelbee/lb-annotation'; interface IProps { + activeToolPanel?: string; checkMode?: boolean; annotation?: any; showTips?: boolean; @@ -35,7 +31,7 @@ interface IProps { } const NLPViewCls = `${prefix}-NLPView`; const NLPToolView: React.FC = (props) => { - const { annotation, checkMode, tips, showTips, remarkLayer, remark } = props; + const { annotation, checkMode, tips, showTips, remarkLayer, remark, activeToolPanel } = props; const { imgIndex, imgList, stepList, step } = annotation; const { highlightKey, setHighlightKey } = useContext(NLPContext); @@ -193,6 +189,7 @@ const NLPToolView: React.FC = (props) => { onSelectionChange={onSelectionChange} remarkLayer={remarkLayer} remark={remark} + activeToolPanel={activeToolPanel} />
diff --git a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx index 75e322cbc..81ff5c39e 100644 --- a/packages/lb-components/src/components/NLPToolView/textContent/index.tsx +++ b/packages/lb-components/src/components/NLPToolView/textContent/index.tsx @@ -17,7 +17,7 @@ import { IRemarkInterval, IRemarkAnnotation, } from '../types'; -import { prefix } from '@/constant'; +import { prefix, TOOL_PANEL_KEY } from '@/constant'; import { useTextSelection } from 'ahooks'; import _ from 'lodash'; import { CommonToolUtils, uuid } from '@labelbee/lb-annotation'; @@ -38,6 +38,7 @@ interface IProps { remarkLayer?: (values: IRemarkLayer) => void; remark?: any; isSourceView?: boolean; + activeToolPanel?: string; } const NLPViewCls = `${prefix}-NLPView`; @@ -77,9 +78,12 @@ const TextContent: React.FC = (props) => { onSelectionChange, textAnnotation, remark, + activeToolPanel, } = props; const { enableRemark, displayRemarkList } = remark || {}; + const hideRemark = activeToolPanel ? activeToolPanel !== TOOL_PANEL_KEY.Remark : false; + const { t } = useTranslation(); const contentRef = useRef(null); const selection = useTextSelection(contentRef); @@ -118,9 +122,9 @@ const TextContent: React.FC = (props) => { }, []); useEffect(() => { - if (enableRemark) { + if (enableRemark && !hideRemark) { onSelectionRemark(selection.text); - } else { + } else if (activeToolPanel && activeToolPanel === TOOL_PANEL_KEY.Tool) { onSelectionChange?.(selection.text); } }, [selection.text]); @@ -218,14 +222,14 @@ const TextContent: React.FC = (props) => { {displayRemarkList?.length > 0 && ( )} - - {renderRemarkModal({ - remarkLayer: props?.remarkLayer, - setRemarkStyle, - remarkStyle, - remarkResut, - setRemarkResut, - })} + {!hideRemark && + renderRemarkModal({ + remarkLayer: props?.remarkLayer, + setRemarkStyle, + remarkStyle, + remarkResut, + setRemarkResut, + })}
{content} diff --git a/packages/lb-components/src/constant/index.tsx b/packages/lb-components/src/constant/index.tsx index 932cf2968..31899e780 100644 --- a/packages/lb-components/src/constant/index.tsx +++ b/packages/lb-components/src/constant/index.tsx @@ -35,3 +35,9 @@ export enum ELLMDataType { Text = 'text', None = 'none', } + +// 侧边栏工具面板 +export const TOOL_PANEL_KEY = { + Tool: 'tool', + Remark: 'remark', +}; diff --git a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx index 975de989d..2310be038 100644 --- a/packages/lb-components/src/views/MainView/NLPLayout/index.tsx +++ b/packages/lb-components/src/views/MainView/NLPLayout/index.tsx @@ -15,6 +15,7 @@ interface IProps { loading: boolean; remarkLayer?: any; remark?: any; + activeToolPanel?: string; } const { Sider, Content } = Layout; @@ -46,6 +47,7 @@ const NLPLayout: React.FC = (props) => { tips={props.tips} remarkLayer={props?.remarkLayer} remark={props?.remark} + activeToolPanel={props?.activeToolPanel} /> From cab7cfc99c85c5d87808814fee90f570d94f2c9d Mon Sep 17 00:00:00 2001 From: "lixinghua.vendor" Date: Thu, 1 Feb 2024 11:50:51 +0800 Subject: [PATCH 32/43] fix: NLP indicator judgment to obtain data --- .../views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx index 02504e41e..7e39ed2a1 100644 --- a/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/NLPSidebar/indicatorDetermine.tsx @@ -42,6 +42,8 @@ const IndicatorDetermineList = (props: IProps) => { useEffect(() => { if (toolInstance) { toolInstance.on('changeIndicatorDetermine', (index: number) => { + const [result] = toolInstance.exportData(); + setCurrentResult(result?.[0]) forceRender((s) => s + 1); }); } From e5c5217cf059531486d9dc1d787288b3d8a6ec3a Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Wed, 24 Jan 2024 15:41:10 +0800 Subject: [PATCH 33/43] feat: Add pipeline: get basic result --- .../src/store/annotation/reducer.ts | 17 +- packages/lb-components/src/utils/Pipeline.ts | 396 ++++++++++++++++++ packages/lb-demo/src/App.js | 2 + 3 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 packages/lb-components/src/utils/Pipeline.ts diff --git a/packages/lb-components/src/store/annotation/reducer.ts b/packages/lb-components/src/store/annotation/reducer.ts index 4467b3831..509ce5308 100644 --- a/packages/lb-components/src/store/annotation/reducer.ts +++ b/packages/lb-components/src/store/annotation/reducer.ts @@ -7,6 +7,7 @@ import { IStepInfo } from '@/types/step'; import { jsonParser } from '@/utils'; import AnnotationDataUtils from '@/utils/AnnotationDataUtils'; import { ConfigUtils } from '@/utils/ConfigUtils'; +import { getBasicResult } from '@/utils/Pipeline' import { composeResult, composeResultWithBasicImgInfo } from '@/utils/data'; import StepUtils from '@/utils/StepUtils'; import ToolUtils from '@/utils/ToolUtils'; @@ -457,6 +458,7 @@ export const annotationReducer = ( const basicIndex = nextBasicIndex ?? 0; const fileResult = jsonParser(imgList[nextIndex]?.result); + const preResult = imgList[nextIndex]?.preResult const stepResult = fileResult[`step_${currentStepInfo.step}`]; @@ -480,7 +482,20 @@ export const annotationReducer = ( const { dataSourceStep, tool } = stepConfig; const dependStepConfig = getStepConfig(stepList, dataSourceStep); const hasDataSourceStep = dataSourceStep && tool; - const stepBasicResultList = fileResult[`step_${dataSourceStep}`]?.result ?? []; + const oldStepBasicResultList = fileResult[`step_${dataSourceStep}`]?.result ?? []; + + console.log(fileResult) + const stepBasicResultList = getBasicResult( + currentStepInfo.step, + currentStepInfo.dataSourceStep, + stepList, + fileResult, + true, + preResult, + ); + + console.log(oldStepBasicResultList) + console.log(stepBasicResultList) const result = AnnotationDataUtils.getInitialResultList( stepResult?.result, diff --git a/packages/lb-components/src/utils/Pipeline.ts b/packages/lb-components/src/utils/Pipeline.ts new file mode 100644 index 000000000..30404c050 --- /dev/null +++ b/packages/lb-components/src/utils/Pipeline.ts @@ -0,0 +1,396 @@ +import { CommonToolUtils } from '@labelbee/lb-annotation'; +import { IStepInfo } from '@/types/step'; +import { jsonParser } from '@/utils'; +import { EToolName } from '@labelbee/lb-annotation'; + +/** + * 获取当前步骤下的依赖情况,用于依赖框体的展示 + * + * + * @export + * @param {number} currentStep 正常标注的步骤!(如果是质检则需要转换成正常标注步骤) + * @param {number} dataSourceStep 正常标注依赖的步骤 + * @param {IStepInfo[]} stepList 步骤信息 + * @param {*} result 当前图片的所有结果 + * @param {boolean} [filterNeeded=true] + * @param {boolean} [forbidFilter=false] + * @returns + */ +export function getBasicResult( + currentStep: number, + dataSourceStep: number, + stepList: IStepInfo[], + result: any, + filterNeeded: boolean = true, + preResult: string = '{}', + forbidFilter: boolean = false, +) { + const currentStepInfo = CommonToolUtils.getCurrentStepInfo(currentStep, stepList); + let dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(dataSourceStep, stepList); + + console.log('result', result) + console.log(currentStepInfo) + console.log(dataSourceStepInfo) + console.log([ + EToolName.Rect, + EToolName.RectTrack, + EToolName.Polygon, + EToolName.Point, + EToolName.Line, + ].includes(dataSourceStepInfo?.tool)) + if (currentStepInfo?.preDataSourceStep > 0) { + if (typeof preResult === 'string') { + result = jsonParser(preResult); + } else { + result = preResult; + } + + dataSourceStep = currentStepInfo?.preDataSourceStep; + dataSourceStepInfo = { + tool: result[`step_${currentStepInfo?.preDataSourceStep}`]?.toolName, + step: 1, + }; + } + + if (!result[`step_${dataSourceStep}`] || !result[`step_${dataSourceStep}`].result) { + console.log(111) + return []; + } + const sourceData = result[`step_${dataSourceStep}`].result; + + if (!dataSourceStepInfo && currentStepInfo?.preDataSourceStep <= 0) { + console.log(222) + return []; + } + try { + const currentStepInfoConfig = jsonParser(currentStepInfo.config); + // 如果为非框型工具就需要递归下去 + const filterData = filterDataAdaptor(currentStepInfoConfig.filterData, dataSourceStepInfo); + if (!filterData && filterNeeded) { + console.log(333) + return []; + } + + if (dataSourceStepInfo.tool === EToolName.Tag) { + // 过滤标签工具的数据 + if ( + dataSourceStepInfo.dataSourceStep === 0 && + (!dataSourceStepInfo?.preDataSourceStep || dataSourceStepInfo?.preDataSourceStep === 0) + ) { + return []; + } + + // 过滤出当前符合标准的标签数据 + const newData = filterTagResult(sourceData, filterData, forbidFilter); + const sourceIDList = newData.map((v) => ({ id: v.id, sourceID: v.sourceID })); + return getSourceData( + dataSourceStepInfo.step, + dataSourceStepInfo.dataSourceStep, + stepList, + result, + sourceIDList, + preResult, + ); + } else if ( + [ + EToolName.Rect, + EToolName.RectTrack, + EToolName.Polygon, + EToolName.Point, + EToolName.Line, + ].includes(dataSourceStepInfo.tool) + ) { + return useConfigFilterData(currentStep, stepList, sourceData, forbidFilter); + } else if (dataSourceStepInfo.tool === EToolName.Filter) { + // 过滤筛选工具的数据 + if ( + dataSourceStepInfo.dataSourceStep === 0 && + (!dataSourceStepInfo?.preDataSourceStep || dataSourceStepInfo?.preDataSourceStep === 0) + ) { + return []; + } + // 过滤出当前符合标准的标签数据 + const newData = sourceData.filter( + (v) => filterData.indexOf(v.filterLabel) > -1 || forbidFilter, + ); + const sourceIDList = newData.map((v) => ({ id: v.id, sourceID: v.sourceID })); + return getSourceData( + dataSourceStepInfo.step, + dataSourceStepInfo.dataSourceStep, + stepList, + result, + sourceIDList, + preResult, + ); + } + } catch (e) { + console.error(e); + } +} + +export const DEFAULT_LINK = '@@'; +const DEFAULT_TOOL_ATTRIBUTE = ['valid', 'invalid']; +/** + * 将旧版的 filterData 转换为新版的 filterData + * + * @export + * @param {*} oldFilterData + * @param {IStepInfo} dataSourceStepInfo 注意这个是依赖项的 stepInfo + * @returns + */ +export function filterDataAdaptor( + oldFilterData: any, + dataSourceStepInfo: IStepInfo, +): string[] { + const config = jsonParser(dataSourceStepInfo?.config); + + if (oldFilterData?.constructor === Object) { + // 说明为旧数据 + const keyList = Object.keys(oldFilterData).reduce((acc, cur) => { + if (Array.isArray(oldFilterData[cur])) { + // 在旧格式中仅为 标签工具 + return [...acc, ...oldFilterData[cur].map((v) => `${cur}${DEFAULT_LINK}${v}`)]; + } + + // 对之前旧的图形工具的适配 + if ( + DEFAULT_TOOL_ATTRIBUTE.includes(cur) && + oldFilterData[cur] === true && + judgeIsAttribute(config) + ) { + return [ + ...acc, + `${cur}${DEFAULT_LINK}`, // 无属性 + ...config.attributeList.reduce((a, v) => { + return [...a, `${cur}${DEFAULT_LINK}${v?.value}`]; + }, []), + ]; + } + + return [...acc, cur]; + }, []); + + return keyList; + } + + return oldFilterData; +} + +/** + * 判断是否是按属性进行标注 + * + * @export + * @param {*} config + * @returns + */ +export function judgeIsAttribute(config: any) { + return ( + config?.attributeConfigurable && config?.attributeList && config?.attributeList?.length > 0 + ); +} + +/** + * 过滤标签结果 + * @param sourceData + * @param filterData + * @param forbidFilter + */ +export function filterTagResult( + sourceData: any[], + filterData: string[], + forbidFilter?: boolean, +) { + const tagInputList = transformFilterDataToObject(filterData); + + return sourceData.filter((data) => { + if (forbidFilter === true) { + return true; + } + + for (const i of Object.keys(data.result)) { + // 注意,标签工具的结果是 是一个对象 + + if (tagInputList.hasOwnProperty(i)) { + const dataList = data.result[i].split(';'); + for (const d of dataList) { + if (tagInputList[i].indexOf(d) > -1) { + return true; + } + } + } + } + return false; + }); +} + +/** + * 将 filterData 转换为 Object 形式,便于判断 + * + * @export + * @param {FilterDataState} filterData + * @returns + */ + +export function transformFilterDataToObject(filterData: string[]): any { + return filterData.reduce((acc, cur) => { + const [key, value] = cur.split(DEFAULT_LINK); + + if (typeof value === 'undefined') { + // 表示为空,例如: ['valid', 'invalid'] + return { + ...acc, + [key]: [], + }; + } + + if (acc.hasOwnProperty(key)) { + return { + ...acc, + [key]: [...acc[key], value], + }; + } else { + return { + ...acc, + [key]: [value], + }; + } + }, {}); +} + +/** + * 根据 config 过滤当前数据 + * + * 注意: 该函数目前仅适用于图形类的步骤进行过滤 + * + * @export + * @param {number} currentStep + * @param {IStepInfo[]} stepList + * @param {any[]} basicData + * @returns + */ +export function useConfigFilterData( + currentStep: number, + stepList: IStepInfo[], + basicData: any[], + forbidFilter: boolean = false, +) { + const currentStepInfo = CommonToolUtils.getStepInfo(currentStep, stepList); + const dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(currentStepInfo.dataSourceStep, stepList); + if (currentStepInfo.config) { + const config = jsonParser(currentStepInfo.config); + console.log('===', config) + if (config.filterData) { + const filterData = filterDataAdaptor(config.filterData, dataSourceStepInfo); + + const filterDataObject = transformFilterDataToObject(filterData); + + basicData = basicData.filter((v) => { + if (forbidFilter === true) { + return true; + } + + if (filterData.some((v) => v === 'valid') || filterData.some((v) => v === 'invalid')) { + // 说明为图形统一管理 + if (filterData.some((v) => v === 'valid') && v?.valid === true) { + // 说明是原始的图形的过滤 + return true; + } + + if (filterData.some((v) => v === 'invalid') && v?.valid === false) { + // 说明是原始的图形的过滤 + return true; + } + + return false; + } + // 含有属性的数据过滤 + if (judgeIsAttribute(jsonParser(dataSourceStepInfo.config))) { + if (v?.valid === true) { + return filterDataObject?.valid?.includes(v?.attribute ?? ''); + } else { + return filterDataObject?.invalid?.includes(v?.attribute ?? ''); + } + } + + return false; + }); + } + } + return basicData; +} + +/** + * 过滤出指定步骤中的指定依赖框出来 + * @param currentStep + * @param dataSourceStep + * @param stepList + * @param result + * @param sourceIDList + * @param preResult + */ +export function getSourceData( + currentStep: number, + dataSourceStep: number, + stepList: IStepInfo[], + result: any, + sourceIDList: any[], + preResult: string = '{}', +): any { + const currentStepInfo = CommonToolUtils.getCurrentStepInfo(currentStep, stepList); + + if (currentStepInfo?.preDataSourceStep > 0) { + // 说明该步骤依赖的是预标注数据 + const stepName = `step_${currentStepInfo?.preDataSourceStep}`; + const result = jsonParser(preResult)[stepName]?.result; + + if (result) { + return ( + result?.filter((v) => { + for (const i of sourceIDList) { + if (i?.sourceID === v.id) { + return true; + } + } + return false; + }) ?? [] + ); + } + } + + if (currentStep === 0 || !result[`step_${dataSourceStep}`]?.result) { + return []; + } + const dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(dataSourceStep, stepList); + if (!dataSourceStepInfo) { + return []; + } + + const sourceData = result[`step_${dataSourceStep}`].result; + let resultList = []; + resultList = sourceData.filter((v) => { + for (const i of sourceIDList) { + if (i?.sourceID === v.id) { + return true; + } + } + return false; + }); + + if (dataSourceStepInfo.tool === EToolName.Tag || dataSourceStepInfo.tool === EToolName.Filter) { + // 如果 dataSource 工具还是为标签的话,就继续递归,找到为止 + return getSourceData( + dataSourceStepInfo.step, + dataSourceStepInfo.dataSourceStep, + stepList, + result, + sourceIDList, // 注意: 其实比较怀疑这边是否能正确使用,表示怀疑 - 后续需要关注 todo + preResult, + ); + } else if ( + [EToolName.Rect, EToolName.RectTrack, EToolName.Polygon].includes(dataSourceStepInfo.tool) + ) { + return resultList; + } + + return resultList; +} diff --git a/packages/lb-demo/src/App.js b/packages/lb-demo/src/App.js index 889b36320..565c10e12 100644 --- a/packages/lb-demo/src/App.js +++ b/packages/lb-demo/src/App.js @@ -32,6 +32,8 @@ const App = () => { comma: true, }).tool; + console.log(tool) + const isSingleTool = !Array.isArray(tool); const stepList = isSingleTool ? getStepList(tool) : getDependStepList(tool); const currentIsVideo = StepUtils.currentToolIsVideo(1, stepList); From 9a296d459676a5d6c6c3efc7faca1a3fdc9f5868 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Tue, 30 Jan 2024 22:14:32 +0800 Subject: [PATCH 34/43] feat: Pipeline v1-temp --- packages/lb-annotation/src/core/basicLayer.ts | 221 ++++++++++++++++++ packages/lb-annotation/src/core/index.ts | 81 ++++++- packages/lb-annotation/src/core/scheduler.ts | 37 ++- .../core/toolOperation/basicToolOperation.ts | 27 ++- .../src/utils/tool/CanvasUtils.ts | 1 - .../src/store/annotation/reducer.ts | 5 - packages/lb-components/src/utils/Pipeline.ts | 30 +-- packages/lb-demo/src/App.js | 2 - packages/lb-demo/src/mock/taskConfig.js | 1 + 9 files changed, 356 insertions(+), 49 deletions(-) create mode 100644 packages/lb-annotation/src/core/basicLayer.ts diff --git a/packages/lb-annotation/src/core/basicLayer.ts b/packages/lb-annotation/src/core/basicLayer.ts new file mode 100644 index 000000000..c8fb22bcd --- /dev/null +++ b/packages/lb-annotation/src/core/basicLayer.ts @@ -0,0 +1,221 @@ +/** + * @author wanghaiqing wanghaiqing@sensetime.com + * @date 1/30/24 + */ + +import { EToolName, THybridToolName } from '@/constant/tool'; +import CanvasUtils from '@/utils/tool/CanvasUtils'; +import DrawUtils from '@/utils/tool/DrawUtils'; +import AxisUtils, { CoordinateUtils } from '@/utils/tool/AxisUtils'; +import { HybridToolUtils } from '@/core/scheduler'; +import { ICommonProps } from './index' + +interface IBasicLayerProps extends ICommonProps { + container: HTMLElement; + size: ISize; + toolName: THybridToolName; + imgNode?: HTMLImageElement; // 展示图片的内容 + config?: string; // 任务配置 + style?: any; + forbidBasicResultRender?: boolean; +} + +export default class BasicLayer { + public container: HTMLElement; // 当前结构绑定 container + public size: ISize; + + public imgNode?: HTMLImageElement; + + public basicCanvas!: HTMLCanvasElement; // dom for basic layer + + public basicResult?: any; // data of depended tool + + public dependToolName?: EToolName; + + public forbidBasicResultRender: boolean; // 禁止渲染基础依赖图形 + + public zoom: number; + + public currentPos: ICoordinate; // 存储实时偏移的位置 + + public basicImgInfo: any; // 用于存储当前图片的信息 + + public coordUtils?: CoordinateUtils; + + private hiddenImg: boolean; + + private _imgAttribute?: any; + + constructor(props: IBasicLayerProps) { + this.container = props.container; + this.size = props.size; + this.zoom = props.zoom ?? 1; + this.currentPos = props.currentPos ?? { x: 0, y: 0}; + this.basicImgInfo = props.basicImgInfo; + this.coordUtils = props.coordUtils; + this._imgAttribute = props.imgAttribute ?? {}; + + this.imgNode = props.imgNode; + this.hiddenImg = !HybridToolUtils.isSingleTool(props.toolName) || false; + this.forbidBasicResultRender = props.forbidBasicResultRender ?? false; + + this.createBasicCanvas(props.size) + } + + // getters + get basicCtx() { + return this.basicCanvas?.getContext('2d'); + } + + public get pixelRatio() { + return CanvasUtils.getPixelRatio(this.basicCanvas?.getContext('2d')); + } + + get rotate() { + return this.basicImgInfo?.rotate ?? 0; + } + + // setters + public setZoom(zoom: number) { + this.zoom = zoom; + } + + public setCurrentPos(currentPos: ICoordinate) { + this.currentPos = currentPos; + } + + public setBasicImgInfo(basicImgInfo: any) { + this.basicImgInfo = basicImgInfo; + } + + public setImgAttribute(imgAttribute: IImageAttribute) { + this._imgAttribute = imgAttribute; + this.renderBasicCanvas() + } + + /** + * 更改当前 canvas 整体的大小,需要重新初始化 + * @param size + */ + public setSize(size: ISize) { + this.size = size; + console.log(size) + if (this.container.contains(this.basicCanvas)) { + this.destroyBasicCanvas(); + this.createBasicCanvas(size); + this.renderBasicCanvas() + } + } + + public createBasicCanvas(size: ISize) { + const basicCanvas = document.createElement('canvas'); + basicCanvas.className = 'bee-basic-layer' + this.updateCanvasBasicStyle(basicCanvas, size, 0); + this.basicCanvas = basicCanvas + if (this.container.hasChildNodes()) { + this.container.insertBefore(basicCanvas, this.container.childNodes[0]); + } else { + this.container.appendChild(basicCanvas) + } + this.basicCtx?.scale(this.pixelRatio, this.pixelRatio) + } + + public updateCanvasBasicStyle(canvas: HTMLCanvasElement, size: ISize, zIndex: number) { + const pixel = this.pixelRatio; + canvas.style.position = 'absolute'; + canvas.width = size.width * pixel; + canvas.height = size.height * pixel; + canvas.style.width = `${size.width}px`; + canvas.style.height = `${size.height}px`; + canvas.style.left = '0'; + canvas.style.top = '0'; + canvas.style.zIndex = `${zIndex} `; + } + + public drawImg = () => { + if (!this.imgNode || this.hiddenImg === true) return; + + console.log('drawimg') + console.log(this.zoom) + + DrawUtils.drawImg(this.basicCanvas, this.imgNode, { + zoom: this.zoom, + currentPos: this.currentPos, + rotate: this.rotate, + imgAttribute: this._imgAttribute, + }); + }; + + public destroyBasicCanvas() { + if (this.basicCanvas && this.container.contains(this.basicCanvas)) { + this.container.removeChild(this.basicCanvas); + } + } + public clearBasicCanvas() { + this.basicCtx?.clearRect(0, 0, this.size.width, this.size.height); + } + + public renderBasicCanvas() { + if (!this.basicCanvas) { + return; + } + + this.clearBasicCanvas(); + this.drawImg(); + + const thickness = 3; + + if (this.forbidBasicResultRender) { + return; + } + + if (this.basicResult && this.dependToolName) { + switch (this.dependToolName) { + case EToolName.Rect: { + DrawUtils.drawRect( + this.basicCanvas, + AxisUtils.changeRectByZoom(this.basicResult, this.zoom, this.currentPos), + { + color: 'rgba(204,204,204,1.00)', + thickness, + }, + ); + break; + } + + case EToolName.Polygon: { + DrawUtils.drawPolygonWithFillAndLine( + this.basicCanvas, + AxisUtils.changePointListByZoom(this.basicResult.pointList, this.zoom, this.currentPos), + { + fillColor: 'transparent', + strokeColor: 'rgba(204,204,204,1.00)', + isClose: true, + thickness, + }, + ); + + break; + } + + case EToolName.Line: { + DrawUtils.drawLineWithPointList( + this.basicCanvas, + AxisUtils.changePointListByZoom(this.basicResult.pointList, this.zoom, this.currentPos), + { + color: 'rgba(204,204,204,1.00)', + thickness, + }, + ); + + break; + } + + default: { + // + } + } + } + } + +} \ No newline at end of file diff --git a/packages/lb-annotation/src/core/index.ts b/packages/lb-annotation/src/core/index.ts index 8aebf65f2..d2b13a629 100644 --- a/packages/lb-annotation/src/core/index.ts +++ b/packages/lb-annotation/src/core/index.ts @@ -7,6 +7,9 @@ import { getConfig, styleDefaultConfig } from '@/constant/defaultConfig'; import { EToolName, THybridToolName } from '@/constant/tool'; import { IPolygonData } from '@/types/tool/polygon'; import { HybridToolUtils, ToolScheduler } from './scheduler'; +import CanvasUtils from '@/utils/tool/CanvasUtils'; +import BasicLayer from '@/core/basicLayer'; +import { CoordinateUtils } from '@/utils/tool/AxisUtils'; interface IProps { container: HTMLElement; @@ -17,6 +20,14 @@ interface IProps { style?: any; } +export interface ICommonProps { + zoom?: number; + currentPos?: ICoordinate; + coordUtils?: CoordinateUtils; + basicImgInfo?: any; + imgAttribute?: IImageAttribute; +} + const loadImage = (imgSrc: string) => { return new Promise((resolve, reject) => { const img = document.createElement('img'); @@ -39,6 +50,14 @@ export default class AnnotationEngine { public i18nLanguage: 'en' | 'cn'; // 存储当前 i18n 初始化数据 + public zoom: number; + + public currentPos: ICoordinate; // 存储实时偏移的位置 + + public coordUtils: CoordinateUtils; + + public basicImgInfo: any; // 用于存储当前图片的信息 + private container: HTMLElement; // 当前结构绑定 container private size: ISize; @@ -56,20 +75,44 @@ export default class AnnotationEngine { private toolScheduler: ToolScheduler; // For multi-level management of tools + private basicInstance: BasicLayer; + constructor(props: IProps) { this.container = props.container; this.size = props.size; this.toolName = props.toolName; this.imgNode = props.imgNode; + this.zoom = 1; + this.currentPos = { + x: 0, + y: 0, + } + this.basicImgInfo = { + width: props.imgNode?.width ?? 0, + height: props.imgNode?.height ?? 0, + valid: true, + rotate: 0, + }; + this.coordUtils = new CoordinateUtils(this); this.config = props.config ?? JSON.stringify(getConfig(HybridToolUtils.getTopToolName(props.toolName))); // 设置默认操作 this.style = props.style ?? styleDefaultConfig; // 设置默认操作 - this.toolScheduler = new ToolScheduler(props); - + this.toolScheduler = new ToolScheduler({ ...props, ...this.commonProps }); + this.basicInstance = new BasicLayer({ ...props, ...this.commonProps }); + console.log(this.basicInstance) this.i18nLanguage = 'cn'; // 默认为中文(跟 basicOperation 内同步) this._initToolOperation(); + this._initBasicLayer(); } + get commonProps() { + return { + zoom: this.zoom, + currentPos: this.currentPos, + basicImgInfo: this.basicImgInfo, + coordUtils: this.coordUtils, + } + } /** * 同步各种基础类型信息 * 1. imgNode @@ -78,6 +121,28 @@ export default class AnnotationEngine { * 4. style */ + public syncZoom(zoom: number){ + this.toolScheduler.setZoom(zoom) + this.basicInstance.setZoom(zoom) + this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); + } + + public syncCurrentPos(currentPos: ICoordinate) { + this.toolScheduler.setCurrentPos(currentPos) + this.basicInstance.setCurrentPos(currentPos) + this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); + } + + public syncBasicImgInfo(basicImgInfo: any) { + this.toolScheduler.setBasicImgInfo(basicImgInfo) + this.basicInstance.setBasicImgInfo(basicImgInfo) + this.coordUtils.setBasicImgInfo(basicImgInfo); + } + + public setImgAttribute(imgAttribute: IImageAttribute) { + this.toolScheduler.setImgAttribute(imgAttribute); + this.basicInstance.setImgAttribute(imgAttribute) + } /** * 设置当前工具类型 * @param toolName @@ -110,13 +175,10 @@ export default class AnnotationEngine { this.imgNode = imgNode; } - public setImgAttribute(imgAttribute: IImageAttribute) { - this.toolScheduler.setImgAttribute(imgAttribute); - } - public setSize(size: ISize) { this.size = size; this.toolScheduler.setSize(size); + this.basicInstance.setSize(size); } public setStyle(style: any) { @@ -158,6 +220,13 @@ export default class AnnotationEngine { this.setLang(this.i18nLanguage); } + /** + * 初始化依赖渲染层 + */ + private _initBasicLayer() { + this.basicInstance.renderBasicCanvas() + } + /** * 设置当前依赖物体渲染 * @param dependToolName diff --git a/packages/lb-annotation/src/core/scheduler.ts b/packages/lb-annotation/src/core/scheduler.ts index c5f43ae09..32f17ca93 100644 --- a/packages/lb-annotation/src/core/scheduler.ts +++ b/packages/lb-annotation/src/core/scheduler.ts @@ -11,10 +11,12 @@ import { RectOperation } from './toolOperation/rectOperation'; import PolygonOperation from './toolOperation/polygonOperation'; import { BasicToolOperation } from './toolOperation/basicToolOperation'; import SegmentByRect from './toolOperation/segmentByRect'; +import { ICommonProps } from './index' +import { CoordinateUtils } from '@/utils/tool/AxisUtils'; interface IToolSchedulerOperation {} -interface IToolSchedulerProps { +interface IToolSchedulerProps extends ICommonProps{ container: HTMLElement; size: ISize; toolName: THybridToolName; @@ -82,6 +84,8 @@ export class ToolScheduler implements IToolSchedulerOperation { private proxyMode?: boolean; + private coordUtils?: CoordinateUtils; + constructor(props: IToolSchedulerProps) { this.container = props.container; this.size = props.size; @@ -89,6 +93,7 @@ export class ToolScheduler implements IToolSchedulerOperation { this.config = props.config ?? JSON.stringify(getConfig(HybridToolUtils.getTopToolName(props.toolName))); // 设置默认操作 this.style = props.style ?? styleDefaultConfig; // 设置默认操作 this.proxyMode = props.proxyMode; + this.coordUtils = props.coordUtils; this.onWheel = this.onWheel.bind(this); this.onMouseDown = this.onMouseDown.bind(this); this.onMouseMove = this.onMouseMove.bind(this); @@ -108,6 +113,24 @@ export class ToolScheduler implements IToolSchedulerOperation { }); } + public setZoom(zoom: number){ + this.toolOperationList.forEach((toolInstance) => { + toolInstance.setZoom(zoom) + }); + } + + public setCurrentPos(currentPos: ICoordinate) { + this.toolOperationList.forEach((toolInstance) => { + toolInstance.setCurrentPos(currentPos) + }); + } + + public setBasicImgInfo(basicImgInfo: any) { + this.toolOperationList.forEach((toolInstance) => { + toolInstance.setBasicImgInfo(basicImgInfo) + }); + } + public setImgAttribute(imgAttribute: IImageAttribute) { this.toolOperationList.forEach((toolInstance) => { toolInstance.setImgAttribute(imgAttribute); @@ -198,6 +221,18 @@ export class ToolScheduler implements IToolSchedulerOperation { style: this.style, imgNode: imgNode || emptyImgNode, hiddenImg: !!tool, + zoom: 1, + currentPos: { + x: 0, + y: 0, + }, + basicImgInfo: { + width: imgNode?.width ?? 0, + height: imgNode?.height ?? 0, + valid: true, + rotate: 0, + }, + coordUtils: this.coordUtils, }; if (config) { Object.assign(defaultData, config); diff --git a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts index 0672f5735..ef672c924 100644 --- a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts @@ -19,13 +19,14 @@ import DrawUtils from '../../utils/tool/DrawUtils'; import RenderDomUtils from '../../utils/tool/RenderDomUtils'; import ZoomUtils from '../../utils/tool/ZoomUtils'; import EventListener from './eventListener'; +import { ICommonProps } from '../index' const LANGUAGE_MAP = { [ELang.Zh]: 'cn', [ELang.US]: 'en', }; -interface IBasicToolOperationProps { +interface IBasicToolOperationProps extends ICommonProps { container: HTMLElement; size: ISize; imgNode?: HTMLImageElement; // 展示图片的内容 @@ -193,7 +194,7 @@ class BasicToolOperation extends EventListener { this.imgNode = props.imgNode; this.staticMode = props.staticMode ?? false; this.isImgError = !props.imgNode; - this.basicImgInfo = { + this.basicImgInfo = props.basicImgInfo ?? { width: props.imgNode?.width ?? 0, height: props.imgNode?.height ?? 0, valid: true, @@ -203,11 +204,8 @@ class BasicToolOperation extends EventListener { this.forbidBasicResultRender = props.forbidBasicResultRender ?? false; this.size = props.size; - this.currentPos = { - x: 0, - y: 0, - }; - this.zoom = 1; + this.zoom = props.zoom ?? 1 + this.currentPos = props.currentPos ?? { x: 0, y: 0} this.coord = { x: -1, y: -1, @@ -242,8 +240,8 @@ class BasicToolOperation extends EventListener { // 初始化监听事件 this.dblClickListener = new DblClickEventListener(this.container, 200); - this.coordUtils = new CoordinateUtils(this); - this.coordUtils.setBasicImgInfo(this.basicImgInfo); + this.coordUtils = props.coordUtils ?? new CoordinateUtils(this); + this.coordUtils.setBasicImgInfo(this.basicImgInfo) this.hiddenImg = props.hiddenImg || false; @@ -311,12 +309,10 @@ class BasicToolOperation extends EventListener { public setZoom(zoom: number) { this.zoom = zoom; this.innerZoom = zoom; - this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); } public setCurrentPos(currentPos: ICoordinate) { this.currentPos = currentPos; - this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); } public setReferenceData(referenceData: IReferenceData) { @@ -495,6 +491,7 @@ class BasicToolOperation extends EventListener { this.initImgPos(); this.render(); + this.renderBasicCanvas(); } @@ -539,7 +536,6 @@ class BasicToolOperation extends EventListener { public setBasicImgInfo(basicImgInfo: any) { this.basicImgInfo = basicImgInfo; - this.coordUtils.setBasicImgInfo(basicImgInfo); } public setForbidOperation(forbidOperation: boolean) { @@ -709,6 +705,7 @@ class BasicToolOperation extends EventListener { this.setImgInfo(imgInfo); this.setZoom(zoom); this.render(); + this.renderBasicCanvas(); this.emit('dependRender'); @@ -906,6 +903,7 @@ class BasicToolOperation extends EventListener { this.isDrag = true; this.container.style.cursor = 'grabbing'; this.forbidCursorLine = true; + this.renderBasicCanvas(); // 依赖渲染触发 @@ -1071,6 +1069,7 @@ class BasicToolOperation extends EventListener { if (isRender) { this.render(); } + this.renderBasicCanvas(); } @@ -1175,6 +1174,9 @@ class BasicToolOperation extends EventListener { public drawImg = () => { if (!this.imgNode || this.hiddenImg === true) return; + console.log('basic operation draw') + console.log(this.zoom) + DrawUtils.drawImg(this.basicCanvas, this.imgNode, { zoom: this.zoom, currentPos: this.currentPos, @@ -1202,6 +1204,7 @@ class BasicToolOperation extends EventListener { */ public setSize(size: ISize) { this.size = size; + console.log(size) this.updateZoomInfo(); if (this.container.contains(this.canvas)) { this.destroyCanvas(); diff --git a/packages/lb-annotation/src/utils/tool/CanvasUtils.ts b/packages/lb-annotation/src/utils/tool/CanvasUtils.ts index c94c420fe..35dd6eeb0 100644 --- a/packages/lb-annotation/src/utils/tool/CanvasUtils.ts +++ b/packages/lb-annotation/src/utils/tool/CanvasUtils.ts @@ -91,7 +91,6 @@ export default class CanvasUtils { context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || - context.backingStorePixelRatio || 1; return (window.devicePixelRatio || 1) / backingStore; diff --git a/packages/lb-components/src/store/annotation/reducer.ts b/packages/lb-components/src/store/annotation/reducer.ts index 509ce5308..31e07811c 100644 --- a/packages/lb-components/src/store/annotation/reducer.ts +++ b/packages/lb-components/src/store/annotation/reducer.ts @@ -482,9 +482,7 @@ export const annotationReducer = ( const { dataSourceStep, tool } = stepConfig; const dependStepConfig = getStepConfig(stepList, dataSourceStep); const hasDataSourceStep = dataSourceStep && tool; - const oldStepBasicResultList = fileResult[`step_${dataSourceStep}`]?.result ?? []; - console.log(fileResult) const stepBasicResultList = getBasicResult( currentStepInfo.step, currentStepInfo.dataSourceStep, @@ -494,9 +492,6 @@ export const annotationReducer = ( preResult, ); - console.log(oldStepBasicResultList) - console.log(stepBasicResultList) - const result = AnnotationDataUtils.getInitialResultList( stepResult?.result, toolInstance, diff --git a/packages/lb-components/src/utils/Pipeline.ts b/packages/lb-components/src/utils/Pipeline.ts index 30404c050..eaa6e5253 100644 --- a/packages/lb-components/src/utils/Pipeline.ts +++ b/packages/lb-components/src/utils/Pipeline.ts @@ -28,16 +28,6 @@ export function getBasicResult( const currentStepInfo = CommonToolUtils.getCurrentStepInfo(currentStep, stepList); let dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(dataSourceStep, stepList); - console.log('result', result) - console.log(currentStepInfo) - console.log(dataSourceStepInfo) - console.log([ - EToolName.Rect, - EToolName.RectTrack, - EToolName.Polygon, - EToolName.Point, - EToolName.Line, - ].includes(dataSourceStepInfo?.tool)) if (currentStepInfo?.preDataSourceStep > 0) { if (typeof preResult === 'string') { result = jsonParser(preResult); @@ -53,13 +43,11 @@ export function getBasicResult( } if (!result[`step_${dataSourceStep}`] || !result[`step_${dataSourceStep}`].result) { - console.log(111) return []; } const sourceData = result[`step_${dataSourceStep}`].result; if (!dataSourceStepInfo && currentStepInfo?.preDataSourceStep <= 0) { - console.log(222) return []; } try { @@ -67,7 +55,6 @@ export function getBasicResult( // 如果为非框型工具就需要递归下去 const filterData = filterDataAdaptor(currentStepInfoConfig.filterData, dataSourceStepInfo); if (!filterData && filterNeeded) { - console.log(333) return []; } @@ -111,9 +98,9 @@ export function getBasicResult( } // 过滤出当前符合标准的标签数据 const newData = sourceData.filter( - (v) => filterData.indexOf(v.filterLabel) > -1 || forbidFilter, + (v: any) => filterData.indexOf(v.filterLabel) > -1 || forbidFilter, ); - const sourceIDList = newData.map((v) => ({ id: v.id, sourceID: v.sourceID })); + const sourceIDList = newData.map((v: any) => ({ id: v.id, sourceID: v.sourceID })); return getSourceData( dataSourceStepInfo.step, dataSourceStepInfo.dataSourceStep, @@ -146,10 +133,10 @@ export function filterDataAdaptor( if (oldFilterData?.constructor === Object) { // 说明为旧数据 - const keyList = Object.keys(oldFilterData).reduce((acc, cur) => { + const keyList = Object.keys(oldFilterData).reduce((acc: any[], cur: string) => { if (Array.isArray(oldFilterData[cur])) { // 在旧格式中仅为 标签工具 - return [...acc, ...oldFilterData[cur].map((v) => `${cur}${DEFAULT_LINK}${v}`)]; + return [...acc, ...oldFilterData[cur].map((v: any) => `${cur}${DEFAULT_LINK}${v}`)]; } // 对之前旧的图形工具的适配 @@ -161,7 +148,7 @@ export function filterDataAdaptor( return [ ...acc, `${cur}${DEFAULT_LINK}`, // 无属性 - ...config.attributeList.reduce((a, v) => { + ...config.attributeList.reduce((a: any, v: any) => { return [...a, `${cur}${DEFAULT_LINK}${v?.value}`]; }, []), ]; @@ -232,7 +219,7 @@ export function filterTagResult( */ export function transformFilterDataToObject(filterData: string[]): any { - return filterData.reduce((acc, cur) => { + return filterData.reduce((acc: { [key: string]: any }, cur) => { const [key, value] = cur.split(DEFAULT_LINK); if (typeof value === 'undefined') { @@ -278,7 +265,6 @@ export function useConfigFilterData( const dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(currentStepInfo.dataSourceStep, stepList); if (currentStepInfo.config) { const config = jsonParser(currentStepInfo.config); - console.log('===', config) if (config.filterData) { const filterData = filterDataAdaptor(config.filterData, dataSourceStepInfo); @@ -345,7 +331,7 @@ export function getSourceData( if (result) { return ( - result?.filter((v) => { + result?.filter((v: any) => { for (const i of sourceIDList) { if (i?.sourceID === v.id) { return true; @@ -367,7 +353,7 @@ export function getSourceData( const sourceData = result[`step_${dataSourceStep}`].result; let resultList = []; - resultList = sourceData.filter((v) => { + resultList = sourceData.filter((v: any) => { for (const i of sourceIDList) { if (i?.sourceID === v.id) { return true; diff --git a/packages/lb-demo/src/App.js b/packages/lb-demo/src/App.js index 565c10e12..889b36320 100644 --- a/packages/lb-demo/src/App.js +++ b/packages/lb-demo/src/App.js @@ -32,8 +32,6 @@ const App = () => { comma: true, }).tool; - console.log(tool) - const isSingleTool = !Array.isArray(tool); const stepList = isSingleTool ? getStepList(tool) : getDependStepList(tool); const currentIsVideo = StepUtils.currentToolIsVideo(1, stepList); diff --git a/packages/lb-demo/src/mock/taskConfig.js b/packages/lb-demo/src/mock/taskConfig.js index 55e5b8d1c..94ac50d11 100644 --- a/packages/lb-demo/src/mock/taskConfig.js +++ b/packages/lb-demo/src/mock/taskConfig.js @@ -163,6 +163,7 @@ const polygonConfig = { { key: '纸巾', value: 'tissue' }, { key: '水壶', value: 'kettle' }, ], + filterData: ['valid', 'invalid'], textConfigurable: true, textCheckType: 0, customFormat: '', From 8ccfeae63a7bef564dfc19cf30106955219ae7d0 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Wed, 31 Jan 2024 16:45:16 +0800 Subject: [PATCH 35/43] feat: Separation of basic layer from basic operation tool --- packages/lb-annotation/src/core/basicLayer.ts | 86 ++++++++--- packages/lb-annotation/src/core/index.ts | 43 +++--- packages/lb-annotation/src/core/scheduler.ts | 34 ++++- .../core/toolOperation/basicToolOperation.ts | 140 +++++------------- .../toolOperation/pointCloud2dOperation.ts | 4 +- .../lb-annotation/src/utils/tool/AxisUtils.ts | 2 +- packages/lb-components/src/utils/Pipeline.ts | 25 ++-- .../MainView/annotationOperation/index.tsx | 4 +- 8 files changed, 176 insertions(+), 162 deletions(-) diff --git a/packages/lb-annotation/src/core/basicLayer.ts b/packages/lb-annotation/src/core/basicLayer.ts index c8fb22bcd..e2d77ada9 100644 --- a/packages/lb-annotation/src/core/basicLayer.ts +++ b/packages/lb-annotation/src/core/basicLayer.ts @@ -8,7 +8,9 @@ import CanvasUtils from '@/utils/tool/CanvasUtils'; import DrawUtils from '@/utils/tool/DrawUtils'; import AxisUtils, { CoordinateUtils } from '@/utils/tool/AxisUtils'; import { HybridToolUtils } from '@/core/scheduler'; -import { ICommonProps } from './index' +import EventListener from '@/core/toolOperation/eventListener'; +import { IPolygonConfig } from '@/types/tool/polygon'; +import { ICommonProps } from './index'; interface IBasicLayerProps extends ICommonProps { container: HTMLElement; @@ -20,13 +22,14 @@ interface IBasicLayerProps extends ICommonProps { forbidBasicResultRender?: boolean; } -export default class BasicLayer { +export default class BasicLayer extends EventListener { public container: HTMLElement; // 当前结构绑定 container + public size: ISize; public imgNode?: HTMLImageElement; - public basicCanvas!: HTMLCanvasElement; // dom for basic layer + public basicCanvas!: HTMLCanvasElement; // dom for basic layer public basicResult?: any; // data of depended tool @@ -38,19 +41,25 @@ export default class BasicLayer { public currentPos: ICoordinate; // 存储实时偏移的位置 + public coordUtils?: CoordinateUtils; + public basicImgInfo: any; // 用于存储当前图片的信息 - public coordUtils?: CoordinateUtils; + public imgInfo?: ISize; private hiddenImg: boolean; private _imgAttribute?: any; + private currentPosStorage?: ICoordinate; // 存储当前点击的平移位置 + constructor(props: IBasicLayerProps) { + super(); this.container = props.container; + this.size = props.size; this.zoom = props.zoom ?? 1; - this.currentPos = props.currentPos ?? { x: 0, y: 0}; + this.currentPos = props.currentPos ?? { x: 0, y: 0 }; this.basicImgInfo = props.basicImgInfo; this.coordUtils = props.coordUtils; this._imgAttribute = props.imgAttribute ?? {}; @@ -59,7 +68,8 @@ export default class BasicLayer { this.hiddenImg = !HybridToolUtils.isSingleTool(props.toolName) || false; this.forbidBasicResultRender = props.forbidBasicResultRender ?? false; - this.createBasicCanvas(props.size) + this.destroyBasicCanvas(); + this.createBasicCanvas(props.size); } // getters @@ -82,6 +92,7 @@ export default class BasicLayer { public setCurrentPos(currentPos: ICoordinate) { this.currentPos = currentPos; + this.currentPosStorage = currentPos; } public setBasicImgInfo(basicImgInfo: any) { @@ -90,7 +101,31 @@ export default class BasicLayer { public setImgAttribute(imgAttribute: IImageAttribute) { this._imgAttribute = imgAttribute; - this.renderBasicCanvas() + this.renderBasicCanvas(); + } + + public setImgInfo(size?: ISize) { + this.imgInfo = size; + } + + public setDependName(dependToolName?: EToolName, dependToolConfig?: IRectConfig | IPolygonConfig) { + this.dependToolName = dependToolName; + this.coordUtils?.setDependInfo(dependToolName, dependToolConfig); + } + + public setBasicResult(basicResult: any) { + this.basicResult = basicResult; + this.coordUtils?.setBasicResult(basicResult); + } + + /** + * 同步currentPos, zoom等common信息 + */ + public syncCommonInfo(info: ICommonProps) { + this.setZoom(info?.zoom ?? this.zoom); + this.setCurrentPos(info?.currentPos ?? this.currentPos); + this.setBasicImgInfo(info?.basicImgInfo ?? this.basicImgInfo); + this.setImgInfo(info?.imgInfo ?? this.imgInfo); } /** @@ -99,25 +134,43 @@ export default class BasicLayer { */ public setSize(size: ISize) { this.size = size; - console.log(size) if (this.container.contains(this.basicCanvas)) { this.destroyBasicCanvas(); this.createBasicCanvas(size); - this.renderBasicCanvas() + this.renderBasicCanvas(); } } + /** + * Notice. It needs to set the default imgInfo. Because it will needs to create info when it doesn't have + * @param imgNode + * @param basicImgInfo + */ + public setImgNode(imgNode: HTMLImageElement, basicImgInfo: Partial<{ valid: boolean; rotate: number }> = {}) { + this.imgNode = imgNode; + + this.setBasicImgInfo({ + width: imgNode.width, + height: imgNode.height, + valid: true, + rotate: 0, + ...basicImgInfo, + }); + + this.renderBasicCanvas(); + } + public createBasicCanvas(size: ISize) { const basicCanvas = document.createElement('canvas'); - basicCanvas.className = 'bee-basic-layer' + basicCanvas.className = 'bee-basic-layer'; this.updateCanvasBasicStyle(basicCanvas, size, 0); - this.basicCanvas = basicCanvas + this.basicCanvas = basicCanvas; if (this.container.hasChildNodes()) { this.container.insertBefore(basicCanvas, this.container.childNodes[0]); } else { - this.container.appendChild(basicCanvas) + this.container.appendChild(basicCanvas); } - this.basicCtx?.scale(this.pixelRatio, this.pixelRatio) + this.basicCtx?.scale(this.pixelRatio, this.pixelRatio); } public updateCanvasBasicStyle(canvas: HTMLCanvasElement, size: ISize, zIndex: number) { @@ -135,9 +188,6 @@ export default class BasicLayer { public drawImg = () => { if (!this.imgNode || this.hiddenImg === true) return; - console.log('drawimg') - console.log(this.zoom) - DrawUtils.drawImg(this.basicCanvas, this.imgNode, { zoom: this.zoom, currentPos: this.currentPos, @@ -151,6 +201,7 @@ export default class BasicLayer { this.container.removeChild(this.basicCanvas); } } + public clearBasicCanvas() { this.basicCtx?.clearRect(0, 0, this.size.width, this.size.height); } @@ -217,5 +268,4 @@ export default class BasicLayer { } } } - -} \ No newline at end of file +} diff --git a/packages/lb-annotation/src/core/index.ts b/packages/lb-annotation/src/core/index.ts index d2b13a629..131c1dac2 100644 --- a/packages/lb-annotation/src/core/index.ts +++ b/packages/lb-annotation/src/core/index.ts @@ -6,10 +6,9 @@ import { ELang } from '@/constant/annotation'; import { getConfig, styleDefaultConfig } from '@/constant/defaultConfig'; import { EToolName, THybridToolName } from '@/constant/tool'; import { IPolygonData } from '@/types/tool/polygon'; -import { HybridToolUtils, ToolScheduler } from './scheduler'; -import CanvasUtils from '@/utils/tool/CanvasUtils'; import BasicLayer from '@/core/basicLayer'; import { CoordinateUtils } from '@/utils/tool/AxisUtils'; +import { HybridToolUtils, ToolScheduler } from './scheduler'; interface IProps { container: HTMLElement; @@ -26,6 +25,7 @@ export interface ICommonProps { coordUtils?: CoordinateUtils; basicImgInfo?: any; imgAttribute?: IImageAttribute; + imgInfo?: ISize; } const loadImage = (imgSrc: string) => { @@ -58,6 +58,8 @@ export default class AnnotationEngine { public basicImgInfo: any; // 用于存储当前图片的信息 + public imgInfo?: ISize; + private container: HTMLElement; // 当前结构绑定 container private size: ISize; @@ -86,7 +88,7 @@ export default class AnnotationEngine { this.currentPos = { x: 0, y: 0, - } + }; this.basicImgInfo = { width: props.imgNode?.width ?? 0, height: props.imgNode?.height ?? 0, @@ -99,7 +101,6 @@ export default class AnnotationEngine { this.style = props.style ?? styleDefaultConfig; // 设置默认操作 this.toolScheduler = new ToolScheduler({ ...props, ...this.commonProps }); this.basicInstance = new BasicLayer({ ...props, ...this.commonProps }); - console.log(this.basicInstance) this.i18nLanguage = 'cn'; // 默认为中文(跟 basicOperation 内同步) this._initToolOperation(); this._initBasicLayer(); @@ -111,8 +112,9 @@ export default class AnnotationEngine { currentPos: this.currentPos, basicImgInfo: this.basicImgInfo, coordUtils: this.coordUtils, - } + }; } + /** * 同步各种基础类型信息 * 1. imgNode @@ -121,28 +123,29 @@ export default class AnnotationEngine { * 4. style */ - public syncZoom(zoom: number){ - this.toolScheduler.setZoom(zoom) - this.basicInstance.setZoom(zoom) + public syncZoom(zoom: number) { + this.toolScheduler.setZoom(zoom); + this.basicInstance.setZoom(zoom); this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); } public syncCurrentPos(currentPos: ICoordinate) { - this.toolScheduler.setCurrentPos(currentPos) - this.basicInstance.setCurrentPos(currentPos) + this.toolScheduler.setCurrentPos(currentPos); + this.basicInstance.setCurrentPos(currentPos); this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); } public syncBasicImgInfo(basicImgInfo: any) { - this.toolScheduler.setBasicImgInfo(basicImgInfo) - this.basicInstance.setBasicImgInfo(basicImgInfo) + this.toolScheduler.setBasicImgInfo(basicImgInfo); + this.basicInstance.setBasicImgInfo(basicImgInfo); this.coordUtils.setBasicImgInfo(basicImgInfo); } - public setImgAttribute(imgAttribute: IImageAttribute) { + public syncImgAttribute(imgAttribute: IImageAttribute) { this.toolScheduler.setImgAttribute(imgAttribute); - this.basicInstance.setImgAttribute(imgAttribute) + this.basicInstance.setImgAttribute(imgAttribute); } + /** * 设置当前工具类型 * @param toolName @@ -172,6 +175,7 @@ export default class AnnotationEngine { }>, ) { this.toolScheduler.setImgNode(imgNode, basicImgInfo); + this.basicInstance.setImgNode(imgNode, basicImgInfo); this.imgNode = imgNode; } @@ -216,6 +220,7 @@ export default class AnnotationEngine { } }); + this.toolScheduler.setBasicInstance(this.basicInstance); // 实时同步语言 this.setLang(this.i18nLanguage); } @@ -224,7 +229,7 @@ export default class AnnotationEngine { * 初始化依赖渲染层 */ private _initBasicLayer() { - this.basicInstance.renderBasicCanvas() + this.basicInstance.renderBasicCanvas(); } /** @@ -236,9 +241,11 @@ export default class AnnotationEngine { this.dependToolName = dependToolName; this.basicResult = basicResult; - this.toolInstance.setDependName(dependToolName); - this.toolInstance.setBasicResult(basicResult); - this.toolInstance.renderBasicCanvas(); + this.toolScheduler.setDependName(dependToolName); + this.toolScheduler.setBasicResult(basicResult); + this.basicInstance.setBasicResult(basicResult); + this.basicInstance.setDependName(dependToolName); + this.basicInstance.renderBasicCanvas(); } /** diff --git a/packages/lb-annotation/src/core/scheduler.ts b/packages/lb-annotation/src/core/scheduler.ts index 32f17ca93..7a0d54a3d 100644 --- a/packages/lb-annotation/src/core/scheduler.ts +++ b/packages/lb-annotation/src/core/scheduler.ts @@ -7,16 +7,18 @@ import { getConfig, styleDefaultConfig } from '@/constant/defaultConfig'; import { EToolName, THybridToolName } from '@/constant/tool'; import { getCurrentOperation } from '@/utils/tool/EnhanceCommonToolUtils'; +import { CoordinateUtils } from '@/utils/tool/AxisUtils'; +import BasicLayer from '@/core/basicLayer'; +import { IPolygonData } from '@/types/tool/polygon'; import { RectOperation } from './toolOperation/rectOperation'; import PolygonOperation from './toolOperation/polygonOperation'; import { BasicToolOperation } from './toolOperation/basicToolOperation'; import SegmentByRect from './toolOperation/segmentByRect'; -import { ICommonProps } from './index' -import { CoordinateUtils } from '@/utils/tool/AxisUtils'; +import { ICommonProps } from './index'; interface IToolSchedulerOperation {} -interface IToolSchedulerProps extends ICommonProps{ +interface IToolSchedulerProps extends ICommonProps { container: HTMLElement; size: ISize; toolName: THybridToolName; @@ -113,21 +115,21 @@ export class ToolScheduler implements IToolSchedulerOperation { }); } - public setZoom(zoom: number){ + public setZoom(zoom: number) { this.toolOperationList.forEach((toolInstance) => { - toolInstance.setZoom(zoom) + toolInstance.setZoom(zoom); }); } public setCurrentPos(currentPos: ICoordinate) { this.toolOperationList.forEach((toolInstance) => { - toolInstance.setCurrentPos(currentPos) + toolInstance.setCurrentPos(currentPos); }); } public setBasicImgInfo(basicImgInfo: any) { this.toolOperationList.forEach((toolInstance) => { - toolInstance.setBasicImgInfo(basicImgInfo) + toolInstance.setBasicImgInfo(basicImgInfo); }); } @@ -137,6 +139,24 @@ export class ToolScheduler implements IToolSchedulerOperation { }); } + public setBasicInstance(basicInstance: BasicLayer) { + this.toolOperationList.forEach((toolInstance) => { + toolInstance.setBasicInstance(basicInstance); + }); + } + + public setDependName(dependToolName?: EToolName) { + this.toolOperationList.forEach((toolInstance) => { + toolInstance.setDependName(dependToolName); + }); + } + + public setBasicResult(basicResult?: IRect | IPolygonData) { + this.toolOperationList.forEach((toolInstance) => { + toolInstance.setBasicResult(basicResult); + }); + } + public syncAllAttributeListInConfig(attributeList: any[]) { this.toolOperationList.forEach((toolInstance) => { const newConfig = { diff --git a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts index ef672c924..7549da466 100644 --- a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts @@ -3,7 +3,7 @@ import { isNumber } from 'lodash'; import { EOperationMode, EToolName } from '@/constant/tool'; import { IPolygonConfig, IPolygonData } from '@/types/tool/polygon'; import MathUtils from '@/utils/MathUtils'; -import AxisUtils, { CoordinateUtils } from '@/utils/tool/AxisUtils'; +import { CoordinateUtils } from '@/utils/tool/AxisUtils'; import CanvasUtils from '@/utils/tool/CanvasUtils'; import CommonToolUtils from '@/utils/tool/CommonToolUtils'; import LineToolUtils from '@/utils/tool/LineToolUtils'; @@ -19,7 +19,8 @@ import DrawUtils from '../../utils/tool/DrawUtils'; import RenderDomUtils from '../../utils/tool/RenderDomUtils'; import ZoomUtils from '../../utils/tool/ZoomUtils'; import EventListener from './eventListener'; -import { ICommonProps } from '../index' +import { ICommonProps } from '../index'; +import BasicLayer from '../basicLayer'; const LANGUAGE_MAP = { [ELang.Zh]: 'cn', @@ -54,6 +55,7 @@ interface IBasicToolOperationProps extends ICommonProps { ratio: number; }; language?: ELang; + basicInstance?: BasicLayer; } /** @@ -77,8 +79,6 @@ class BasicToolOperation extends EventListener { public canvas!: HTMLCanvasElement; - public basicCanvas!: HTMLCanvasElement; - public imgNode?: HTMLImageElement; public staticImgNode?: HTMLImageElement; @@ -179,6 +179,8 @@ class BasicToolOperation extends EventListener { private hiddenImg: boolean; + public basicInstance?: BasicLayer; + public coordUtils: CoordinateUtils; public zoomInfo = DEFAULT_ZOOM_INFO; @@ -204,8 +206,8 @@ class BasicToolOperation extends EventListener { this.forbidBasicResultRender = props.forbidBasicResultRender ?? false; this.size = props.size; - this.zoom = props.zoom ?? 1 - this.currentPos = props.currentPos ?? { x: 0, y: 0} + this.zoom = props.zoom ?? 1; + this.currentPos = props.currentPos ?? { x: 0, y: 0 }; this.coord = { x: -1, y: -1, @@ -241,7 +243,7 @@ class BasicToolOperation extends EventListener { // 初始化监听事件 this.dblClickListener = new DblClickEventListener(this.container, 200); this.coordUtils = props.coordUtils ?? new CoordinateUtils(this); - this.coordUtils.setBasicImgInfo(this.basicImgInfo) + this.coordUtils.setBasicImgInfo(this.basicImgInfo); this.hiddenImg = props.hiddenImg || false; @@ -258,6 +260,10 @@ class BasicToolOperation extends EventListener { return this._ctx || this.canvas?.getContext('2d'); } + get basicCanvas() { + return this.basicInstance?.basicCanvas; + } + get basicCtx() { return this.basicCanvas?.getContext('2d'); } @@ -309,10 +315,18 @@ class BasicToolOperation extends EventListener { public setZoom(zoom: number) { this.zoom = zoom; this.innerZoom = zoom; + this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); + this.basicInstance?.syncCommonInfo({ + zoom, + }); } public setCurrentPos(currentPos: ICoordinate) { this.currentPos = currentPos; + this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); + this.basicInstance?.syncCommonInfo({ + currentPos, + }); } public setReferenceData(referenceData: IReferenceData) { @@ -321,6 +335,9 @@ class BasicToolOperation extends EventListener { public setImgInfo(size: ISize) { this.imgInfo = size; + this.basicInstance?.syncCommonInfo({ + imgInfo: size, + }); } public setCurrentPosStorage(currentPosStorage: ICoordinate) { @@ -331,6 +348,10 @@ class BasicToolOperation extends EventListener { this.operationMode = operationMode; } + public setBasicInstance(basicInstance: BasicLayer) { + this.basicInstance = basicInstance; + } + public recoverOperationMode() { if (this.operationMode === EOperationMode.MultiMove) { this.setOperationMode(EOperationMode.General); @@ -407,11 +428,6 @@ class BasicToolOperation extends EventListener { // TODO 后续需要将 canvas 抽离出来,迭代器叠加 const pixel = this.pixelRatio; - const basicCanvas = document.createElement('canvas'); - this.updateCanvasBasicStyle(basicCanvas, size, 0); - - this.basicCanvas = basicCanvas; - const canvas = document.createElement('canvas'); this.updateCanvasBasicStyle(canvas, size, 10); @@ -421,9 +437,7 @@ class BasicToolOperation extends EventListener { if (isAppend) { if (this.container.hasChildNodes()) { this.container.insertBefore(canvas, this.container.childNodes[0]); - this.container.insertBefore(basicCanvas, this.container.childNodes[0]); } else { - this.container.appendChild(basicCanvas); this.container.appendChild(canvas); } } @@ -442,11 +456,6 @@ class BasicToolOperation extends EventListener { // container 内可能包含其他元素,故需单独清楚 this.container.removeChild(this.canvas); } - - if (this.basicCanvas && this.container.contains(this.basicCanvas)) { - this.container.removeChild(this.basicCanvas); - } - // 恢复初始状态 this.clearInvalidPage(); this.clearImgDrag(); @@ -536,6 +545,7 @@ class BasicToolOperation extends EventListener { public setBasicImgInfo(basicImgInfo: any) { this.basicImgInfo = basicImgInfo; + this.coordUtils.setBasicImgInfo(basicImgInfo); } public setForbidOperation(forbidOperation: boolean) { @@ -754,13 +764,14 @@ class BasicToolOperation extends EventListener { _imgAttribute?.isOriginalSize, ); if (pos) { - this.setCurrentPos(pos.currentPos); - this.currentPosStorage = this.currentPos; - this.setImgInfo({ + const newImgInfo = { ...imgInfo, width: (imgInfo.width / this.innerZoom) * pos.innerZoom, height: (imgInfo.height / this.innerZoom) * pos.innerZoom, - }); + }; + this.setCurrentPos(pos.currentPos); + this.currentPosStorage = this.currentPos; + this.setImgInfo(newImgInfo); // 需要加载下更改当前的 imgInfo this.setZoom(pos.innerZoom); @@ -813,10 +824,6 @@ class BasicToolOperation extends EventListener { this.ctx?.clearRect(0, 0, this.size.width, this.size.height); } - public clearBasicCanvas() { - this.basicCtx?.clearRect(0, 0, this.size.width, this.size.height); - } - /** 事件绑定 */ public eventBinding() { this.dblClickListener.addEvent(() => {}, this.onLeftDblClick, this.onRightDblClick); @@ -1171,21 +1178,6 @@ class BasicToolOperation extends EventListener { } }; - public drawImg = () => { - if (!this.imgNode || this.hiddenImg === true) return; - - console.log('basic operation draw') - console.log(this.zoom) - - DrawUtils.drawImg(this.basicCanvas, this.imgNode, { - zoom: this.zoom, - currentPos: this.currentPos, - rotate: this.rotate, - imgAttribute: this._imgAttribute, - }); - this.drawStaticImg(); - }; - public drawStaticImg = () => { if (!this.staticImgNode || !this.staticMode) return; @@ -1204,7 +1196,6 @@ class BasicToolOperation extends EventListener { */ public setSize(size: ISize) { this.size = size; - console.log(size) this.updateZoomInfo(); if (this.container.contains(this.canvas)) { this.destroyCanvas(); @@ -1260,7 +1251,7 @@ class BasicToolOperation extends EventListener { this.emit('dependRender'); } - public setDependName(dependToolName: EToolName, dependToolConfig?: IRectConfig | IPolygonConfig) { + public setDependName(dependToolName?: EToolName, dependToolConfig?: IRectConfig | IPolygonConfig) { this.dependToolName = dependToolName; this.coordUtils.setDependInfo(dependToolName, dependToolConfig); } @@ -1351,66 +1342,7 @@ class BasicToolOperation extends EventListener { } public renderBasicCanvas() { - if (!this.basicCanvas) { - return; - } - - this.clearBasicCanvas(); - this.drawImg(); - - const thickness = 3; - - if (this.forbidBasicResultRender) { - return; - } - - if (this.basicResult && this.dependToolName) { - switch (this.dependToolName) { - case EToolName.Rect: { - DrawUtils.drawRect( - this.basicCanvas, - AxisUtils.changeRectByZoom(this.basicResult, this.zoom, this.currentPos), - { - color: 'rgba(204,204,204,1.00)', - thickness, - }, - ); - break; - } - - case EToolName.Polygon: { - DrawUtils.drawPolygonWithFillAndLine( - this.basicCanvas, - AxisUtils.changePointListByZoom(this.basicResult.pointList, this.zoom, this.currentPos), - { - fillColor: 'transparent', - strokeColor: 'rgba(204,204,204,1.00)', - isClose: true, - thickness, - }, - ); - - break; - } - - case EToolName.Line: { - DrawUtils.drawLineWithPointList( - this.basicCanvas, - AxisUtils.changePointListByZoom(this.basicResult.pointList, this.zoom, this.currentPos), - { - color: 'rgba(204,204,204,1.00)', - thickness, - }, - ); - - break; - } - - default: { - // - } - } - } + this.basicInstance?.renderBasicCanvas(); } public render() { diff --git a/packages/lb-annotation/src/core/toolOperation/pointCloud2dOperation.ts b/packages/lb-annotation/src/core/toolOperation/pointCloud2dOperation.ts index a9718d932..dd04dd5be 100644 --- a/packages/lb-annotation/src/core/toolOperation/pointCloud2dOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/pointCloud2dOperation.ts @@ -409,7 +409,9 @@ class PointCloud2dOperation extends PolygonOperation { this.setImgInfo(size); // Update canvas size. - this.updateCanvasBasicStyle(this.basicCanvas, size, 0); + if (this.basicCanvas) { + this.updateCanvasBasicStyle(this.basicCanvas, size, 0); + } this.updateCanvasBasicStyle(this.canvas, size, 10); this.ctx?.scale(pixel, pixel); this.basicCtx?.scale(pixel, pixel); diff --git a/packages/lb-annotation/src/utils/tool/AxisUtils.ts b/packages/lb-annotation/src/utils/tool/AxisUtils.ts index f66de3d75..057b33ef0 100644 --- a/packages/lb-annotation/src/utils/tool/AxisUtils.ts +++ b/packages/lb-annotation/src/utils/tool/AxisUtils.ts @@ -530,7 +530,7 @@ export class CoordinateUtils { } } - public setDependInfo(dependToolName: EToolName | '', dependToolConfig?: IRectConfig | IPolygonConfig) { + public setDependInfo(dependToolName?: EToolName | '', dependToolConfig?: IRectConfig | IPolygonConfig) { this.dependToolName = dependToolName ?? ''; this.dependToolConfig = dependToolName ? dependToolConfig : undefined; } diff --git a/packages/lb-components/src/utils/Pipeline.ts b/packages/lb-components/src/utils/Pipeline.ts index eaa6e5253..75a63cec3 100644 --- a/packages/lb-components/src/utils/Pipeline.ts +++ b/packages/lb-components/src/utils/Pipeline.ts @@ -1,7 +1,6 @@ -import { CommonToolUtils } from '@labelbee/lb-annotation'; +import { CommonToolUtils, EToolName } from '@labelbee/lb-annotation'; import { IStepInfo } from '@/types/step'; import { jsonParser } from '@/utils'; -import { EToolName } from '@labelbee/lb-annotation'; /** * 获取当前步骤下的依赖情况,用于依赖框体的展示 @@ -18,13 +17,16 @@ import { EToolName } from '@labelbee/lb-annotation'; */ export function getBasicResult( currentStep: number, - dataSourceStep: number, + dataSourceStepParam: number, stepList: IStepInfo[], - result: any, - filterNeeded: boolean = true, - preResult: string = '{}', - forbidFilter: boolean = false, + resultParam: any, + filterNeeded = true, + preResult = '{}', + forbidFilter = false, ) { + let result = resultParam; + let dataSourceStep = dataSourceStepParam; + const currentStepInfo = CommonToolUtils.getCurrentStepInfo(currentStep, stepList); let dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(dataSourceStep, stepList); @@ -259,10 +261,11 @@ export function useConfigFilterData( currentStep: number, stepList: IStepInfo[], basicData: any[], - forbidFilter: boolean = false, + forbidFilter = false, ) { const currentStepInfo = CommonToolUtils.getStepInfo(currentStep, stepList); const dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(currentStepInfo.dataSourceStep, stepList); + let res = basicData; if (currentStepInfo.config) { const config = jsonParser(currentStepInfo.config); if (config.filterData) { @@ -270,7 +273,7 @@ export function useConfigFilterData( const filterDataObject = transformFilterDataToObject(filterData); - basicData = basicData.filter((v) => { + res = basicData.filter((v) => { if (forbidFilter === true) { return true; } @@ -302,7 +305,7 @@ export function useConfigFilterData( }); } } - return basicData; + return res; } /** @@ -320,7 +323,7 @@ export function getSourceData( stepList: IStepInfo[], result: any, sourceIDList: any[], - preResult: string = '{}', + preResult = '{}', ): any { const currentStepInfo = CommonToolUtils.getCurrentStepInfo(currentStep, stepList); diff --git a/packages/lb-components/src/views/MainView/annotationOperation/index.tsx b/packages/lb-components/src/views/MainView/annotationOperation/index.tsx index 2a36a9757..1a8e1ca7b 100644 --- a/packages/lb-components/src/views/MainView/annotationOperation/index.tsx +++ b/packages/lb-components/src/views/MainView/annotationOperation/index.tsx @@ -109,8 +109,8 @@ const AnnotationOperation: React.FC = (props: IProps) => { }, [toolInstance]); useEffect(() => { - if (annotationEngine?.setImgAttribute) { - annotationEngine.setImgAttribute(imgAttribute); + if (annotationEngine?.syncImgAttribute) { + annotationEngine.syncImgAttribute(imgAttribute); } else { // Old version. toolInstance?.setImgAttribute?.(imgAttribute); From 2c9d8e1fa6bfc7dd6d4c4864611c6314dbaac362 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Thu, 1 Feb 2024 15:00:37 +0800 Subject: [PATCH 36/43] chore: Remove Chinese comments --- packages/lb-annotation/src/core/basicLayer.ts | 18 +++--- .../core/toolOperation/basicToolOperation.ts | 20 +++--- packages/lb-components/src/utils/Pipeline.ts | 62 +++++++++---------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/lb-annotation/src/core/basicLayer.ts b/packages/lb-annotation/src/core/basicLayer.ts index e2d77ada9..2217200bf 100644 --- a/packages/lb-annotation/src/core/basicLayer.ts +++ b/packages/lb-annotation/src/core/basicLayer.ts @@ -16,14 +16,14 @@ interface IBasicLayerProps extends ICommonProps { container: HTMLElement; size: ISize; toolName: THybridToolName; - imgNode?: HTMLImageElement; // 展示图片的内容 - config?: string; // 任务配置 + imgNode?: HTMLImageElement; // dom node of image + config?: string; // config of annotation task style?: any; forbidBasicResultRender?: boolean; } export default class BasicLayer extends EventListener { - public container: HTMLElement; // 当前结构绑定 container + public container: HTMLElement; // external dom node public size: ISize; @@ -35,15 +35,15 @@ export default class BasicLayer extends EventListener { public dependToolName?: EToolName; - public forbidBasicResultRender: boolean; // 禁止渲染基础依赖图形 + public forbidBasicResultRender: boolean; // disable rendering of basic result public zoom: number; - public currentPos: ICoordinate; // 存储实时偏移的位置 + public currentPos: ICoordinate; // store real-time offset position public coordUtils?: CoordinateUtils; - public basicImgInfo: any; // 用于存储当前图片的信息 + public basicImgInfo: any; // store info of current image public imgInfo?: ISize; @@ -51,7 +51,7 @@ export default class BasicLayer extends EventListener { private _imgAttribute?: any; - private currentPosStorage?: ICoordinate; // 存储当前点击的平移位置 + private currentPosStorage?: ICoordinate; // store the current clicked translation position constructor(props: IBasicLayerProps) { super(); @@ -119,7 +119,7 @@ export default class BasicLayer extends EventListener { } /** - * 同步currentPos, zoom等common信息 + * Synchronize common information such as currentPos, zoom, etc */ public syncCommonInfo(info: ICommonProps) { this.setZoom(info?.zoom ?? this.zoom); @@ -129,7 +129,7 @@ export default class BasicLayer extends EventListener { } /** - * 更改当前 canvas 整体的大小,需要重新初始化 + * Resize the current canvas and reinitialize * @param size */ public setSize(size: ISize) { diff --git a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts index 7549da466..3ea5e242e 100644 --- a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts @@ -30,24 +30,24 @@ const LANGUAGE_MAP = { interface IBasicToolOperationProps extends ICommonProps { container: HTMLElement; size: ISize; - imgNode?: HTMLImageElement; // 展示图片的内容 - staticMode?: boolean; // 是否为静态模式 - style?: any; // 后期一定要补上!! + imgNode?: HTMLImageElement; // dom for image + staticMode?: boolean; // whether it is static mode + style?: any; // need to be done!! rotate?: number; - imgAttribute?: any; // 占个坑,用于全局的一些配置,是否展示原图比例 + imgAttribute?: any; // Used for some global configurations, such as whether to display the original image ratio forbidOperation?: boolean; - config?: string; // 任务配置 + config?: string; // config of annotation task defaultAttribute?: string; forbidCursorLine?: boolean; - showDefaultCursor?: boolean; // 默认会展示为 none + showDefaultCursor?: boolean; // default value: none forbidBasicResultRender?: boolean; - isAppend?: boolean; // 用于 canvas 层次的关闭 - hiddenImg?: boolean; // 隐藏图片渲染 + isAppend?: boolean; // Used for closing the canvas hierarchy + hiddenImg?: boolean; // Whether to hide image zoomInfo?: { min: number; @@ -59,7 +59,7 @@ interface IBasicToolOperationProps extends ICommonProps { } /** - * 参考显示数据 + * Reference display data */ interface IReferenceData { toolName: EToolName.Polygon | EToolName.Line | EToolName.LineMarker; @@ -67,7 +67,7 @@ interface IReferenceData { config: any; } -// zoom 的限制 +// limit of zoom const DEFAULT_ZOOM_INFO = { min: 0.2, max: 1000, diff --git a/packages/lb-components/src/utils/Pipeline.ts b/packages/lb-components/src/utils/Pipeline.ts index 75a63cec3..8f79b3069 100644 --- a/packages/lb-components/src/utils/Pipeline.ts +++ b/packages/lb-components/src/utils/Pipeline.ts @@ -3,14 +3,14 @@ import { IStepInfo } from '@/types/step'; import { jsonParser } from '@/utils'; /** - * 获取当前步骤下的依赖情况,用于依赖框体的展示 + * Retrieve the dependencies for the current step to be used in displaying the dependency framework * * * @export - * @param {number} currentStep 正常标注的步骤!(如果是质检则需要转换成正常标注步骤) - * @param {number} dataSourceStep 正常标注依赖的步骤 - * @param {IStepInfo[]} stepList 步骤信息 - * @param {*} result 当前图片的所有结果 + * @param {number} currentStep current annotation step(needs to be converted into normal annotation steps if in quality inspection case) + * @param {number} dataSourceStep step of datasource + * @param {IStepInfo[]} stepList + * @param {*} result all results for the current image * @param {boolean} [filterNeeded=true] * @param {boolean} [forbidFilter=false] * @returns @@ -54,14 +54,14 @@ export function getBasicResult( } try { const currentStepInfoConfig = jsonParser(currentStepInfo.config); - // 如果为非框型工具就需要递归下去 + // Recursion is needed if it is a non-box tool const filterData = filterDataAdaptor(currentStepInfoConfig.filterData, dataSourceStepInfo); if (!filterData && filterNeeded) { return []; } if (dataSourceStepInfo.tool === EToolName.Tag) { - // 过滤标签工具的数据 + // Filtering the data from the tagging tool if ( dataSourceStepInfo.dataSourceStep === 0 && (!dataSourceStepInfo?.preDataSourceStep || dataSourceStepInfo?.preDataSourceStep === 0) @@ -69,7 +69,7 @@ export function getBasicResult( return []; } - // 过滤出当前符合标准的标签数据 + // Filter out the current tags data that meets the criteria const newData = filterTagResult(sourceData, filterData, forbidFilter); const sourceIDList = newData.map((v) => ({ id: v.id, sourceID: v.sourceID })); return getSourceData( @@ -91,14 +91,14 @@ export function getBasicResult( ) { return useConfigFilterData(currentStep, stepList, sourceData, forbidFilter); } else if (dataSourceStepInfo.tool === EToolName.Filter) { - // 过滤筛选工具的数据 + // Filtering the data from the selection tool if ( dataSourceStepInfo.dataSourceStep === 0 && (!dataSourceStepInfo?.preDataSourceStep || dataSourceStepInfo?.preDataSourceStep === 0) ) { return []; } - // 过滤出当前符合标准的标签数据 + // Filter out the current tags data that meets the criteria const newData = sourceData.filter( (v: any) => filterData.indexOf(v.filterLabel) > -1 || forbidFilter, ); @@ -120,11 +120,11 @@ export function getBasicResult( export const DEFAULT_LINK = '@@'; const DEFAULT_TOOL_ATTRIBUTE = ['valid', 'invalid']; /** - * 将旧版的 filterData 转换为新版的 filterData + * turn filterData into new filterData * * @export * @param {*} oldFilterData - * @param {IStepInfo} dataSourceStepInfo 注意这个是依赖项的 stepInfo + * @param {IStepInfo} dataSourceStepInfo attention: this is stepInfo of data source * @returns */ export function filterDataAdaptor( @@ -134,14 +134,14 @@ export function filterDataAdaptor( const config = jsonParser(dataSourceStepInfo?.config); if (oldFilterData?.constructor === Object) { - // 说明为旧数据 + // means old data const keyList = Object.keys(oldFilterData).reduce((acc: any[], cur: string) => { if (Array.isArray(oldFilterData[cur])) { - // 在旧格式中仅为 标签工具 + // In the old format, it only consists of the tagging tool return [...acc, ...oldFilterData[cur].map((v: any) => `${cur}${DEFAULT_LINK}${v}`)]; } - // 对之前旧的图形工具的适配 + // Adaptation for the previous old image tools if ( DEFAULT_TOOL_ATTRIBUTE.includes(cur) && oldFilterData[cur] === true && @@ -149,7 +149,7 @@ export function filterDataAdaptor( ) { return [ ...acc, - `${cur}${DEFAULT_LINK}`, // 无属性 + `${cur}${DEFAULT_LINK}`, // no attribute ...config.attributeList.reduce((a: any, v: any) => { return [...a, `${cur}${DEFAULT_LINK}${v?.value}`]; }, []), @@ -166,7 +166,7 @@ export function filterDataAdaptor( } /** - * 判断是否是按属性进行标注 + * Determine whether the annotation is based on attributes * * @export * @param {*} config @@ -179,7 +179,7 @@ export function judgeIsAttribute(config: any) { } /** - * 过滤标签结果 + * Filtering tag results * @param sourceData * @param filterData * @param forbidFilter @@ -197,7 +197,7 @@ export function filterTagResult( } for (const i of Object.keys(data.result)) { - // 注意,标签工具的结果是 是一个对象 + // Note that the result of the tagging tool is an object if (tagInputList.hasOwnProperty(i)) { const dataList = data.result[i].split(';'); @@ -213,7 +213,7 @@ export function filterTagResult( } /** - * 将 filterData 转换为 Object 形式,便于判断 + * turn filterData into Object format,making it easy to determine * * @export * @param {FilterDataState} filterData @@ -225,7 +225,7 @@ export function transformFilterDataToObject(filterData: string[]): any { const [key, value] = cur.split(DEFAULT_LINK); if (typeof value === 'undefined') { - // 表示为空,例如: ['valid', 'invalid'] + // means empty, like: ['valid', 'invalid'] return { ...acc, [key]: [], @@ -247,9 +247,9 @@ export function transformFilterDataToObject(filterData: string[]): any { } /** - * 根据 config 过滤当前数据 + * Filtering current data by config * - * 注意: 该函数目前仅适用于图形类的步骤进行过滤 + * Note: This function is currently only applicable for filtering steps related to graphical elements. * * @export * @param {number} currentStep @@ -279,20 +279,20 @@ export function useConfigFilterData( } if (filterData.some((v) => v === 'valid') || filterData.some((v) => v === 'invalid')) { - // 说明为图形统一管理 + // This means centralized management of graphical elements if (filterData.some((v) => v === 'valid') && v?.valid === true) { - // 说明是原始的图形的过滤 + // This indicates filtering based on original graphical elements return true; } if (filterData.some((v) => v === 'invalid') && v?.valid === false) { - // 说明是原始的图形的过滤 + // This indicates filtering based on original graphical elements return true; } return false; } - // 含有属性的数据过滤 + // Filtering data containing attributes if (judgeIsAttribute(jsonParser(dataSourceStepInfo.config))) { if (v?.valid === true) { return filterDataObject?.valid?.includes(v?.attribute ?? ''); @@ -309,7 +309,7 @@ export function useConfigFilterData( } /** - * 过滤出指定步骤中的指定依赖框出来 + * Filter out specified dependency boxes in a specific step * @param currentStep * @param dataSourceStep * @param stepList @@ -328,7 +328,7 @@ export function getSourceData( const currentStepInfo = CommonToolUtils.getCurrentStepInfo(currentStep, stepList); if (currentStepInfo?.preDataSourceStep > 0) { - // 说明该步骤依赖的是预标注数据 + // This indicates that the step relies on pre-annotated data const stepName = `step_${currentStepInfo?.preDataSourceStep}`; const result = jsonParser(preResult)[stepName]?.result; @@ -366,13 +366,13 @@ export function getSourceData( }); if (dataSourceStepInfo.tool === EToolName.Tag || dataSourceStepInfo.tool === EToolName.Filter) { - // 如果 dataSource 工具还是为标签的话,就继续递归,找到为止 + // If the dataSource tool is still tag tool, continue the recursion return getSourceData( dataSourceStepInfo.step, dataSourceStepInfo.dataSourceStep, stepList, result, - sourceIDList, // 注意: 其实比较怀疑这边是否能正确使用,表示怀疑 - 后续需要关注 todo + sourceIDList, // Note: There is some doubt about whether this part can be used correctly. It indicates skepticism - further attention is needed todo preResult, ); } else if ( From cdccc42660b5eadf5140667902843a236913e085 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Thu, 1 Feb 2024 17:21:47 +0800 Subject: [PATCH 37/43] fix: Resolve comments --- packages/lb-annotation/src/core/basicLayer.ts | 5 ++-- packages/lb-annotation/src/core/index.ts | 23 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/lb-annotation/src/core/basicLayer.ts b/packages/lb-annotation/src/core/basicLayer.ts index 2217200bf..e463cea84 100644 --- a/packages/lb-annotation/src/core/basicLayer.ts +++ b/packages/lb-annotation/src/core/basicLayer.ts @@ -1,6 +1,7 @@ /** - * @author wanghaiqing wanghaiqing@sensetime.com - * @date 1/30/24 + * @file render layer of basic results, should be initialized in annotationEngine + * @Author wanghaiqing wanghaiqing@sensetime.com + * @Date 2024-01-31 */ import { EToolName, THybridToolName } from '@/constant/tool'; diff --git a/packages/lb-annotation/src/core/index.ts b/packages/lb-annotation/src/core/index.ts index 131c1dac2..22b405372 100644 --- a/packages/lb-annotation/src/core/index.ts +++ b/packages/lb-annotation/src/core/index.ts @@ -123,19 +123,8 @@ export default class AnnotationEngine { * 4. style */ - public syncZoom(zoom: number) { - this.toolScheduler.setZoom(zoom); - this.basicInstance.setZoom(zoom); - this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); - } - - public syncCurrentPos(currentPos: ICoordinate) { - this.toolScheduler.setCurrentPos(currentPos); - this.basicInstance.setCurrentPos(currentPos); - this.coordUtils.setZoomAndCurrentPos(this.zoom, this.currentPos); - } - public syncBasicImgInfo(basicImgInfo: any) { + this.basicImgInfo = basicImgInfo; this.toolScheduler.setBasicImgInfo(basicImgInfo); this.basicInstance.setBasicImgInfo(basicImgInfo); this.coordUtils.setBasicImgInfo(basicImgInfo); @@ -146,6 +135,16 @@ export default class AnnotationEngine { this.basicInstance.setImgAttribute(imgAttribute); } + public syncZoomAndCurrentPos(zoom: number, currentPos: ICoordinate) { + this.zoom = zoom + this.currentPos = currentPos + this.toolScheduler.setZoom(zoom); + this.basicInstance.setZoom(zoom); + this.toolScheduler.setCurrentPos(currentPos); + this.basicInstance.setCurrentPos(currentPos); + this.coordUtils.setZoomAndCurrentPos(zoom, currentPos); + } + /** * 设置当前工具类型 * @param toolName From 8ceafebdbb0ede9a766f6f6b484b2b0297ff5aa7 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Thu, 22 Feb 2024 16:35:31 +0800 Subject: [PATCH 38/43] feat: Support the render of reference data --- packages/lb-annotation/src/core/basicLayer.ts | 130 +++++++++++++- packages/lb-annotation/src/core/index.ts | 16 +- packages/lb-annotation/src/core/scheduler.ts | 3 +- .../src/utils/tool/StyleUtils.ts | 7 + .../src/store/annotation/reducer.ts | 11 +- packages/lb-components/src/utils/Pipeline.ts | 163 ++++++++++++++++++ packages/lb-demo/src/mock/taskConfig.js | 8 +- 7 files changed, 324 insertions(+), 14 deletions(-) diff --git a/packages/lb-annotation/src/core/basicLayer.ts b/packages/lb-annotation/src/core/basicLayer.ts index e463cea84..908cba498 100644 --- a/packages/lb-annotation/src/core/basicLayer.ts +++ b/packages/lb-annotation/src/core/basicLayer.ts @@ -4,14 +4,28 @@ * @Date 2024-01-31 */ -import { EToolName, THybridToolName } from '@/constant/tool'; +import { EToolName, THybridToolName, ToolName } from '@/constant/tool'; import CanvasUtils from '@/utils/tool/CanvasUtils'; import DrawUtils from '@/utils/tool/DrawUtils'; import AxisUtils, { CoordinateUtils } from '@/utils/tool/AxisUtils'; import { HybridToolUtils } from '@/core/scheduler'; import EventListener from '@/core/toolOperation/eventListener'; import { IPolygonConfig } from '@/types/tool/polygon'; +import { ILineConfig } from '@/types/tool/lineTool'; +import { IPointToolConfig } from '@/types/tool/pointTool'; import { ICommonProps } from './index'; +import { toolStyleConverter } from '@labelbee/lb-utils'; +import CommonToolUtils from '@/utils/tool/CommonToolUtils'; +import { styleString } from '@/constant/style'; +import StyleUtils from '@/utils/tool/StyleUtils'; + +type IReferenceConfig = IRectConfig | IPolygonConfig | ILineConfig | IPointToolConfig; + +export interface IReferenceInfoProps { + referenceConfig: IReferenceConfig; + referenceResult: any; + referenceToolName: ToolName; +} interface IBasicLayerProps extends ICommonProps { container: HTMLElement; @@ -26,6 +40,10 @@ interface IBasicLayerProps extends ICommonProps { export default class BasicLayer extends EventListener { public container: HTMLElement; // external dom node + public config: any; + + public style: any; + public size: ISize; public imgNode?: HTMLImageElement; @@ -34,6 +52,8 @@ export default class BasicLayer extends EventListener { public basicResult?: any; // data of depended tool + public referenceInfo?: IReferenceInfoProps; + public dependToolName?: EToolName; public forbidBasicResultRender: boolean; // disable rendering of basic result @@ -57,6 +77,8 @@ export default class BasicLayer extends EventListener { constructor(props: IBasicLayerProps) { super(); this.container = props.container; + this.config = CommonToolUtils.jsonParser(props.config); + this.style = props.style ?? CommonToolUtils.jsonParser(styleString); this.size = props.size; this.zoom = props.zoom ?? 1; @@ -119,6 +141,9 @@ export default class BasicLayer extends EventListener { this.coordUtils?.setBasicResult(basicResult); } + public setReferenceInfo(referenceInfo: IReferenceInfoProps) { + this.referenceInfo = referenceInfo; + } /** * Synchronize common information such as currentPos, zoom, etc */ @@ -142,6 +167,11 @@ export default class BasicLayer extends EventListener { } } + /** Get the current property color */ + public getColor(attribute = '', config = this.config) { + return toolStyleConverter.getColorByConfig({ attribute, config, style: this.style }); + } + /** * Notice. It needs to set the default imgInfo. Because it will needs to create info when it doesn't have * @param imgNode @@ -215,13 +245,9 @@ export default class BasicLayer extends EventListener { this.clearBasicCanvas(); this.drawImg(); - const thickness = 3; - - if (this.forbidBasicResultRender) { - return; - } + const thickness = 2; - if (this.basicResult && this.dependToolName) { + if (this.basicResult && this.dependToolName && !this.forbidBasicResultRender) { switch (this.dependToolName) { case EToolName.Rect: { DrawUtils.drawRect( @@ -268,5 +294,95 @@ export default class BasicLayer extends EventListener { } } } + + this.renderReference() + } + + public renderReference(){ + const thickness = 2; + + if (this.referenceInfo) { + const { referenceResult, referenceConfig, referenceToolName } = this.referenceInfo + switch (referenceToolName) { + case EToolName.RectTrack: + case EToolName.Rect: { + let rectList = referenceResult; + rectList.map((rect) => { + const toolColor = this.getColor(rect.attribute); + const toolData = StyleUtils.getStrokeAndFill(toolColor, rect.valid); + DrawUtils.drawRect( + this.basicCanvas, + AxisUtils.changeRectByZoom(rect, this.zoom, this.currentPos), + { + color: toolData.stroke, + thickness, + lineDash: [24], + }, + ); + }) + break; + } + + case EToolName.Point: { + let pointList = referenceResult; + pointList.map((point) => { + const toolColor = this.getColor(point.attribute); + const toolData = StyleUtils.getStrokeAndFill(toolColor, point.valid); + DrawUtils.drawCircle( + this.basicCanvas, + AxisUtils.changePointByZoom(point, this.zoom, this.currentPos), 5, { + color: toolData.stroke, + fill: toolData.fill, + }); + }) + break; + } + + case EToolName.Polygon: { + let polygonList = referenceResult; + polygonList.map((polygon) => { + const toolColor = this.getColor(polygon.attribute); + const toolData = StyleUtils.getStrokeAndFill(toolColor, polygon.valid); + + DrawUtils.drawPolygonWithFillAndLine( + this.basicCanvas, + AxisUtils.changePointListByZoom(polygon.pointList, this.zoom, this.currentPos), + { + fillColor: toolData.fill, + strokeColor: toolData.stroke, + isClose: true, + thickness, + }, + ); + }) + + break; + } + + case EToolName.Line: { + let lineList = referenceResult; + lineList.map((line) => { + const toolColor = this.getColor(line.attribute); + const toolData = StyleUtils.getStrokeAndFill(toolColor, line.valid) + + DrawUtils.drawLineWithPointList( + this.basicCanvas, + AxisUtils.changePointListByZoom(line.pointList, this.zoom, this.currentPos), + { + color: toolData.stroke, + thickness, + }, + ); + }) + + break; + } + + default: { + // + } + } + } + } } diff --git a/packages/lb-annotation/src/core/index.ts b/packages/lb-annotation/src/core/index.ts index 22b405372..f243d2fc6 100644 --- a/packages/lb-annotation/src/core/index.ts +++ b/packages/lb-annotation/src/core/index.ts @@ -6,7 +6,7 @@ import { ELang } from '@/constant/annotation'; import { getConfig, styleDefaultConfig } from '@/constant/defaultConfig'; import { EToolName, THybridToolName } from '@/constant/tool'; import { IPolygonData } from '@/types/tool/polygon'; -import BasicLayer from '@/core/basicLayer'; +import BasicLayer, { IReferenceInfoProps } from '@/core/basicLayer'; import { CoordinateUtils } from '@/utils/tool/AxisUtils'; import { HybridToolUtils, ToolScheduler } from './scheduler'; @@ -136,8 +136,8 @@ export default class AnnotationEngine { } public syncZoomAndCurrentPos(zoom: number, currentPos: ICoordinate) { - this.zoom = zoom - this.currentPos = currentPos + this.zoom = zoom; + this.currentPos = currentPos; this.toolScheduler.setZoom(zoom); this.basicInstance.setZoom(zoom); this.toolScheduler.setCurrentPos(currentPos); @@ -247,6 +247,16 @@ export default class AnnotationEngine { this.basicInstance.renderBasicCanvas(); } + /** + * 设置参考显示的信息 + * @param referenceInfo + */ + + public setReferenceInfo(referenceInfo: IReferenceInfoProps) { + this.basicInstance.setReferenceInfo(referenceInfo); + this.basicInstance.renderBasicCanvas(); + } + /** * 清空当前依赖 */ diff --git a/packages/lb-annotation/src/core/scheduler.ts b/packages/lb-annotation/src/core/scheduler.ts index 7a0d54a3d..ebb7882fa 100644 --- a/packages/lb-annotation/src/core/scheduler.ts +++ b/packages/lb-annotation/src/core/scheduler.ts @@ -422,7 +422,8 @@ export class ToolScheduler implements IToolSchedulerOperation { public destroyAllLayer() { this.toolOperationList.forEach((toolInstance) => { - toolInstance.destroyCanvas(); + // use destroy instead of destroyCanvas to delete textAttribute layer + toolInstance.destroy(); this.init(); }); } diff --git a/packages/lb-annotation/src/utils/tool/StyleUtils.ts b/packages/lb-annotation/src/utils/tool/StyleUtils.ts index a19767364..bf72d7099 100644 --- a/packages/lb-annotation/src/utils/tool/StyleUtils.ts +++ b/packages/lb-annotation/src/utils/tool/StyleUtils.ts @@ -58,4 +58,11 @@ export default class StyleUtils { }); return el; } + + public static colorSplit(color: string, opacity: number) { + return color + .split(' ') + .join('') + .replace(/,[0-9]+([.]{1}[0-9]+){0,1}\)/, `,${opacity.toFixed(2)})`); + } } diff --git a/packages/lb-components/src/store/annotation/reducer.ts b/packages/lb-components/src/store/annotation/reducer.ts index 31e07811c..4fcb29460 100644 --- a/packages/lb-components/src/store/annotation/reducer.ts +++ b/packages/lb-components/src/store/annotation/reducer.ts @@ -7,7 +7,7 @@ import { IStepInfo } from '@/types/step'; import { jsonParser } from '@/utils'; import AnnotationDataUtils from '@/utils/AnnotationDataUtils'; import { ConfigUtils } from '@/utils/ConfigUtils'; -import { getBasicResult } from '@/utils/Pipeline' +import { getBasicResult, getReferenceConfig, getReferenceInfo} from '@/utils/Pipeline' import { composeResult, composeResultWithBasicImgInfo } from '@/utils/data'; import StepUtils from '@/utils/StepUtils'; import ToolUtils from '@/utils/ToolUtils'; @@ -492,6 +492,11 @@ export const annotationReducer = ( preResult, ); + const referenceInfo = getReferenceInfo(step, stepList, { + result: imgList[nextIndex].result, + preResult, + }) + const result = AnnotationDataUtils.getInitialResultList( stepResult?.result, toolInstance, @@ -502,6 +507,10 @@ export const annotationReducer = ( annotationEngine?.launchOperation(); + if (referenceInfo && referenceInfo.referenceToolName) { + annotationEngine.setReferenceInfo(referenceInfo) + } + if (hasDataSourceStep) { if (stepBasicResultList?.length > 0) { annotationEngine?.setBasicInfo(dependStepConfig.tool, stepBasicResultList[basicIndex]); diff --git a/packages/lb-components/src/utils/Pipeline.ts b/packages/lb-components/src/utils/Pipeline.ts index 8f79b3069..d87941334 100644 --- a/packages/lb-components/src/utils/Pipeline.ts +++ b/packages/lb-components/src/utils/Pipeline.ts @@ -383,3 +383,166 @@ export function getSourceData( return resultList; } + + +/** + * 查找当前依赖步骤的配置信息 + * @param dataSourceStep + * @param stepList + */ +export function getBasicConfig(dataSourceStep: number, stepList: IStepInfo[]) { + const dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(dataSourceStep, stepList); + if (!dataSourceStepInfo) { + return {}; + } + if ( + [ + EToolName.Rect, + EToolName.RectTrack, + EToolName.Polygon, + EToolName.Point, + EToolName.Line, + ].includes(dataSourceStepInfo.tool) + ) { + return jsonParser(dataSourceStepInfo?.config); + } + + return getBasicConfig(dataSourceStepInfo.dataSourceStep, stepList); +} + +/** + * 获取当前依赖下的工具名 + * @param dataSourceStep + * @param stepList + * @param preData + */ +export function getBasicToolName( + dataSourceStep: number, + stepList: IStepInfo[], + preData?: { preDataSourceStep: number; preResult?: string }, +): any { + if (preData && preData.preDataSourceStep > 0 && preData.preResult) { + const preResult = jsonParser(preData.preResult); + const stepResult = preResult[`step_${preData.preDataSourceStep}`]; + return stepResult?.toolName; + } + + const dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(dataSourceStep, stepList); + if (!dataSourceStepInfo) { + return EToolName.Empty; + } + if ( + [ + EToolName.Rect, + EToolName.RectTrack, + EToolName.Polygon, + EToolName.Point, + EToolName.Line, + ].includes(dataSourceStepInfo.tool) + ) { + return dataSourceStepInfo.tool; + } + + if (preData && preData.preResult && dataSourceStepInfo.preDataSourceStep > 0) { + const preResult = jsonParser(preData.preResult); + // 依赖预标注 + return preResult[`step_${dataSourceStepInfo.preDataSourceStep}`]?.toolName ?? EToolName.Empty; + } + + let newPreData; + if (preData) { + newPreData = { + ...preData, + preDataSourceStep: dataSourceStepInfo.preDataSourceStep, + }; + } + + return getBasicToolName(dataSourceStepInfo.dataSourceStep, stepList, newPreData); +} + +/** + * 获取参考显示的配置、结果、工具名 + * @param stepList + * @param step + * @param result + * @param preResult + */ +export const getReferenceConfig = ( + stepList: IStepInfo[], + step: number, + result: any, + preResult: any, +) => { + if (stepList.length === 0) { + return {}; + } + + let referenceResult; + let referenceConfig; + let referenceToolName; + + const stepInfo = CommonToolUtils.getCurrentStepInfo(step, stepList); + const stepConfig = jsonParser(stepInfo.config); + + const { referenceStep, referenceFilterData, preReferenceStep } = stepConfig; + + const hasReferenceStep = referenceStep > 0 || preReferenceStep > 0; + + if (hasReferenceStep && referenceFilterData) { + const referenceStepList = stepList.map((v) => { + if (v.step === stepInfo.step) { + return { + ...v, + preDataSourceStep: preReferenceStep ?? 0, + dataSourceStep: referenceStep, + config: JSON.stringify({ + ...jsonParser(v?.config ?? '{}'), + filterData: referenceFilterData, + }), + }; + } + return v; + }); + + referenceResult = getBasicResult( + stepInfo.step, + referenceStep, + referenceStepList, + jsonParser(result), + true, + preResult, + ); + referenceConfig = getBasicConfig(referenceStep, stepList); + referenceToolName = getBasicToolName(referenceStep, stepList, { + preDataSourceStep: preReferenceStep, + preResult, + }); + } + + return { referenceResult, referenceConfig, referenceToolName }; +}; + +export const getReferenceInfo = ( + step: number, + stepList: IStepInfo[], + fileData: { preResult: any; result: any } = { preResult: '', result: '' }, +) => { + if (!stepList?.length || !step) { + return; + } + + const { result, preResult } = fileData; + + const { referenceResult, referenceConfig, referenceToolName } = getReferenceConfig( + stepList, + step, + result, + preResult, + ); + + return { + referenceResult, + referenceConfig, + referenceToolName, + }; +}; diff --git a/packages/lb-demo/src/mock/taskConfig.js b/packages/lb-demo/src/mock/taskConfig.js index 94ac50d11..5890b4e0c 100644 --- a/packages/lb-demo/src/mock/taskConfig.js +++ b/packages/lb-demo/src/mock/taskConfig.js @@ -510,9 +510,13 @@ const getStepConfig = (tool, step, sourceStep) => { return { step: step ?? 1, - dataSourceStep: sourceStep || 0, + dataSourceStep: 0, tool: toolList, - config: JSON.stringify(getConfig(toolName)), + config: JSON.stringify({ + ...getConfig(toolName), + referenceStep: sourceStep || 0, + referenceFilterData: ['valid', 'invalid'], + }), }; }; From a0154c6299275bdd1fe940b768b1be7339bbddd9 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Thu, 22 Feb 2024 17:12:02 +0800 Subject: [PATCH 39/43] fix: Lint problems --- packages/lb-annotation/src/core/basicLayer.ts | 67 +++++++++---------- .../src/utils/tool/StyleUtils.ts | 2 +- .../src/store/annotation/reducer.ts | 6 +- packages/lb-components/src/utils/Pipeline.ts | 4 +- 4 files changed, 36 insertions(+), 43 deletions(-) diff --git a/packages/lb-annotation/src/core/basicLayer.ts b/packages/lb-annotation/src/core/basicLayer.ts index 908cba498..952b75310 100644 --- a/packages/lb-annotation/src/core/basicLayer.ts +++ b/packages/lb-annotation/src/core/basicLayer.ts @@ -4,27 +4,25 @@ * @Date 2024-01-31 */ -import { EToolName, THybridToolName, ToolName } from '@/constant/tool'; +import { EToolName, THybridToolName } from '@/constant/tool'; import CanvasUtils from '@/utils/tool/CanvasUtils'; import DrawUtils from '@/utils/tool/DrawUtils'; import AxisUtils, { CoordinateUtils } from '@/utils/tool/AxisUtils'; import { HybridToolUtils } from '@/core/scheduler'; import EventListener from '@/core/toolOperation/eventListener'; import { IPolygonConfig } from '@/types/tool/polygon'; -import { ILineConfig } from '@/types/tool/lineTool'; -import { IPointToolConfig } from '@/types/tool/pointTool'; -import { ICommonProps } from './index'; -import { toolStyleConverter } from '@labelbee/lb-utils'; +import { IPolygonData, toolStyleConverter } from '@labelbee/lb-utils'; import CommonToolUtils from '@/utils/tool/CommonToolUtils'; import { styleString } from '@/constant/style'; import StyleUtils from '@/utils/tool/StyleUtils'; +import { ICommonProps } from './index'; type IReferenceConfig = IRectConfig | IPolygonConfig | ILineConfig | IPointToolConfig; export interface IReferenceInfoProps { referenceConfig: IReferenceConfig; referenceResult: any; - referenceToolName: ToolName; + referenceToolName: EToolName; } interface IBasicLayerProps extends ICommonProps { @@ -144,6 +142,7 @@ export default class BasicLayer extends EventListener { public setReferenceInfo(referenceInfo: IReferenceInfoProps) { this.referenceInfo = referenceInfo; } + /** * Synchronize common information such as currentPos, zoom, etc */ @@ -295,52 +294,46 @@ export default class BasicLayer extends EventListener { } } - this.renderReference() + this.renderReference(); } - public renderReference(){ + public renderReference() { const thickness = 2; if (this.referenceInfo) { - const { referenceResult, referenceConfig, referenceToolName } = this.referenceInfo + const { referenceResult, referenceToolName } = this.referenceInfo; switch (referenceToolName) { case EToolName.RectTrack: case EToolName.Rect: { - let rectList = referenceResult; - rectList.map((rect) => { + const rectList = referenceResult; + rectList.forEach((rect: IRect) => { const toolColor = this.getColor(rect.attribute); const toolData = StyleUtils.getStrokeAndFill(toolColor, rect.valid); - DrawUtils.drawRect( - this.basicCanvas, - AxisUtils.changeRectByZoom(rect, this.zoom, this.currentPos), - { - color: toolData.stroke, - thickness, - lineDash: [24], - }, - ); - }) + DrawUtils.drawRect(this.basicCanvas, AxisUtils.changeRectByZoom(rect, this.zoom, this.currentPos), { + color: toolData.stroke, + thickness, + lineDash: [24], + }); + }); break; } case EToolName.Point: { - let pointList = referenceResult; - pointList.map((point) => { + const pointList = referenceResult; + pointList.forEach((point: IPointUnit) => { const toolColor = this.getColor(point.attribute); const toolData = StyleUtils.getStrokeAndFill(toolColor, point.valid); - DrawUtils.drawCircle( - this.basicCanvas, - AxisUtils.changePointByZoom(point, this.zoom, this.currentPos), 5, { - color: toolData.stroke, - fill: toolData.fill, + DrawUtils.drawCircle(this.basicCanvas, AxisUtils.changePointByZoom(point, this.zoom, this.currentPos), 5, { + color: toolData.stroke, + fill: toolData.fill, }); - }) + }); break; } case EToolName.Polygon: { - let polygonList = referenceResult; - polygonList.map((polygon) => { + const polygonList = referenceResult; + polygonList.forEach((polygon: IPolygonData) => { const toolColor = this.getColor(polygon.attribute); const toolData = StyleUtils.getStrokeAndFill(toolColor, polygon.valid); @@ -354,16 +347,17 @@ export default class BasicLayer extends EventListener { thickness, }, ); - }) + }); break; } + case EToolName.LineMarker: case EToolName.Line: { - let lineList = referenceResult; - lineList.map((line) => { + const lineList = referenceResult; + lineList.forEach((line: any) => { const toolColor = this.getColor(line.attribute); - const toolData = StyleUtils.getStrokeAndFill(toolColor, line.valid) + const toolData = StyleUtils.getStrokeAndFill(toolColor, line.valid); DrawUtils.drawLineWithPointList( this.basicCanvas, @@ -373,7 +367,7 @@ export default class BasicLayer extends EventListener { thickness, }, ); - }) + }); break; } @@ -383,6 +377,5 @@ export default class BasicLayer extends EventListener { } } } - } } diff --git a/packages/lb-annotation/src/utils/tool/StyleUtils.ts b/packages/lb-annotation/src/utils/tool/StyleUtils.ts index bf72d7099..99d6f0aa5 100644 --- a/packages/lb-annotation/src/utils/tool/StyleUtils.ts +++ b/packages/lb-annotation/src/utils/tool/StyleUtils.ts @@ -63,6 +63,6 @@ export default class StyleUtils { return color .split(' ') .join('') - .replace(/,[0-9]+([.]{1}[0-9]+){0,1}\)/, `,${opacity.toFixed(2)})`); + .replace(/,\d+(\.\d+){0,1}\)/, `,${opacity.toFixed(2)})`); } } diff --git a/packages/lb-components/src/store/annotation/reducer.ts b/packages/lb-components/src/store/annotation/reducer.ts index 4fcb29460..5badb8327 100644 --- a/packages/lb-components/src/store/annotation/reducer.ts +++ b/packages/lb-components/src/store/annotation/reducer.ts @@ -7,7 +7,7 @@ import { IStepInfo } from '@/types/step'; import { jsonParser } from '@/utils'; import AnnotationDataUtils from '@/utils/AnnotationDataUtils'; import { ConfigUtils } from '@/utils/ConfigUtils'; -import { getBasicResult, getReferenceConfig, getReferenceInfo} from '@/utils/Pipeline' +import { getBasicResult, getReferenceInfo} from '@/utils/Pipeline' import { composeResult, composeResultWithBasicImgInfo } from '@/utils/data'; import StepUtils from '@/utils/StepUtils'; import ToolUtils from '@/utils/ToolUtils'; @@ -507,8 +507,8 @@ export const annotationReducer = ( annotationEngine?.launchOperation(); - if (referenceInfo && referenceInfo.referenceToolName) { - annotationEngine.setReferenceInfo(referenceInfo) + if (referenceInfo?.referenceToolName) { + annotationEngine?.setReferenceInfo(referenceInfo) } if (hasDataSourceStep) { diff --git a/packages/lb-components/src/utils/Pipeline.ts b/packages/lb-components/src/utils/Pipeline.ts index d87941334..2868a79bd 100644 --- a/packages/lb-components/src/utils/Pipeline.ts +++ b/packages/lb-components/src/utils/Pipeline.ts @@ -390,7 +390,7 @@ export function getSourceData( * @param dataSourceStep * @param stepList */ -export function getBasicConfig(dataSourceStep: number, stepList: IStepInfo[]) { +export function getBasicConfig(dataSourceStep: number, stepList: IStepInfo[]) : any{ const dataSourceStepInfo = CommonToolUtils.getCurrentStepInfo(dataSourceStep, stepList); if (!dataSourceStepInfo) { return {}; @@ -443,7 +443,7 @@ export function getBasicToolName( return dataSourceStepInfo.tool; } - if (preData && preData.preResult && dataSourceStepInfo.preDataSourceStep > 0) { + if (preData?.preResult && dataSourceStepInfo.preDataSourceStep > 0) { const preResult = jsonParser(preData.preResult); // 依赖预标注 return preResult[`step_${dataSourceStepInfo.preDataSourceStep}`]?.toolName ?? EToolName.Empty; From 715f7a9e1eecaecb770e1ddc8582475ab21e6397 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Mon, 26 Feb 2024 17:09:02 +0800 Subject: [PATCH 40/43] fix: Resolve comments --- packages/lb-annotation/src/core/basicLayer.ts | 10 +++++----- packages/lb-annotation/src/core/index.ts | 2 +- packages/lb-annotation/src/utils/tool/StyleUtils.ts | 7 ------- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/lb-annotation/src/core/basicLayer.ts b/packages/lb-annotation/src/core/basicLayer.ts index 952b75310..cb44f0da3 100644 --- a/packages/lb-annotation/src/core/basicLayer.ts +++ b/packages/lb-annotation/src/core/basicLayer.ts @@ -301,13 +301,13 @@ export default class BasicLayer extends EventListener { const thickness = 2; if (this.referenceInfo) { - const { referenceResult, referenceToolName } = this.referenceInfo; + const { referenceResult, referenceToolName, referenceConfig } = this.referenceInfo; switch (referenceToolName) { case EToolName.RectTrack: case EToolName.Rect: { const rectList = referenceResult; rectList.forEach((rect: IRect) => { - const toolColor = this.getColor(rect.attribute); + const toolColor = this.getColor(rect.attribute, referenceConfig); const toolData = StyleUtils.getStrokeAndFill(toolColor, rect.valid); DrawUtils.drawRect(this.basicCanvas, AxisUtils.changeRectByZoom(rect, this.zoom, this.currentPos), { color: toolData.stroke, @@ -321,7 +321,7 @@ export default class BasicLayer extends EventListener { case EToolName.Point: { const pointList = referenceResult; pointList.forEach((point: IPointUnit) => { - const toolColor = this.getColor(point.attribute); + const toolColor = this.getColor(point.attribute, referenceConfig); const toolData = StyleUtils.getStrokeAndFill(toolColor, point.valid); DrawUtils.drawCircle(this.basicCanvas, AxisUtils.changePointByZoom(point, this.zoom, this.currentPos), 5, { color: toolData.stroke, @@ -334,7 +334,7 @@ export default class BasicLayer extends EventListener { case EToolName.Polygon: { const polygonList = referenceResult; polygonList.forEach((polygon: IPolygonData) => { - const toolColor = this.getColor(polygon.attribute); + const toolColor = this.getColor(polygon.attribute, referenceConfig); const toolData = StyleUtils.getStrokeAndFill(toolColor, polygon.valid); DrawUtils.drawPolygonWithFillAndLine( @@ -356,7 +356,7 @@ export default class BasicLayer extends EventListener { case EToolName.Line: { const lineList = referenceResult; lineList.forEach((line: any) => { - const toolColor = this.getColor(line.attribute); + const toolColor = this.getColor(line.attribute, referenceConfig); const toolData = StyleUtils.getStrokeAndFill(toolColor, line.valid); DrawUtils.drawLineWithPointList( diff --git a/packages/lb-annotation/src/core/index.ts b/packages/lb-annotation/src/core/index.ts index f243d2fc6..c85a0d90d 100644 --- a/packages/lb-annotation/src/core/index.ts +++ b/packages/lb-annotation/src/core/index.ts @@ -248,7 +248,7 @@ export default class AnnotationEngine { } /** - * 设置参考显示的信息 + * set reference info * @param referenceInfo */ diff --git a/packages/lb-annotation/src/utils/tool/StyleUtils.ts b/packages/lb-annotation/src/utils/tool/StyleUtils.ts index 99d6f0aa5..a19767364 100644 --- a/packages/lb-annotation/src/utils/tool/StyleUtils.ts +++ b/packages/lb-annotation/src/utils/tool/StyleUtils.ts @@ -58,11 +58,4 @@ export default class StyleUtils { }); return el; } - - public static colorSplit(color: string, opacity: number) { - return color - .split(' ') - .join('') - .replace(/,\d+(\.\d+){0,1}\)/, `,${opacity.toFixed(2)})`); - } } From 267235fbab15a2782abf5d46dfde2e40994e8033 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Wed, 13 Mar 2024 17:20:34 +0800 Subject: [PATCH 41/43] feat: Add hiddenTextSwitch and highlightBadge in rect tool --- .../lb-annotation/src/constant/keyCode.ts | 1 + .../core/toolOperation/basicToolOperation.ts | 13 +++ .../annotation/rectTool/flag_active.svg | 4 + .../annotation/rectTool/flag_default.svg | 4 + .../src/components/attributeList/index.tsx | 16 ++++ .../audioSide/labelSidebar/index.module.scss | 41 +++++---- .../highlightBadge/index.module.scss | 33 ++++++++ .../src/components/highlightBadge/index.tsx | 81 ++++++++++++++++++ packages/lb-components/src/index.scss | 4 +- packages/lb-components/src/index.tsx | 2 + .../src/store/toolStyle/reducer.ts | 1 + .../src/store/toolStyle/types.ts | 1 + packages/lb-components/src/types/main.d.ts | 3 + .../sidebar/SwitchAttributeList/index.tsx | 7 +- .../MainView/toolFooter/FooterPopover.tsx | 2 +- .../HiddenTextSwitch/index.module.scss | 16 ++++ .../toolFooter/HiddenTextSwitch/index.tsx | 84 +++++++++++++++++++ .../src/views/MainView/toolFooter/index.tsx | 6 ++ .../toolHeader/headerOption/index.tsx | 3 +- packages/lb-utils/src/i18n/resources.json | 10 ++- packages/lb-utils/src/types/index.ts | 1 + packages/lb-utils/src/types/rect.ts | 40 +++++++++ packages/lb-utils/tsconfig.json | 6 +- 23 files changed, 350 insertions(+), 29 deletions(-) create mode 100644 packages/lb-components/src/assets/annotation/rectTool/flag_active.svg create mode 100644 packages/lb-components/src/assets/annotation/rectTool/flag_default.svg create mode 100644 packages/lb-components/src/components/highlightBadge/index.module.scss create mode 100644 packages/lb-components/src/components/highlightBadge/index.tsx create mode 100644 packages/lb-components/src/views/MainView/toolFooter/HiddenTextSwitch/index.module.scss create mode 100644 packages/lb-components/src/views/MainView/toolFooter/HiddenTextSwitch/index.tsx create mode 100644 packages/lb-utils/src/types/rect.ts diff --git a/packages/lb-annotation/src/constant/keyCode.ts b/packages/lb-annotation/src/constant/keyCode.ts index f9fd2873e..46d2f92df 100644 --- a/packages/lb-annotation/src/constant/keyCode.ts +++ b/packages/lb-annotation/src/constant/keyCode.ts @@ -12,6 +12,7 @@ enum EKeyCode { L = 76, R = 82, Z = 90, + V = 86, W = 87, X = 88, Y = 89, diff --git a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts index 3ea5e242e..a37c1d688 100644 --- a/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts +++ b/packages/lb-annotation/src/core/toolOperation/basicToolOperation.ts @@ -545,6 +545,9 @@ class BasicToolOperation extends EventListener { public setBasicImgInfo(basicImgInfo: any) { this.basicImgInfo = basicImgInfo; + this.basicInstance?.syncCommonInfo({ + basicImgInfo, + }); this.coordUtils.setBasicImgInfo(basicImgInfo); } @@ -1232,6 +1235,9 @@ class BasicToolOperation extends EventListener { public setValid(valid: boolean) { this.basicImgInfo.valid = valid; + this.basicInstance?.syncCommonInfo({ + basicImgInfo: this.basicImgInfo, + }); if (valid === false) { this.renderInvalidPage(); this.clearResult(false); @@ -1242,6 +1248,9 @@ class BasicToolOperation extends EventListener { public setRotate(rotate: number) { this.basicImgInfo.rotate = rotate; + this.basicInstance?.syncCommonInfo({ + basicImgInfo: this.basicImgInfo, + }); } public setBasicResult(basicResult: any) { @@ -1259,6 +1268,7 @@ class BasicToolOperation extends EventListener { public setAttributeLockList(attributeLockList: string[]) { this.attributeLockList = attributeLockList; this.render(); + this.emit('attributeLockChanged', attributeLockList); } public setConfig(config: string) { @@ -1299,6 +1309,9 @@ class BasicToolOperation extends EventListener { // 更改当前图片的旋转方式 const rotate = MathUtils.getRotate(this.basicImgInfo.rotate); this.basicImgInfo.rotate = rotate; + this.basicInstance?.syncCommonInfo({ + basicImgInfo: this.basicImgInfo, + }); this.initImgPos(); // 触发外层 result 的更改 diff --git a/packages/lb-components/src/assets/annotation/rectTool/flag_active.svg b/packages/lb-components/src/assets/annotation/rectTool/flag_active.svg new file mode 100644 index 000000000..af2471a54 --- /dev/null +++ b/packages/lb-components/src/assets/annotation/rectTool/flag_active.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/lb-components/src/assets/annotation/rectTool/flag_default.svg b/packages/lb-components/src/assets/annotation/rectTool/flag_default.svg new file mode 100644 index 000000000..d8c89dc4c --- /dev/null +++ b/packages/lb-components/src/assets/annotation/rectTool/flag_default.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/lb-components/src/components/attributeList/index.tsx b/packages/lb-components/src/components/attributeList/index.tsx index 0e4f47266..e80a170ee 100644 --- a/packages/lb-components/src/components/attributeList/index.tsx +++ b/packages/lb-components/src/components/attributeList/index.tsx @@ -12,6 +12,7 @@ import { ILimit, IDefaultSize } from '@labelbee/lb-utils'; import LimitPopover from './components/limitPopover'; import _ from 'lodash'; import { CommonToolUtils, MathUtils } from '@labelbee/lb-annotation'; +import { GraphToolInstance } from '@/store/annotation/types'; export const ATTRIBUTE_COLORS = [NULL_COLOR].concat(COLORS_ARRAY); @@ -34,12 +35,14 @@ interface IProps { updateSize?: (size: IDefaultSize) => void; attributeLockChange?: (list: string[]) => void; forbidShowLimitPopover?: boolean; + toolInstance?: GraphToolInstance; } const AttributeList = React.forwardRef((props: IProps, ref) => { const radioRef = React.useRef(); const { t } = useTranslation(); const list = props.list || []; + const { toolInstance } = props const [paletteVisible, setPaletteVisible] = useState(false); const [editConfigIndex, setEditConfigIndex] = useState(undefined); @@ -47,6 +50,19 @@ const AttributeList = React.forwardRef((props: IProps, ref) => { let NEW_ATTRIBUTE_COLORS = [...ATTRIBUTE_COLORS]; + useEffect(() => { + if (toolInstance) { + toolInstance.on('attributeLockChanged', onAttributeLockChanged) + } + return () => { + toolInstance?.unbind('attributeLockChanged', onAttributeLockChanged); + }; + }, [toolInstance]) + + const onAttributeLockChanged = (list: string[]) => { + setAttributeLockList(list) + } + // 去除默认的颜色 if (props.forbidDefault === true) { NEW_ATTRIBUTE_COLORS = NEW_ATTRIBUTE_COLORS.slice(1); diff --git a/packages/lb-components/src/components/audioAnnotate/audioSide/labelSidebar/index.module.scss b/packages/lb-components/src/components/audioAnnotate/audioSide/labelSidebar/index.module.scss index 2f1ab369b..fed0d64e7 100644 --- a/packages/lb-components/src/components/audioAnnotate/audioSide/labelSidebar/index.module.scss +++ b/packages/lb-components/src/components/audioAnnotate/audioSide/labelSidebar/index.module.scss @@ -28,6 +28,26 @@ -webkit-user-select: none; -ms-user-select: none; user-select: none; + :global { + .ant-collapse-borderless > .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box { + padding-top: 4px; + } + + .ant-collapse-content > .ant-collapse-content-box { + padding: 0; + } + + .ant-collapse-icon-position-right > .ant-collapse-item > .ant-collapse-header { + padding-right: 40px; + } + + .ant-collapse-borderless > .ant-collapse-item { + border-bottom: 0px; + .ant-collapse-header { + padding: 16px; + } + } + } } .main::-webkit-scrollbar { @@ -86,27 +106,6 @@ padding: 0 3px 0 8px; } -:global { - .ant-collapse-borderless > .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box { - padding-top: 4px; - } - - .ant-collapse-content > .ant-collapse-content-box { - padding: 0; - } - - .ant-collapse-icon-position-right > .ant-collapse-item > .ant-collapse-header { - padding-right: 40px; - } - - .ant-collapse-borderless > .ant-collapse-item { - border-bottom: 0px; - .ant-collapse-header { - padding: 16px; - } - } -} - .filterContainer { position: relative; height: 100%; diff --git a/packages/lb-components/src/components/highlightBadge/index.module.scss b/packages/lb-components/src/components/highlightBadge/index.module.scss new file mode 100644 index 000000000..834c6f462 --- /dev/null +++ b/packages/lb-components/src/components/highlightBadge/index.module.scss @@ -0,0 +1,33 @@ +.highlightBadge { + cursor: pointer; + padding: 2px 4px; + gap: 2px; + border-radius: 2px; + font-size: 12px; + color: rgb(153, 153, 153); + path { + fill: #999; + } + &.active, + &.active:hover { + color: #666fff; + path { + fill: #666fff; + } + } + &:hover { + color: #8c9aff; + background-color: #eeefff; + path { + fill: #8c9aff; + } + } + + .flag { + display: flex; + justify-content: center; + } + .text { + margin-top: 8px; + } +} diff --git a/packages/lb-components/src/components/highlightBadge/index.tsx b/packages/lb-components/src/components/highlightBadge/index.tsx new file mode 100644 index 000000000..c678f3f9c --- /dev/null +++ b/packages/lb-components/src/components/highlightBadge/index.tsx @@ -0,0 +1,81 @@ +/** + * @file 拉框工具-标注模式-只看高亮框标识和本页高亮框个数 + * 切换标识需要将 attributeLockList 设置为空数组 + * @author lihuaqi + * @date 2023年12月25日 + */ + +import { ReactComponent as FlagActive } from '@/assets/annotation/rectTool/flag_active.svg'; +import { ReactComponent as FlagDefault } from '@/assets/annotation/rectTool/flag_default.svg'; +import { classnames } from '@/utils'; +import { Badge, Tooltip } from 'antd'; +import React, { useCallback, useState, useMemo, useEffect } from 'react'; +import { connect } from 'react-redux'; +import styles from './index.module.scss'; +import { LabelBeeContext, useDispatch } from '@/store/ctx'; +import { IRect } from '@labelbee/lb-utils'; +import { useTranslation } from 'react-i18next'; + +const HighlightBadge = (props: any) => { + const { t } = useTranslation(); + + const [rectsVisible, setRectsVisible] = useState(false) + const toolInstance = props?.annotation?.toolInstance; + + const handleClick = useCallback(() => { + if (toolInstance) { + const nextRectsVisible = !rectsVisible; + setRectsVisible(nextRectsVisible) + toolInstance?.setAttributeLockList([]); + toolInstance?.setHighlightVisible(nextRectsVisible); + } + }, [rectsVisible, toolInstance]); + + const count = useMemo(() => { + if (!toolInstance?.rectList) return 0 + return toolInstance?.rectList?.filter((rect: IRect) => rect?.isHighlight)?.length + }, [toolInstance?.rectList]) + + useEffect(() => { + if (toolInstance) { + toolInstance.on('attributeLockChanged', onAttributeLockChanged) + } + return () => { + toolInstance?.unbind('attributeLockChanged', onAttributeLockChanged); + }; + }, [toolInstance]) + + const onAttributeLockChanged = (list: string[]) => { + if (list.length > 0) { + setRectsVisible(false) + } + } + if (count === 0) { + return null; + } + + return ( + + +
+
{rectsVisible ? : }
+ +
{t("ShowHighlightRect")}
+
+
+
+ ); +}; + +const mapStateToProps = ({ annotation }: any) => { + return { annotation }; +}; + +export default connect(mapStateToProps, null, null, { context: LabelBeeContext })(HighlightBadge); + diff --git a/packages/lb-components/src/index.scss b/packages/lb-components/src/index.scss index dfb2dc111..ac1b053e7 100644 --- a/packages/lb-components/src/index.scss +++ b/packages/lb-components/src/index.scss @@ -793,7 +793,7 @@ $hotkey-container-padding: 7px; width: 100%; display: flex; flex-direction: column; - + overflow-x: hidden; .styleSlider { .title { min-width: 60px; @@ -2067,7 +2067,7 @@ $ptToolHeight: 40px; &__footer { display: flex; - justify-content: end; + justify-content: flex-end; span { display: inline-flex; align-items: center; diff --git a/packages/lb-components/src/index.tsx b/packages/lb-components/src/index.tsx index cbc8161e7..6aaf67650 100644 --- a/packages/lb-components/src/index.tsx +++ b/packages/lb-components/src/index.tsx @@ -18,6 +18,7 @@ import { PointCloudProvider } from './components/pointCloudView/PointCloudContex import { AppState } from './store'; import { LabelBeeContext } from '@/store/ctx'; import PredictTracking from '@/components/predictTracking'; +import HighlightBadge from '@/components/highlightBadge'; import LLMToolView from '@/components/LLMToolView'; import SwitchCuboidBoxIn2DView from '@/views/MainView/toolFooter/SwitchCuboidBoxIn2DView'; import MeasureCanvas from './components/measureCanvas'; @@ -79,6 +80,7 @@ export { MeasureCanvas, AnnotatedBox, FindTrackIDIndex, + HighlightBadge }; export * from './constant'; diff --git a/packages/lb-components/src/store/toolStyle/reducer.ts b/packages/lb-components/src/store/toolStyle/reducer.ts index a6e2de257..60cd9cdb6 100644 --- a/packages/lb-components/src/store/toolStyle/reducer.ts +++ b/packages/lb-components/src/store/toolStyle/reducer.ts @@ -12,6 +12,7 @@ const initialState: ToolStyleState = { attributeColor: ToolStyleUtils.getAttributeColors(), lineColor: ToolStyleUtils.getDefaultToolLineColors(), attributeLineColor: [NULL_COLOR].concat(COLORS_ARRAY), + hiddenText: false, }; export function toolStyleReducer( diff --git a/packages/lb-components/src/store/toolStyle/types.ts b/packages/lb-components/src/store/toolStyle/types.ts index 29321ded4..cc27f1d33 100644 --- a/packages/lb-components/src/store/toolStyle/types.ts +++ b/packages/lb-components/src/store/toolStyle/types.ts @@ -12,6 +12,7 @@ export interface ToolStyleState { attributeColor: any[]; lineColor: string; attributeLineColor: string[]; + hiddenText: boolean; } interface UpdateToolStyleConfig { diff --git a/packages/lb-components/src/types/main.d.ts b/packages/lb-components/src/types/main.d.ts index 4abe29f2d..b25f969d9 100644 --- a/packages/lb-components/src/types/main.d.ts +++ b/packages/lb-components/src/types/main.d.ts @@ -84,6 +84,8 @@ interface IFooter { shortCutTable: { [a: string]: any; }; + /** hide text info */ + hiddenTextSwitch: React.ReactNode; } export type RenderFooter = ({ @@ -96,6 +98,7 @@ export type RenderFooter = ({ footerDivider, ToolHotKeyCom, shortCutTable, + hiddenTextSwitch, }: IFooter) => React.ReactNode; export type Header = ({ diff --git a/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx b/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx index 52020d59f..56651c516 100644 --- a/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx +++ b/packages/lb-components/src/views/MainView/sidebar/SwitchAttributeList/index.tsx @@ -68,6 +68,10 @@ const SwitchAttributeList: React.FC = (props) => { } }; + const defaultAttributeLockChange = (list: string[]) => { + toolInstance?.setAttributeLockList(list); + }; + return ( = (props) => { selectedAttribute={toolInstance?.defaultAttribute ?? ''} ref={listRef} forbidDefault={isScribbleTool} - attributeLockChange={attributeLockChange} + attributeLockChange={attributeLockChange ?? defaultAttributeLockChange} + toolInstance={toolInstance} /> ); } diff --git a/packages/lb-components/src/views/MainView/toolFooter/FooterPopover.tsx b/packages/lb-components/src/views/MainView/toolFooter/FooterPopover.tsx index ffe6b70f3..2babb05e5 100644 --- a/packages/lb-components/src/views/MainView/toolFooter/FooterPopover.tsx +++ b/packages/lb-components/src/views/MainView/toolFooter/FooterPopover.tsx @@ -5,7 +5,7 @@ type Source = string | React.ReactElement; const Icon = ({ source }: { source: Source }) => { if (typeof source === 'string') { - return ; + return ; } return source; diff --git a/packages/lb-components/src/views/MainView/toolFooter/HiddenTextSwitch/index.module.scss b/packages/lb-components/src/views/MainView/toolFooter/HiddenTextSwitch/index.module.scss new file mode 100644 index 000000000..725175648 --- /dev/null +++ b/packages/lb-components/src/views/MainView/toolFooter/HiddenTextSwitch/index.module.scss @@ -0,0 +1,16 @@ +.hiddenTextSwitch { + display: flex; + align-items: center; + margin-right: 10px; + color: #fff; + font-size: 12px; + :global { + .ant-switch { + margin-left: 5px; + background-color: #a3a3a3; + } + .ant-switch-checked { + background-color: #666fff; + } + } +} diff --git a/packages/lb-components/src/views/MainView/toolFooter/HiddenTextSwitch/index.tsx b/packages/lb-components/src/views/MainView/toolFooter/HiddenTextSwitch/index.tsx new file mode 100644 index 000000000..b8d3088f0 --- /dev/null +++ b/packages/lb-components/src/views/MainView/toolFooter/HiddenTextSwitch/index.tsx @@ -0,0 +1,84 @@ +import { cKeyCode, EToolName } from '@labelbee/lb-annotation'; +import { Switch } from 'antd'; +import React, { useEffect, useRef } from 'react'; +import styles from './index.module.scss'; +import { AppState } from '@/store'; +import { connect } from 'react-redux'; +import { LabelBeeContext } from '@/store/ctx'; +import { UpdateToolStyleConfig } from '@/store/toolStyle/actionCreators'; +import { store } from '@/index'; +import { useTranslation } from 'react-i18next'; + +const EKeyCode = cKeyCode.default; + +interface IProps { + toolName: string; + toolStyle: any; +} + +// 不看图形信息 +const HiddenTextSwitch = (props: IProps) => { + const { + toolName, + toolStyle: { hiddenText }, + } = props; + + const {t} = useTranslation(); + + const prevHiddenText = useRef(hiddenText); + const showHiddenText = [ + EToolName.Rect, + EToolName.Point, + EToolName.Polygon, + EToolName.Line, + ].includes(toolName as EToolName); + + const changeHiddenText = (hiddenText: boolean) => { + prevHiddenText.current = hiddenText; + store.dispatch(UpdateToolStyleConfig({ hiddenText })) + }; + + const onKeydown = (e: KeyboardEvent) => { + switch (e.keyCode) { + case EKeyCode.V: + if (showHiddenText && !e.ctrlKey) { + changeHiddenText(!prevHiddenText.current); + } + break; + } + }; + + useEffect(() => { + // 查看模式切换标注工具时 对于不显示hiddenText的工具设为false + if (showHiddenText) { + changeHiddenText(prevHiddenText.current); + } else { + store.dispatch(UpdateToolStyleConfig({ hiddenText: false })) + } + window.addEventListener('keydown', onKeydown); + return () => { + window.removeEventListener('keydown', onKeydown); + }; + }, [toolName]); + + useEffect(() => { + return () => changeHiddenText(false); + }, []); + + if (!showHiddenText) { + return null; + } + + return ( +
+ {t("HideTextInfo")}(V) + +
+ ); +}; + +const mapStateToProps = ({ toolStyle }: AppState) => ({ + toolStyle, +}); + +export default connect(mapStateToProps, null, null, { context: LabelBeeContext })(HiddenTextSwitch); diff --git a/packages/lb-components/src/views/MainView/toolFooter/index.tsx b/packages/lb-components/src/views/MainView/toolFooter/index.tsx index 9159509be..e78b47224 100644 --- a/packages/lb-components/src/views/MainView/toolFooter/index.tsx +++ b/packages/lb-components/src/views/MainView/toolFooter/index.tsx @@ -18,6 +18,7 @@ import { Pagination } from './Pagination'; import { AnnotatedAttributesIcon } from './AnnotatedAttributes'; import { cTool } from '@labelbee/lb-annotation'; import { shortCutTable, ToolHotKeyCom } from './FooterTips/ToolHotKey'; +import HiddenTextSwitch from './HiddenTextSwitch'; const { EPointCloudName } = cTool; @@ -56,6 +57,7 @@ const renderFooter: RenderFooter = ({ curItems, footerDivider, annotateAttrList, + hiddenTextSwitch, }) => { return ( <> @@ -63,6 +65,7 @@ const renderFooter: RenderFooter = ({ {annotateAttrList}
{hiddenTips} + {hiddenTextSwitch} {pageNumber} {pagination} {curItems} @@ -125,6 +128,8 @@ const ToolFooter: React.FC = (props: IProps) => { /> ); + const hiddenTextSwitch = + const curItems = hasSourceStep && basicResultList.length > 0 ? ( {t('curItems', { current: basicIndex + 1, total: basicResultList.length })} @@ -156,6 +161,7 @@ const ToolFooter: React.FC = (props: IProps) => { footerDivider: , shortCutTable, ToolHotKeyCom, + hiddenTextSwitch, })}
); diff --git a/packages/lb-components/src/views/MainView/toolHeader/headerOption/index.tsx b/packages/lb-components/src/views/MainView/toolHeader/headerOption/index.tsx index 10416b352..ae8380e28 100644 --- a/packages/lb-components/src/views/MainView/toolHeader/headerOption/index.tsx +++ b/packages/lb-components/src/views/MainView/toolHeader/headerOption/index.tsx @@ -170,8 +170,9 @@ const HeaderOption: React.FC = (props) => { className='item' onMouseEnter={() => setToolHover(info.toolName)} onMouseLeave={() => setToolHover('')} + onClick={info.click} > - + Date: Tue, 19 Mar 2024 11:09:34 +0800 Subject: [PATCH 42/43] feat: Add basicLayer in annotationView --- packages/lb-annotation/src/core/basicLayer.ts | 4 ++-- packages/lb-annotation/src/index.ts | 2 ++ .../src/components/AnnotationView/index.tsx | 11 ++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/lb-annotation/src/core/basicLayer.ts b/packages/lb-annotation/src/core/basicLayer.ts index cb44f0da3..adcfc5083 100644 --- a/packages/lb-annotation/src/core/basicLayer.ts +++ b/packages/lb-annotation/src/core/basicLayer.ts @@ -28,7 +28,7 @@ export interface IReferenceInfoProps { interface IBasicLayerProps extends ICommonProps { container: HTMLElement; size: ISize; - toolName: THybridToolName; + toolName?: THybridToolName; imgNode?: HTMLImageElement; // dom node of image config?: string; // config of annotation task style?: any; @@ -86,7 +86,7 @@ export default class BasicLayer extends EventListener { this._imgAttribute = props.imgAttribute ?? {}; this.imgNode = props.imgNode; - this.hiddenImg = !HybridToolUtils.isSingleTool(props.toolName) || false; + this.hiddenImg = props.toolName ? !HybridToolUtils.isSingleTool(props.toolName) : false; this.forbidBasicResultRender = props.forbidBasicResultRender ?? false; this.destroyBasicCanvas(); diff --git a/packages/lb-annotation/src/index.ts b/packages/lb-annotation/src/index.ts index ee230bcb6..08518bb92 100644 --- a/packages/lb-annotation/src/index.ts +++ b/packages/lb-annotation/src/index.ts @@ -18,6 +18,7 @@ import ScribbleTool from './core/toolOperation/ScribbleTool'; import PointCloud2dOperation from './core/toolOperation/pointCloud2dOperation'; import SegmentByRect from './core/toolOperation/segmentByRect'; import SegmentBySAM from './core/toolOperation/segmentBySAM'; +import BasicLayer from './core/basicLayer'; // Constant import * as cAnnotation from './constant/annotation'; @@ -69,6 +70,7 @@ export { SegmentByRect, SegmentBySAM, CursorTextClass, + BasicLayer, // 固定操作 cAnnotation, cAnnotationTask, diff --git a/packages/lb-components/src/components/AnnotationView/index.tsx b/packages/lb-components/src/components/AnnotationView/index.tsx index 3ac8ed594..1948c2ad8 100644 --- a/packages/lb-components/src/components/AnnotationView/index.tsx +++ b/packages/lb-components/src/components/AnnotationView/index.tsx @@ -4,7 +4,7 @@ */ import React, { useEffect, useRef, useImperativeHandle, useState } from 'react'; -import { ViewOperation, ImgUtils } from '@labelbee/lb-annotation'; +import { ViewOperation, ImgUtils, BasicLayer } from '@labelbee/lb-annotation'; import { Spin } from 'antd/es'; import useRefCache from '@/hooks/useRefCache'; import { TAnnotationViewData } from '@labelbee/lb-utils'; @@ -89,6 +89,7 @@ const AnnotationView = (props: IProps, ref: any) => { const [loading, setLoading] = useState(false); const annotationRef = useRef(null); const viewOperation = useRef(); + const basicInstance = useRef(); const afterImgOnLoadRef = useRefCache(afterImgOnLoad); const annotationListCacheRef = useRef([]); const canUpdateRef = useRef(true); // Judge if rending is Possible. @@ -124,6 +125,13 @@ const AnnotationView = (props: IProps, ref: any) => { }); viewOperation.current.init(); + basicInstance.current = new BasicLayer({ + container: annotationRef.current, + size, + style, + config: '{}', + }) + viewOperation.current?.setBasicInstance(basicInstance.current) } return () => { @@ -141,6 +149,7 @@ const AnnotationView = (props: IProps, ref: any) => { setLoading(false); viewOperation.current?.setImgNode(imgNode); + basicInstance.current?.setImgNode(imgNode); if (afterImgOnLoadRef.current) { afterImgOnLoadRef.current(imgNode); From 6b8eb9b6ba280fc39a249b297eb8bcb7017567f9 Mon Sep 17 00:00:00 2001 From: "752342314@qq.com" Date: Tue, 30 Apr 2024 12:53:07 +0800 Subject: [PATCH 43/43] feat: Add errorboundary --- packages/lb-components/src/App.tsx | 4 +++ .../src/components/errorBoundary/index.tsx | 32 +++++++++++++++++++ packages/lb-components/src/index.tsx | 4 ++- packages/lb-demo/src/mock/taskConfig.js | 2 +- 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 packages/lb-components/src/components/errorBoundary/index.tsx diff --git a/packages/lb-components/src/App.tsx b/packages/lb-components/src/App.tsx index dc7054dc9..ce3e55c7e 100644 --- a/packages/lb-components/src/App.tsx +++ b/packages/lb-components/src/App.tsx @@ -29,6 +29,7 @@ import { ConfigProvider } from 'antd/es'; import zhCN from 'antd/es/locale/zh_CN'; import enUS from 'antd/es/locale/en_US'; import { EPointCloudName } from '@labelbee/lb-annotation'; +import { useError } from '@/components/errorBoundary'; interface IAnnotationStyle { strokeColor: string; @@ -132,6 +133,8 @@ const App: React.FC = (props) => { preDataProcess, } = props; + const { onError } = useError() + useEffect(() => { store.dispatch( InitTaskData({ @@ -208,6 +211,7 @@ const App: React.FC = (props) => { // 初始化imgList 优先以loadFileList方式加载数据 const initImgList = () => { + onError?.('test error provider') if (loadFileList) { loadImgList(store.dispatch, store.getState, initialIndex, true).then((isSuccess) => { if (isSuccess) { diff --git a/packages/lb-components/src/components/errorBoundary/index.tsx b/packages/lb-components/src/components/errorBoundary/index.tsx new file mode 100644 index 000000000..91686bc48 --- /dev/null +++ b/packages/lb-components/src/components/errorBoundary/index.tsx @@ -0,0 +1,32 @@ +import React, { createContext, useContext, useMemo } from 'react'; + +// Create a Context to pass onError event +interface ErrorContextType { + onError?: (msg: string) => void; +} + +const ErrorContext = createContext({ + onError: () => {}, +}); + +// Error handling function +const handleError = () => { + console.log('Error occurred!'); +}; + +// Error Provider component +export const ErrorProvider: React.FC<{ onError?: () => void }> = ({ onError, children }) => { + const errorHandler = useMemo(() => { + return { + onError: onError || handleError, + } + }, [onError]) + return ( + + {children} + + ); +}; + +// Custom hook to get throwError method in child components +export const useError = (): ErrorContextType => useContext(ErrorContext) diff --git a/packages/lb-components/src/index.tsx b/packages/lb-components/src/index.tsx index 6aaf67650..08bf04039 100644 --- a/packages/lb-components/src/index.tsx +++ b/packages/lb-components/src/index.tsx @@ -24,6 +24,7 @@ import SwitchCuboidBoxIn2DView from '@/views/MainView/toolFooter/SwitchCuboidBox import MeasureCanvas from './components/measureCanvas'; import AnnotatedBox from './views/MainView/sidebar/PointCloudToolSidebar/components/annotatedBox'; import { FindTrackIDIndexInCheckMode as FindTrackIDIndex } from './views/MainView/sidebar/PointCloudToolSidebar/components/findTrackIDIndex'; +import { ErrorProvider } from '@/components/errorBoundary'; export const store = configureStore(); @@ -80,7 +81,8 @@ export { MeasureCanvas, AnnotatedBox, FindTrackIDIndex, - HighlightBadge + HighlightBadge, + ErrorProvider }; export * from './constant'; diff --git a/packages/lb-demo/src/mock/taskConfig.js b/packages/lb-demo/src/mock/taskConfig.js index 5890b4e0c..7f4c65a65 100644 --- a/packages/lb-demo/src/mock/taskConfig.js +++ b/packages/lb-demo/src/mock/taskConfig.js @@ -510,7 +510,7 @@ const getStepConfig = (tool, step, sourceStep) => { return { step: step ?? 1, - dataSourceStep: 0, + dataSourceStep: sourceStep || 0, tool: toolList, config: JSON.stringify({ ...getConfig(toolName),