From 133cfab7554bfffb2fc5884a538e4e566509cc05 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Fri, 22 Jul 2022 18:15:47 +0200 Subject: [PATCH 01/13] initial commit --- src/data/enums/Notification.ts | 4 +++- src/data/info/NotificationsData.ts | 5 +++++ src/utils/LabelUtil.ts | 9 +++++++++ .../LabelInputField/LabelInputField.tsx | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/data/enums/Notification.ts b/src/data/enums/Notification.ts index 6071540a..bf41069e 100644 --- a/src/data/enums/Notification.ts +++ b/src/data/enums/Notification.ts @@ -1,4 +1,6 @@ export enum Notification { EMPTY_LABEL_NAME_ERROR = 0, - NON_UNIQUE_LABEL_NAMES_ERROR = 1 + NON_UNIQUE_LABEL_NAMES_ERROR = 1, + ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING = 2, + REMOVED_USED_LABEL_NAME_WARNING = 3 } diff --git a/src/data/info/NotificationsData.ts b/src/data/info/NotificationsData.ts index 30ab2108..db2247c0 100644 --- a/src/data/info/NotificationsData.ts +++ b/src/data/info/NotificationsData.ts @@ -17,5 +17,10 @@ export const NotificationsDataMap = { header: 'Non unique label names', description: 'Looks like not all your label names are unique. Unique names are necessary to guarantee correct' + ' data export when you complete your work. Make your names unique and try again.' + }, + [Notification.ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING]: { + header: 'Used label names', + description: 'Looks like you are about to remove label name that is currently used by some of your annotations. ' + + 'Keep in mind that annotations without specified label name can not be exported.' } } diff --git a/src/utils/LabelUtil.ts b/src/utils/LabelUtil.ts index feec03d6..fffbe316 100644 --- a/src/utils/LabelUtil.ts +++ b/src/utils/LabelUtil.ts @@ -7,6 +7,15 @@ import {IPoint} from '../interfaces/IPoint'; import { sample } from 'lodash'; import {Settings} from '../settings/Settings'; +export type LabelCount = { + point: number; + line: number; + polygon: number; + rect: number; +} + +export type LabelCountSummary = Record; + export class LabelUtil { public static createLabelName(name: string): LabelName { return { diff --git a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx index 8a047d40..1c47d218 100644 --- a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx +++ b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx @@ -181,7 +181,7 @@ class LabelInputField extends React.Component { >
From 451c8416cd01970e416562ae22ad67a7224ff3ac Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Fri, 22 Jul 2022 18:15:47 +0200 Subject: [PATCH 02/13] initial commit --- src/data/enums/Notification.ts | 4 +++- src/data/info/NotificationsData.ts | 5 +++++ src/utils/LabelUtil.ts | 9 +++++++++ .../LabelInputField/LabelInputField.tsx | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/data/enums/Notification.ts b/src/data/enums/Notification.ts index 6071540a..bf41069e 100644 --- a/src/data/enums/Notification.ts +++ b/src/data/enums/Notification.ts @@ -1,4 +1,6 @@ export enum Notification { EMPTY_LABEL_NAME_ERROR = 0, - NON_UNIQUE_LABEL_NAMES_ERROR = 1 + NON_UNIQUE_LABEL_NAMES_ERROR = 1, + ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING = 2, + REMOVED_USED_LABEL_NAME_WARNING = 3 } diff --git a/src/data/info/NotificationsData.ts b/src/data/info/NotificationsData.ts index 30ab2108..db2247c0 100644 --- a/src/data/info/NotificationsData.ts +++ b/src/data/info/NotificationsData.ts @@ -17,5 +17,10 @@ export const NotificationsDataMap = { header: 'Non unique label names', description: 'Looks like not all your label names are unique. Unique names are necessary to guarantee correct' + ' data export when you complete your work. Make your names unique and try again.' + }, + [Notification.ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING]: { + header: 'Used label names', + description: 'Looks like you are about to remove label name that is currently used by some of your annotations. ' + + 'Keep in mind that annotations without specified label name can not be exported.' } } diff --git a/src/utils/LabelUtil.ts b/src/utils/LabelUtil.ts index feec03d6..fffbe316 100644 --- a/src/utils/LabelUtil.ts +++ b/src/utils/LabelUtil.ts @@ -7,6 +7,15 @@ import {IPoint} from '../interfaces/IPoint'; import { sample } from 'lodash'; import {Settings} from '../settings/Settings'; +export type LabelCount = { + point: number; + line: number; + polygon: number; + rect: number; +} + +export type LabelCountSummary = Record; + export class LabelUtil { public static createLabelName(name: string): LabelName { return { diff --git a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx index 786472f1..35d324e9 100644 --- a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx +++ b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx @@ -181,7 +181,7 @@ class LabelInputField extends React.Component { >
From c412d61bd465ce5b9a025ec2599609b6b8d34b08 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Fri, 22 Jul 2022 18:15:47 +0200 Subject: [PATCH 03/13] initial commit --- src/data/enums/Notification.ts | 4 +++- src/data/info/NotificationsData.ts | 5 +++++ src/utils/LabelUtil.ts | 9 +++++++++ .../LabelInputField/LabelInputField.tsx | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/data/enums/Notification.ts b/src/data/enums/Notification.ts index 6071540a..bf41069e 100644 --- a/src/data/enums/Notification.ts +++ b/src/data/enums/Notification.ts @@ -1,4 +1,6 @@ export enum Notification { EMPTY_LABEL_NAME_ERROR = 0, - NON_UNIQUE_LABEL_NAMES_ERROR = 1 + NON_UNIQUE_LABEL_NAMES_ERROR = 1, + ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING = 2, + REMOVED_USED_LABEL_NAME_WARNING = 3 } diff --git a/src/data/info/NotificationsData.ts b/src/data/info/NotificationsData.ts index 30ab2108..db2247c0 100644 --- a/src/data/info/NotificationsData.ts +++ b/src/data/info/NotificationsData.ts @@ -17,5 +17,10 @@ export const NotificationsDataMap = { header: 'Non unique label names', description: 'Looks like not all your label names are unique. Unique names are necessary to guarantee correct' + ' data export when you complete your work. Make your names unique and try again.' + }, + [Notification.ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING]: { + header: 'Used label names', + description: 'Looks like you are about to remove label name that is currently used by some of your annotations. ' + + 'Keep in mind that annotations without specified label name can not be exported.' } } diff --git a/src/utils/LabelUtil.ts b/src/utils/LabelUtil.ts index feec03d6..fffbe316 100644 --- a/src/utils/LabelUtil.ts +++ b/src/utils/LabelUtil.ts @@ -7,6 +7,15 @@ import {IPoint} from '../interfaces/IPoint'; import { sample } from 'lodash'; import {Settings} from '../settings/Settings'; +export type LabelCount = { + point: number; + line: number; + polygon: number; + rect: number; +} + +export type LabelCountSummary = Record; + export class LabelUtil { public static createLabelName(name: string): LabelName { return { diff --git a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx index 786472f1..35d324e9 100644 --- a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx +++ b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx @@ -181,7 +181,7 @@ class LabelInputField extends React.Component { >
From c076d492022670cf8f33d59e90056e4a06ca6464 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Mon, 1 Aug 2022 19:39:53 +0200 Subject: [PATCH 04/13] unit tests for LabelUtil.calculateMissingLabelNamesIds and LabelUtil.calculateLabelCountSummary added. Refactor to create createLabelLine --- mkdocs.yml | 2 +- package-lock.json | 2 +- package.json | 2 +- src/logic/render/LineRenderEngine.ts | 9 +- src/utils/LabelUtil.ts | 30 ++- src/utils/__tests__/LabelUtil.test.ts | 248 +++++++++++++++++- .../InsertLabelNamesPopup.tsx | 3 +- vite.config.ts | 48 ++-- 8 files changed, 305 insertions(+), 39 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index fa518bec..dedabd4d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -15,7 +15,7 @@ copyright: Copyright © 2019 Piotr Skalski # Customization extra: - version: 1.8.0-alpha + version: 1.9.0-alpha social: - icon: fontawesome/brands/github link: https://github.com/SkalskiP diff --git a/package-lock.json b/package-lock.json index db672389..c707dace 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "make-sense", - "version": "1.0.0", + "version": "1.9.0-alpha", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 90865e16..72335fae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "make-sense", - "version": "1.0.0", + "version": "1.9.0-alpha", "private": true, "dependencies": { "@emotion/react": "^11.9.3", diff --git a/src/logic/render/LineRenderEngine.ts b/src/logic/render/LineRenderEngine.ts index 008654f8..84afd435 100644 --- a/src/logic/render/LineRenderEngine.ts +++ b/src/logic/render/LineRenderEngine.ts @@ -17,13 +17,13 @@ import {EditorActions} from '../actions/EditorActions'; import {LabelsSelector} from '../../store/selectors/LabelsSelector'; import {DrawUtil} from '../../utils/DrawUtil'; import {GeneralSelector} from '../../store/selectors/GeneralSelector'; -import { v4 as uuidv4 } from 'uuid'; import {ILine} from '../../interfaces/ILine'; import {LineUtil} from '../../utils/LineUtil'; import {updateCustomCursorStyle} from '../../store/general/actionCreators'; import {CustomCursorStyle} from '../../data/enums/CustomCursorStyle'; import {LineAnchorType} from '../../data/enums/LineAnchorType'; import {Settings} from '../../settings/Settings'; +import {LabelUtil} from '../../utils/LabelUtil'; export class LineRenderEngine extends BaseRenderEngine { @@ -203,12 +203,7 @@ export class LineRenderEngine extends BaseRenderEngine { const lineOnImage = RenderEngineUtil.transferLineFromViewPortContentToImage(lineOnCanvas, data); const activeLabelId = LabelsSelector.getActiveLabelNameId(); const imageData: ImageData = LabelsSelector.getActiveImageData(); - const labelLine: LabelLine = { - id: uuidv4(), - labelId: activeLabelId, - line: lineOnImage, - isVisible: true - }; + const labelLine: LabelLine = LabelUtil.createLabelLine(activeLabelId, lineOnImage); imageData.labelLines.push(labelLine); store.dispatch(updateImageDataById(imageData.id, imageData)); store.dispatch(updateFirstLabelCreatedFlag(true)); diff --git a/src/utils/LabelUtil.ts b/src/utils/LabelUtil.ts index fffbe316..f0cd9115 100644 --- a/src/utils/LabelUtil.ts +++ b/src/utils/LabelUtil.ts @@ -1,4 +1,4 @@ -import {Annotation, LabelName, LabelPoint, LabelPolygon, LabelRect} from '../store/labels/types'; +import {Annotation, ImageData, LabelLine, LabelName, LabelPoint, LabelPolygon, LabelRect} from '../store/labels/types'; import { v4 as uuidv4 } from 'uuid'; import {find} from 'lodash'; import {IRect} from '../interfaces/IRect'; @@ -6,6 +6,7 @@ import {LabelStatus} from '../data/enums/LabelStatus'; import {IPoint} from '../interfaces/IPoint'; import { sample } from 'lodash'; import {Settings} from '../settings/Settings'; +import { ILine } from 'src/interfaces/ILine'; export type LabelCount = { point: number; @@ -14,7 +15,7 @@ export type LabelCount = { rect: number; } -export type LabelCountSummary = Record; +export type LabelCountSummary = Record; export class LabelUtil { public static createLabelName(name: string): LabelName { @@ -58,6 +59,15 @@ export class LabelUtil { } } + public static createLabelLine(labelId: string, line: ILine): LabelLine { + return { + id: uuidv4(), + labelId, + line, + isVisible: true + } + } + public static toggleAnnotationVisibility(annotation: AnnotationType): AnnotationType { return { ...annotation, @@ -65,7 +75,21 @@ export class LabelUtil { } } - public static labelNamesIdsDiff(oldLabelNames: LabelName[], newLabelNames: LabelName[]): string[] { + public static calculateLabelCountSummary(labels: LabelName[], imagesData: ImageData[]): LabelCountSummary { + let labelCount = labels.reduce((acc: LabelCountSummary, label: LabelName) => { + acc[label.id] = { point: 0, line: 0, polygon: 0, rect: 0} + return acc; + }, {}); + labelCount = imagesData.reduce((acc: LabelCountSummary, imageData: ImageData) => { + for (const labelRect of imageData.labelRects) { + acc[labelRect.id].rect += 1 + } + return acc; + }, labelCount) + return labelCount + } + + public static calculateMissingLabelNamesIds(oldLabelNames: LabelName[], newLabelNames: LabelName[]): string[] { return oldLabelNames.reduce((missingIds: string[], labelName: LabelName) => { if (!find(newLabelNames, { 'id': labelName.id })) { missingIds.push(labelName.id); diff --git a/src/utils/__tests__/LabelUtil.test.ts b/src/utils/__tests__/LabelUtil.test.ts index 13a0e599..eb06ed69 100644 --- a/src/utils/__tests__/LabelUtil.test.ts +++ b/src/utils/__tests__/LabelUtil.test.ts @@ -1,13 +1,34 @@ import { IRect } from '../../interfaces/IRect'; import { LabelUtil } from '../LabelUtil'; -import {LabelPoint, LabelPolygon, LabelRect} from '../../store/labels/types'; +import {LabelLine, LabelPoint, LabelPolygon, LabelRect} from '../../store/labels/types'; import {LabelStatus} from '../../data/enums/LabelStatus'; import {IPoint} from '../../interfaces/IPoint'; +import {ImageData} from '../../store/labels/types'; const mockUUID: string = '123e4567-e89b-12d3-a456-426614174000' jest.mock('uuid', () => ({ v4: () => mockUUID })); +const mockImageData = ( + labelRects: LabelRect[] = [], + labelPoints: LabelPoint[] = [], + labelLines: LabelLine[] = [], + labelPolygons: LabelPolygon[] = [] +): ImageData => { + return { + id: mockUUID, + fileData: null, + loadStatus: true, + labelRects, + labelPoints, + labelLines, + labelPolygons, + labelNameIds: [], + isVisitedByObjectDetector: false, + isVisitedByPoseDetector: false + } +} + describe('LabelUtil createLabelRect method', () => { it('return correct LabelRect object', () => { // given @@ -94,3 +115,228 @@ describe('LabelUtil createLabelPoint method', () => { expect(result).toEqual(expectedResult); }); }); + +describe('LabelUtil.calculateMissingLabelNamesIds tests', () => { + test('return empty list when oldLabelNames and newLabelNames are empty', () => { + // given + const oldLabelNames = []; + const newLabelNames = []; + + // when + const result = LabelUtil.calculateMissingLabelNamesIds(oldLabelNames, newLabelNames); + + // then + const expectedResult = []; + expect(result).toEqual(expectedResult); + }); + + test('return correct result when oldLabelNames contains items and newLabelNames is empty', () => { + // given + const oldLabelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const newLabelNames = []; + + // when + const result = LabelUtil.calculateMissingLabelNamesIds(oldLabelNames, newLabelNames); + + // then + const expectedResult = ['label-id-1', 'label-id-2', 'label-id-3']; + expect(result).toEqual(expectedResult); + }); + + test('return empty list when oldLabelNames is empty and newLabelNames contains items', () => { + // given + const oldLabelNames = []; + const newLabelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + + + // when + const result = LabelUtil.calculateMissingLabelNamesIds(oldLabelNames, newLabelNames); + + // then + const expectedResult = []; + expect(result).toEqual(expectedResult); + }); + + test('return empty list when oldLabelNames and newLabelNames are the same', () => { + // given + const oldLabelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const newLabelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + + // when + const result = LabelUtil.calculateMissingLabelNamesIds(oldLabelNames, newLabelNames); + + // then + const expectedResult = []; + expect(result).toEqual(expectedResult); + }); + + test('return correct list with oldLabelNames and newLabelNames ids diff', () => { + // given + const oldLabelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const newLabelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-3', + name: 'label-name-3' + }, + { + id: 'label-id-4', + name: 'label-name-3' + } + ]; + + // when + const result = LabelUtil.calculateMissingLabelNamesIds(oldLabelNames, newLabelNames); + + // then + const expectedResult = ['label-id-2']; + expect(result).toEqual(expectedResult); + }); +}); + +describe('LabelUtil.calculateLabelCountSummary tests', () => { + test('return empty summary object when labelNames is empty', () => { + // given + const labelNames = []; + const imagesData = []; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = {}; + expect(result).toEqual(expectedResult); + }); + + test('return summary object with empty counts when imagesData is empty', () => { + // given + const labelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const imagesData = []; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = { + 'label-id-1': { point: 0, line: 0, polygon: 0, rect: 0}, + 'label-id-2': { point: 0, line: 0, polygon: 0, rect: 0}, + 'label-id-3': { point: 0, line: 0, polygon: 0, rect: 0}, + }; + expect(result).toEqual(expectedResult); + }); + + test('return summary object with empty counts when imagesData does not contain any annotations', () => { + // given + const labelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const imagesData = [ + mockImageData(), + mockImageData(), + mockImageData() + ]; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = { + 'label-id-1': { point: 0, line: 0, polygon: 0, rect: 0}, + 'label-id-2': { point: 0, line: 0, polygon: 0, rect: 0}, + 'label-id-3': { point: 0, line: 0, polygon: 0, rect: 0}, + }; + expect(result).toEqual(expectedResult); + }); +}); diff --git a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx index 73d9da0e..4246fb5e 100644 --- a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx +++ b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx @@ -184,7 +184,8 @@ const InsertLabelNamesPopup: React.FC = ( const onUpdateAcceptCallback = () => { const nonEmptyLabelNames: LabelName[] = reject(labelNames, (labelName: LabelName) => labelName.name.length === 0); - const missingIds: string[] = LabelUtil.labelNamesIdsDiff(LabelsSelector.getLabelNames(), nonEmptyLabelNames); + const missingIds: string[] = LabelUtil.calculateMissingLabelNamesIds( + LabelsSelector.getLabelNames(), nonEmptyLabelNames); LabelActions.removeLabelNames(missingIds); updateLabelNamesAction(nonEmptyLabelNames); updateActivePopupTypeAction(null); diff --git a/vite.config.ts b/vite.config.ts index 94342289..04bd6948 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,39 +3,39 @@ import { loadEnv, UserConfig, UserConfigExport, -} from "vite"; +} from 'vite'; -import react from "@vitejs/plugin-react"; +import react from '@vitejs/plugin-react'; export default ({ mode }: UserConfig): UserConfigExport => { - process.env = { ...process.env, ...loadEnv(mode || "development", process.cwd()) }; + process.env = { ...process.env, ...loadEnv(mode || 'development', process.cwd()) }; return defineConfig({ plugins: [react()], build: { - minify: "terser", - sourcemap: mode === "development", + minify: 'terser', + sourcemap: mode === 'development', chunkSizeWarningLimit: 1024 * 1024, rollupOptions: { treeshake: true, maxParallelFileReads: 4, output: { manualChunks: { - lodash: ["lodash"], - classnames: ["classnames"], - runtime: ["react", "react-is"], - "runtime-dom": ["react-dom"], + lodash: ['lodash'], + classnames: ['classnames'], + runtime: ['react', 'react-is'], + 'runtime-dom': ['react-dom'], - ai: ["@tensorflow/tfjs", - "@tensorflow/tfjs-backend-cpu", - "@tensorflow/tfjs-backend-webgl", - "@tensorflow/tfjs-core", - "@tensorflow/tfjs-node"], + ai: ['@tensorflow/tfjs', + '@tensorflow/tfjs-backend-cpu', + '@tensorflow/tfjs-backend-webgl', + '@tensorflow/tfjs-core', + '@tensorflow/tfjs-node'], models: [ - "@tensorflow-models/coco-ssd", - "@tensorflow-models/posenet", + '@tensorflow-models/coco-ssd', + '@tensorflow-models/posenet', ], - ui: ["@mui/material", "@mui/system"], - moment: ["moment"] + ui: ['@mui/material', '@mui/system'], + moment: ['moment'] }, }, @@ -46,17 +46,17 @@ export default ({ mode }: UserConfig): UserConfigExport => { }, css: { modules: { - generateScopedName: mode === "development" ? "[name]__[local]___[hash:base64:5]" : "[hash:base64:8]", - scopeBehaviour: "local", - localsConvention: "camelCase", + generateScopedName: mode === 'development' ? '[name]__[local]___[hash:base64:5]' : '[hash:base64:8]', + scopeBehaviour: 'local', + localsConvention: 'camelCase', }, postcss: { plugins: [ { - postcssPlugin: "internal:charset-removal", + postcssPlugin: 'internal:charset-removal', AtRule: { charset: (atRule) => { - if (atRule.name === "charset") { + if (atRule.name === 'charset') { atRule.remove(); } }, @@ -65,6 +65,6 @@ export default ({ mode }: UserConfig): UserConfigExport => { ], }, }, - + }); }; From 89642aa63abbd62fbfdb0f93d0cd666a157f05f2 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Mon, 1 Aug 2022 22:22:19 +0200 Subject: [PATCH 05/13] unit tests for LabelUtil.calculateLabelCountSummary --- src/utils/LabelUtil.ts | 11 +- src/utils/__tests__/LabelUtil.test.ts | 210 +++++++++++++++++++++++++- 2 files changed, 214 insertions(+), 7 deletions(-) diff --git a/src/utils/LabelUtil.ts b/src/utils/LabelUtil.ts index f0cd9115..62039bcd 100644 --- a/src/utils/LabelUtil.ts +++ b/src/utils/LabelUtil.ts @@ -82,7 +82,16 @@ export class LabelUtil { }, {}); labelCount = imagesData.reduce((acc: LabelCountSummary, imageData: ImageData) => { for (const labelRect of imageData.labelRects) { - acc[labelRect.id].rect += 1 + acc[labelRect.labelId].rect += 1 + } + for (const labelPoint of imageData.labelPoints) { + acc[labelPoint.labelId].point += 1 + } + for (const labelLine of imageData.labelLines) { + acc[labelLine.labelId].line += 1 + } + for (const labelPolygon of imageData.labelPolygons) { + acc[labelPolygon.labelId].polygon += 1 } return acc; }, labelCount) diff --git a/src/utils/__tests__/LabelUtil.test.ts b/src/utils/__tests__/LabelUtil.test.ts index eb06ed69..de0d4867 100644 --- a/src/utils/__tests__/LabelUtil.test.ts +++ b/src/utils/__tests__/LabelUtil.test.ts @@ -4,6 +4,7 @@ import {LabelLine, LabelPoint, LabelPolygon, LabelRect} from '../../store/labels import {LabelStatus} from '../../data/enums/LabelStatus'; import {IPoint} from '../../interfaces/IPoint'; import {ImageData} from '../../store/labels/types'; +import {ILine} from '../../interfaces/ILine'; const mockUUID: string = '123e4567-e89b-12d3-a456-426614174000' @@ -29,8 +30,8 @@ const mockImageData = ( } } -describe('LabelUtil createLabelRect method', () => { - it('return correct LabelRect object', () => { +describe('LabelUtil.createLabelRect tests', () => { + test('return correct LabelRect object', () => { // given const labelId: string = '1'; const rect: IRect = { @@ -57,8 +58,8 @@ describe('LabelUtil createLabelRect method', () => { }); }); -describe('LabelUtil createLabelPolygon method', () => { - it('return correct LabelPolygon object', () => { +describe('LabelUtil.createLabelPolygon tests', () => { + test('return correct LabelPolygon object', () => { // given const labelId: string = '1'; const vertices: IPoint[] = [ @@ -90,8 +91,8 @@ describe('LabelUtil createLabelPolygon method', () => { }); }); -describe('LabelUtil createLabelPoint method', () => { - it('return correct LabelPoint object', () => { +describe('LabelUtil.createLabelPoint tests', () => { + test('return correct LabelPoint object', () => { // given const labelId: string = '1'; const point: IPoint = { @@ -116,6 +117,29 @@ describe('LabelUtil createLabelPoint method', () => { }); }); +describe('LabelUtil.createLabelLine tests', () => { + test('return correct LabelLine object', () => { + // given + const labelId: string = '1'; + const line: ILine = { + start: {x: 0, y: 0}, + end: {x: 10, y: 10} + }; + + // when + const result = LabelUtil.createLabelLine(labelId, line); + + // then + const expectedResult: LabelLine = { + id: mockUUID, + labelId, + line, + isVisible: true + } + expect(result).toEqual(expectedResult); + }); +}); + describe('LabelUtil.calculateMissingLabelNamesIds tests', () => { test('return empty list when oldLabelNames and newLabelNames are empty', () => { // given @@ -339,4 +363,178 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { }; expect(result).toEqual(expectedResult); }); + + test('return summary object with only rect counts when imagesData does contain only rect annotations', () => { + // given + const labelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const imagesData = [ + mockImageData( + [ + LabelUtil.createLabelRect('label-id-1', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-1', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-2', {x: 0, y: 0, width: 1, height: 1}), + ] + ), + mockImageData( + [ + LabelUtil.createLabelRect('label-id-1', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-2', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-2', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-3', {x: 0, y: 0, width: 1, height: 1}), + ] + ), + mockImageData() + ]; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = { + 'label-id-1': { point: 0, line: 0, polygon: 0, rect: 3}, + 'label-id-2': { point: 0, line: 0, polygon: 0, rect: 3}, + 'label-id-3': { point: 0, line: 0, polygon: 0, rect: 1}, + }; + expect(result).toEqual(expectedResult); + }); + + test('return summary object with only point counts when imagesData does contain only point annotations', () => { + // given + const labelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const imagesData = [ + mockImageData([], + [ + LabelUtil.createLabelPoint('label-id-1', {x: 0, y: 0}), + LabelUtil.createLabelPoint('label-id-2', {x: 0, y: 0}), + ] + ), + mockImageData(), + mockImageData([], + [ + LabelUtil.createLabelPoint('label-id-3', {x: 0, y: 0}), + LabelUtil.createLabelPoint('label-id-2', {x: 0, y: 0}), + LabelUtil.createLabelPoint('label-id-2', {x: 0, y: 0}), + ] + ), + ]; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = { + 'label-id-1': { point: 1, line: 0, polygon: 0, rect: 0}, + 'label-id-2': { point: 3, line: 0, polygon: 0, rect: 0}, + 'label-id-3': { point: 1, line: 0, polygon: 0, rect: 0}, + }; + expect(result).toEqual(expectedResult); + }); + + test('return summary object with only line counts when imagesData does contain only line annotations', () => { + // given + const labelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const imagesData = [ + mockImageData([], [], + [ + LabelUtil.createLabelLine('label-id-1', {start: {x: 0, y: 0}, end: {x: 1, y: 1}}), + LabelUtil.createLabelLine('label-id-1', {start: {x: 0, y: 0}, end: {x: 1, y: 1}}), + LabelUtil.createLabelLine('label-id-1', {start: {x: 0, y: 0}, end: {x: 1, y: 1}}), + ] + ), + mockImageData([], [], + [ + LabelUtil.createLabelLine('label-id-2', {start: {x: 0, y: 0}, end: {x: 1, y: 1}}), + LabelUtil.createLabelLine('label-id-2', {start: {x: 0, y: 0}, end: {x: 1, y: 1}}) + ] + ), + ]; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = { + 'label-id-1': { point: 0, line: 3, polygon: 0, rect: 0}, + 'label-id-2': { point: 0, line: 2, polygon: 0, rect: 0}, + 'label-id-3': { point: 0, line: 0, polygon: 0, rect: 0}, + }; + expect(result).toEqual(expectedResult); + }); + + test('return summary object with only polygon counts when imagesData does contain only polygon annotations', () => { + // given + const labelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const imagesData = [ + mockImageData([], [], [], + [ + LabelUtil.createLabelPolygon('label-id-1', [{x: 0, y: 0}, {x: 1, y: 0}, {x: 0, y: 1}]), + LabelUtil.createLabelPolygon('label-id-2', [{x: 0, y: 0}, {x: 1, y: 0}, {x: 0, y: 1}]), + LabelUtil.createLabelPolygon('label-id-3', [{x: 0, y: 0}, {x: 1, y: 0}, {x: 0, y: 1}]), + ] + ) + ]; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = { + 'label-id-1': { point: 0, line: 0, polygon: 1, rect: 0}, + 'label-id-2': { point: 0, line: 0, polygon: 1, rect: 0}, + 'label-id-3': { point: 0, line: 0, polygon: 1, rect: 0}, + }; + expect(result).toEqual(expectedResult); + }); }); From 2af01ce8f2c2a679831e520c107191f7c81d8bd2 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Tue, 2 Aug 2022 12:24:56 +0200 Subject: [PATCH 06/13] warning when user is ubout to delete used label --- package-lock.json | 2 +- .../InsertLabelNamesPopup/InsertLabelNamesPopup.tsx | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index c707dace..679e84f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "make-sense", - "version": "1.0.0", + "version": "1.9.0-alpha", "dependencies": { "@emotion/react": "^11.9.3", "@emotion/styled": "^11.9.3", diff --git a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx index 4246fb5e..5b40555b 100644 --- a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx +++ b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx @@ -68,6 +68,11 @@ const InsertLabelNamesPopup: React.FC = ( }) => { const [labelNames, setLabelNames] = useState(LabelsSelector.getLabelNames()); + const labelsCountSummary = LabelUtil.calculateLabelCountSummary( + LabelsSelector.getLabelNames(), + LabelsSelector.getImagesData() + ) + const validateEmptyLabelNames = (): boolean => { const emptyLabelNames = filter(labelNames, (labelName: LabelName) => labelName.name === ''); return emptyLabelNames.length === 0; @@ -107,6 +112,10 @@ const InsertLabelNamesPopup: React.FC = ( const deleteLabelNameCallback = (id: string) => { const newLabelNames = reject(labelNames, { id }); setLabelNames(newLabelNames); + if (!Object.values(labelsCountSummary[id]).every((value: number) => value === 0)) { + submitNewNotificationAction(NotificationUtil + .createWarningNotification(NotificationsDataMap[Notification.ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING])); + } }; const togglePerClassColorationCallback = () => { From 2360919f5bff9b18c00f2917f9937ea2a1dcd1c3 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Tue, 2 Aug 2022 12:53:30 +0200 Subject: [PATCH 07/13] fix problems with LabelUtil.calculateLabelCountSummary when annotation has labelId === null --- src/data/info/NotificationsData.ts | 3 +- src/utils/LabelUtil.ts | 29 ++--- src/utils/__tests__/LabelUtil.test.ts | 101 +++++++++++++++++- .../ImagePreview/ImagePreview.tsx | 46 ++++---- 4 files changed, 137 insertions(+), 42 deletions(-) diff --git a/src/data/info/NotificationsData.ts b/src/data/info/NotificationsData.ts index db2247c0..8c6ae7d4 100644 --- a/src/data/info/NotificationsData.ts +++ b/src/data/info/NotificationsData.ts @@ -21,6 +21,7 @@ export const NotificationsDataMap = { [Notification.ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING]: { header: 'Used label names', description: 'Looks like you are about to remove label name that is currently used by some of your annotations. ' + - 'Keep in mind that annotations without specified label name can not be exported.' + 'Keep in mind that annotations without specified label name can not be exported. You can still abort by ' + + 'pressing "Cancel".' } } diff --git a/src/utils/LabelUtil.ts b/src/utils/LabelUtil.ts index 62039bcd..19dfefc1 100644 --- a/src/utils/LabelUtil.ts +++ b/src/utils/LabelUtil.ts @@ -1,12 +1,11 @@ import {Annotation, ImageData, LabelLine, LabelName, LabelPoint, LabelPolygon, LabelRect} from '../store/labels/types'; -import { v4 as uuidv4 } from 'uuid'; -import {find} from 'lodash'; +import {v4 as uuidv4} from 'uuid'; +import {find, sample} from 'lodash'; import {IRect} from '../interfaces/IRect'; import {LabelStatus} from '../data/enums/LabelStatus'; import {IPoint} from '../interfaces/IPoint'; -import { sample } from 'lodash'; import {Settings} from '../settings/Settings'; -import { ILine } from 'src/interfaces/ILine'; +import {ILine} from 'src/interfaces/ILine'; export type LabelCount = { point: number; @@ -26,19 +25,19 @@ export class LabelUtil { } } - public static createLabelRect(labelId: string, rect: IRect): LabelRect { + public static createLabelRect(labelId: string | null, rect: IRect, status: LabelStatus = LabelStatus.ACCEPTED): LabelRect { return { id: uuidv4(), labelId, rect, isVisible: true, isCreatedByAI: false, - status: LabelStatus.ACCEPTED, + status, suggestedLabel: null } } - public static createLabelPolygon(labelId: string, vertices: IPoint[]): LabelPolygon { + public static createLabelPolygon(labelId: string | null, vertices: IPoint[]): LabelPolygon { return { id: uuidv4(), labelId, @@ -47,19 +46,19 @@ export class LabelUtil { } } - public static createLabelPoint(labelId: string, point: IPoint): LabelPoint { + public static createLabelPoint(labelId: string | null, point: IPoint, status: LabelStatus = LabelStatus.ACCEPTED): LabelPoint { return { id: uuidv4(), labelId, point, isVisible: true, isCreatedByAI: false, - status: LabelStatus.ACCEPTED, + status, suggestedLabel: null } } - public static createLabelLine(labelId: string, line: ILine): LabelLine { + public static createLabelLine(labelId: string | null, line: ILine): LabelLine { return { id: uuidv4(), labelId, @@ -82,16 +81,18 @@ export class LabelUtil { }, {}); labelCount = imagesData.reduce((acc: LabelCountSummary, imageData: ImageData) => { for (const labelRect of imageData.labelRects) { - acc[labelRect.labelId].rect += 1 + if (labelRect.labelId !== null && labelRect.status === LabelStatus.ACCEPTED) + acc[labelRect.labelId].rect += 1 } for (const labelPoint of imageData.labelPoints) { - acc[labelPoint.labelId].point += 1 + if (labelPoint.labelId !== null && labelPoint.status === LabelStatus.ACCEPTED) + acc[labelPoint.labelId].point += 1 } for (const labelLine of imageData.labelLines) { - acc[labelLine.labelId].line += 1 + if (labelLine.labelId !== null) acc[labelLine.labelId].line += 1 } for (const labelPolygon of imageData.labelPolygons) { - acc[labelPolygon.labelId].polygon += 1 + if (labelPolygon.labelId !== null) acc[labelPolygon.labelId].polygon += 1 } return acc; }, labelCount) diff --git a/src/utils/__tests__/LabelUtil.test.ts b/src/utils/__tests__/LabelUtil.test.ts index de0d4867..975557d6 100644 --- a/src/utils/__tests__/LabelUtil.test.ts +++ b/src/utils/__tests__/LabelUtil.test.ts @@ -1,9 +1,8 @@ -import { IRect } from '../../interfaces/IRect'; -import { LabelUtil } from '../LabelUtil'; -import {LabelLine, LabelPoint, LabelPolygon, LabelRect} from '../../store/labels/types'; +import {IRect} from '../../interfaces/IRect'; +import {LabelUtil} from '../LabelUtil'; +import {ImageData, LabelLine, LabelPoint, LabelPolygon, LabelRect} from '../../store/labels/types'; import {LabelStatus} from '../../data/enums/LabelStatus'; import {IPoint} from '../../interfaces/IPoint'; -import {ImageData} from '../../store/labels/types'; import {ILine} from '../../interfaces/ILine'; const mockUUID: string = '123e4567-e89b-12d3-a456-426614174000' @@ -411,6 +410,100 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { expect(result).toEqual(expectedResult); }); + test('return summary object with only rect counts when imagesData does contain only rect annotations but some labelIds are null', () => { + // given + const labelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const imagesData = [ + mockImageData( + [ + LabelUtil.createLabelRect(null, {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-1', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-2', {x: 0, y: 0, width: 1, height: 1}), + ] + ), + mockImageData( + [ + LabelUtil.createLabelRect('label-id-1', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-2', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect(null, {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-3', {x: 0, y: 0, width: 1, height: 1}), + ] + ), + mockImageData() + ]; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = { + 'label-id-1': { point: 0, line: 0, polygon: 0, rect: 2}, + 'label-id-2': { point: 0, line: 0, polygon: 0, rect: 2}, + 'label-id-3': { point: 0, line: 0, polygon: 0, rect: 1}, + }; + expect(result).toEqual(expectedResult); + }); + + test('return summary object with only rect counts when imagesData does contain only rect annotations but labels are rejected', () => { + // given + const labelNames = [ + { + id: 'label-id-1', + name: 'label-name-1' + }, + { + id: 'label-id-2', + name: 'label-name-2' + }, + { + id: 'label-id-3', + name: 'label-name-3' + } + ]; + const imagesData = [ + mockImageData( + [ + LabelUtil.createLabelRect('label-id-1', {x: 0, y: 0, width: 1, height: 1}, LabelStatus.REJECTED), + LabelUtil.createLabelRect('label-id-1', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-2', {x: 0, y: 0, width: 1, height: 1}), + ] + ), + mockImageData( + [ + LabelUtil.createLabelRect('label-id-1', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-2', {x: 0, y: 0, width: 1, height: 1}), + LabelUtil.createLabelRect('label-id-2', {x: 0, y: 0, width: 1, height: 1}, LabelStatus.REJECTED), + LabelUtil.createLabelRect('label-id-3', {x: 0, y: 0, width: 1, height: 1}), + ] + ), + mockImageData() + ]; + + // when + const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + + // then + const expectedResult = { + 'label-id-1': { point: 0, line: 0, polygon: 0, rect: 2}, + 'label-id-2': { point: 0, line: 0, polygon: 0, rect: 2}, + 'label-id-3': { point: 0, line: 0, polygon: 0, rect: 1}, + }; + expect(result).toEqual(expectedResult); + }); + test('return summary object with only point counts when imagesData does contain only point annotations', () => { // given const labelNames = [ diff --git a/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.tsx b/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.tsx index 36f573d2..f89f1065 100644 --- a/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.tsx +++ b/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.tsx @@ -1,18 +1,18 @@ -import classNames from "classnames"; +import classNames from 'classnames'; import React from 'react'; -import { connect } from "react-redux"; -import { ClipLoader } from "react-spinners"; -import { ImageLoadManager } from "../../../../logic/imageRepository/ImageLoadManager"; -import { IRect } from "../../../../interfaces/IRect"; -import { ISize } from "../../../../interfaces/ISize"; -import { ImageRepository } from "../../../../logic/imageRepository/ImageRepository"; -import { AppState } from "../../../../store"; -import { updateImageDataById } from "../../../../store/labels/actionCreators"; -import { ImageData } from "../../../../store/labels/types"; -import { FileUtil } from "../../../../utils/FileUtil"; -import { RectUtil } from "../../../../utils/RectUtil"; +import { connect } from 'react-redux'; +import { ClipLoader } from 'react-spinners'; +import { ImageLoadManager } from '../../../../logic/imageRepository/ImageLoadManager'; +import { IRect } from '../../../../interfaces/IRect'; +import { ISize } from '../../../../interfaces/ISize'; +import { ImageRepository } from '../../../../logic/imageRepository/ImageRepository'; +import { AppState } from '../../../../store'; +import { updateImageDataById } from '../../../../store/labels/actionCreators'; +import { ImageData } from '../../../../store/labels/types'; +import { FileUtil } from '../../../../utils/FileUtil'; +import { RectUtil } from '../../../../utils/RectUtil'; import './ImagePreview.scss'; -import { CSSHelper } from "../../../../logic/helpers/CSSHelper"; +import { CSSHelper } from '../../../../logic/helpers/CSSHelper'; interface IProps { imageData: ImageData; @@ -126,9 +126,9 @@ class ImagePreview extends React.Component { private getClassName = () => { return classNames( - "ImagePreview", + 'ImagePreview', { - "selected": this.props.isSelected, + 'selected': this.props.isSelected, } ); }; @@ -149,27 +149,27 @@ class ImagePreview extends React.Component { {(!!this.state.image) ? [
{this.state.image.alt} {isChecked && {"checkbox"}}
,
] : From 5761e42e55304f5009fd952a0d5c0bb732feefee Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Tue, 2 Aug 2022 13:54:16 +0200 Subject: [PATCH 08/13] Label count indicator visible in ImagePreview --- .../ImagePreview/ImagePreview.scss | 23 ++++--- .../ImagePreview/ImagePreview.tsx | 21 +++---- .../ImagesList/ImagesList.tsx | 61 +++++++++++-------- 3 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.scss b/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.scss index f1161bfa..c49ced12 100644 --- a/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.scss +++ b/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.scss @@ -29,15 +29,22 @@ user-select: none; } - .CheckBox { + .annotations-count { position: absolute; z-index: 1000; - max-width: 20px; - max-height: 20px; - bottom: -10px; - left: -10px; - filter: invert(1) brightness(35%) sepia(100%) hue-rotate(172deg) saturate(2000%); // fallback if new css variables are not supported by browser - filter: invert(1) brightness(35%) sepia(100%) hue-rotate(var(--hue-value)) saturate(2000%); + width: 24px; + height: 24px; + bottom: -12px; + left: -12px; + border-radius: 2px; + background-color: $secondaryColor; // fallback if new css variables are not supported by browser + background-color: var(--leading-color); + color: white; + font-weight: 700; + + display: flex; + justify-content: center; + align-items: center; } } @@ -67,4 +74,4 @@ transform: translate(2px, -2px); } } -} \ No newline at end of file +} diff --git a/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.tsx b/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.tsx index f89f1065..58f45462 100644 --- a/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.tsx +++ b/src/views/EditorView/SideNavigationBar/ImagePreview/ImagePreview.tsx @@ -19,8 +19,8 @@ interface IProps { style: React.CSSProperties; size: ISize; isScrolling?: boolean; - isChecked?: boolean; - onClick?: () => any; + annotationsCount?: number; + onClick?: () => void; isSelected?: boolean; updateImageDataById: (id: string, newImageData: ImageData) => any; } @@ -64,7 +64,7 @@ class ImagePreview extends React.Component { this.props.imageData.id !== nextProps.imageData.id || this.state.image !== nextState.image || this.props.isSelected !== nextProps.isSelected || - this.props.isChecked !== nextProps.isChecked + this.props.annotationsCount !== nextProps.annotationsCount ) } @@ -135,7 +135,7 @@ class ImagePreview extends React.Component { public render() { const { - isChecked, + annotationsCount, style, onClick } = this.props; @@ -160,12 +160,11 @@ class ImagePreview extends React.Component { alt={this.state.image.alt} style={{ ...this.getStyle(), left: 0, top: 0 }} /> - {isChecked && {'checkbox'}} + {annotationsCount &&
+ {annotationsCount} +
}
,
({}); export default connect( mapStateToProps, mapDispatchToProps -)(ImagePreview); \ No newline at end of file +)(ImagePreview); diff --git a/src/views/EditorView/SideNavigationBar/ImagesList/ImagesList.tsx b/src/views/EditorView/SideNavigationBar/ImagesList/ImagesList.tsx index eb425014..27239853 100644 --- a/src/views/EditorView/SideNavigationBar/ImagesList/ImagesList.tsx +++ b/src/views/EditorView/SideNavigationBar/ImagesList/ImagesList.tsx @@ -1,22 +1,23 @@ import React from 'react'; -import {connect} from "react-redux"; -import {LabelType} from "../../../../data/enums/LabelType"; -import {ISize} from "../../../../interfaces/ISize"; -import {AppState} from "../../../../store"; -import {ImageData, LabelPoint, LabelRect} from "../../../../store/labels/types"; -import {VirtualList} from "../../../Common/VirtualList/VirtualList"; -import ImagePreview from "../ImagePreview/ImagePreview"; +import {connect} from 'react-redux'; +import {LabelType} from '../../../../data/enums/LabelType'; +import {ISize} from '../../../../interfaces/ISize'; +import {AppState} from '../../../../store'; +import {ImageData, LabelName, LabelPoint, LabelRect} from '../../../../store/labels/types'; +import {VirtualList} from '../../../Common/VirtualList/VirtualList'; +import ImagePreview from '../ImagePreview/ImagePreview'; import './ImagesList.scss'; -import {ContextManager} from "../../../../logic/context/ContextManager"; -import {ContextType} from "../../../../data/enums/ContextType"; -import {ImageActions} from "../../../../logic/actions/ImageActions"; -import {EventType} from "../../../../data/enums/EventType"; -import {LabelStatus} from "../../../../data/enums/LabelStatus"; +import {ContextManager} from '../../../../logic/context/ContextManager'; +import {ContextType} from '../../../../data/enums/ContextType'; +import {ImageActions} from '../../../../logic/actions/ImageActions'; +import {EventType} from '../../../../data/enums/EventType'; +import {LabelStatus} from '../../../../data/enums/LabelStatus'; interface IProps { activeImageIndex: number; imagesData: ImageData[]; activeLabelType: LabelType; + labels: LabelName[]; } interface IState { @@ -56,50 +57,57 @@ class ImagesList extends React.Component { }) }; - private isImageChecked = (index:number): boolean => { + private getAnnotationCount = (index:number): number => { const imageData = this.props.imagesData[index] switch (this.props.activeLabelType) { case LabelType.LINE: - return imageData.labelLines.length > 0 + return imageData.labelLines.length case LabelType.IMAGE_RECOGNITION: - return imageData.labelNameIds.length > 0 + return imageData.labelNameIds.length case LabelType.POINT: return imageData.labelPoints .filter((labelPoint: LabelPoint) => labelPoint.status === LabelStatus.ACCEPTED) - .length > 0 + .length case LabelType.POLYGON: - return imageData.labelPolygons.length > 0 + return imageData.labelPolygons.length case LabelType.RECT: return imageData.labelRects .filter((labelRect: LabelRect) => labelRect.status === LabelStatus.ACCEPTED) - .length > 0 + .length } }; - private onClickHandler = (index: number) => { + private onClickHandler = (index: number): void => { ImageActions.getImageByIndex(index) }; - private renderImagePreview = (index: number, isScrolling: boolean, isVisible: boolean, style: React.CSSProperties) => { + private renderImagePreview = ( + index: number, + isScrolling: boolean, + isVisible: boolean, + style: React.CSSProperties + ) => { + const imagePreviewOnClickHandler = () => this.onClickHandler(index) return this.onClickHandler(index)} + onClick={imagePreviewOnClickHandler} isSelected={this.props.activeImageIndex === index} /> }; public render() { const { size } = this.state; + const imageListOnnClickHandler = () => ContextManager.switchCtx(ContextType.LEFT_NAVBAR) return(
this.imagesListRef = ref} - onClick={() => ContextManager.switchCtx(ContextType.LEFT_NAVBAR)} + onClick={imageListOnnClickHandler} > {!!size && ({ activeImageIndex: state.labels.activeImageIndex, imagesData: state.labels.imagesData, - activeLabelType: state.labels.activeLabelType + activeLabelType: state.labels.activeLabelType, + labels: state.labels.labels }); export default connect( mapStateToProps, mapDispatchToProps -)(ImagesList); \ No newline at end of file +)(ImagesList); From b67b6b60cd2ba3fd4f94f5f644a6faff794d35e7 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Tue, 2 Aug 2022 14:33:39 +0200 Subject: [PATCH 09/13] Updated DropDownMenuData --- public/ico/poll.png | Bin 0 -> 1474 bytes public/ico/stats.png | Bin 0 -> 1338 bytes src/data/enums/PopupWindowType.ts | 3 +- src/data/info/DropDownMenuData.ts | 24 +++++++++++++++ src/utils/LabelUtil.ts | 2 +- src/utils/__tests__/LabelUtil.test.ts | 20 ++++++------- .../InsertLabelNamesPopup.tsx | 4 +-- src/views/PopupView/PopupView.tsx | 28 +++++++++--------- 8 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 public/ico/poll.png create mode 100644 public/ico/stats.png diff --git a/public/ico/poll.png b/public/ico/poll.png new file mode 100644 index 0000000000000000000000000000000000000000..f0428cb0474b7dc01660c1ee632c2b9c97162163 GIT binary patch literal 1474 zcmV;z1wHzSP))xbvJ z4`9ZzjsWiikMmzmYfG*LHX%)n>m(#;40s#pwODSiI1_jh*nz(JY1#oi5yjkcoB=!t z{D8jEdG!m>2h1_@^F4PtiBbl|BX*7;(-5S5eI$?XvnlD!W%a1dO6@j+RCv54@lcm_pwaZA}Bu zmh`eT8=^q8&G1Fjz>B3EEx{`p0&G{kT*B8vJWQw=%`^=>R@mK~+ysnUE>J7z1aNCX zk2ABJ)_&kKt;0yiniV>b&K&JRR!v`#vDWQAS?kUfGWl{r~x}YC{)$;4Ti=rJnBJVY$y#I*L)P zpWg>uoFVu?HkKlu7NQ0WqW_oNN;PB#d0n4a5t`MIRlsE#0uSWlQsB;{51S?A8S}9O zA~qy_kaQc?4IHEbea0n7y9LeCtQBPnxBz(#^*@pfU)AKZKzZf>_r!gWWct1KVmU=E zMXgS($8iPkKt_n$9!;UyWcYCXBS^C~dHTV)8q$NuLt9#i^N6{NjZ4Ut_G1Y|Ggp4`}Bp_r;$N>j|1cZ_L191uY-9aD$VSoLBxP%Nk2qYlv zsy`5ykUb6p2?&`IveQ8z0bzIjfw+758gK|$WI33!&k@?^m*p^UA=1Bointmw1N2)C zCIFvNmkdbmQ{QSgmH@IuM*~UKb^$UBsOdP=l4v(MnoZUuGSn*5a|qEFKe8B5yR(Iu zWTbIBevhryKEI4^%W(Zzwp$D;4d2f%s=bsLcXqeMz|!#j{Gxg~!?m08g{8pK@csNE z+J}KP9Ph9cTpGTgUqov&TsIT%S_-h8v<-t}md{7}Nwf=9RgGg|k+anv6s)Rxses>^ zc*ldnRZaa^oNLpCEM%`;$g1cd4wjT%M0+lFnOA=UHx%?ZH$BMy9NLBURZ|$e#m&mi zz_F5nx1y$j=Suimie6;;o3;tQNHrO=^Z>u1Z-%xek*zqqU>WijQ`;0R9Rt>scF{0h zz+32><648j6$bpX##6{GpSDph^Z^yww`wK&X4%v*4%g*1!3y+Ev!(Yizm=bL)}wEn zX$=5(nKa2IZ=!FYDeVOwpu+2~)PN!MjV!Crkv&1GGQ}^EUi8f@s-wUgwAgAEuc2>j z9t{JVkq$VUBQ-N&x?P-y%)m0S_`m?NOl=Qln~h(Q@n*A|6AvKopu8Y%T67uKV>> zUGoCvld%!=wYDwkMrH&RO~eRcD-5G-rg$>?k>7=YA#S1A24RE94|ipsfPG{>a`%!a z5d+98`nd}6wmh52mV^Vq>%i4IVOEDS&Ba<|zw}uqfH7qG>>*_A*0;#Ox?MqR{HW$OxUGFD)Z*!kKOl%bTa+&b@1&z4phsvwyJO z&ONjC&*i%N?6c3gP$(1%&Wxl^z?dmu8?X_$6SxXo1llQ+8bsP%;xcd)co~sjFrb_0 zYRmyAfER$VL^%!w{X|#eC*W=1)+CuW92e1@xCk7iRJap$M0es_;IUAN4TUaEAH^j8u1RMa)<2G;&R+6s(Pk4%pl-UjZgO#+r<~fKRdOn;Mo}C2s|O!%lKY(T%O-?MU*Ll0%7Z zY$d;hO+Styy3v(2t znNTXxv)HzaZtIH&(uN!UfBSfcB;i-j?d!=}k$hXkKSdUTT+2aW+Z8k}nwabnJK(p9rXMO(Cs zh4HaLUjJi=>l?b8DB7xBER4$-Z{~k6K$5XUhmzch^_nEKJ$jS!xZIdPTAoEuUJR8R zt>|?giKJp2d88urI`RXWgS4WXwzJZ5cF~VDnu7*Wbc}XU<=#ktCo4jK@pX$Iql_#g zQb=wi)7~XRD#>kR#jr0fm*9m0P+CV+8vkWD6*>9ELUWrc#Ntt}_(~&C+oOL_9v7Mx zav{>|cGU@F`7+jXg5PTnYMN+}JtarzEqfAphB&fKMW6LZBm`r?VS2ZbN#I$O(=QLc zvpm^a()Z_Xp}FfF`U~GtKsn_Pt2yo6Lxj~UV zO@s~Uuh&Q{Rm>u7q8D}A$Uzrjby-gRcW$Jr?CpKVTP zKSqGB6-nh37H#A-kslTUc|BUp$KO&{_8hRmRgx9&bV(@ps9V|Zx$Ci`1$;>wVKHS- zk-;l9S0RstrIzO!+BPyxMmx2(kpDVJ-OA49H4+_m(Y!B5t?caBN!kaB>V~F`WXEpe z-tJ%0mA#obZ>6!03uM@n`>_VuT#{OzHl~o*7B!L<@Gf#UBDKiHVh*_&Nj^`u;BoR? z8#ztF%j!^!A} store.dispatch(updateActivePopupType(PopupWindowType.LABEL_COUNTS_STATISTICS)) + }, + ] + }, { name: 'Community', imageSrc: 'ico/plant.png', @@ -82,6 +98,14 @@ export const DropDownMenuData: DropDownMenuNode[] = [ imageAlt: 'bug', disabled: false, onClick: () => window.open('https://github.com/SkalskiP/make-sense/issues', '_blank') + }, + { + name: 'Vote for Next Big Feature', + description: 'Vote for next big feature that we will add to Make Sense', + imageSrc: 'ico/poll.png', + imageAlt: 'vote', + disabled: false, + onClick: () => window.open('https://github.com/SkalskiP/make-sense/discussions/269', '_blank') } ] } diff --git a/src/utils/LabelUtil.ts b/src/utils/LabelUtil.ts index 19dfefc1..f341de9c 100644 --- a/src/utils/LabelUtil.ts +++ b/src/utils/LabelUtil.ts @@ -74,7 +74,7 @@ export class LabelUtil { } } - public static calculateLabelCountSummary(labels: LabelName[], imagesData: ImageData[]): LabelCountSummary { + public static calculatePerLabelIdCountSummary(labels: LabelName[], imagesData: ImageData[]): LabelCountSummary { let labelCount = labels.reduce((acc: LabelCountSummary, label: LabelName) => { acc[label.id] = { point: 0, line: 0, polygon: 0, rect: 0} return acc; diff --git a/src/utils/__tests__/LabelUtil.test.ts b/src/utils/__tests__/LabelUtil.test.ts index 975557d6..8a3e58f2 100644 --- a/src/utils/__tests__/LabelUtil.test.ts +++ b/src/utils/__tests__/LabelUtil.test.ts @@ -285,14 +285,14 @@ describe('LabelUtil.calculateMissingLabelNamesIds tests', () => { }); }); -describe('LabelUtil.calculateLabelCountSummary tests', () => { +describe('LabelUtil.calculatePerLabelIdCountSummary tests', () => { test('return empty summary object when labelNames is empty', () => { // given const labelNames = []; const imagesData = []; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = {}; @@ -318,7 +318,7 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { const imagesData = []; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = { @@ -352,7 +352,7 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { ]; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = { @@ -399,7 +399,7 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { ]; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = { @@ -446,7 +446,7 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { ]; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = { @@ -493,7 +493,7 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { ]; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = { @@ -538,7 +538,7 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { ]; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = { @@ -582,7 +582,7 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { ]; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = { @@ -620,7 +620,7 @@ describe('LabelUtil.calculateLabelCountSummary tests', () => { ]; // when - const result = LabelUtil.calculateLabelCountSummary(labelNames, imagesData); + const result = LabelUtil.calculatePerLabelIdCountSummary(labelNames, imagesData); // then const expectedResult = { diff --git a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx index 5b40555b..43e1baa3 100644 --- a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx +++ b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx @@ -68,7 +68,7 @@ const InsertLabelNamesPopup: React.FC = ( }) => { const [labelNames, setLabelNames] = useState(LabelsSelector.getLabelNames()); - const labelsCountSummary = LabelUtil.calculateLabelCountSummary( + const labelsCountSummary = LabelUtil.calculatePerLabelIdCountSummary( LabelsSelector.getLabelNames(), LabelsSelector.getImagesData() ) @@ -112,7 +112,7 @@ const InsertLabelNamesPopup: React.FC = ( const deleteLabelNameCallback = (id: string) => { const newLabelNames = reject(labelNames, { id }); setLabelNames(newLabelNames); - if (!Object.values(labelsCountSummary[id]).every((value: number) => value === 0)) { + if (id in labelsCountSummary && !Object.values(labelsCountSummary[id]).every((value: number) => value === 0)) { submitNewNotificationAction(NotificationUtil .createWarningNotification(NotificationsDataMap[Notification.ABOUT_TO_REMOVE_USED_LABEL_NAME_WARNING])); } diff --git a/src/views/PopupView/PopupView.tsx b/src/views/PopupView/PopupView.tsx index 700b47b5..c7651f1f 100644 --- a/src/views/PopupView/PopupView.tsx +++ b/src/views/PopupView/PopupView.tsx @@ -1,18 +1,18 @@ import React from 'react'; import './PopupView.scss'; -import { PopupWindowType } from "../../data/enums/PopupWindowType"; -import { AppState } from "../../store"; -import { connect } from "react-redux"; -import LoadLabelsPopup from "./LoadLabelNamesPopup/LoadLabelNamesPopup"; -import InsertLabelNamesPopup from "./InsertLabelNamesPopup/InsertLabelNamesPopup"; -import ExitProjectPopup from "./ExitProjectPopup/ExitProjectPopup"; -import LoadMoreImagesPopup from "./LoadMoreImagesPopup/LoadMoreImagesPopup"; -import { LoadModelPopup } from "./LoadModelPopup/LoadModelPopup"; -import SuggestLabelNamesPopup from "./SuggestLabelNamesPopup/SuggestLabelNamesPopup"; -import { CSSHelper } from "../../logic/helpers/CSSHelper"; -import { ClipLoader } from "react-spinners"; -import ImportLabelPopup from "./ImportLabelPopup/ImportLabelPopup"; -import ExportLabelPopup from "./ExportLabelsPopup/ExportLabelPopup"; +import { PopupWindowType } from '../../data/enums/PopupWindowType'; +import { AppState } from '../../store'; +import { connect } from 'react-redux'; +import LoadLabelsPopup from './LoadLabelNamesPopup/LoadLabelNamesPopup'; +import InsertLabelNamesPopup from './InsertLabelNamesPopup/InsertLabelNamesPopup'; +import ExitProjectPopup from './ExitProjectPopup/ExitProjectPopup'; +import LoadMoreImagesPopup from './LoadMoreImagesPopup/LoadMoreImagesPopup'; +import { LoadModelPopup } from './LoadModelPopup/LoadModelPopup'; +import SuggestLabelNamesPopup from './SuggestLabelNamesPopup/SuggestLabelNamesPopup'; +import { CSSHelper } from '../../logic/helpers/CSSHelper'; +import { ClipLoader } from 'react-spinners'; +import ImportLabelPopup from './ImportLabelPopup/ImportLabelPopup'; +import ExportLabelPopup from './ExportLabelsPopup/ExportLabelPopup'; interface IProps { activePopupType: PopupWindowType; @@ -56,7 +56,7 @@ const PopupView: React.FC = ({ activePopupType }) => { }; return ( - activePopupType &&
+ activePopupType &&
{selectPopup()}
); From 8dc7a4af5a427ee04a787274ddbb0900a7d6a625 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Tue, 2 Aug 2022 20:29:19 +0200 Subject: [PATCH 10/13] Insights popup opens when selected from dropdown menu --- src/data/info/DropDownMenuData.ts | 5 ++- src/utils/LabelUtil.ts | 8 ++-- .../LabelInputField/LabelInputField.scss | 3 +- .../LabelInputField/LabelInputField.tsx | 2 +- .../DropDownMenu/DropDownMenu.tsx | 33 +++++++++-------- .../GenericLabelTypePopup.tsx | 30 +++++++-------- .../GenericYesNoPopup/GenericYesNoPopup.tsx | 25 ++++++------- .../LabelCountsStatisticsPopup.scss | 0 .../LabelCountsStatisticsPopup.tsx | 37 +++++++++++++++++++ src/views/PopupView/PopupView.tsx | 5 ++- 10 files changed, 95 insertions(+), 53 deletions(-) create mode 100644 src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.scss create mode 100644 src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.tsx diff --git a/src/data/info/DropDownMenuData.ts b/src/data/info/DropDownMenuData.ts index 6f02fe5f..a92c54a0 100644 --- a/src/data/info/DropDownMenuData.ts +++ b/src/data/info/DropDownMenuData.ts @@ -1,13 +1,14 @@ import {updateActivePopupType} from '../../store/general/actionCreators'; import {PopupWindowType} from '../enums/PopupWindowType'; import {store} from '../../index'; +import {LabelsSelector} from '../../store/selectors/LabelsSelector'; export type DropDownMenuNode = { name: string description?: string imageSrc: string imageAlt: string - disabled: boolean + disabled: (() => boolean) | boolean onClick?: () => void children?: DropDownMenuNode[] } @@ -72,7 +73,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ description: 'Display per label name annotations count', imageSrc: 'ico/tags.png', imageAlt: 'label-counts', - disabled: false, + disabled: () => LabelsSelector.getLabelNames().length === 0, onClick: () => store.dispatch(updateActivePopupType(PopupWindowType.LABEL_COUNTS_STATISTICS)) }, ] diff --git a/src/utils/LabelUtil.ts b/src/utils/LabelUtil.ts index f341de9c..ec5dacdc 100644 --- a/src/utils/LabelUtil.ts +++ b/src/utils/LabelUtil.ts @@ -14,7 +14,7 @@ export type LabelCount = { rect: number; } -export type LabelCountSummary = Record; +export type PerLabelIdCountSummary = Record; export class LabelUtil { public static createLabelName(name: string): LabelName { @@ -74,12 +74,12 @@ export class LabelUtil { } } - public static calculatePerLabelIdCountSummary(labels: LabelName[], imagesData: ImageData[]): LabelCountSummary { - let labelCount = labels.reduce((acc: LabelCountSummary, label: LabelName) => { + public static calculatePerLabelIdCountSummary(labels: LabelName[], imagesData: ImageData[]): PerLabelIdCountSummary { + let labelCount = labels.reduce((acc: PerLabelIdCountSummary, label: LabelName) => { acc[label.id] = { point: 0, line: 0, polygon: 0, rect: 0} return acc; }, {}); - labelCount = imagesData.reduce((acc: LabelCountSummary, imageData: ImageData) => { + labelCount = imagesData.reduce((acc: PerLabelIdCountSummary, imageData: ImageData) => { for (const labelRect of imageData.labelRects) { if (labelRect.labelId !== null && labelRect.status === LabelStatus.ACCEPTED) acc[labelRect.labelId].rect += 1 diff --git a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.scss b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.scss index a9f328b7..a16aa469 100644 --- a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.scss +++ b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.scss @@ -18,10 +18,11 @@ align-items: center; align-content: space-between; - .Marker { + .marker { margin-left: 10px; width: 24px; height: 24px; + border-radius: 2px; } .Content { diff --git a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx index 35d324e9..cf3c12f7 100644 --- a/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx +++ b/src/views/EditorView/SideNavigationBar/LabelInputField/LabelInputField.tsx @@ -180,7 +180,7 @@ class LabelInputField extends React.Component { }} >
diff --git a/src/views/EditorView/TopNavigationBar/DropDownMenu/DropDownMenu.tsx b/src/views/EditorView/TopNavigationBar/DropDownMenu/DropDownMenu.tsx index b5dec111..f2c5a79e 100644 --- a/src/views/EditorView/TopNavigationBar/DropDownMenu/DropDownMenu.tsx +++ b/src/views/EditorView/TopNavigationBar/DropDownMenu/DropDownMenu.tsx @@ -68,20 +68,6 @@ const DropDownMenu: React.FC = ({updatePreventCustomCursorStatusAction}) ); } - const getDropDownContent = () => { - return DropDownMenuData.map((data: DropDownMenuNode, index: number) => getDropDownTab(data, index)) - } - - const wrapOnClick = (onClick?: () => void, disabled?: boolean): () => void => { - return () => { - if (!!disabled) return; - if (!!onClick) onClick(); - setActiveTabIdx(null); - updatePreventCustomCursorStatusAction(false); - document.removeEventListener(EventType.MOUSE_DOWN, onMouseDownBeyondDropDown); - } - } - const getDropDownTab = (data: DropDownMenuNode, index: number) => { return
= ({updatePreventCustomCursorStatusAction})
} + const getDropDownContent = () => { + return DropDownMenuData.map((data: DropDownMenuNode, index: number) => getDropDownTab(data, index)) + } + + const wrapOnClick = (onClick?: () => void, disabled?: boolean): () => void => { + return () => { + if (!!disabled) return; + if (!!onClick) onClick(); + setActiveTabIdx(null); + updatePreventCustomCursorStatusAction(false); + document.removeEventListener(EventType.MOUSE_DOWN, onMouseDownBeyondDropDown); + } + } + const getDropDownWindow = (data: DropDownMenuNode) => { if (activeTabIdx !== null) { const style: React.CSSProperties = { @@ -112,8 +112,9 @@ const DropDownMenu: React.FC = ({updatePreventCustomCursorStatusAction}) onMouseLeave={onMouseLeaveWindow} > {data.children.map((element: DropDownMenuNode, index: number) => { - return
diff --git a/src/views/PopupView/GenericLabelTypePopup/GenericLabelTypePopup.tsx b/src/views/PopupView/GenericLabelTypePopup/GenericLabelTypePopup.tsx index e22f082c..21dfc732 100644 --- a/src/views/PopupView/GenericLabelTypePopup/GenericLabelTypePopup.tsx +++ b/src/views/PopupView/GenericLabelTypePopup/GenericLabelTypePopup.tsx @@ -1,25 +1,25 @@ import React, {useState} from 'react' import './GenericLabelTypePopup.scss' -import {LabelType} from "../../../data/enums/LabelType"; -import {AppState} from "../../../store"; -import {connect} from "react-redux"; -import {ImageButton} from "../../Common/ImageButton/ImageButton"; -import {GenericYesNoPopup} from "../GenericYesNoPopup/GenericYesNoPopup"; -import {ILabelToolkit, LabelToolkitData} from "../../../data/info/LabelToolkitData"; -import {ProjectType} from "../../../data/enums/ProjectType"; +import {LabelType} from '../../../data/enums/LabelType'; +import {AppState} from '../../../store'; +import {connect} from 'react-redux'; +import {ImageButton} from '../../Common/ImageButton/ImageButton'; +import {GenericYesNoPopup} from '../GenericYesNoPopup/GenericYesNoPopup'; +import {ILabelToolkit, LabelToolkitData} from '../../../data/info/LabelToolkitData'; +import {ProjectType} from '../../../data/enums/ProjectType'; interface IProps { title: string, activeLabelType: LabelType, projectType: ProjectType; - onLabelTypeChange?: (labelType: LabelType) => any; + onLabelTypeChange?: (labelType: LabelType) => void; acceptLabel: string; - onAccept: (labelType: LabelType) => any; + onAccept: (labelType: LabelType) => void; skipAcceptButton?: boolean; disableAcceptButton?: boolean; rejectLabel: string; - onReject: (labelType: LabelType) => any; - renderInternalContent: (labelType: LabelType) => any; + onReject: (labelType: LabelType) => void; + renderInternalContent: (labelType: LabelType) => JSX.Element; } const GenericLabelTypePopup: React.FC = ( @@ -59,11 +59,11 @@ const GenericLabelTypePopup: React.FC = ( } const renderContent = () => { - return (
-
+ return (
+
{getSidebarButtons()}
-
+
{renderInternalContent(labelType)}
); @@ -92,4 +92,4 @@ const mapStateToProps = (state: AppState) => ({ export default connect( mapStateToProps, mapDispatchToProps -)(GenericLabelTypePopup); \ No newline at end of file +)(GenericLabelTypePopup); diff --git a/src/views/PopupView/GenericYesNoPopup/GenericYesNoPopup.tsx b/src/views/PopupView/GenericYesNoPopup/GenericYesNoPopup.tsx index 6de51899..3a8facc6 100644 --- a/src/views/PopupView/GenericYesNoPopup/GenericYesNoPopup.tsx +++ b/src/views/PopupView/GenericYesNoPopup/GenericYesNoPopup.tsx @@ -1,8 +1,8 @@ import React, {useEffect, useState} from 'react' import './GenericYesNoPopup.scss' -import {TextButton} from "../../Common/TextButton/TextButton"; -import {ContextManager} from "../../../logic/context/ContextManager"; -import {ContextType} from "../../../data/enums/ContextType"; +import {TextButton} from '../../Common/TextButton/TextButton'; +import {ContextManager} from '../../../logic/context/ContextManager'; +import {ContextType} from '../../../data/enums/ContextType'; interface IProps { title: string; @@ -30,7 +30,6 @@ export const GenericYesNoPopup: React.FC = ( skipRejectButton, disableRejectButton }) => { - const [status, setMountStatus] = useState(false); useEffect(() => { if (!status) { @@ -40,27 +39,27 @@ export const GenericYesNoPopup: React.FC = ( }, [status]); return ( -
-
+
+
{title}
-
+
{renderContent()}
-
+
{!skipAcceptButton && } {!skipRejectButton && }
) -}; \ No newline at end of file +}; diff --git a/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.scss b/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.tsx b/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.tsx new file mode 100644 index 00000000..a716acc6 --- /dev/null +++ b/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.tsx @@ -0,0 +1,37 @@ +import {PopupWindowType} from '../../../data/enums/PopupWindowType'; +import React from 'react'; +import {AppState} from '../../../store'; +import {connect} from 'react-redux'; +import { updateActivePopupType as storeUpdateActivePopupType } from '../../../store/general/actionCreators'; +import {GenericYesNoPopup} from '../GenericYesNoPopup/GenericYesNoPopup'; + +interface IProps { + updateActivePopupTypeAction: (activePopupType: PopupWindowType) => void; +} + +const LabelCountsStatisticsPopup: React.FC = ({ updateActivePopupTypeAction }) => { + + const onReject = () => { + updateActivePopupTypeAction(null); + }; + + return ( + null} + skipAcceptButton={true} + onReject={onReject} + /> + ); +} + +const mapDispatchToProps = { + updateActivePopupTypeAction: storeUpdateActivePopupType +}; + +const mapStateToProps = (state: AppState) => ({}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(LabelCountsStatisticsPopup); diff --git a/src/views/PopupView/PopupView.tsx b/src/views/PopupView/PopupView.tsx index c7651f1f..b3021d06 100644 --- a/src/views/PopupView/PopupView.tsx +++ b/src/views/PopupView/PopupView.tsx @@ -13,6 +13,7 @@ import { CSSHelper } from '../../logic/helpers/CSSHelper'; import { ClipLoader } from 'react-spinners'; import ImportLabelPopup from './ImportLabelPopup/ImportLabelPopup'; import ExportLabelPopup from './ExportLabelsPopup/ExportLabelPopup'; +import LabelCountsStatisticsPopup from './LabelCountsStatisticsPopup/LabelCountsStatisticsPopup'; interface IProps { activePopupType: PopupWindowType; @@ -44,6 +45,8 @@ const PopupView: React.FC = ({ activePopupType }) => { return ; case PopupWindowType.SUGGEST_LABEL_NAMES: return ; + case PopupWindowType.LABEL_COUNTS_STATISTICS: + return ; case PopupWindowType.LOADER: return ({ export default connect( mapStateToProps -)(PopupView); \ No newline at end of file +)(PopupView); From 40dc26c88d18b585a8f608bf6657c16df5646962 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Tue, 2 Aug 2022 22:30:17 +0200 Subject: [PATCH 11/13] Popups style refactoring --- src/data/enums/PopupWindowType.ts | 2 +- src/data/info/DropDownMenuData.ts | 2 +- .../ExportLabelsPopup/ExportLabelPopup.scss | 4 +- .../GenericYesNoPopup/GenericYesNoPopup.scss | 5 +- .../ImportLabelPopup/ImportLabelPopup.scss | 4 +- .../InsertLabelNamesPopup.scss | 3 +- .../LabelCountsStatisticsPopup.scss | 0 .../LoadLabelNamesPopup.tsx | 58 +++++++++---------- .../LoadModelPopup/LoadModelPopup.scss | 3 +- .../LoadMoreImagesPopup.scss | 3 +- .../PerLabelIdCountsStatisticsPopup.scss | 4 ++ .../PerLabelIdCountsStatisticsPopup.tsx} | 15 +++-- src/views/PopupView/PopupView.tsx | 6 +- .../SuggestLabelNamesPopup.scss | 1 + 14 files changed, 62 insertions(+), 48 deletions(-) delete mode 100644 src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.scss create mode 100644 src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.scss rename src/views/PopupView/{LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.tsx => PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.tsx} (63%) diff --git a/src/data/enums/PopupWindowType.ts b/src/data/enums/PopupWindowType.ts index 574d866d..3f5a35c7 100644 --- a/src/data/enums/PopupWindowType.ts +++ b/src/data/enums/PopupWindowType.ts @@ -9,5 +9,5 @@ export enum PopupWindowType { INSERT_LABEL_NAMES = 'INSERT_LABEL_NAMES', EXIT_PROJECT = 'EXIT_PROJECT', LOADER = 'LOADER', - LABEL_COUNTS_STATISTICS = 'LABEL_COUNTS_STATISTICS' + PER_LABEL_ID_COUNTS_STATISTICS = 'PER_LABEL_ID_COUNTS_STATISTICS' } diff --git a/src/data/info/DropDownMenuData.ts b/src/data/info/DropDownMenuData.ts index a92c54a0..09a6f0f9 100644 --- a/src/data/info/DropDownMenuData.ts +++ b/src/data/info/DropDownMenuData.ts @@ -74,7 +74,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ imageSrc: 'ico/tags.png', imageAlt: 'label-counts', disabled: () => LabelsSelector.getLabelNames().length === 0, - onClick: () => store.dispatch(updateActivePopupType(PopupWindowType.LABEL_COUNTS_STATISTICS)) + onClick: () => store.dispatch(updateActivePopupType(PopupWindowType.PER_LABEL_ID_COUNTS_STATISTICS)) }, ] }, diff --git a/src/views/PopupView/ExportLabelsPopup/ExportLabelPopup.scss b/src/views/PopupView/ExportLabelsPopup/ExportLabelPopup.scss index 2aee0acb..58c2bbac 100644 --- a/src/views/PopupView/ExportLabelsPopup/ExportLabelPopup.scss +++ b/src/views/PopupView/ExportLabelsPopup/ExportLabelPopup.scss @@ -3,6 +3,8 @@ .GenericLabelTypePopupContent { .RightContainer { + width: 500px; + display: flex; flex-direction: column; flex-wrap: nowrap; @@ -54,4 +56,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/views/PopupView/GenericYesNoPopup/GenericYesNoPopup.scss b/src/views/PopupView/GenericYesNoPopup/GenericYesNoPopup.scss index 7232882c..3f814a15 100644 --- a/src/views/PopupView/GenericYesNoPopup/GenericYesNoPopup.scss +++ b/src/views/PopupView/GenericYesNoPopup/GenericYesNoPopup.scss @@ -1,9 +1,6 @@ @import '../../../settings/Settings'; .GenericYesNoPopup { - max-height: 600px; - max-width: 500px; - width: 50%; border-radius: 5px; user-select: none; box-shadow: 0px 0px 10px 3px rgba(255, 255, 255, 0.02); @@ -87,4 +84,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/views/PopupView/ImportLabelPopup/ImportLabelPopup.scss b/src/views/PopupView/ImportLabelPopup/ImportLabelPopup.scss index cef30593..cc4d9b71 100644 --- a/src/views/PopupView/ImportLabelPopup/ImportLabelPopup.scss +++ b/src/views/PopupView/ImportLabelPopup/ImportLabelPopup.scss @@ -3,6 +3,8 @@ .GenericLabelTypePopupContent { .RightContainer { + width: 500px; + display: flex; flex-direction: column; flex-wrap: nowrap; @@ -56,4 +58,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss index 6d235288..d8c4ba15 100644 --- a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss +++ b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss @@ -47,9 +47,10 @@ } .RightContainer { + width: 500px; + align-self: stretch; flex: 1; - display: flex; flex-direction: column; flex-wrap: nowrap; diff --git a/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.scss b/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/src/views/PopupView/LoadLabelNamesPopup/LoadLabelNamesPopup.tsx b/src/views/PopupView/LoadLabelNamesPopup/LoadLabelNamesPopup.tsx index 84e413ee..e9d981ef 100644 --- a/src/views/PopupView/LoadLabelNamesPopup/LoadLabelNamesPopup.tsx +++ b/src/views/PopupView/LoadLabelNamesPopup/LoadLabelNamesPopup.tsx @@ -1,14 +1,14 @@ import React, { useState } from 'react'; import './LoadLabelNamesPopup.scss'; -import { AppState } from "../../../store"; -import { connect } from "react-redux"; -import { updateLabelNames } from "../../../store/labels/actionCreators"; -import { GenericYesNoPopup } from "../GenericYesNoPopup/GenericYesNoPopup"; -import { PopupWindowType } from "../../../data/enums/PopupWindowType"; -import { updateActivePopupType as storeUpdateActivePopupType } from "../../../store/general/actionCreators"; -import { useDropzone } from "react-dropzone"; -import { LabelName } from "../../../store/labels/types"; -import { YOLOUtils } from "../../../logic/import/yolo/YOLOUtils"; +import { AppState } from '../../../store'; +import { connect } from 'react-redux'; +import { updateLabelNames } from '../../../store/labels/actionCreators'; +import { GenericYesNoPopup } from '../GenericYesNoPopup/GenericYesNoPopup'; +import { PopupWindowType } from '../../../data/enums/PopupWindowType'; +import { updateActivePopupType as storeUpdateActivePopupType } from '../../../store/general/actionCreators'; +import { useDropzone } from 'react-dropzone'; +import { LabelName } from '../../../store/labels/types'; +import { YOLOUtils } from '../../../logic/import/yolo/YOLOUtils'; interface IProps { updateActivePopupType: (activePopupType: PopupWindowType) => any; @@ -29,7 +29,7 @@ const LoadLabelNamesPopup: React.FC = ({ updateActivePopupType, updateLa }; const { acceptedFiles, getRootProps, getInputProps } = useDropzone({ - accept: { "text/plain": [".txt"] }, + accept: { 'text/plain': ['.txt'] }, multiple: false, onDrop: (accepted) => { if (accepted.length === 1) { @@ -55,47 +55,47 @@ const LoadLabelNamesPopup: React.FC = ({ updateActivePopupType, updateLa {"upload"} -

Loading of labels file was unsuccessful

-

Try again

+

Loading of labels file was unsuccessful

+

Try again

; else if (acceptedFiles.length === 0) return <> {"upload"} -

Drop labels file

+

Drop labels file

or

-

Click here to select it

+

Click here to select it

; else if (labelsList.length === 1) return <> {"uploaded"} -

only 1 label found

+

only 1 label found

; else return <> {"uploaded"} -

{labelsList.length} labels found

+

{labelsList.length} labels found

; }; const renderContent = () => { - return (
-
+ return (
+
Load a text file with a list of labels you are planning to use. The names of each label should be separated by new line. If you don't have a prepared file, no problem. You can create your own list now. @@ -108,12 +108,12 @@ const LoadLabelNamesPopup: React.FC = ({ updateActivePopupType, updateLa return ( ); diff --git a/src/views/PopupView/LoadModelPopup/LoadModelPopup.scss b/src/views/PopupView/LoadModelPopup/LoadModelPopup.scss index 7cc09721..ea0e5fd1 100644 --- a/src/views/PopupView/LoadModelPopup/LoadModelPopup.scss +++ b/src/views/PopupView/LoadModelPopup/LoadModelPopup.scss @@ -1,6 +1,7 @@ @import '../../../settings/Settings'; .LoadModelPopupContent { + width: 550px; display: flex; flex-direction: column; flex-wrap: nowrap; @@ -58,4 +59,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/views/PopupView/LoadMoreImagesPopup/LoadMoreImagesPopup.scss b/src/views/PopupView/LoadMoreImagesPopup/LoadMoreImagesPopup.scss index 93966bda..72fa7351 100644 --- a/src/views/PopupView/LoadMoreImagesPopup/LoadMoreImagesPopup.scss +++ b/src/views/PopupView/LoadMoreImagesPopup/LoadMoreImagesPopup.scss @@ -1,6 +1,7 @@ @import '../../../settings/Settings'; .LoadMoreImagesPopupContent { + width: 550px; display: flex; flex-direction: column; flex-wrap: nowrap; @@ -52,4 +53,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.scss b/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.scss new file mode 100644 index 00000000..8b8fd7ea --- /dev/null +++ b/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.scss @@ -0,0 +1,4 @@ +.per-label-id-counts-statistics-popup-content { + height: calc(100vh - 200px); + width: calc(100vw - 100px); +} diff --git a/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.tsx b/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.tsx similarity index 63% rename from src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.tsx rename to src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.tsx index a716acc6..706208f0 100644 --- a/src/views/PopupView/LabelCountsStatisticsPopup/LabelCountsStatisticsPopup.tsx +++ b/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.tsx @@ -1,24 +1,29 @@ -import {PopupWindowType} from '../../../data/enums/PopupWindowType'; import React from 'react'; +import './PerLabelIdCountsStatisticsPopup.scss'; +import {PopupWindowType} from '../../../data/enums/PopupWindowType'; import {AppState} from '../../../store'; import {connect} from 'react-redux'; -import { updateActivePopupType as storeUpdateActivePopupType } from '../../../store/general/actionCreators'; +import {updateActivePopupType as storeUpdateActivePopupType} from '../../../store/general/actionCreators'; import {GenericYesNoPopup} from '../GenericYesNoPopup/GenericYesNoPopup'; interface IProps { updateActivePopupTypeAction: (activePopupType: PopupWindowType) => void; } -const LabelCountsStatisticsPopup: React.FC = ({ updateActivePopupTypeAction }) => { +const PerLabelIdCountsStatisticsPopup: React.FC = ({ updateActivePopupTypeAction }) => { const onReject = () => { updateActivePopupTypeAction(null); }; + const renderContent = () => { + return
+ } + return ( null} + renderContent={renderContent} skipAcceptButton={true} onReject={onReject} /> @@ -34,4 +39,4 @@ const mapStateToProps = (state: AppState) => ({}); export default connect( mapStateToProps, mapDispatchToProps -)(LabelCountsStatisticsPopup); +)(PerLabelIdCountsStatisticsPopup); diff --git a/src/views/PopupView/PopupView.tsx b/src/views/PopupView/PopupView.tsx index b3021d06..7315f39b 100644 --- a/src/views/PopupView/PopupView.tsx +++ b/src/views/PopupView/PopupView.tsx @@ -13,7 +13,7 @@ import { CSSHelper } from '../../logic/helpers/CSSHelper'; import { ClipLoader } from 'react-spinners'; import ImportLabelPopup from './ImportLabelPopup/ImportLabelPopup'; import ExportLabelPopup from './ExportLabelsPopup/ExportLabelPopup'; -import LabelCountsStatisticsPopup from './LabelCountsStatisticsPopup/LabelCountsStatisticsPopup'; +import PerLabelIdCountsStatisticsPopup from "./PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup"; interface IProps { activePopupType: PopupWindowType; @@ -45,8 +45,8 @@ const PopupView: React.FC = ({ activePopupType }) => { return ; case PopupWindowType.SUGGEST_LABEL_NAMES: return ; - case PopupWindowType.LABEL_COUNTS_STATISTICS: - return ; + case PopupWindowType.PER_LABEL_ID_COUNTS_STATISTICS: + return ; case PopupWindowType.LOADER: return Date: Thu, 11 Aug 2022 22:14:56 +0200 Subject: [PATCH 12/13] Save work in progress --- src/utils/RectUtil.ts | 20 +++++++++---------- .../UnderlineTextButton.tsx | 6 +++--- .../PerLabelIdCountsStatisticsPopup.tsx | 9 ++++++++- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/utils/RectUtil.ts b/src/utils/RectUtil.ts index 4129ca03..28baa5ac 100644 --- a/src/utils/RectUtil.ts +++ b/src/utils/RectUtil.ts @@ -1,9 +1,9 @@ -import {IRect} from "../interfaces/IRect"; -import {IPoint} from "../interfaces/IPoint"; -import {ISize} from "../interfaces/ISize"; -import {RectAnchor} from "../data/RectAnchor"; -import {NumberUtil} from "./NumberUtil"; -import {Direction} from "../data/enums/Direction"; +import {IRect} from '../interfaces/IRect'; +import {IPoint} from '../interfaces/IPoint'; +import {ISize} from '../interfaces/ISize'; +import {RectAnchor} from '../data/RectAnchor'; +import {NumberUtil} from './NumberUtil'; +import {Direction} from '../data/enums/Direction'; export class RectUtil { public static getRatio(rect: IRect): number { @@ -61,7 +61,7 @@ export class RectUtil { } } } - + public static resizeRect(inputRect: IRect, rectAnchor: Direction, delta): IRect { const rect: IRect = {...inputRect}; switch (rectAnchor) { @@ -100,17 +100,17 @@ export class RectUtil { rect.height += delta.y; break; } - + if (rect.width < 0) { rect.x = rect.x + rect.width; rect.width = -rect.width; } - + if (rect.height < 0) { rect.y = rect.y + rect.height; rect.height = -rect.height; } - + return rect; } diff --git a/src/views/Common/UnderlineTextButton/UnderlineTextButton.tsx b/src/views/Common/UnderlineTextButton/UnderlineTextButton.tsx index ed58f5fe..8d4448a2 100644 --- a/src/views/Common/UnderlineTextButton/UnderlineTextButton.tsx +++ b/src/views/Common/UnderlineTextButton/UnderlineTextButton.tsx @@ -17,9 +17,9 @@ export const UnderlineTextButton = (props: IProps) => { const getClassName = () => { return classNames('UnderlineTextButton', { - under: under, - over: over, - active: active, + under, + over, + active, }) }; diff --git a/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.tsx b/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.tsx index 706208f0..1ae7be7a 100644 --- a/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.tsx +++ b/src/views/PopupView/PerLabelIdCountsStatisticsPopup/PerLabelIdCountsStatisticsPopup.tsx @@ -5,6 +5,7 @@ import {AppState} from '../../../store'; import {connect} from 'react-redux'; import {updateActivePopupType as storeUpdateActivePopupType} from '../../../store/general/actionCreators'; import {GenericYesNoPopup} from '../GenericYesNoPopup/GenericYesNoPopup'; +import Scrollbars from 'react-custom-scrollbars-2'; interface IProps { updateActivePopupTypeAction: (activePopupType: PopupWindowType) => void; @@ -17,7 +18,13 @@ const PerLabelIdCountsStatisticsPopup: React.FC = ({ updateActivePopupTy }; const renderContent = () => { - return
+ return
+ +
+ {null} +
+
+
} return ( From f8cf0cfc584c236bb5e080e3f2f5a805fd4c13d6 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Fri, 12 Aug 2022 17:13:44 +0200 Subject: [PATCH 13/13] Save labels stats popup progress --- src/data/info/DropDownMenuData.ts | 18 +++--- .../InsertLabelNamesPopup.scss | 19 +++--- .../InsertLabelNamesPopup.tsx | 16 ++--- .../PerLabelIdCountsStatisticsPopup.scss | 59 ++++++++++++++++++- .../PerLabelIdCountsStatisticsPopup.tsx | 43 +++++++++++--- 5 files changed, 120 insertions(+), 35 deletions(-) diff --git a/src/data/info/DropDownMenuData.ts b/src/data/info/DropDownMenuData.ts index 09a6f0f9..e62d5b78 100644 --- a/src/data/info/DropDownMenuData.ts +++ b/src/data/info/DropDownMenuData.ts @@ -21,7 +21,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ disabled: false, children: [ { - name: 'Edit Labels', + name: 'Edit labels', description: 'Modify labels list', imageSrc: 'ico/tags.png', imageAlt: 'labels', @@ -29,7 +29,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ onClick: () => store.dispatch(updateActivePopupType(PopupWindowType.UPDATE_LABEL)) }, { - name: 'Import Images', + name: 'Import images', description: 'Load more images', imageSrc: 'ico/camera.png', imageAlt: 'images', @@ -37,7 +37,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ onClick: () => store.dispatch(updateActivePopupType(PopupWindowType.IMPORT_IMAGES)) }, { - name: 'Import Annotations', + name: 'Import annotations', description: 'Import annotations from file', imageSrc: 'ico/import-labels.png', imageAlt: 'import-labels', @@ -45,7 +45,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ onClick: () => store.dispatch(updateActivePopupType(PopupWindowType.IMPORT_ANNOTATIONS)) }, { - name: 'Export Annotations', + name: 'Export annotations', description: 'Export annotations to file', imageSrc: 'ico/export-labels.png', imageAlt: 'export-labels', @@ -53,7 +53,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ onClick: () => store.dispatch(updateActivePopupType(PopupWindowType.EXPORT_ANNOTATIONS)) }, { - name: 'Load AI Model', + name: 'Load AI model', description: 'Load our pre-trained annotation models', imageSrc: 'ico/ai.png', imageAlt: 'load-ai-model', @@ -69,10 +69,10 @@ export const DropDownMenuData: DropDownMenuNode[] = [ disabled: false, children: [ { - name: 'Label Counts', + name: 'Label distribution', description: 'Display per label name annotations count', imageSrc: 'ico/tags.png', - imageAlt: 'label-counts', + imageAlt: 'label-distribution', disabled: () => LabelsSelector.getLabelNames().length === 0, onClick: () => store.dispatch(updateActivePopupType(PopupWindowType.PER_LABEL_ID_COUNTS_STATISTICS)) }, @@ -93,7 +93,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ onClick: () => window.open('https://skalskip.github.io/make-sense', '_blank') }, { - name: 'Bugs and Features', + name: 'Bugs and features', description: 'Report a bug or propose a new feature', imageSrc: 'ico/bug.png', imageAlt: 'bug', @@ -101,7 +101,7 @@ export const DropDownMenuData: DropDownMenuNode[] = [ onClick: () => window.open('https://github.com/SkalskiP/make-sense/issues', '_blank') }, { - name: 'Vote for Next Big Feature', + name: 'Vote for next BIG feature', description: 'Vote for next big feature that we will add to Make Sense', imageSrc: 'ico/poll.png', imageAlt: 'vote', diff --git a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss index d8c4ba15..448aa36f 100644 --- a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss +++ b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss @@ -1,6 +1,8 @@ @import '../../../settings/Settings'; -.InsertLabelNamesPopup { +.insert-label-names-popup { + min-height: 400px; + display: flex; flex-direction: row; flex-wrap: nowrap; @@ -10,9 +12,8 @@ align-self: stretch; flex: 1; - min-height: 400px; - .LeftContainer { + .left-container { width: 50px; align-self: stretch; border-right: solid 1px $darkThemeFirstColor; @@ -46,7 +47,7 @@ } } - .RightContainer { + .right-container { width: 500px; align-self: stretch; @@ -58,7 +59,7 @@ align-items: center; align-content: flex-start; - .Message { + .message { align-self: stretch; color: white; font-size: 15px; @@ -66,7 +67,7 @@ border-bottom: solid 1px $darkThemeFirstColor; } - .LabelsContainer { + .labels-container { align-self: stretch; flex: 1; @@ -97,7 +98,7 @@ } } - .EmptyList { + .empty-list { align-self: stretch; flex: 1; @@ -133,12 +134,12 @@ } } - .InsertLabelNamesPopupContent { + .insert-label-names-popup-content { margin-left: 40px; margin-right: 10px; margin-top: 20px; - .LabelEntry { + .label-entry { width: 100%; display: flex; flex-direction: row; diff --git a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx index 43e1baa3..7af137e5 100644 --- a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx +++ b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx @@ -149,7 +149,7 @@ const InsertLabelNamesPopup: React.FC = ( onChange(labelName.id, event.target.value); const onDeleteCallback = () => deleteLabelNameCallback(labelName.id); const onChangeColorCallback = () => changeLabelNameColorCallback(labelName.id); - return
+ return
= ( }; const renderContent = () => { - return (
-
+ return (
+
= ( externalClassName={enablePerClassColoration ? '' : 'monochrome'} />}
-
-
+
+
{ isUpdate ? 'You can now edit the label names you use to describe the objects in the photos. Use the ' + @@ -241,16 +241,16 @@ const InsertLabelNamesPopup: React.FC = ( 'project. You can also choose to skip that part for now and define label names as you go.' }
-
+
{Object.keys(labelNames).length !== 0 ?
{labelInputs}
:
void; @@ -13,26 +14,54 @@ interface IProps { const PerLabelIdCountsStatisticsPopup: React.FC = ({ updateActivePopupTypeAction }) => { + const labelNames = LabelsSelector.getLabelNames() + const imageData = LabelsSelector.getImagesData() + const onReject = () => { updateActivePopupTypeAction(null); }; const renderContent = () => { return
- -
- {null} -
-
+
+ +
+
+ + + + + + + + + + + + + + + + +
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
+
} return ( ); }