annofabcli 1.111.2__py3-none-any.whl → 1.113.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.
Files changed (186) hide show
  1. annofabcli/__main__.py +1 -2
  2. annofabcli/annotation/annotation_query.py +10 -10
  3. annofabcli/annotation/change_annotation_attributes.py +10 -10
  4. annofabcli/annotation/change_annotation_attributes_per_annotation.py +4 -5
  5. annofabcli/annotation/change_annotation_properties.py +14 -14
  6. annofabcli/annotation/copy_annotation.py +6 -6
  7. annofabcli/annotation/create_classification_annotation.py +7 -7
  8. annofabcli/annotation/delete_annotation.py +9 -9
  9. annofabcli/annotation/download_annotation_zip.py +1 -3
  10. annofabcli/annotation/dump_annotation.py +8 -8
  11. annofabcli/annotation/import_annotation.py +13 -13
  12. annofabcli/annotation/list_annotation.py +9 -9
  13. annofabcli/annotation/list_annotation_count.py +2 -3
  14. annofabcli/annotation/merge_segmentation.py +6 -6
  15. annofabcli/annotation/remove_segmentation_overlap.py +5 -5
  16. annofabcli/annotation/restore_annotation.py +7 -7
  17. annofabcli/annotation/subcommand_annotation.py +1 -2
  18. annofabcli/annotation_specs/add_attribute_restriction.py +4 -5
  19. annofabcli/annotation_specs/attribute_restriction.py +8 -8
  20. annofabcli/annotation_specs/export_annotation_specs.py +4 -5
  21. annofabcli/annotation_specs/get_annotation_specs_with_attribute_id_replaced.py +3 -4
  22. annofabcli/annotation_specs/get_annotation_specs_with_choice_id_replaced.py +3 -4
  23. annofabcli/annotation_specs/get_annotation_specs_with_label_id_replaced.py +3 -4
  24. annofabcli/annotation_specs/list_annotation_specs_attribute.py +9 -10
  25. annofabcli/annotation_specs/list_annotation_specs_choice.py +9 -10
  26. annofabcli/annotation_specs/list_annotation_specs_history.py +3 -3
  27. annofabcli/annotation_specs/list_annotation_specs_label.py +8 -9
  28. annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +10 -11
  29. annofabcli/annotation_specs/list_attribute_restriction.py +2 -4
  30. annofabcli/annotation_specs/list_label_color.py +2 -3
  31. annofabcli/annotation_specs/put_label_color.py +3 -4
  32. annofabcli/annotation_specs/subcommand_annotation_specs.py +1 -3
  33. annofabcli/annotation_zip/list_annotation_3d_bounding_box.py +365 -0
  34. annofabcli/annotation_zip/list_annotation_bounding_box_2d.py +37 -38
  35. annofabcli/annotation_zip/list_polygon_annotation.py +390 -0
  36. annofabcli/annotation_zip/list_polyline_annotation.py +402 -0
  37. annofabcli/annotation_zip/list_range_annotation.py +25 -15
  38. annofabcli/annotation_zip/list_single_point_annotation.py +25 -34
  39. annofabcli/annotation_zip/subcommand_annotation_zip.py +7 -2
  40. annofabcli/annotation_zip/validate_annotation.py +8 -7
  41. annofabcli/comment/delete_comment.py +4 -6
  42. annofabcli/comment/download_comment_json.py +4 -6
  43. annofabcli/comment/list_all_comment.py +5 -6
  44. annofabcli/comment/list_comment.py +3 -4
  45. annofabcli/comment/put_comment.py +9 -10
  46. annofabcli/comment/put_comment_simply.py +5 -6
  47. annofabcli/comment/put_inspection_comment.py +1 -3
  48. annofabcli/comment/put_inspection_comment_simply.py +1 -3
  49. annofabcli/comment/put_onhold_comment.py +1 -3
  50. annofabcli/comment/put_onhold_comment_simply.py +1 -3
  51. annofabcli/comment/subcommand_comment.py +1 -3
  52. annofabcli/common/bokeh.py +4 -4
  53. annofabcli/common/cli.py +18 -17
  54. annofabcli/common/download.py +28 -29
  55. annofabcli/common/facade.py +37 -38
  56. annofabcli/common/image.py +14 -14
  57. annofabcli/common/utils.py +8 -8
  58. annofabcli/common/visualize.py +13 -13
  59. annofabcli/experimental/list_out_of_range_annotation_for_movie.py +3 -4
  60. annofabcli/experimental/subcommand_experimental.py +1 -3
  61. annofabcli/filesystem/draw_annotation.py +27 -27
  62. annofabcli/filesystem/filter_annotation.py +9 -10
  63. annofabcli/filesystem/mask_user_info.py +15 -15
  64. annofabcli/filesystem/merge_annotation.py +9 -9
  65. annofabcli/filesystem/subcommand_filesystem.py +1 -3
  66. annofabcli/input_data/copy_input_data.py +8 -9
  67. annofabcli/input_data/delete_input_data.py +3 -3
  68. annofabcli/input_data/delete_metadata_key_of_input_data.py +3 -5
  69. annofabcli/input_data/download_input_data_json.py +4 -6
  70. annofabcli/input_data/list_all_input_data.py +9 -9
  71. annofabcli/input_data/list_all_input_data_merged_task.py +5 -5
  72. annofabcli/input_data/list_input_data.py +5 -5
  73. annofabcli/input_data/put_input_data.py +6 -6
  74. annofabcli/input_data/put_input_data_with_zip.py +3 -4
  75. annofabcli/input_data/subcommand_input_data.py +1 -3
  76. annofabcli/input_data/update_input_data.py +6 -8
  77. annofabcli/input_data/update_metadata_of_input_data.py +3 -5
  78. annofabcli/instruction/copy_instruction.py +5 -6
  79. annofabcli/instruction/download_instruction.py +5 -6
  80. annofabcli/instruction/list_instruction_history.py +3 -3
  81. annofabcli/instruction/subcommand_instruction.py +1 -3
  82. annofabcli/instruction/upload_instruction.py +3 -4
  83. annofabcli/job/delete_job.py +2 -3
  84. annofabcli/job/list_job.py +5 -5
  85. annofabcli/job/list_last_job.py +4 -4
  86. annofabcli/job/subcommand_job.py +1 -3
  87. annofabcli/job/wait_job.py +4 -5
  88. annofabcli/my_account/get_my_account.py +2 -3
  89. annofabcli/my_account/subcommand_my_account.py +1 -3
  90. annofabcli/organization/list_organization.py +2 -3
  91. annofabcli/organization/subcommand_organization.py +1 -3
  92. annofabcli/organization_member/change_organization_member.py +3 -4
  93. annofabcli/organization_member/delete_organization_member.py +3 -4
  94. annofabcli/organization_member/invite_organization_member.py +1 -3
  95. annofabcli/organization_member/list_organization_member.py +3 -3
  96. annofabcli/organization_member/subcommand_organization_member.py +1 -3
  97. annofabcli/project/change_organization_of_project.py +4 -4
  98. annofabcli/project/change_project_status.py +4 -4
  99. annofabcli/project/copy_project.py +5 -5
  100. annofabcli/project/create_project.py +8 -8
  101. annofabcli/project/diff_projects.py +4 -5
  102. annofabcli/project/list_project.py +5 -5
  103. annofabcli/project/put_project.py +2 -3
  104. annofabcli/project/subcommand_project.py +1 -2
  105. annofabcli/project/update_configuration.py +4 -4
  106. annofabcli/project/update_project.py +6 -8
  107. annofabcli/project_member/change_project_members.py +8 -8
  108. annofabcli/project_member/copy_project_members.py +4 -4
  109. annofabcli/project_member/drop_project_members.py +2 -3
  110. annofabcli/project_member/invite_project_members.py +1 -3
  111. annofabcli/project_member/list_users.py +2 -3
  112. annofabcli/project_member/put_project_members.py +6 -6
  113. annofabcli/project_member/subcommand_project_member.py +1 -3
  114. annofabcli/stat_visualization/mask_visualization_dir.py +8 -9
  115. annofabcli/stat_visualization/merge_visualization_dir.py +6 -7
  116. annofabcli/stat_visualization/subcommand_stat_visualization.py +1 -2
  117. annofabcli/stat_visualization/summarize_whole_performance_csv.py +1 -2
  118. annofabcli/stat_visualization/write_graph.py +2 -3
  119. annofabcli/stat_visualization/write_performance_rating_csv.py +20 -27
  120. annofabcli/statistics/histogram.py +5 -6
  121. annofabcli/statistics/linegraph.py +13 -14
  122. annofabcli/statistics/list_annotation_area.py +38 -13
  123. annofabcli/statistics/list_annotation_attribute.py +9 -10
  124. annofabcli/statistics/list_annotation_attribute_filled_count.py +30 -31
  125. annofabcli/statistics/list_annotation_count.py +57 -58
  126. annofabcli/statistics/list_annotation_duration.py +33 -34
  127. annofabcli/statistics/list_video_duration.py +4 -5
  128. annofabcli/statistics/list_worktime.py +4 -4
  129. annofabcli/statistics/scatter.py +9 -8
  130. annofabcli/statistics/subcommand_statistics.py +1 -4
  131. annofabcli/statistics/summarize_task_count.py +4 -6
  132. annofabcli/statistics/summarize_task_count_by_task_id_group.py +2 -4
  133. annofabcli/statistics/summarize_task_count_by_user.py +1 -3
  134. annofabcli/statistics/visualization/dataframe/annotation_count.py +5 -4
  135. annofabcli/statistics/visualization/dataframe/annotation_duration.py +2 -3
  136. annofabcli/statistics/visualization/dataframe/cumulative_productivity.py +15 -17
  137. annofabcli/statistics/visualization/dataframe/productivity_per_date.py +17 -19
  138. annofabcli/statistics/visualization/dataframe/project_performance.py +3 -12
  139. annofabcli/statistics/visualization/dataframe/task.py +11 -12
  140. annofabcli/statistics/visualization/dataframe/task_worktime_by_phase_user.py +9 -10
  141. annofabcli/statistics/visualization/dataframe/user_performance.py +21 -19
  142. annofabcli/statistics/visualization/dataframe/whole_performance.py +3 -4
  143. annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py +12 -14
  144. annofabcli/statistics/visualization/dataframe/worktime_per_date.py +11 -13
  145. annofabcli/statistics/visualization/filtering_query.py +7 -7
  146. annofabcli/statistics/visualization/project_dir.py +27 -14
  147. annofabcli/statistics/visualize_annotation_count.py +22 -23
  148. annofabcli/statistics/visualize_annotation_duration.py +21 -22
  149. annofabcli/statistics/visualize_statistics.py +36 -33
  150. annofabcli/statistics/visualize_video_duration.py +18 -20
  151. annofabcli/supplementary/delete_supplementary_data.py +5 -5
  152. annofabcli/supplementary/list_supplementary_data.py +4 -4
  153. annofabcli/supplementary/put_supplementary_data.py +9 -9
  154. annofabcli/supplementary/subcommand_supplementary.py +1 -3
  155. annofabcli/task/cancel_acceptance.py +16 -17
  156. annofabcli/task/change_operator.py +10 -12
  157. annofabcli/task/change_status_to_break.py +7 -9
  158. annofabcli/task/change_status_to_on_hold.py +10 -12
  159. annofabcli/task/complete_tasks.py +17 -18
  160. annofabcli/task/copy_tasks.py +3 -5
  161. annofabcli/task/delete_metadata_key_of_task.py +4 -6
  162. annofabcli/task/delete_tasks.py +7 -7
  163. annofabcli/task/download_task_json.py +4 -6
  164. annofabcli/task/list_all_tasks.py +8 -8
  165. annofabcli/task/list_all_tasks_added_task_history.py +14 -13
  166. annofabcli/task/list_tasks.py +7 -7
  167. annofabcli/task/list_tasks_added_task_history.py +10 -10
  168. annofabcli/task/put_tasks.py +5 -6
  169. annofabcli/task/put_tasks_by_count.py +2 -3
  170. annofabcli/task/reject_tasks.py +18 -20
  171. annofabcli/task/subcommand_task.py +1 -3
  172. annofabcli/task/update_metadata_of_task.py +5 -6
  173. annofabcli/task_history/download_task_history_json.py +4 -6
  174. annofabcli/task_history/list_all_task_history.py +6 -7
  175. annofabcli/task_history/list_task_history.py +4 -5
  176. annofabcli/task_history/subcommand_task_history.py +1 -3
  177. annofabcli/task_history_event/download_task_history_event_json.py +4 -6
  178. annofabcli/task_history_event/list_all_task_history_event.py +7 -7
  179. annofabcli/task_history_event/list_worktime.py +17 -16
  180. annofabcli/task_history_event/subcommand_task_history_event.py +1 -2
  181. {annofabcli-1.111.2.dist-info → annofabcli-1.113.0.dist-info}/METADATA +9 -15
  182. annofabcli-1.113.0.dist-info/RECORD +231 -0
  183. {annofabcli-1.111.2.dist-info → annofabcli-1.113.0.dist-info}/WHEEL +1 -1
  184. annofabcli-1.111.2.dist-info/RECORD +0 -228
  185. {annofabcli-1.111.2.dist-info → annofabcli-1.113.0.dist-info}/entry_points.txt +0 -0
  186. {annofabcli-1.111.2.dist-info → annofabcli-1.113.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, Optional
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.annofab.annotation_specs import keybind_to_text
19
18
  from annofabcli.common.cli import (
@@ -36,9 +35,9 @@ class FlattenLabel(DataClassJsonMixin):
36
35
  """
37
36
 
38
37
  label_id: str
39
- label_name_en: Optional[str]
40
- label_name_ja: Optional[str]
41
- label_name_vi: Optional[str]
38
+ label_name_en: str | None
39
+ label_name_ja: str | None
40
+ label_name_vi: str | None
42
41
  annotation_type: str
43
42
  color: str
44
43
  """16進数カラーコード
@@ -51,7 +50,7 @@ class FlattenLabel(DataClassJsonMixin):
51
50
  Notes:
52
51
  APIでは`additional_data_definitions`のような名前だが、分かりにくかったので"attribute"という名前に変えた。
53
52
  """
54
- keybind: Optional[str]
53
+ keybind: str | None
55
54
  """キーバインド"""
56
55
 
57
56
 
@@ -102,7 +101,7 @@ class PrintAnnotationSpecsLabel(CommandLine):
102
101
 
103
102
  COMMON_MESSAGE = "annofabcli annotation_specs list_label: error:"
104
103
 
105
- def print_annotation_specs_label(self, annotation_specs_v3: dict[str, Any], output_format: FormatArgument, output: Optional[str] = None) -> None:
104
+ def print_annotation_specs_label(self, annotation_specs_v3: dict[str, Any], output_format: FormatArgument, output: str | None = None) -> None:
106
105
  label_list = create_label_list(annotation_specs_v3["labels"])
107
106
  if output_format == FormatArgument.CSV:
108
107
  df = pandas.DataFrame(label_list)
@@ -113,7 +112,7 @@ class PrintAnnotationSpecsLabel(CommandLine):
113
112
  elif output_format in [FormatArgument.JSON, FormatArgument.PRETTY_JSON]:
114
113
  annofabcli.common.utils.print_according_to_format([e.to_dict() for e in label_list], format=FormatArgument(output_format), output=output)
115
114
 
116
- def get_history_id_from_before_index(self, project_id: str, before: int) -> Optional[str]:
115
+ def get_history_id_from_before_index(self, project_id: str, before: int) -> str | None:
117
116
  histories, _ = self.service.api.get_annotation_specs_histories(project_id)
118
117
  if before + 1 > len(histories):
119
118
  logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
@@ -203,7 +202,7 @@ def main(args: argparse.Namespace) -> None:
203
202
  PrintAnnotationSpecsLabel(service, facade, args).main()
204
203
 
205
204
 
206
- def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
205
+ def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
207
206
  subcommand_name = "list_label"
208
207
 
209
208
  subcommand_help = "アノテーション仕様のラベル情報を出力します。"
@@ -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, Optional
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: Optional[str]
35
- label_name_ja: Optional[str]
36
- label_name_vi: Optional[str]
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: Optional[str]
47
- attribute_name_ja: Optional[str]
48
- attribute_name_vi: Optional[str]
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: Optional[str] = None) -> None:
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) -> Optional[str]:
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: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
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) -> Optional[str]:
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: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
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, Optional
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: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
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, Optional
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: Optional[str]) -> None:
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: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
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: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
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