Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/lb-annotation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"@rollup/plugin-node-resolve": "^11.2.1",
"@types/jest": "^24.0.19",
"@types/lodash": "^4.14.165",
"@types/node": "^18.19.130",
"@types/three": "^0.141.0",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
Expand Down
1 change: 1 addition & 0 deletions packages/lb-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"@svgr/rollup": "^8.1.0",
"@types/diff-match-patch": "^1.0.36",
"@types/lodash": "^4.14.168",
"@types/node": "^18.19.130",
"@types/react": "16.14.62",
"@types/react-redux": "^7.1.16",
"@types/react-syntax-highlighter": "^15.5.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const PointCloudListener: React.FC<IProps> = ({

const { updateRotate } = useRotate({ currentData });
const { updateRotateEdge } = useRotateEdge({ currentData });
const { updatePointCloudData, topViewSelectedChanged } = usePointCloudViews({
const { updatePointCloudData, restorePreResult, topViewSelectedChanged } = usePointCloudViews({
setResourceLoading,
});
const {
Expand Down Expand Up @@ -327,7 +327,18 @@ const PointCloudListener: React.FC<IProps> = ({
}
}
};
toolInstanceRef.current.clearResult = () => {
toolInstanceRef.current.clearResult = async (options?: { keepPreResult?: boolean }) => {
if (options?.keepPreResult) {
try {
const restored = await restorePreResult?.();
if (!restored) {
message.error(t('ClearKeepPreAnnotationFailed'));
}
} catch {
message.error(t('ClearKeepPreAnnotationFailed'));
}
return;
}
clearAllResult?.();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,8 @@ export const usePointCloudViews = (params?: IUsePointCloudViewsParams) => {
history,
linkageImageNameRectMap,
} = ptCtx;
const { addHistory, initHistory, pushHistoryUnderUpdatePolygon } = useHistory();
const { addHistory, initHistory, pushHistoryUnderUpdatePolygon, pushHistoryWithList } =
useHistory();
const { selectedPolygon } = usePolygon();

const { getPointCloudSphereByID, updatePointCloudSphere, selectedSphere } = useSphere();
Expand Down Expand Up @@ -721,6 +722,7 @@ export const usePointCloudViews = (params?: IUsePointCloudViewsParams) => {
topViewSelectedChanged: () => {},
sideViewUpdateBox: () => {},
backViewUpdateBox: () => {},
restorePreResult: async () => false,
};
}

Expand Down Expand Up @@ -1427,6 +1429,93 @@ export const usePointCloudViews = (params?: IUsePointCloudViewsParams) => {
params?.setResourceLoading?.(false);
};

/**
* Restore current frame to pre-annotation only (no PCD reload).
* @returns whether restore succeeded
*/
const restorePreResult = async () => {
if (!currentData?.url || !mainViewInstance) {
return false;
}

const preStep = jsonParser(currentData.preResult)?.[POINT_CLOUD_DEFAULT_STEP];
if (!preStep) {
return false;
}

const resultObj = jsonParser(currentData.result || '{}');
resultObj[POINT_CLOUD_DEFAULT_STEP] = _.cloneDeep(preStep);
let resultString = JSON.stringify(resultObj);
const url = currentData.url;

SetAnnotationLoading(dispatch, true);
params?.setResourceLoading?.(true);

try {
setHighlight2DDataList([]);
setSelectedIDs(undefined);

mainViewInstance.clearAllBox();
mainViewInstance.clearAllSphere();

let boxParamsList: any[] = [];
let lineList: any[] = [];
let polygonList: any[] = [];
let sphereParamsList: IPointCloudSphere[] = [];

ptCtx.setPointCloudValid(resolveFileItemValid(resultString, currentData.preResult));
ptCtx.sideViewInstance?.clearAllData();
ptCtx.backViewInstance?.clearAllData();

boxParamsList = PointCloudUtils.getBoxParamsFromResultList(resultString);

if (boxParamsList?.length > 0 && config?.lowerLimitPointsNumInBox > 0) {
// @ts-ignore
boxParamsList = await mainViewInstance?.filterPreResult(url, config, boxParamsList);
const newDataResultObj = jsonParser(resultString);
newDataResultObj[POINT_CLOUD_DEFAULT_STEP].result = boxParamsList;
resultString = JSON.stringify(newDataResultObj);
ptCtx.setPointCloudResult(boxParamsList);
}

polygonList = PointCloudUtils.getPolygonListFromResultList(resultString);
lineList = PointCloudUtils.getLineListFromResultList(resultString);
sphereParamsList = PointCloudUtils.getSphereParamsFromResultList(resultString);
const { rectList } = PointCloudUtils.parsePointCloudCurrentResult(resultString);

// Context must be set here: unlike page-enter, imgIndex does not change to trigger PointCloudView sync.
ptCtx.setPointCloudResult(boxParamsList);
ptCtx.setPolygonList(polygonList);
ptCtx.setLineList(lineList);
ptCtx.setPointCloudSphereList(sphereParamsList);
ptCtx.setRectList(rectList);

topViewInstance.updateData(url, resultString, {
radius: config?.radius ?? DEFAULT_RADIUS,
});
mainViewInstance.generateBoxes(boxParamsList);
mainViewInstance.generateSpheres(sphereParamsList);

await ptCtx.syncAllViewPointCloudColor(
EPointCloudBoxRenderTrigger.Default,
boxParamsList,
[],
);

pushHistoryWithList({
pointCloudBoxList: boxParamsList,
polygonList,
lineList,
pointCloudSphereList: sphereParamsList,
});

return true;
} finally {
SetAnnotationLoading(dispatch, false);
params?.setResourceLoading?.(false);
}
};

return {
topViewAddSphere,
topViewAddBox,
Expand All @@ -1442,6 +1531,7 @@ export const usePointCloudViews = (params?: IUsePointCloudViewsParams) => {
pointCloudBoxListUpdated,
initPointCloud3d,
updatePointCloudData,
restorePreResult,
updateViewsByDefaultSize,
generateRects,
update2DViewRect,
Expand Down
2 changes: 1 addition & 1 deletion packages/lb-components/src/hooks/annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface ICustomToolInstance {
exportData: () => [any, {}];
exportCustomData: () => {};
singleOn: (eventName: string, callback: (...args: any[]) => void) => void;
clearResult: () => void;
clearResult: (options?: { keepPreResult?: boolean }) => void | Promise<void>;
on: (eventName: string, callback: (...args: any[]) => void) => void;
unbind: () => void;
setResult: (result: any) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export interface IOperationConfig {
forbidConfirm?: boolean; // 是否禁止二次确认
forbidOperation?: boolean; // 禁止操作,会有置灰操作(该部分由用户自己更改 ImgSvg 进行展示)
content?: React.ReactElement;
/** Explicit extra UI under Popconfirm title; only rendered when set (opt-in). */
confirmExtra?: React.ReactElement;
onConfirmVisibleChange?: (visible: boolean) => void;
}

// 禁止的样式
Expand All @@ -22,16 +25,25 @@ const forbidStyle = {

const PopconfirmTitle = ({ info }: { info: IOperationConfig }) => {
const { t } = useTranslation();
if (info.key.startsWith('sure')) {
return (
<div key={info.key}>
{`${t('ConfirmTo')}${info.name.slice(0)}?`}
{info?.content ? info.content : ''}
</div>
);
const title = info.key.startsWith('sure') ? (
<div key={info.key}>
{`${t('ConfirmTo')}${info.name.slice(0)}?`}
{info?.content ? info.content : ''}
</div>
) : (
<span>{info.name}</span>
);

if (!info.confirmExtra) {
return title;
}

return <span>{info.name}</span>;
return (
<div>
{title}
{info.confirmExtra}
</div>
);
};

const ActionIcon = ({ icon }: { icon: React.ReactElement | string }) => {
Expand Down Expand Up @@ -94,6 +106,7 @@ const ActionsConfirm: React.FC<{
cancelText={t('Cancel')}
getPopupContainer={() => ref.current ?? document.body}
onConfirm={info.onClick}
onVisibleChange={info.onConfirmVisibleChange}
overlayClassName={`${prefix}-pop-confirm`}
>
<div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { Checkbox } from 'antd';
import { AppState } from '@/store';
import { connect, ConnectedComponent } from 'react-redux';
import { ToolInstance } from '@/store/annotation/types';
Expand All @@ -17,6 +18,8 @@ import UnifyParamsModal from '../../../../components/pointCloudView/components/U
import { useSingleBox } from '@/components/pointCloudView/hooks/useSingleBox';
import { composeResultByToolInstance } from '@/store/annotation/reducer';
import { useStatus } from '@/components/pointCloudView/hooks/useStatus';
import { ICustomToolInstance } from '@/hooks/annotation';
import { POINT_CLOUD_DEFAULT_STEP } from '@labelbee/lb-utils';

const mapStateToProps = (state: AppState) => {
const stepInfo = StepUtils.getCurrentStepInfo(state.annotation?.step, state.annotation?.stepList);
Expand Down Expand Up @@ -79,12 +82,52 @@ export const PointCloudOperation: ConnectedComponent<
const operationList = useOperationList(toolInstance);
const [isShowModal, setShowModal] = useState(false);
const [composeImgList, setComposeImgList] = useState<IFileItem[]>([]);
const [keepPreResult, setKeepPreResult] = useState(false);
const { isPointCloudDetectionPattern, isPointCloudSegmentationPattern } = useStatus();

const config = jsonParser(stepInfo.config);
const currentData = imgList?.[imgIndex];
const hasPreResult = useMemo(() => {
const preStep = jsonParser(currentData?.preResult)?.[POINT_CLOUD_DEFAULT_STEP];
return Boolean(preStep);
}, [currentData?.preResult]);

// Detection-only: keep-pre-annotation restore is not wired for segmentation clearResult.
const showKeepPreResultOption = hasPreResult && isPointCloudDetectionPattern;

const emptyOperation: IOperationConfig = {
...operationList.empty,
confirmExtra: showKeepPreResultOption ? (
<div
onClick={(e) => e.stopPropagation()}
style={{ marginTop: 8, textAlign: 'left', width: '100%' }}
>
<Checkbox
checked={keepPreResult}
onChange={(e) => setKeepPreResult(e.target.checked)}
style={{ marginLeft: 0 }}
>
{t('KeepPreAnnotation')}
</Checkbox>
</div>
) : undefined,
onConfirmVisibleChange: (visible) => {
if (visible) {
setKeepPreResult(false);
}
},
onClick: () => {
const pointCloudToolInstance = toolInstance as unknown as ICustomToolInstance;
pointCloudToolInstance.clearResult({
keepPreResult: showKeepPreResultOption && keepPreResult,
});
setKeepPreResult(false);
},
};

let allOperation: IOperationConfig[] = [
operationList.copyPrevious,
operationList.empty,
emptyOperation,
operationList.setValidity,
];

Expand Down
4 changes: 4 additions & 0 deletions packages/lb-utils/src/i18n/resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"OriginalScale": "Original Scale",
"CopyThePrevious": "Copy Prev",
"ClearLabel": "Clear",
"KeepPreAnnotation": "Keep pre-annotation",
"ClearKeepPreAnnotationFailed": "Failed to clear and restore pre-annotation",
"SetAsInvalid": "Set As Invalid",
"SetAsValid": "Set As Valid",
"Confirm": "Confirm",
Expand Down Expand Up @@ -408,6 +410,8 @@
"OriginalScale": "按原图比例显示",
"CopyThePrevious": "复制上张",
"ClearLabel": "清空标注",
"KeepPreAnnotation": "保留预标注数据",
"ClearKeepPreAnnotationFailed": "清空并保留预标注失败",
"SetAsInvalid": "标为无效",
"SetAsValid": "取消无效",
"Confirm": "确认",
Expand Down
1 change: 1 addition & 0 deletions packages/wavesurfer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"lint-staged": "^13.0.3",
"load-script": "^2.0.0",
"pre-commit": "^1.2.2",
"terser-webpack-plugin": "^5.6.1",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.3",
Expand Down
Loading