diff --git a/CHANGELOG.md b/CHANGELOG.md index 8877ab6b095d..5c2719e21b8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `datumaro_project` export format (https://github.com/opencv/cvat/pull/1352) - Ability to configure user agreements for the user registration form (https://github.com/opencv/cvat/pull/1464) - Added cuboid interpolation and cuboid drawing from rectangles () +- Ability to configure custom pageViewHit, which can be useful for web analytics integration (https://github.com/opencv/cvat/pull/1566) ### Changed - Downloaded file name in annotations export became more informative (https://github.com/opencv/cvat/pull/1352) @@ -29,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - `annotation` application is replaced with `dataset_manager` (https://github.com/opencv/cvat/pull/1352) +- `_DATUMARO_INIT_LOGLEVEL` env. variable is removed in favor of regular `--loglevel` cli parameter (https://github.com/opencv/cvat/pull/1583) ### Fixed - Categories for empty projects with no sources are taken from own dataset (https://github.com/opencv/cvat/pull/1352) @@ -43,10 +45,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Task/Job buttons has no "Open in new tab" option () - Delete point context menu option has no shortcut hint () - Fixed issue with unnecessary tag activation in cvat-canvas () -- Fixed an issue with large number of instances in instance mask (https://github.com/opencv/cvat/issues/1539) -- Fixed full COCO dataset import error with conflicting labels in keypoints and detection (https://github.com/opencv/cvat/pull/1548) -- Fixed COCO keypoints skeleton parsing and saving (https://github.com/opencv/cvat/issues/1539) -- `tf.placeholder() is not compatible with eager execution` exception for auto_segmentation (https://github.com/opencv/cvat/pull/1562) +- Fixed an issue with large number of instances in instance mask () +- Fixed full COCO dataset import error with conflicting labels in keypoints and detection () +- Fixed COCO keypoints skeleton parsing and saving () +- `tf.placeholder() is not compatible with eager execution` exception for auto_segmentation () +- Canvas cannot be moved with move functionality on left mouse key () +- Deep extreme cut request is sent when draw any shape with Make AI polygon option enabled () +- Fixed an error when exporting a task with cuboids to any format except CVAT () +- Synchronization with remote git repo () +- A problem with mask to polygons conversion when polygons are too small () ### Security - diff --git a/Dockerfile.ui b/Dockerfile.ui index c0b04e3d104f..48fd3c735028 100644 --- a/Dockerfile.ui +++ b/Dockerfile.ui @@ -5,6 +5,7 @@ ARG https_proxy ARG no_proxy ARG socks_proxy ARG PUBLIC_INSTANCE +ARG WA_PAGE_VIEW_HIT ENV TERM=xterm \ http_proxy=${http_proxy} \ diff --git a/cvat-canvas/package-lock.json b/cvat-canvas/package-lock.json index b05d81260330..50645fbd1a3b 100644 --- a/cvat-canvas/package-lock.json +++ b/cvat-canvas/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-canvas", - "version": "1.1.0", + "version": "1.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-canvas/package.json b/cvat-canvas/package.json index 7fafda292a84..73a7c98a3918 100644 --- a/cvat-canvas/package.json +++ b/cvat-canvas/package.json @@ -1,6 +1,6 @@ { "name": "cvat-canvas", - "version": "1.1.0", + "version": "1.1.1", "description": "Part of Computer Vision Annotation Tool which presents its canvas library", "main": "src/canvas.ts", "scripts": { diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 71f6b05d900d..002b4f093cf3 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -451,7 +451,7 @@ export class CanvasViewImpl implements CanvasView, Listener { self.onEditDone( state, points, - ) + ); e.preventDefault(); return; } @@ -687,7 +687,7 @@ export class CanvasViewImpl implements CanvasView, Listener { this.content.addEventListener('mousedown', (event): void => { if ([0, 1].includes(event.button)) { - if ([Mode.IDLE, Mode.DRAG, Mode.MERGE, Mode.SPLIT].includes(this.mode) + if ([Mode.IDLE, Mode.DRAG_CANVAS, Mode.MERGE, Mode.SPLIT].includes(this.mode) || event.button === 1 || event.altKey ) { self.controller.enableDrag(event.clientX, event.clientY); diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 5ec99958c483..6960db8dcb68 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.1.4", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 92adc5bd3e1f..00831d57a648 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.1.4", + "version": "1.2.0", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx index ac012adef724..1c61ed9fdd06 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx @@ -51,7 +51,8 @@ function DrawShapePopoverComponent(props: Props): JSX.Element { onChangeCuboidDrawingMethod, } = props; - const trackDisabled = shapeType === ShapeType.POLYGON || shapeType === ShapeType.POLYLINE + const trackDisabled = shapeType === ShapeType.POLYGON + || shapeType === ShapeType.POLYLINE || (shapeType === ShapeType.POINTS && numberOfPoints !== 1); return ( @@ -85,9 +86,7 @@ function DrawShapePopoverComponent(props: Props): JSX.Element { - { - shapeType === ShapeType.POLYGON && - } + { shapeType === ShapeType.POLYGON && } { shapeType === ShapeType.RECTANGLE && ( <> @@ -183,19 +182,14 @@ function DrawShapePopoverComponent(props: Props): JSX.Element { - - diff --git a/cvat-ui/src/components/create-task-page/create-task-page.tsx b/cvat-ui/src/components/create-task-page/create-task-page.tsx index 439439f92519..15c5f18c47a6 100644 --- a/cvat-ui/src/components/create-task-page/create-task-page.tsx +++ b/cvat-ui/src/components/create-task-page/create-task-page.tsx @@ -12,7 +12,6 @@ import TextArea from 'antd/lib/input/TextArea'; import CreateTaskContent, { CreateTaskData } from './create-task-content'; - interface Props { onCreate: (data: CreateTaskData) => void; status: string; diff --git a/cvat-ui/src/components/create-task-page/styles.scss b/cvat-ui/src/components/create-task-page/styles.scss index df2bcc459cca..ccf664b2fcd4 100644 --- a/cvat-ui/src/components/create-task-page/styles.scss +++ b/cvat-ui/src/components/create-task-page/styles.scss @@ -8,7 +8,8 @@ text-align: center; padding-top: 40px; overflow-y: auto; - height: 90%; + height: 100%; + padding-bottom: 40px; > div > span { font-size: 36px; diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index fdc7f9157feb..ad83d05bbbc2 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -24,6 +24,7 @@ import AnnotationPageContainer from 'containers/annotation-page/annotation-page' import LoginPageContainer from 'containers/login-page/login-page'; import RegisterPageContainer from 'containers/register-page/register-page'; import HeaderContainer from 'containers/header/header'; +import { customWaViewHit } from 'utils/enviroment'; import getCore from 'cvat-core-wrapper'; import { NotificationsState } from 'reducers/interfaces'; @@ -61,7 +62,7 @@ interface CVATAppProps { class CVATApplication extends React.PureComponent { public componentDidMount(): void { const core = getCore(); - const { verifyAuthorized, loadUserAgreements } = this.props; + const { verifyAuthorized, history } = this.props; configure({ ignoreRepeatedEventsWhenKeyHeldDown: false }); // Logger configuration @@ -71,6 +72,11 @@ class CVATApplication extends React.PureComponent window.document.hasFocus, userActivityCallback); + customWaViewHit(location.pathname, location.search, location.hash); + history.listen((location) => { + customWaViewHit(location.pathname, location.search, location.hash); + }); + verifyAuthorized(); } diff --git a/cvat-ui/src/components/register-page/register-form.tsx b/cvat-ui/src/components/register-page/register-form.tsx index 813b7bbcaee1..daa38c8eddad 100644 --- a/cvat-ui/src/components/register-page/register-form.tsx +++ b/cvat-ui/src/components/register-page/register-form.tsx @@ -12,6 +12,7 @@ import Checkbox from 'antd/lib/checkbox'; import patterns from 'utils/validation-patterns'; import { UserAgreement } from 'reducers/interfaces' +import { Row, Col } from 'antd/lib/grid'; export interface UserConfirmation { name: string; @@ -107,9 +108,9 @@ class RegisterFormComponent extends React.PureComponent { form.validateFields((error, values): void => { if (!error) { values.confirmations = [] - + for (const userAgreement of userAgreements) { - + values.confirmations.push({ name: userAgreement.name, value: values[userAgreement.name] @@ -289,8 +290,14 @@ class RegisterFormComponent extends React.PureComponent { return (
- {this.renderFirstNameField()} - {this.renderLastNameField()} + + + {this.renderFirstNameField()} + + + {this.renderLastNameField()} + + {this.renderUsernameField()} {this.renderEmailField()} {this.renderPasswordField()} diff --git a/cvat-ui/src/components/register-page/register-page.tsx b/cvat-ui/src/components/register-page/register-page.tsx index 872766fdb800..1ab1d42a7c84 100644 --- a/cvat-ui/src/components/register-page/register-page.tsx +++ b/cvat-ui/src/components/register-page/register-page.tsx @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: MIT +import './styles.scss'; import React from 'react'; import { RouteComponentProps } from 'react-router'; import { Link, withRouter } from 'react-router-dom'; @@ -10,8 +11,8 @@ import Text from 'antd/lib/typography/Text'; import { Row, Col } from 'antd/lib/grid'; import { UserAgreement } from 'reducers/interfaces' -import RegisterForm, { RegisterData, UserConfirmation } from './register-form'; import CookieDrawer from 'components/login-page/cookie-policy-drawer'; +import RegisterForm, { RegisterData, UserConfirmation } from './register-form'; interface RegisterPageComponentProps { fetching: boolean; @@ -29,8 +30,8 @@ function RegisterPageComponent( xs: { span: 14 }, sm: { span: 14 }, md: { span: 10 }, - lg: { span: 4 }, - xl: { span: 4 }, + lg: { span: 6 }, + xl: { span: 5 }, }; const { diff --git a/cvat-ui/src/components/register-page/styles.scss b/cvat-ui/src/components/register-page/styles.scss new file mode 100644 index 000000000000..3eaf5beaf515 --- /dev/null +++ b/cvat-ui/src/components/register-page/styles.scss @@ -0,0 +1,7 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +.ant-form-item { + margin-bottom: 12px; +} \ No newline at end of file diff --git a/cvat-ui/src/components/settings-page/styles.scss b/cvat-ui/src/components/settings-page/styles.scss index b3d475725aeb..4858af603834 100644 --- a/cvat-ui/src/components/settings-page/styles.scss +++ b/cvat-ui/src/components/settings-page/styles.scss @@ -5,8 +5,9 @@ @import '../../base.scss'; .cvat-settings-page { - height: 90%; + height: 100%; overflow-y: auto; + padding-bottom: 15px; > div:nth-child(1) { margin-top: 30px; diff --git a/cvat-ui/src/components/tasks-page/top-bar.tsx b/cvat-ui/src/components/tasks-page/top-bar.tsx index 9f3c1e532024..72216d73003a 100644 --- a/cvat-ui/src/components/tasks-page/top-bar.tsx +++ b/cvat-ui/src/components/tasks-page/top-bar.tsx @@ -52,6 +52,7 @@ function TopBarComponent(props: VisibleTopBarProps & RouteComponentProps): JSX.E onClick={ (): void => history.push('/tasks/create') } + icon='plus' > Create new task diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx index ba10cf17359c..1d9a09340cd2 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx @@ -107,11 +107,7 @@ class DrawShapePopoverContainer extends React.PureComponent { } private onDraw(objectType: ObjectType): void { - const { - canvasInstance, - shapeType, - onDrawStart, - } = this.props; + const { canvasInstance, shapeType, onDrawStart } = this.props; const { rectDrawingMethod, @@ -143,8 +139,8 @@ class DrawShapePopoverContainer extends React.PureComponent { private onChangeCuboidDrawingMethod = (event: RadioChangeEvent): void => { this.setState({ cuboidDrawingMethod: event.target.value, - }) - } + }); + }; private onDrawShape = (): void => { this.onDraw(ObjectType.SHAPE); diff --git a/cvat-ui/src/cvat-store.ts b/cvat-ui/src/cvat-store.ts index 1c528c4ec928..ee48f3634015 100644 --- a/cvat-ui/src/cvat-store.ts +++ b/cvat-ui/src/cvat-store.ts @@ -10,7 +10,7 @@ import { Reducer, } from 'redux'; import { createLogger } from 'redux-logger'; -import {isDev} from 'utils/enviroment'; +import { isDev } from 'utils/enviroment'; const logger = createLogger({ diff --git a/cvat-ui/src/utils/dextr-utils.ts b/cvat-ui/src/utils/dextr-utils.ts index a884d586549b..03280cface55 100644 --- a/cvat-ui/src/utils/dextr-utils.ts +++ b/cvat-ui/src/utils/dextr-utils.ts @@ -4,7 +4,8 @@ import getCore from 'cvat-core-wrapper'; import { Canvas } from 'cvat-canvas-wrapper'; -import { ShapeType, RQStatus } from 'reducers/interfaces'; +import { ShapeType, RQStatus, CombinedState } from 'reducers/interfaces'; +import { getCVATStore } from 'cvat-store'; const core = getCore(); const baseURL = core.config.backendAPI.slice(0, -7); @@ -145,6 +146,60 @@ function serverRequest( }); } +async function enter(this: any, self: DEXTRPlugin, objects: any[]): Promise { + try { + if (self.data.enabled && objects.length === 1) { + const state = (getCVATStore().getState() as CombinedState); + const isPolygon = state.annotation + .drawing.activeShapeType === ShapeType.POLYGON; + if (!isPolygon) return; + + document.body.append(antModalRoot); + const promises: Record> = {}; + for (let i = 0; i < objects.length; i++) { + if (objects[i].points.length >= 8) { + promises[i] = serverRequest( + self, + this.id, + objects[i].frame, + objects[i].points, + ); + } else { + promises[i] = new Promise((resolve) => { + resolve(objects[i].points); + }); + } + } + + const transformed = await Promise + .all(Object.values(promises)); + for (let i = 0; i < objects.length; i++) { + // eslint-disable-next-line no-param-reassign + objects[i] = new core.classes.ObjectState({ + frame: objects[i].frame, + objectType: objects[i].objectType, + label: objects[i].label, + shapeType: ShapeType.POLYGON, + points: transformed[i], + occluded: objects[i].occluded, + zOrder: objects[i].zOrder, + }); + } + } + + return; + } catch (error) { + throw new core.exceptions.PluginError(error.toString()); + } finally { + // eslint-disable-next-line no-param-reassign + self.data.canceled = false; + antModalButton.disabled = true; + if (antModalRoot.parentElement === document.body) { + document.body.removeChild(antModalRoot); + } + } +} + const plugin: DEXTRPlugin = { name: 'Deep extreme cut', description: 'Plugin allows to get a polygon from extreme points using AI', @@ -154,54 +209,7 @@ const plugin: DEXTRPlugin = { prototype: { annotations: { put: { - async enter(self: DEXTRPlugin, objects: any[]): Promise { - try { - if (self.data.enabled) { - document.body.append(antModalRoot); - const promises: Record> = {}; - for (let i = 0; i < objects.length; i++) { - if (objects[i].points.length >= 8) { - promises[i] = serverRequest( - self, - (this as any).id, - objects[i].frame, - objects[i].points, - ); - } else { - promises[i] = new Promise((resolve) => { - resolve(objects[i].points); - }); - } - } - - const transformed = await Promise - .all(Object.values(promises)); - for (let i = 0; i < objects.length; i++) { - // eslint-disable-next-line no-param-reassign - objects[i] = new core.classes.ObjectState({ - frame: objects[i].frame, - objectType: objects[i].objectType, - label: objects[i].label, - shapeType: ShapeType.POLYGON, - points: transformed[i], - occluded: objects[i].occluded, - zOrder: objects[i].zOrder, - }); - } - } - - return; - } catch (error) { - throw new core.exceptions.PluginError(error.toString()); - } finally { - // eslint-disable-next-line no-param-reassign - self.data.canceled = false; - antModalButton.disabled = true; - if (antModalRoot.parentElement === document.body) { - document.body.removeChild(antModalRoot); - } - } - }, + enter, }, }, }, diff --git a/cvat-ui/src/utils/enviroment.ts b/cvat-ui/src/utils/enviroment.ts index 677dbc6f370e..895a510386e2 100644 --- a/cvat-ui/src/utils/enviroment.ts +++ b/cvat-ui/src/utils/enviroment.ts @@ -9,3 +9,14 @@ export function isDev(): boolean { export function isPublic(): boolean { return process.env.PUBLIC_INSTANCE === 'true'; } + +export function customWaViewHit(pageName?: string, queryString?: string, hashInfo?: string) { + const waHitFunctionName = process.env.WA_PAGE_VIEW_HIT + if (waHitFunctionName) { + const waHitFunction = new Function('pageName', 'queryString', 'hashInfo', + `if (typeof ${waHitFunctionName} === 'function') { + ${waHitFunctionName}(pageName, queryString, hashInfo); + }`); + waHitFunction(pageName, queryString, hashInfo); + } +} diff --git a/cvat/apps/dataset_manager/bindings.py b/cvat/apps/dataset_manager/bindings.py index 94dac6b24898..55f98accbb90 100644 --- a/cvat/apps/dataset_manager/bindings.py +++ b/cvat/apps/dataset_manager/bindings.py @@ -521,6 +521,8 @@ def convert_attrs(label, cvat_attrs): anno = datumaro.Bbox(x0, y0, x1 - x0, y1 - y0, label=anno_label, attributes=anno_attr, group=anno_group, z_order=shape_obj.z_order) + elif shape_obj.type == ShapeType.CUBOID: + continue # Datumaro does not support cuboids else: raise Exception("Unknown shape type '%s'" % shape_obj.type) diff --git a/cvat/apps/git/git.py b/cvat/apps/git/git.py index bb960a23bd73..5d0e817829b3 100644 --- a/cvat/apps/git/git.py +++ b/cvat/apps/git/git.py @@ -10,13 +10,12 @@ import shutil import subprocess from glob import glob -from tempfile import TemporaryDirectory +import zipfile import django_rq import git from django.db import transaction from django.utils import timezone -from pyunpack import Archive from cvat.apps.dataset_manager.task import export_task from cvat.apps.engine.log import slogger @@ -284,16 +283,16 @@ def push(self, user, scheme, host, db_task, last_save): if ext == '.zip': shutil.move(dump_name, self._annotation_file) elif ext == '.xml': - with TemporaryDirectory() as tmp_dir: - # TODO: remove extra packing-unpacking - Archive(src_path).extractall(tmp_dir) - anno_paths = glob(osp.join(tmp_dir, '**', '*.xml'), - recursive=True) - shutil.move(anno_paths[0], self._annotation_file) + with zipfile.ZipFile(dump_name) as archive: + for f in archive.namelist(): + if f.endswith('.xml'): + with open(self._annotation_file, 'wb') as output: + output.write(archive.read(f)) + break + os.remove(dump_name) else: raise Exception("Got unknown annotation file type") - os.remove(dump_name) self._rep.git.add(self._annotation_file) # Merge diffs diff --git a/datumaro/datumaro/cli/__main__.py b/datumaro/datumaro/cli/__main__.py index b68de2267409..a7d7dd992a70 100644 --- a/datumaro/datumaro/cli/__main__.py +++ b/datumaro/datumaro/cli/__main__.py @@ -5,8 +5,6 @@ import argparse import logging as log -import logging.handlers -import os import sys from . import contexts, commands @@ -25,6 +23,25 @@ def loglevel(name): return _log_levels[name] +class _LogManager: + @classmethod + def init_logger(cls, args=None): + # Define minimalistic parser only to obtain loglevel + parser = argparse.ArgumentParser(add_help=False) + cls._define_loglevel_option(parser) + args, _ = parser.parse_known_args(args) + + log.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', + level=args.loglevel) + + @staticmethod + def _define_loglevel_option(parser): + parser.add_argument('--loglevel', type=loglevel, default='info', + help="Logging level (options: %s; default: %s)" % \ + (', '.join(_log_levels.keys()), "%(default)s")) + return parser + + def _make_subcommands_help(commands, help_line_start=0): desc = "" for command_name, _, command_help in commands: @@ -38,9 +55,7 @@ def make_parser(): formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('--version', action='version', version=VERSION) - parser.add_argument('--loglevel', type=loglevel, default='info', - help="Logging level (options: %s; default: %s)" % \ - (', '.join(_log_levels.keys()), "%(default)s")) + _LogManager._define_loglevel_option(parser) known_contexts = [ ('project', contexts.project, "Actions on projects (datasets)"), @@ -83,71 +98,13 @@ def make_parser(): return parser -class _LogManager: - _LOGLEVEL_ENV_NAME = '_DATUMARO_INIT_LOGLEVEL' - _BUFFER_SIZE = 1000 - _root = None - _init_handler = None - _default_handler = None - - @classmethod - def init_basic_logger(cls): - base_loglevel = os.getenv(cls._LOGLEVEL_ENV_NAME, 'info') - base_loglevel = loglevel(base_loglevel) - root = log.getLogger() - root.setLevel(base_loglevel) - - # NOTE: defer use of this handler until the logger - # is properly initialized, but keep logging enabled before this. - # Store messages obtained during initialization and print them after - # if necessary. - default_handler = log.StreamHandler() - default_handler.setFormatter( - log.Formatter('%(asctime)s %(levelname)s: %(message)s')) - - init_handler = logging.handlers.MemoryHandler(cls._BUFFER_SIZE, - target=default_handler) - root.addHandler(init_handler) - - cls._root = root - cls._init_handler = init_handler - cls._default_handler = default_handler - - @classmethod - def set_up_logger(cls, level): - log.getLogger().setLevel(level) - - if cls._init_handler: - # NOTE: Handlers are not capable of filtering with loglevel - # despite a level can be set for a handler. The level is checked - # by Logger. However, handler filters are checked at handler level. - class LevelFilter: - def __init__(self, level): - super().__init__() - self.level = level - - def filter(self, record): - return record.levelno >= self.level - filt = LevelFilter(level) - cls._default_handler.addFilter(filt) - - cls._root.removeHandler(cls._init_handler) - cls._init_handler.close() - del cls._init_handler - cls._init_handler = None - - cls._default_handler.removeFilter(filt) - - cls._root.addHandler(cls._default_handler) def main(args=None): - _LogManager.init_basic_logger() + _LogManager.init_logger(args) parser = make_parser() args = parser.parse_args(args) - _LogManager.set_up_logger(args.loglevel) - if 'command' not in args: parser.print_help() return 1 diff --git a/datumaro/datumaro/plugins/transforms.py b/datumaro/datumaro/plugins/transforms.py index 17f0b2a08fda..d37e284a9853 100644 --- a/datumaro/datumaro/plugins/transforms.py +++ b/datumaro/datumaro/plugins/transforms.py @@ -216,7 +216,7 @@ def transform_item(self, item): log.debug("[%s]: item %s: " "Mask conversion to polygons resulted in too " "small polygons, which were discarded" % \ - (self.NAME, item.id)) + (self._get_name(__class__), item.id)) annotations.extend(polygons) else: annotations.append(ann) diff --git a/datumaro/tests/test_transforms.py b/datumaro/tests/test_transforms.py index 58c677a275dd..522cf2b520f0 100644 --- a/datumaro/tests/test_transforms.py +++ b/datumaro/tests/test_transforms.py @@ -1,3 +1,4 @@ +import logging as log import numpy as np from unittest import TestCase @@ -65,6 +66,33 @@ def __iter__(self): actual = transforms.MasksToPolygons(SrcExtractor()) compare_datasets(self, DstExtractor(), actual) + def test_mask_to_polygons_small_polygons_message(self): + class SrcExtractor(Extractor): + def __iter__(self): + items = [ + DatasetItem(id=1, image=np.zeros((5, 10, 3)), + annotations=[ + Mask(np.array([ + [0, 0, 0], + [0, 1, 0], + [0, 0, 0], + ]), + ), + ] + ), + ] + return iter(items) + + class DstExtractor(Extractor): + def __iter__(self): + return iter([ DatasetItem(id=1, image=np.zeros((5, 10, 3))), ]) + + with self.assertLogs(level=log.DEBUG) as logs: + actual = transforms.MasksToPolygons(SrcExtractor()) + + compare_datasets(self, DstExtractor(), actual) + self.assertRegex('\n'.join(logs.output), 'too small polygons') + def test_polygons_to_masks(self): class SrcExtractor(Extractor): def __iter__(self):