修正GT未知类别导入为待分类
- GT Mask 未知 maskid 选择保留时落到黑色 maskid:0 的待分类类别,而不是绿色未定义类别。 - 前端导入预览中未知 maskid 使用黑色待分类覆盖色,并把按钮文案改为导入为待分类。 - 待分类兜底颜色统一为 #000000,和模板保留类、GT_label/Pro_label 导出规则一致。 - 补充后端回归断言并更新 AGENTS 与文档说明,保留 gt_unknown_class 和原始 gt_label_value 供后续重命名追溯。
This commit is contained in:
@@ -265,7 +265,7 @@ describe('VideoWorkspace', () => {
|
||||
className: '待分类',
|
||||
classMaskId: 0,
|
||||
classId: undefined,
|
||||
color: '#9ca3af',
|
||||
color: '#000000',
|
||||
saved: false,
|
||||
saveStatus: 'dirty',
|
||||
metadata: expect.objectContaining({
|
||||
@@ -1712,8 +1712,8 @@ describe('VideoWorkspace', () => {
|
||||
const file = new File(['mask'], 'mask.png', { type: 'image/png' });
|
||||
fireEvent.change(fileInput, { target: { files: [file] } });
|
||||
expect(screen.getByText('导入结果预览')).toBeInTheDocument();
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: '导入为未定义' })).not.toBeDisabled());
|
||||
fireEvent.click(screen.getByRole('button', { name: '导入为未定义' }));
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: '导入为待分类' })).not.toBeDisabled());
|
||||
fireEvent.click(screen.getByRole('button', { name: '导入为待分类' }));
|
||||
|
||||
await waitFor(() => expect(apiMock.importGtMask).toHaveBeenCalledWith(file, '1', '10', null, {
|
||||
unknownColorPolicy: 'undefined',
|
||||
|
||||
@@ -148,7 +148,7 @@ const classByMaskId = (classes: TemplateClass[]) => new Map(
|
||||
);
|
||||
|
||||
const UNCLASSIFIED_MASK_LABEL = '待分类';
|
||||
const UNCLASSIFIED_MASK_COLOR = '#9ca3af';
|
||||
const UNCLASSIFIED_MASK_COLOR = '#000000';
|
||||
|
||||
const normalizeMaskAgainstTemplates = (mask: Mask, templates: Template[]): Mask => {
|
||||
const hasClassReference = Boolean(mask.classId || mask.className || mask.classMaskId !== undefined);
|
||||
@@ -1430,6 +1430,7 @@ export function VideoWorkspace({ onNavigateToAI }: { onNavigateToAI?: () => void
|
||||
const targetImage = targetContext.getImageData(0, 0, targetWidth, targetHeight);
|
||||
const overlayImage = overlayContext.createImageData(targetWidth, targetHeight);
|
||||
const classesByMaskId = classByMaskId(gtTemplateClasses);
|
||||
const hasPositiveTemplateClasses = gtTemplateClasses.some((templateClass) => Number(templateClass.maskId) > 0);
|
||||
for (let index = 0; index < targetImage.data.length; index += 4) {
|
||||
const maskId = targetImage.data[index];
|
||||
const alpha = targetImage.data[index + 3];
|
||||
@@ -1437,7 +1438,9 @@ export function VideoWorkspace({ onNavigateToAI }: { onNavigateToAI?: () => void
|
||||
const templateClass = classesByMaskId.get(maskId);
|
||||
const [red, green, blue] = templateClass
|
||||
? parseHexColor(templateClass.color)
|
||||
: fallbackMaskColor(maskId);
|
||||
: hasPositiveTemplateClasses
|
||||
? parseHexColor(UNCLASSIFIED_MASK_COLOR, [0, 0, 0])
|
||||
: fallbackMaskColor(maskId);
|
||||
overlayImage.data[index] = red;
|
||||
overlayImage.data[index + 1] = green;
|
||||
overlayImage.data[index + 2] = blue;
|
||||
@@ -1446,7 +1449,7 @@ export function VideoWorkspace({ onNavigateToAI }: { onNavigateToAI?: () => void
|
||||
overlayContext.putImageData(overlayImage, 0, 0);
|
||||
|
||||
const sortedMaskIds = Array.from(maskIds).sort((a, b) => a - b);
|
||||
const unknownMaskIds = gtTemplateClasses.length > 0
|
||||
const unknownMaskIds = hasPositiveTemplateClasses
|
||||
? sortedMaskIds.filter((maskId) => !classesByMaskId.has(maskId))
|
||||
: [];
|
||||
const resized = sourceWidth !== targetWidth || sourceHeight !== targetHeight;
|
||||
@@ -2015,7 +2018,7 @@ export function VideoWorkspace({ onNavigateToAI }: { onNavigateToAI?: () => void
|
||||
disabled={gtMaskPreview?.status !== 'ready'}
|
||||
className="rounded border border-cyan-500/30 bg-cyan-500/15 px-3 py-2 text-xs text-cyan-100 hover:bg-cyan-500/25"
|
||||
>
|
||||
导入为未定义
|
||||
导入为待分类
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user