annofabcli 1.111.2__py3-none-any.whl → 1.112.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- annofabcli/__main__.py +1 -2
- annofabcli/annotation/annotation_query.py +10 -10
- annofabcli/annotation/change_annotation_attributes.py +9 -9
- annofabcli/annotation/change_annotation_attributes_per_annotation.py +3 -4
- annofabcli/annotation/change_annotation_properties.py +13 -13
- annofabcli/annotation/copy_annotation.py +5 -5
- annofabcli/annotation/create_classification_annotation.py +6 -6
- annofabcli/annotation/delete_annotation.py +8 -8
- annofabcli/annotation/download_annotation_zip.py +1 -3
- annofabcli/annotation/dump_annotation.py +7 -7
- annofabcli/annotation/import_annotation.py +12 -12
- annofabcli/annotation/list_annotation.py +8 -8
- annofabcli/annotation/list_annotation_count.py +1 -2
- annofabcli/annotation/merge_segmentation.py +5 -5
- annofabcli/annotation/remove_segmentation_overlap.py +4 -4
- annofabcli/annotation/restore_annotation.py +6 -6
- annofabcli/annotation/subcommand_annotation.py +1 -2
- annofabcli/annotation_specs/add_attribute_restriction.py +4 -5
- annofabcli/annotation_specs/attribute_restriction.py +8 -8
- annofabcli/annotation_specs/export_annotation_specs.py +4 -5
- annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +3 -4
- annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +3 -4
- annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +3 -4
- annofabcli/annotation_specs/list_annotation_specs_attribute.py +9 -10
- annofabcli/annotation_specs/list_annotation_specs_choice.py +9 -10
- annofabcli/annotation_specs/list_annotation_specs_history.py +2 -2
- annofabcli/annotation_specs/list_annotation_specs_label.py +8 -9
- annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +10 -11
- annofabcli/annotation_specs/list_attribute_restriction.py +2 -4
- annofabcli/annotation_specs/list_label_color.py +2 -3
- annofabcli/annotation_specs/put_label_color.py +3 -4
- annofabcli/annotation_specs/subcommand_annotation_specs.py +1 -3
- annofabcli/annotation_zip/list_annotation_3d_bounding_box.py +365 -0
- annofabcli/annotation_zip/list_annotation_bounding_box_2d.py +11 -12
- annofabcli/annotation_zip/list_range_annotation.py +24 -14
- annofabcli/annotation_zip/list_single_point_annotation.py +11 -12
- annofabcli/annotation_zip/subcommand_annotation_zip.py +3 -2
- annofabcli/annotation_zip/validate_annotation.py +8 -7
- annofabcli/comment/delete_comment.py +4 -6
- annofabcli/comment/download_comment_json.py +4 -6
- annofabcli/comment/list_all_comment.py +5 -6
- annofabcli/comment/list_comment.py +3 -4
- annofabcli/comment/put_comment.py +9 -10
- annofabcli/comment/put_comment_simply.py +5 -6
- annofabcli/comment/put_inspection_comment.py +1 -3
- annofabcli/comment/put_inspection_comment_simply.py +1 -3
- annofabcli/comment/put_onhold_comment.py +1 -3
- annofabcli/comment/put_onhold_comment_simply.py +1 -3
- annofabcli/comment/subcommand_comment.py +1 -3
- annofabcli/common/bokeh.py +4 -4
- annofabcli/common/cli.py +17 -17
- annofabcli/common/download.py +28 -29
- annofabcli/common/facade.py +37 -38
- annofabcli/common/image.py +14 -14
- annofabcli/common/utils.py +8 -8
- annofabcli/common/visualize.py +13 -13
- annofabcli/experimental/list_out_of_range_annotation_for_movie.py +3 -4
- annofabcli/experimental/subcommand_experimental.py +1 -3
- annofabcli/filesystem/draw_annotation.py +26 -26
- annofabcli/filesystem/filter_annotation.py +9 -10
- annofabcli/filesystem/mask_user_info.py +14 -14
- annofabcli/filesystem/merge_annotation.py +8 -8
- annofabcli/filesystem/subcommand_filesystem.py +1 -3
- annofabcli/input_data/copy_input_data.py +8 -9
- annofabcli/input_data/delete_input_data.py +2 -2
- annofabcli/input_data/delete_metadata_key_of_input_data.py +3 -5
- annofabcli/input_data/download_input_data_json.py +4 -6
- annofabcli/input_data/list_all_input_data.py +8 -8
- annofabcli/input_data/list_all_input_data_merged_task.py +4 -4
- annofabcli/input_data/list_input_data.py +4 -4
- annofabcli/input_data/put_input_data.py +5 -5
- annofabcli/input_data/put_input_data_with_zip.py +2 -3
- annofabcli/input_data/subcommand_input_data.py +1 -3
- annofabcli/input_data/update_input_data.py +6 -8
- annofabcli/input_data/update_metadata_of_input_data.py +3 -5
- annofabcli/instruction/copy_instruction.py +4 -5
- annofabcli/instruction/download_instruction.py +4 -5
- annofabcli/instruction/list_instruction_history.py +2 -2
- annofabcli/instruction/subcommand_instruction.py +1 -3
- annofabcli/instruction/upload_instruction.py +2 -3
- annofabcli/job/delete_job.py +1 -2
- annofabcli/job/list_job.py +4 -4
- annofabcli/job/list_last_job.py +3 -3
- annofabcli/job/subcommand_job.py +1 -3
- annofabcli/job/wait_job.py +4 -5
- annofabcli/my_account/get_my_account.py +1 -2
- annofabcli/my_account/subcommand_my_account.py +1 -3
- annofabcli/organization/list_organization.py +1 -2
- annofabcli/organization/subcommand_organization.py +1 -3
- annofabcli/organization_member/change_organization_member.py +3 -4
- annofabcli/organization_member/delete_organization_member.py +3 -4
- annofabcli/organization_member/invite_organization_member.py +1 -3
- annofabcli/organization_member/list_organization_member.py +2 -2
- annofabcli/organization_member/subcommand_organization_member.py +1 -3
- annofabcli/project/change_organization_of_project.py +3 -3
- annofabcli/project/change_project_status.py +3 -3
- annofabcli/project/copy_project.py +4 -4
- annofabcli/project/create_project.py +7 -7
- annofabcli/project/diff_projects.py +4 -5
- annofabcli/project/list_project.py +4 -4
- annofabcli/project/put_project.py +1 -2
- annofabcli/project/subcommand_project.py +1 -2
- annofabcli/project/update_configuration.py +3 -3
- annofabcli/project/update_project.py +6 -8
- annofabcli/project_member/change_project_members.py +7 -7
- annofabcli/project_member/copy_project_members.py +3 -3
- annofabcli/project_member/drop_project_members.py +1 -2
- annofabcli/project_member/invite_project_members.py +1 -3
- annofabcli/project_member/list_users.py +1 -2
- annofabcli/project_member/put_project_members.py +5 -5
- annofabcli/project_member/subcommand_project_member.py +1 -3
- annofabcli/stat_visualization/mask_visualization_dir.py +8 -9
- annofabcli/stat_visualization/merge_visualization_dir.py +6 -7
- annofabcli/stat_visualization/subcommand_stat_visualization.py +1 -2
- annofabcli/stat_visualization/summarize_whole_performance_csv.py +1 -2
- annofabcli/stat_visualization/write_graph.py +2 -3
- annofabcli/stat_visualization/write_performance_rating_csv.py +20 -27
- annofabcli/statistics/histogram.py +5 -6
- annofabcli/statistics/linegraph.py +13 -14
- annofabcli/statistics/list_annotation_area.py +38 -13
- annofabcli/statistics/list_annotation_attribute.py +9 -10
- annofabcli/statistics/list_annotation_attribute_filled_count.py +30 -31
- annofabcli/statistics/list_annotation_count.py +57 -58
- annofabcli/statistics/list_annotation_duration.py +33 -34
- annofabcli/statistics/list_video_duration.py +4 -5
- annofabcli/statistics/list_worktime.py +3 -3
- annofabcli/statistics/scatter.py +9 -8
- annofabcli/statistics/subcommand_statistics.py +1 -4
- annofabcli/statistics/summarize_task_count.py +4 -6
- annofabcli/statistics/summarize_task_count_by_task_id_group.py +2 -4
- annofabcli/statistics/summarize_task_count_by_user.py +1 -3
- annofabcli/statistics/visualization/dataframe/annotation_count.py +5 -4
- annofabcli/statistics/visualization/dataframe/annotation_duration.py +2 -3
- annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +15 -17
- annofabcli/statistics/visualization/dataframe/productivity_per_date.py +17 -19
- annofabcli/statistics/visualization/dataframe/project_performance.py +3 -12
- annofabcli/statistics/visualization/dataframe/task.py +11 -12
- annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +9 -10
- annofabcli/statistics/visualization/dataframe/user_performance.py +21 -19
- annofabcli/statistics/visualization/dataframe/whole_performance.py +3 -4
- annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +12 -14
- annofabcli/statistics/visualization/dataframe/worktime_per_date.py +11 -13
- annofabcli/statistics/visualization/filtering_query.py +7 -7
- annofabcli/statistics/visualization/project_dir.py +27 -14
- annofabcli/statistics/visualize_annotation_count.py +22 -23
- annofabcli/statistics/visualize_annotation_duration.py +21 -22
- annofabcli/statistics/visualize_statistics.py +36 -33
- annofabcli/statistics/visualize_video_duration.py +18 -20
- annofabcli/supplementary/delete_supplementary_data.py +4 -4
- annofabcli/supplementary/list_supplementary_data.py +3 -3
- annofabcli/supplementary/put_supplementary_data.py +8 -8
- annofabcli/supplementary/subcommand_supplementary.py +1 -3
- annofabcli/task/cancel_acceptance.py +16 -17
- annofabcli/task/change_operator.py +10 -12
- annofabcli/task/change_status_to_break.py +7 -9
- annofabcli/task/change_status_to_on_hold.py +10 -12
- annofabcli/task/complete_tasks.py +17 -18
- annofabcli/task/copy_tasks.py +3 -5
- annofabcli/task/delete_metadata_key_of_task.py +4 -6
- annofabcli/task/delete_tasks.py +6 -6
- annofabcli/task/download_task_json.py +4 -6
- annofabcli/task/list_all_tasks.py +7 -7
- annofabcli/task/list_all_tasks_added_task_history.py +12 -12
- annofabcli/task/list_tasks.py +6 -6
- annofabcli/task/list_tasks_added_task_history.py +9 -9
- annofabcli/task/put_tasks.py +4 -5
- annofabcli/task/put_tasks_by_count.py +1 -2
- annofabcli/task/reject_tasks.py +18 -20
- annofabcli/task/subcommand_task.py +1 -3
- annofabcli/task/update_metadata_of_task.py +5 -6
- annofabcli/task_history/download_task_history_json.py +4 -6
- annofabcli/task_history/list_all_task_history.py +5 -6
- annofabcli/task_history/list_task_history.py +3 -4
- annofabcli/task_history/subcommand_task_history.py +1 -3
- annofabcli/task_history_event/download_task_history_event_json.py +4 -6
- annofabcli/task_history_event/list_all_task_history_event.py +6 -6
- annofabcli/task_history_event/list_worktime.py +15 -15
- annofabcli/task_history_event/subcommand_task_history_event.py +1 -2
- {annofabcli-1.111.2.dist-info → annofabcli-1.112.0.dist-info}/METADATA +9 -15
- annofabcli-1.112.0.dist-info/RECORD +229 -0
- {annofabcli-1.111.2.dist-info → annofabcli-1.112.0.dist-info}/WHEEL +1 -1
- annofabcli-1.111.2.dist-info/RECORD +0 -228
- {annofabcli-1.111.2.dist-info → annofabcli-1.112.0.dist-info}/entry_points.txt +0 -0
- {annofabcli-1.111.2.dist-info → annofabcli-1.112.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,14 +6,13 @@ import logging
|
|
|
6
6
|
import sys
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import Any
|
|
10
10
|
|
|
11
11
|
import pandas
|
|
12
12
|
from annofabapi.models import Lang
|
|
13
13
|
from annofabapi.util.annotation_specs import get_message_with_lang
|
|
14
14
|
from dataclasses_json import DataClassJsonMixin
|
|
15
15
|
|
|
16
|
-
import annofabcli
|
|
17
16
|
import annofabcli.common.cli
|
|
18
17
|
from annofabcli.common.cli import (
|
|
19
18
|
COMMAND_LINE_ERROR_STATUS_CODE,
|
|
@@ -31,9 +30,9 @@ logger = logging.getLogger(__name__)
|
|
|
31
30
|
@dataclass
|
|
32
31
|
class LabelAndAttribute(DataClassJsonMixin):
|
|
33
32
|
label_id: str
|
|
34
|
-
label_name_en:
|
|
35
|
-
label_name_ja:
|
|
36
|
-
label_name_vi:
|
|
33
|
+
label_name_en: str | None
|
|
34
|
+
label_name_ja: str | None
|
|
35
|
+
label_name_vi: str | None
|
|
37
36
|
annotation_type: str
|
|
38
37
|
|
|
39
38
|
attribute_id: str
|
|
@@ -43,9 +42,9 @@ class LabelAndAttribute(DataClassJsonMixin):
|
|
|
43
42
|
APIレスポンスの ``additional_data_definition_id`` に相当します。
|
|
44
43
|
``additional_data_definition_id`` という名前がアノテーションJSONの `attributes` と対応していることが分かりにくかったので、`attribute_id`という名前に変えました。
|
|
45
44
|
"""
|
|
46
|
-
attribute_name_en:
|
|
47
|
-
attribute_name_ja:
|
|
48
|
-
attribute_name_vi:
|
|
45
|
+
attribute_name_en: str | None
|
|
46
|
+
attribute_name_ja: str | None
|
|
47
|
+
attribute_name_vi: str | None
|
|
49
48
|
attribute_type: str
|
|
50
49
|
|
|
51
50
|
|
|
@@ -91,7 +90,7 @@ def create_label_attribute_list(labels_v3: list[dict[str, Any]], additionals_v3:
|
|
|
91
90
|
class PrintAnnotationSpecsLabelAndAttribute(CommandLine):
|
|
92
91
|
COMMON_MESSAGE = "annofabcli annotation_specs list_label: error:"
|
|
93
92
|
|
|
94
|
-
def print_annotation_specs_label(self, annotation_specs_v3: dict[str, Any], output_format: FormatArgument, output:
|
|
93
|
+
def print_annotation_specs_label(self, annotation_specs_v3: dict[str, Any], output_format: FormatArgument, output: str | None = None) -> None:
|
|
95
94
|
# アノテーション仕様のv2とv3はほとんど同じなので、`convert_annotation_specs_labels_v2_to_v1`にはV3のアノテーション仕様を渡す
|
|
96
95
|
label_attribute_list = create_label_attribute_list(annotation_specs_v3["labels"], annotation_specs_v3["additionals"])
|
|
97
96
|
|
|
@@ -115,7 +114,7 @@ class PrintAnnotationSpecsLabelAndAttribute(CommandLine):
|
|
|
115
114
|
elif output_format in [FormatArgument.JSON, FormatArgument.PRETTY_JSON]:
|
|
116
115
|
print_according_to_format([e.to_dict() for e in label_attribute_list], format=output_format, output=output)
|
|
117
116
|
|
|
118
|
-
def get_history_id_from_before_index(self, project_id: str, before: int) ->
|
|
117
|
+
def get_history_id_from_before_index(self, project_id: str, before: int) -> str | None:
|
|
119
118
|
histories, _ = self.service.api.get_annotation_specs_histories(project_id)
|
|
120
119
|
if before + 1 > len(histories):
|
|
121
120
|
logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
|
|
@@ -205,7 +204,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
205
204
|
PrintAnnotationSpecsLabelAndAttribute(service, facade, args).main()
|
|
206
205
|
|
|
207
206
|
|
|
208
|
-
def add_parser(subparsers:
|
|
207
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
209
208
|
subcommand_name = "list_label_attribute"
|
|
210
209
|
|
|
211
210
|
subcommand_help = "アノテーション仕様のラベルとラベルに含まれている属性の一覧を出力します。"
|
|
@@ -5,9 +5,7 @@ import json
|
|
|
5
5
|
import logging
|
|
6
6
|
import sys
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
|
-
import annofabcli
|
|
11
9
|
import annofabcli.common.cli
|
|
12
10
|
from annofabcli.annotation_specs.attribute_restriction import AttributeRestrictionMessage, OutputFormat
|
|
13
11
|
from annofabcli.common.cli import (
|
|
@@ -25,7 +23,7 @@ logger = logging.getLogger(__name__)
|
|
|
25
23
|
class ListAttributeRestriction(CommandLine):
|
|
26
24
|
COMMON_MESSAGE = "annofabcli annotation_specs list_restriction: error:"
|
|
27
25
|
|
|
28
|
-
def get_history_id_from_before_index(self, project_id: str, before: int) ->
|
|
26
|
+
def get_history_id_from_before_index(self, project_id: str, before: int) -> str | None:
|
|
29
27
|
histories, _ = self.service.api.get_annotation_specs_histories(project_id)
|
|
30
28
|
if before + 1 > len(histories):
|
|
31
29
|
logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
|
|
@@ -133,7 +131,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
133
131
|
ListAttributeRestriction(service, facade, args).main()
|
|
134
132
|
|
|
135
133
|
|
|
136
|
-
def add_parser(subparsers:
|
|
134
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
137
135
|
subcommand_name = "list_attribute_restriction"
|
|
138
136
|
|
|
139
137
|
subcommand_help = "アノテーション仕様の属性の制約情報を出力します。"
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
6
|
import logging
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
|
-
import annofabcli
|
|
10
9
|
import annofabcli.common.cli
|
|
11
10
|
from annofabcli.common.cli import (
|
|
12
11
|
ArgumentParser,
|
|
@@ -65,7 +64,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
65
64
|
PrintLabelColor(service, facade, args).main()
|
|
66
65
|
|
|
67
66
|
|
|
68
|
-
def add_parser(subparsers:
|
|
67
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
69
68
|
subcommand_name = "list_label_color"
|
|
70
69
|
|
|
71
70
|
subcommand_help = "label_name(英名)とRGBの関係をJSONで出力します。"
|
|
@@ -4,11 +4,10 @@ import argparse
|
|
|
4
4
|
import logging
|
|
5
5
|
import sys
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
import annofabapi
|
|
10
10
|
|
|
11
|
-
import annofabcli
|
|
12
11
|
import annofabcli.common.cli
|
|
13
12
|
from annofabcli.common.cli import (
|
|
14
13
|
COMMAND_LINE_ERROR_STATUS_CODE,
|
|
@@ -78,7 +77,7 @@ class PuttingLabelColorMain(CommandLineWithConfirm):
|
|
|
78
77
|
confirm_message = confirm_message + "\n" + str_changed_labels
|
|
79
78
|
return self.confirm_processing(confirm_message)
|
|
80
79
|
|
|
81
|
-
def main(self, label_color: LabelColorDict, comment:
|
|
80
|
+
def main(self, label_color: LabelColorDict, comment: str | None) -> None:
|
|
82
81
|
request_body, changed_labels = self.create_request_body(label_color)
|
|
83
82
|
|
|
84
83
|
if len(changed_labels) == 0:
|
|
@@ -138,7 +137,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
138
137
|
PutLabelColor(service, facade, args).main()
|
|
139
138
|
|
|
140
139
|
|
|
141
|
-
def add_parser(subparsers:
|
|
140
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
142
141
|
subcommand_name = "put_label_color"
|
|
143
142
|
|
|
144
143
|
subcommand_help = "ラベルの色を変更します。"
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
|
-
import annofabcli
|
|
5
3
|
import annofabcli.annotation_specs.add_attribute_restriction
|
|
6
4
|
import annofabcli.annotation_specs.export_annotation_specs
|
|
7
5
|
import annofabcli.annotation_specs.get_annotation_specs_with_attribute_id_replaced
|
|
@@ -37,7 +35,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
|
37
35
|
annofabcli.annotation_specs.put_label_color.add_parser(subparsers)
|
|
38
36
|
|
|
39
37
|
|
|
40
|
-
def add_parser(subparsers:
|
|
38
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
41
39
|
subcommand_name = "annotation_specs"
|
|
42
40
|
subcommand_help = "アノテーション仕様関係のサブコマンド"
|
|
43
41
|
description = "アノテーション仕様関係のサブコマンド"
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
import tempfile
|
|
5
|
+
from collections.abc import Collection
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import pandas
|
|
11
|
+
from annofab_3dpc.annotation import CuboidAnnotationDetailDataV2, convert_annotation_detail_data
|
|
12
|
+
from annofabapi.models import InputDataType, ProjectMemberRole
|
|
13
|
+
from dataclasses_json import DataClassJsonMixin
|
|
14
|
+
|
|
15
|
+
import annofabcli.common.cli
|
|
16
|
+
from annofabcli.common.annofab.annotation_zip import lazy_parse_simple_annotation_by_input_data
|
|
17
|
+
from annofabcli.common.cli import COMMAND_LINE_ERROR_STATUS_CODE, ArgumentParser, CommandLine, build_annofabapi_resource_and_login, get_list_from_args
|
|
18
|
+
from annofabcli.common.download import DownloadingFile
|
|
19
|
+
from annofabcli.common.enums import FormatArgument
|
|
20
|
+
from annofabcli.common.facade import (
|
|
21
|
+
AnnofabApiFacade,
|
|
22
|
+
TaskQuery,
|
|
23
|
+
match_annotation_with_task_query,
|
|
24
|
+
)
|
|
25
|
+
from annofabcli.common.utils import print_csv, print_json
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class Annotation3DBoundingBoxInfo(DataClassJsonMixin):
|
|
32
|
+
project_id: str
|
|
33
|
+
task_id: str
|
|
34
|
+
task_status: str
|
|
35
|
+
task_phase: str
|
|
36
|
+
task_phase_stage: int
|
|
37
|
+
|
|
38
|
+
input_data_id: str
|
|
39
|
+
input_data_name: str
|
|
40
|
+
|
|
41
|
+
label: str
|
|
42
|
+
annotation_id: str
|
|
43
|
+
|
|
44
|
+
dimensions: dict[str, float]
|
|
45
|
+
"""サイズ情報 (width, height, depth)"""
|
|
46
|
+
|
|
47
|
+
location: dict[str, float]
|
|
48
|
+
"""中心座標 (x, y, z)"""
|
|
49
|
+
|
|
50
|
+
rotation: dict[str, float]
|
|
51
|
+
"""回転情報 (x=roll, y=pitch, z=yaw)"""
|
|
52
|
+
|
|
53
|
+
direction: dict[str, dict[str, float]]
|
|
54
|
+
"""方向ベクトル (front, up)"""
|
|
55
|
+
|
|
56
|
+
volume: float
|
|
57
|
+
"""体積(width × height × depth)"""
|
|
58
|
+
|
|
59
|
+
footprint_area: float
|
|
60
|
+
"""底面積(width × depth)。地面占有面積。"""
|
|
61
|
+
|
|
62
|
+
bottom_z: float
|
|
63
|
+
"""底面のZ座標(location.z - height/2)。回転は考慮していない。"""
|
|
64
|
+
|
|
65
|
+
top_z: float
|
|
66
|
+
"""天面のZ座標(location.z + height/2)。回転は考慮していない。"""
|
|
67
|
+
|
|
68
|
+
attributes: dict[str, str | int | bool]
|
|
69
|
+
"""属性情報"""
|
|
70
|
+
|
|
71
|
+
updated_datetime: str | None
|
|
72
|
+
"""アノテーションJSONに格納されているアノテーションの更新日時"""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_annotation_3d_bounding_box_info_list(simple_annotation: dict[str, Any], *, target_label_names: Collection[str] | None = None) -> list[Annotation3DBoundingBoxInfo]:
|
|
76
|
+
result = []
|
|
77
|
+
target_label_names_set = set(target_label_names) if target_label_names is not None else None
|
|
78
|
+
for detail in simple_annotation["details"]:
|
|
79
|
+
label = detail["label"]
|
|
80
|
+
# ラベル名によるフィルタリング
|
|
81
|
+
if target_label_names_set is not None and label not in target_label_names_set:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
annotation_data = convert_annotation_detail_data(detail["data"])
|
|
85
|
+
if not isinstance(annotation_data, CuboidAnnotationDetailDataV2):
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
# 追加情報の計算
|
|
89
|
+
dimensions = annotation_data.shape.dimensions
|
|
90
|
+
location = annotation_data.shape.location
|
|
91
|
+
width = dimensions.width
|
|
92
|
+
height = dimensions.height
|
|
93
|
+
depth = dimensions.depth
|
|
94
|
+
|
|
95
|
+
volume = width * height * depth
|
|
96
|
+
footprint_area = width * depth
|
|
97
|
+
bottom_z = location.z - height / 2
|
|
98
|
+
top_z = location.z + height / 2
|
|
99
|
+
|
|
100
|
+
result.append(
|
|
101
|
+
Annotation3DBoundingBoxInfo(
|
|
102
|
+
project_id=simple_annotation["project_id"],
|
|
103
|
+
task_id=simple_annotation["task_id"],
|
|
104
|
+
task_phase=simple_annotation["task_phase"],
|
|
105
|
+
task_phase_stage=simple_annotation["task_phase_stage"],
|
|
106
|
+
task_status=simple_annotation["task_status"],
|
|
107
|
+
input_data_id=simple_annotation["input_data_id"],
|
|
108
|
+
input_data_name=simple_annotation["input_data_name"],
|
|
109
|
+
label=label,
|
|
110
|
+
annotation_id=detail["annotation_id"],
|
|
111
|
+
dimensions=dimensions.to_dict(), # type: ignore[arg-type]
|
|
112
|
+
location=location.to_dict(), # type: ignore[arg-type]
|
|
113
|
+
rotation=annotation_data.shape.rotation.to_dict(), # type: ignore[arg-type]
|
|
114
|
+
direction=annotation_data.shape.direction.to_dict(), # type: ignore[arg-type]
|
|
115
|
+
volume=volume,
|
|
116
|
+
footprint_area=footprint_area,
|
|
117
|
+
bottom_z=bottom_z,
|
|
118
|
+
top_z=top_z,
|
|
119
|
+
attributes=detail["attributes"],
|
|
120
|
+
updated_datetime=simple_annotation["updated_datetime"],
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return result
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_annotation_3d_bounding_box_info_list_from_annotation_path(
|
|
128
|
+
annotation_path: Path,
|
|
129
|
+
*,
|
|
130
|
+
target_task_ids: Collection[str] | None = None,
|
|
131
|
+
task_query: TaskQuery | None = None,
|
|
132
|
+
target_label_names: Collection[str] | None = None,
|
|
133
|
+
) -> list[Annotation3DBoundingBoxInfo]:
|
|
134
|
+
annotation_bbox_list = []
|
|
135
|
+
target_task_ids_set = set(target_task_ids) if target_task_ids is not None else None
|
|
136
|
+
iter_parser = lazy_parse_simple_annotation_by_input_data(annotation_path)
|
|
137
|
+
logger.info(f"アノテーションZIPまたはディレクトリ'{annotation_path}'を読み込みます。")
|
|
138
|
+
for index, parser in enumerate(iter_parser):
|
|
139
|
+
if (index + 1) % 10000 == 0:
|
|
140
|
+
logger.info(f"{index + 1} 件目のJSONを読み込み中")
|
|
141
|
+
if target_task_ids_set is not None and parser.task_id not in target_task_ids_set:
|
|
142
|
+
continue
|
|
143
|
+
dict_simple_annotation = parser.load_json()
|
|
144
|
+
if task_query is not None and not match_annotation_with_task_query(dict_simple_annotation, task_query):
|
|
145
|
+
continue
|
|
146
|
+
sub_annotation_bbox_list = get_annotation_3d_bounding_box_info_list(dict_simple_annotation, target_label_names=target_label_names)
|
|
147
|
+
annotation_bbox_list.extend(sub_annotation_bbox_list)
|
|
148
|
+
return annotation_bbox_list
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def create_df(
|
|
152
|
+
annotation_bbox_list: list[Annotation3DBoundingBoxInfo],
|
|
153
|
+
) -> pandas.DataFrame:
|
|
154
|
+
tmp_annotation_bbox_list = [e.to_dict(encode_json=True) for e in annotation_bbox_list]
|
|
155
|
+
|
|
156
|
+
base_columns = [
|
|
157
|
+
"project_id",
|
|
158
|
+
"task_id",
|
|
159
|
+
"task_status",
|
|
160
|
+
"task_phase",
|
|
161
|
+
"task_phase_stage",
|
|
162
|
+
"input_data_id",
|
|
163
|
+
"input_data_name",
|
|
164
|
+
"updated_datetime",
|
|
165
|
+
"label",
|
|
166
|
+
"annotation_id",
|
|
167
|
+
"dimensions.width",
|
|
168
|
+
"dimensions.height",
|
|
169
|
+
"dimensions.depth",
|
|
170
|
+
"location.x",
|
|
171
|
+
"location.y",
|
|
172
|
+
"location.z",
|
|
173
|
+
"rotation.x",
|
|
174
|
+
"rotation.y",
|
|
175
|
+
"rotation.z",
|
|
176
|
+
"volume",
|
|
177
|
+
"footprint_area",
|
|
178
|
+
"bottom_z",
|
|
179
|
+
"top_z",
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
if len(tmp_annotation_bbox_list) == 0:
|
|
183
|
+
# 空のDataFrameの場合、base_columnsの列を持つ空のDataFrameを作成
|
|
184
|
+
return pandas.DataFrame(columns=base_columns)
|
|
185
|
+
|
|
186
|
+
df = pandas.json_normalize(tmp_annotation_bbox_list)
|
|
187
|
+
attribute_columns = sorted(col for col in df.columns if col.startswith("attributes."))
|
|
188
|
+
columns = base_columns + attribute_columns
|
|
189
|
+
|
|
190
|
+
return df[columns]
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def print_annotation_3d_bounding_box(
|
|
194
|
+
annotation_path: Path,
|
|
195
|
+
output_file: Path,
|
|
196
|
+
output_format: FormatArgument,
|
|
197
|
+
*,
|
|
198
|
+
target_task_ids: Collection[str] | None = None,
|
|
199
|
+
task_query: TaskQuery | None = None,
|
|
200
|
+
target_label_names: Collection[str] | None = None,
|
|
201
|
+
) -> None:
|
|
202
|
+
annotation_bbox_list = get_annotation_3d_bounding_box_info_list_from_annotation_path(
|
|
203
|
+
annotation_path,
|
|
204
|
+
target_task_ids=target_task_ids,
|
|
205
|
+
task_query=task_query,
|
|
206
|
+
target_label_names=target_label_names,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
logger.info(f"{len(annotation_bbox_list)} 件の3Dバウンディングボックスアノテーションの情報を出力します。 :: output='{output_file}'")
|
|
210
|
+
|
|
211
|
+
if output_format == FormatArgument.CSV:
|
|
212
|
+
df = create_df(annotation_bbox_list)
|
|
213
|
+
print_csv(df, output_file)
|
|
214
|
+
|
|
215
|
+
elif output_format in [FormatArgument.PRETTY_JSON, FormatArgument.JSON]:
|
|
216
|
+
json_is_pretty = output_format == FormatArgument.PRETTY_JSON
|
|
217
|
+
# DataClassJsonMixinを使用したtoJSON処理
|
|
218
|
+
print_json(
|
|
219
|
+
[e.to_dict(encode_json=True) for e in annotation_bbox_list],
|
|
220
|
+
is_pretty=json_is_pretty,
|
|
221
|
+
output=output_file,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
else:
|
|
225
|
+
raise ValueError(f"出力形式 '{output_format}' はサポートされていません。")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class ListAnnotation3DBoundingBox(CommandLine):
|
|
229
|
+
COMMON_MESSAGE = "annofabcli annotation_zip list_3d_bounding_box_annotation: error:"
|
|
230
|
+
|
|
231
|
+
def validate(self, args: argparse.Namespace) -> bool:
|
|
232
|
+
if args.project_id is None and args.annotation is None:
|
|
233
|
+
print( # noqa: T201
|
|
234
|
+
f"{self.COMMON_MESSAGE} argument --project_id: '--annotation'が未指定のときは、'--project_id' を指定してください。",
|
|
235
|
+
file=sys.stderr,
|
|
236
|
+
)
|
|
237
|
+
return False
|
|
238
|
+
return True
|
|
239
|
+
|
|
240
|
+
def main(self) -> None:
|
|
241
|
+
args = self.args
|
|
242
|
+
|
|
243
|
+
if not self.validate(args):
|
|
244
|
+
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
245
|
+
|
|
246
|
+
project_id: str | None = args.project_id
|
|
247
|
+
if project_id is not None:
|
|
248
|
+
super().validate_project(project_id, project_member_roles=[ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
|
|
249
|
+
project, _ = self.service.api.get_project(project_id)
|
|
250
|
+
if project["input_data_type"] != InputDataType.CUSTOM.value:
|
|
251
|
+
print(f"project_id='{project_id}'であるプロジェクトはカスタムプロジェクト(点群など)でないので、終了します", file=sys.stderr) # noqa: T201
|
|
252
|
+
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
253
|
+
|
|
254
|
+
annotation_path = Path(args.annotation) if args.annotation is not None else None
|
|
255
|
+
|
|
256
|
+
task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id) if args.task_id is not None else None
|
|
257
|
+
task_query = TaskQuery.from_dict(annofabcli.common.cli.get_json_from_args(args.task_query)) if args.task_query is not None else None
|
|
258
|
+
label_name_list = get_list_from_args(args.label_name) if args.label_name is not None else None
|
|
259
|
+
|
|
260
|
+
output_file: Path = args.output
|
|
261
|
+
output_format = FormatArgument(args.format)
|
|
262
|
+
|
|
263
|
+
downloading_obj = DownloadingFile(self.service)
|
|
264
|
+
|
|
265
|
+
def download_and_print_annotation_bbox(project_id: str, temp_dir: Path, *, is_latest: bool) -> None:
|
|
266
|
+
annotation_path = temp_dir / f"{project_id}__annotation.zip"
|
|
267
|
+
downloading_obj.download_annotation_zip(
|
|
268
|
+
project_id,
|
|
269
|
+
dest_path=annotation_path,
|
|
270
|
+
is_latest=is_latest,
|
|
271
|
+
)
|
|
272
|
+
print_annotation_3d_bounding_box(
|
|
273
|
+
annotation_path,
|
|
274
|
+
output_file,
|
|
275
|
+
output_format,
|
|
276
|
+
target_task_ids=task_id_list,
|
|
277
|
+
task_query=task_query,
|
|
278
|
+
target_label_names=label_name_list,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
if project_id is not None:
|
|
282
|
+
if args.temp_dir is not None:
|
|
283
|
+
download_and_print_annotation_bbox(project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest)
|
|
284
|
+
else:
|
|
285
|
+
with tempfile.TemporaryDirectory() as str_temp_dir:
|
|
286
|
+
download_and_print_annotation_bbox(
|
|
287
|
+
project_id=project_id,
|
|
288
|
+
temp_dir=Path(str_temp_dir),
|
|
289
|
+
is_latest=args.latest,
|
|
290
|
+
)
|
|
291
|
+
else:
|
|
292
|
+
assert annotation_path is not None
|
|
293
|
+
print_annotation_3d_bounding_box(
|
|
294
|
+
annotation_path,
|
|
295
|
+
output_file,
|
|
296
|
+
output_format,
|
|
297
|
+
target_task_ids=task_id_list,
|
|
298
|
+
task_query=task_query,
|
|
299
|
+
target_label_names=label_name_list,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def parse_args(parser: argparse.ArgumentParser) -> None:
|
|
304
|
+
argument_parser = ArgumentParser(parser)
|
|
305
|
+
|
|
306
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
307
|
+
group.add_argument(
|
|
308
|
+
"--annotation",
|
|
309
|
+
type=str,
|
|
310
|
+
help="アノテーションzip、またはzipを展開したディレクトリを指定します。",
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
group.add_argument("-p", "--project_id", type=str, help="project_id。アノテーションZIPをダウンロードします。")
|
|
314
|
+
|
|
315
|
+
argument_parser.add_format(
|
|
316
|
+
choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
|
|
317
|
+
default=FormatArgument.CSV,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
argument_parser.add_output()
|
|
321
|
+
|
|
322
|
+
parser.add_argument(
|
|
323
|
+
"-tq",
|
|
324
|
+
"--task_query",
|
|
325
|
+
type=str,
|
|
326
|
+
help="集計対象タスクを絞り込むためのクエリ条件をJSON形式で指定します。使用できるキーは task_id, status, phase, phase_stage です。"
|
|
327
|
+
" ``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
|
|
328
|
+
)
|
|
329
|
+
argument_parser.add_task_id(required=False)
|
|
330
|
+
|
|
331
|
+
parser.add_argument(
|
|
332
|
+
"--label_name",
|
|
333
|
+
type=str,
|
|
334
|
+
nargs="*",
|
|
335
|
+
help="指定したラベル名の3Dバウンディングボックスアノテーションのみを対象にします。複数指定できます。",
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
parser.add_argument(
|
|
339
|
+
"--latest",
|
|
340
|
+
action="store_true",
|
|
341
|
+
help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
parser.add_argument(
|
|
345
|
+
"--temp_dir",
|
|
346
|
+
type=Path,
|
|
347
|
+
help="指定したディレクトリに、アノテーションZIPなどの一時ファイルをダウンロードします。",
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
parser.set_defaults(subcommand_func=main)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def main(args: argparse.Namespace) -> None:
|
|
354
|
+
service = build_annofabapi_resource_and_login(args)
|
|
355
|
+
facade = AnnofabApiFacade(service)
|
|
356
|
+
ListAnnotation3DBoundingBox(service, facade, args).main()
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
360
|
+
subcommand_name = "list_3d_bounding_box_annotation"
|
|
361
|
+
subcommand_help = "アノテーションZIPから3Dバウンディングボックス(CUBOID)アノテーションの座標情報を出力します。"
|
|
362
|
+
epilog = "アノテーションZIPをダウンロードする場合は、オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
|
|
363
|
+
parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, epilog=epilog)
|
|
364
|
+
parse_args(parser)
|
|
365
|
+
return parser
|
|
@@ -7,13 +7,12 @@ import tempfile
|
|
|
7
7
|
from collections.abc import Collection
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
import pandas
|
|
13
13
|
from annofabapi.models import InputDataType, ProjectMemberRole
|
|
14
14
|
from dataclasses_json import DataClassJsonMixin
|
|
15
15
|
|
|
16
|
-
import annofabcli
|
|
17
16
|
import annofabcli.common.cli
|
|
18
17
|
from annofabcli.common.annofab.annotation_zip import lazy_parse_simple_annotation_by_input_data
|
|
19
18
|
from annofabcli.common.cli import COMMAND_LINE_ERROR_STATUS_CODE, ArgumentParser, CommandLine, build_annofabapi_resource_and_login, get_list_from_args
|
|
@@ -40,7 +39,7 @@ class AnnotationBoundingBoxInfo(DataClassJsonMixin):
|
|
|
40
39
|
input_data_id: str
|
|
41
40
|
input_data_name: str
|
|
42
41
|
|
|
43
|
-
updated_datetime:
|
|
42
|
+
updated_datetime: str | None
|
|
44
43
|
"""アノテーションJSONに格納されているアノテーションの更新日時"""
|
|
45
44
|
|
|
46
45
|
label: str
|
|
@@ -51,7 +50,7 @@ class AnnotationBoundingBoxInfo(DataClassJsonMixin):
|
|
|
51
50
|
height: int
|
|
52
51
|
|
|
53
52
|
|
|
54
|
-
def get_annotation_bounding_box_info_list(simple_annotation: dict[str, Any], *, target_label_names:
|
|
53
|
+
def get_annotation_bounding_box_info_list(simple_annotation: dict[str, Any], *, target_label_names: Collection[str] | None = None) -> list[AnnotationBoundingBoxInfo]:
|
|
55
54
|
result = []
|
|
56
55
|
target_label_names_set = set(target_label_names) if target_label_names is not None else None
|
|
57
56
|
for detail in simple_annotation["details"]:
|
|
@@ -91,9 +90,9 @@ def get_annotation_bounding_box_info_list(simple_annotation: dict[str, Any], *,
|
|
|
91
90
|
def get_annotation_bounding_box_info_list_from_annotation_path(
|
|
92
91
|
annotation_path: Path,
|
|
93
92
|
*,
|
|
94
|
-
target_task_ids:
|
|
95
|
-
task_query:
|
|
96
|
-
target_label_names:
|
|
93
|
+
target_task_ids: Collection[str] | None = None,
|
|
94
|
+
task_query: TaskQuery | None = None,
|
|
95
|
+
target_label_names: Collection[str] | None = None,
|
|
97
96
|
) -> list[AnnotationBoundingBoxInfo]:
|
|
98
97
|
annotation_bbox_list = []
|
|
99
98
|
target_task_ids = set(target_task_ids) if target_task_ids is not None else None
|
|
@@ -166,9 +165,9 @@ def print_annotation_bounding_box(
|
|
|
166
165
|
output_file: Path,
|
|
167
166
|
output_format: FormatArgument,
|
|
168
167
|
*,
|
|
169
|
-
target_task_ids:
|
|
170
|
-
task_query:
|
|
171
|
-
target_label_names:
|
|
168
|
+
target_task_ids: Collection[str] | None = None,
|
|
169
|
+
task_query: TaskQuery | None = None,
|
|
170
|
+
target_label_names: Collection[str] | None = None,
|
|
172
171
|
) -> None:
|
|
173
172
|
annotation_bbox_list = get_annotation_bounding_box_info_list_from_annotation_path(
|
|
174
173
|
annotation_path,
|
|
@@ -214,7 +213,7 @@ class ListAnnotationBoundingBox2d(CommandLine):
|
|
|
214
213
|
if not self.validate(args):
|
|
215
214
|
sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
|
|
216
215
|
|
|
217
|
-
project_id:
|
|
216
|
+
project_id: str | None = args.project_id
|
|
218
217
|
if project_id is not None:
|
|
219
218
|
super().validate_project(project_id, project_member_roles=[ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
|
|
220
219
|
project, _ = self.service.api.get_project(project_id)
|
|
@@ -327,7 +326,7 @@ def main(args: argparse.Namespace) -> None:
|
|
|
327
326
|
ListAnnotationBoundingBox2d(service, facade, args).main()
|
|
328
327
|
|
|
329
328
|
|
|
330
|
-
def add_parser(subparsers:
|
|
329
|
+
def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
|
|
331
330
|
subcommand_name = "list_bounding_box_annotation"
|
|
332
331
|
subcommand_help = "アノテーションZIPからバウンディングボックス(矩形)アノテーションの座標情報を出力します。"
|
|
333
332
|
epilog = "アノテーションZIPをダウンロードする場合は、オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
|