Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added sorting methods #3937

Merged
merged 29 commits into from
Dec 9, 2021
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2184d56
Draft
Marishka17 Nov 19, 2021
03e47f9
Add sorting
Marishka17 Nov 22, 2021
aadc330
Fixes
Marishka17 Nov 22, 2021
144dfef
Add native sorting
Marishka17 Nov 23, 2021
4bf74fd
Update cli
Marishka17 Nov 24, 2021
abc98a1
Fixes
Marishka17 Nov 24, 2021
4326981
Add tests & fix sorting for cloud storage data
Marishka17 Nov 24, 2021
243447a
Revert
Marishka17 Nov 25, 2021
d494531
Update test
Marishka17 Nov 25, 2021
579b9af
Move imports
Marishka17 Nov 25, 2021
ee70699
Update requirements
Marishka17 Nov 25, 2021
bafdf0b
SortingMethod && remove reversed method && more meaningful names
Marishka17 Nov 25, 2021
0d5e77d
Merge branch 'develop' into mk/data_sorting
Marishka17 Nov 26, 2021
2affd92
Update changelog
Marishka17 Nov 26, 2021
72b496e
Fix default options
Marishka17 Nov 26, 2021
bc3ab6e
Increase versions
Marishka17 Nov 26, 2021
9b07297
keep file order -> predefined
Marishka17 Nov 26, 2021
af72a99
Merge branch 'develop' into mk/data_sorting
Marishka17 Nov 29, 2021
e161a28
Update versions
Marishka17 Nov 29, 2021
121d945
Add help message
Marishka17 Nov 30, 2021
7f508ca
Update documentation
Marishka17 Nov 30, 2021
f831801
revert to develop
Marishka17 Nov 30, 2021
bca08cc
Update cvat-core/src/session.js
Marishka17 Dec 1, 2021
b71f64c
fix
Marishka17 Dec 1, 2021
b122b1d
Fix sorting for import task
Marishka17 Dec 2, 2021
19686f4
Fixes && add tests
Marishka17 Dec 3, 2021
2a64a4d
Merge branch 'develop' into mk/data_sorting
Marishka17 Dec 3, 2021
1ab11e5
version ++
Marishka17 Dec 3, 2021
76744d3
Merge branch 'develop' into mk/data_sorting
Dec 5, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Player option: Smooth image when zoom-in, enabled by default (<https://github.com/openvinotoolkit/cvat/pull/3933>)
- Google Cloud Storage support in UI (<https://github.com/openvinotoolkit/cvat/pull/3919>)
- Add project tasks paginations (<https://github.com/openvinotoolkit/cvat/pull/3910>)
- Data sorting option (<https://github.com/openvinotoolkit/cvat/pull/3937>)

### Changed
- TDB
Expand Down
4 changes: 2 additions & 2 deletions cvat-core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-core",
"version": "3.20.1",
"version": "3.21.0",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
Expand Down
19 changes: 19 additions & 0 deletions cvat-core/src/enums.js
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,24 @@
KEY_FILE_PATH: 'KEY_FILE_PATH',
});

/**
* Sorting methods
* @enum {string}
* @name SortingMethod
* @memberof module:API.cvat.enums
* @property {string} LEXICOGRAPHICAL 'lexicographical'
* @property {string} NATURAL 'natural'
* @property {string} PREDEFINED 'predefined'
* @property {string} RANDOM 'random'
* @readonly
*/
const SortingMethod = Object.freeze({
LEXICOGRAPHICAL: 'lexicographical',
NATURAL: 'natural',
PREDEFINED: 'predefined',
RANDOM: 'random',
});

module.exports = {
ShareFileType,
TaskStatus,
Expand All @@ -384,5 +402,6 @@
DimensionType,
CloudStorageProviderType,
CloudStorageCredentialsType,
SortingMethod,
};
})();
12 changes: 12 additions & 0 deletions cvat-core/src/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,7 @@
copy_data: undefined,
dimension: undefined,
cloud_storage_id: undefined,
sorting_method: undefined,
};

const updatedFields = new FieldUpdateTrigger({
Expand Down Expand Up @@ -1549,6 +1550,16 @@
cloudStorageId: {
get: () => data.cloud_storage_id,
},
sortingMethod: {
/**
* @name enabled
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved
* @type {string}
* @memberof module:API.cvat.enums.SortingMethod
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved
* @instance
* @readonly
*/
get: () => data.sorting_method,
},
_internalData: {
get: () => data,
},
Expand Down Expand Up @@ -2061,6 +2072,7 @@
image_quality: this.imageQuality,
use_zip_chunks: this.useZipChunks,
use_cache: this.useCache,
sorting_method: this.sortingMethod,
};

if (typeof this.startFrame !== 'undefined') {
Expand Down
4 changes: 2 additions & 2 deletions cvat-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.28.0",
"version": "1.29.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions cvat-ui/src/actions/tasks-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ export function createTaskAsync(data: any): ThunkAction<Promise<void>, {}, {}, A
image_quality: 70,
use_zip_chunks: data.advanced.useZipChunks,
use_cache: data.advanced.useCache,
sorting_method: data.advanced.sortingMethod,
};

if (data.projectId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Row, Col } from 'antd/lib/grid';
import { PercentageOutlined } from '@ant-design/icons';
import Input from 'antd/lib/input';
import Select from 'antd/lib/select';
import Radio from 'antd/lib/radio';
import Checkbox from 'antd/lib/checkbox';
import Form, { FormInstance, RuleObject, RuleRender } from 'antd/lib/form';
import Text from 'antd/lib/typography/Text';
Expand All @@ -16,6 +17,13 @@ import patterns from 'utils/validation-patterns';

const { Option } = Select;

export enum SortingMethod {
LEXICOGRAPHICAL = 'lexicographical',
NATURAL = 'natural',
PREDEFINED = 'predefined',
RANDOM = 'random',
}

export interface AdvancedConfiguration {
bugTracker?: string;
imageQuality?: number;
Expand All @@ -31,6 +39,7 @@ export interface AdvancedConfiguration {
dataChunkSize?: number;
useCache: boolean;
copyData?: boolean;
sortingMethod: SortingMethod;
}

const initialValues: AdvancedConfiguration = {
Expand All @@ -39,6 +48,7 @@ const initialValues: AdvancedConfiguration = {
useZipChunks: true,
useCache: true,
copyData: false,
sortingMethod: SortingMethod.LEXICOGRAPHICAL,
};

interface Props {
Expand Down Expand Up @@ -178,6 +188,33 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
);
}

private renderSortingMethodRadio(): JSX.Element {
return (
<Form.Item
label='Sorting method'
name='sortingMethod'
rules={[
{
required: true,
message: 'The field is required.',
},
]}
help='Specify how to sort images. It is not relevant for videos.'
>
<Radio.Group>
<Radio value={SortingMethod.LEXICOGRAPHICAL} key={SortingMethod.LEXICOGRAPHICAL}>
Lexicographical
</Radio>
<Radio value={SortingMethod.NATURAL} key={SortingMethod.NATURAL}>Natural</Radio>
<Radio value={SortingMethod.PREDEFINED} key={SortingMethod.PREDEFINED}>
Predefined
</Radio>
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved
<Radio value={SortingMethod.RANDOM} key={SortingMethod.RANDOM}>Random</Radio>
</Radio.Group>
</Form.Item>
);
}

private renderImageQuality(): JSX.Element {
return (
<CVATTooltip title='Defines images compression level'>
Expand Down Expand Up @@ -290,8 +327,7 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
>
<Select style={{ width: '100%' }} initialValue='CVAT for video 1.1'>
{
dumpers.map((dumper: any) =>
<Option value={dumper.name}>{dumper.name}</Option>)
dumpers.map((dumper: any) => <Option value={dumper.name}>{dumper.name}</Option>)
}
</Select>
</Form.Item>
Expand Down Expand Up @@ -384,6 +420,9 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
const { installedGit, activeFileManagerTab } = this.props;
return (
<Form initialValues={initialValues} ref={this.formRef} layout='vertical'>
<Row>
<Col>{this.renderSortingMethodRadio()}</Col>
</Row>
{activeFileManagerTab === 'share' ? (
<Row>
<Col>{this.renderCopyDataChechbox()}</Col>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { Files } from 'components/file-manager/file-manager';
import BasicConfigurationForm, { BaseConfiguration } from './basic-configuration-form';
import ProjectSearchField from './project-search-field';
import ProjectSubsetField from './project-subset-field';
import AdvancedConfigurationForm, { AdvancedConfiguration } from './advanced-configuration-form';
import AdvancedConfigurationForm, { AdvancedConfiguration, SortingMethod } from './advanced-configuration-form';

export interface CreateTaskData {
projectId: number | null;
Expand Down Expand Up @@ -54,6 +54,7 @@ const defaultState = {
lfs: false,
useZipChunks: true,
useCache: true,
sortingMethod: SortingMethod.LEXICOGRAPHICAL,
},
labels: [],
files: {
Expand Down
66 changes: 54 additions & 12 deletions cvat/apps/engine/media_extractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from pyunpack import Archive
from PIL import Image, ImageFile
import open3d as o3d
from cvat.apps.engine.utils import rotate_image
from cvat.apps.engine.utils import rotate_image, sort, SortingMethod
from cvat.apps.engine.models import DimensionType

# fixes: "OSError:broken data stream" when executing line 72 while loading images downloaded from the web
Expand Down Expand Up @@ -49,7 +49,7 @@ def files_to_ignore(directory):

class IMediaReader(ABC):
def __init__(self, source_path, step, start, stop, dimension):
self._source_path = sorted(source_path)
self._source_path = source_path
self._step = step
self._start = start
self._stop = stop
Expand Down Expand Up @@ -90,7 +90,13 @@ def frame_range(self):
return range(self._start, self._stop, self._step)

class ImageListReader(IMediaReader):
def __init__(self, source_path, step=1, start=0, stop=None, dimension=DimensionType.DIM_2D):
def __init__(self,
source_path,
step=1,
start=0,
stop=None,
dimension=DimensionType.DIM_2D,
sorting_method=SortingMethod.LEXICOGRAPHICAL):
if not source_path:
raise Exception('No image found')

Expand All @@ -102,13 +108,15 @@ def __init__(self, source_path, step=1, start=0, stop=None, dimension=DimensionT
assert stop > start

super().__init__(
source_path=source_path,
source_path=sort(source_path, sorting_method),
step=step,
start=start,
stop=stop,
dimension=dimension
)

self._sorting_method = sorting_method

def __iter__(self):
for i in range(self._start, self._stop, self._step):
yield (self.get_image(i), self.get_path(i), i)
Expand All @@ -121,7 +129,8 @@ def filter(self, callback):
step=self._step,
start=self._start,
stop=self._stop,
dimension=self._dimension
dimension=self._dimension,
sorting_method=self._sorting_method
)

def get_path(self, i):
Expand Down Expand Up @@ -154,7 +163,8 @@ def reconcile(self, source_files, step=1, start=0, stop=None, dimension=Dimensio
source_path=source_files,
step=step,
start=start,
stop=stop
stop=stop,
sorting_method=self._sorting_method,
)
self._dimension = dimension

Expand All @@ -163,7 +173,13 @@ def absolute_source_paths(self):
return [self.get_path(idx) for idx, _ in enumerate(self._source_path)]

class DirectoryReader(ImageListReader):
def __init__(self, source_path, step=1, start=0, stop=None, dimension=DimensionType.DIM_2D):
def __init__(self,
source_path,
step=1,
start=0,
stop=None,
dimension=DimensionType.DIM_2D,
sorting_method=SortingMethod.LEXICOGRAPHICAL):
image_paths = []
for source in source_path:
for root, _, files in os.walk(source):
Expand All @@ -176,10 +192,17 @@ def __init__(self, source_path, step=1, start=0, stop=None, dimension=DimensionT
start=start,
stop=stop,
dimension=dimension,
sorting_method=sorting_method,
)

class ArchiveReader(DirectoryReader):
def __init__(self, source_path, step=1, start=0, stop=None, dimension=DimensionType.DIM_2D):
def __init__(self,
source_path,
step=1,
start=0,
stop=None,
dimension=DimensionType.DIM_2D,
sorting_method=SortingMethod.LEXICOGRAPHICAL):
self._archive_source = source_path[0]
extract_dir = source_path[1] if len(source_path) > 1 else os.path.dirname(source_path[0])
Archive(self._archive_source).extractall(extract_dir)
Expand All @@ -190,11 +213,18 @@ def __init__(self, source_path, step=1, start=0, stop=None, dimension=DimensionT
step=step,
start=start,
stop=stop,
dimension=dimension
dimension=dimension,
sorting_method=sorting_method,
)

class PdfReader(ImageListReader):
def __init__(self, source_path, step=1, start=0, stop=None, dimension=DimensionType.DIM_2D):
def __init__(self,
source_path,
step=1,
start=0,
stop=None,
dimension=DimensionType.DIM_2D,
sorting_method=SortingMethod.LEXICOGRAPHICAL):
if not source_path:
raise Exception('No PDF found')

Expand Down Expand Up @@ -223,14 +253,26 @@ def _make_name():
start=start,
stop=stop,
dimension=dimension,
sorting_method=sorting_method,
)

class ZipReader(ImageListReader):
def __init__(self, source_path, step=1, start=0, stop=None, dimension=DimensionType.DIM_2D):
def __init__(self,
source_path,
step=1,
start=0,
stop=None,
dimension=DimensionType.DIM_2D,
sorting_method=SortingMethod.LEXICOGRAPHICAL):
self._zip_source = zipfile.ZipFile(source_path[0], mode='r')
self.extract_dir = source_path[1] if len(source_path) > 1 else None
file_list = [f for f in self._zip_source.namelist() if files_to_ignore(f) and get_mime(f) == 'image']
super().__init__(file_list, step=step, start=start, stop=stop, dimension=dimension)
super().__init__(file_list,
step=step,
start=start,
stop=stop,
dimension=dimension,
sorting_method=sorting_method)

def __del__(self):
self._zip_source.close()
Expand Down
Loading