annofabcli 1.112.0__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 (73) hide show
  1. annofabcli/annotation/change_annotation_attributes.py +1 -1
  2. annofabcli/annotation/change_annotation_attributes_per_annotation.py +1 -1
  3. annofabcli/annotation/change_annotation_properties.py +1 -1
  4. annofabcli/annotation/copy_annotation.py +1 -1
  5. annofabcli/annotation/create_classification_annotation.py +1 -1
  6. annofabcli/annotation/delete_annotation.py +1 -1
  7. annofabcli/annotation/dump_annotation.py +1 -1
  8. annofabcli/annotation/import_annotation.py +1 -1
  9. annofabcli/annotation/list_annotation.py +1 -1
  10. annofabcli/annotation/list_annotation_count.py +1 -1
  11. annofabcli/annotation/merge_segmentation.py +1 -1
  12. annofabcli/annotation/remove_segmentation_overlap.py +1 -1
  13. annofabcli/annotation/restore_annotation.py +1 -1
  14. annofabcli/annotation_specs/list_annotation_specs_history.py +1 -1
  15. annofabcli/annotation_zip/list_annotation_3d_bounding_box.py +1 -1
  16. annofabcli/annotation_zip/list_annotation_bounding_box_2d.py +26 -26
  17. annofabcli/annotation_zip/list_polygon_annotation.py +390 -0
  18. annofabcli/annotation_zip/list_polyline_annotation.py +402 -0
  19. annofabcli/annotation_zip/list_range_annotation.py +1 -1
  20. annofabcli/annotation_zip/list_single_point_annotation.py +14 -22
  21. annofabcli/annotation_zip/subcommand_annotation_zip.py +4 -0
  22. annofabcli/common/cli.py +1 -0
  23. annofabcli/filesystem/draw_annotation.py +1 -1
  24. annofabcli/filesystem/mask_user_info.py +1 -1
  25. annofabcli/filesystem/merge_annotation.py +1 -1
  26. annofabcli/input_data/delete_input_data.py +1 -1
  27. annofabcli/input_data/list_all_input_data.py +1 -1
  28. annofabcli/input_data/list_all_input_data_merged_task.py +1 -1
  29. annofabcli/input_data/list_input_data.py +1 -1
  30. annofabcli/input_data/put_input_data.py +1 -1
  31. annofabcli/input_data/put_input_data_with_zip.py +1 -1
  32. annofabcli/instruction/copy_instruction.py +1 -1
  33. annofabcli/instruction/download_instruction.py +1 -1
  34. annofabcli/instruction/list_instruction_history.py +1 -1
  35. annofabcli/instruction/upload_instruction.py +1 -1
  36. annofabcli/job/delete_job.py +1 -1
  37. annofabcli/job/list_job.py +1 -1
  38. annofabcli/job/list_last_job.py +1 -1
  39. annofabcli/my_account/get_my_account.py +1 -1
  40. annofabcli/organization/list_organization.py +1 -1
  41. annofabcli/organization_member/list_organization_member.py +1 -1
  42. annofabcli/project/change_organization_of_project.py +1 -1
  43. annofabcli/project/change_project_status.py +1 -1
  44. annofabcli/project/copy_project.py +1 -1
  45. annofabcli/project/create_project.py +1 -1
  46. annofabcli/project/list_project.py +1 -1
  47. annofabcli/project/put_project.py +1 -1
  48. annofabcli/project/update_configuration.py +1 -1
  49. annofabcli/project_member/change_project_members.py +1 -1
  50. annofabcli/project_member/copy_project_members.py +1 -1
  51. annofabcli/project_member/drop_project_members.py +1 -1
  52. annofabcli/project_member/list_users.py +1 -1
  53. annofabcli/project_member/put_project_members.py +1 -1
  54. annofabcli/statistics/list_worktime.py +1 -1
  55. annofabcli/supplementary/delete_supplementary_data.py +1 -1
  56. annofabcli/supplementary/list_supplementary_data.py +1 -1
  57. annofabcli/supplementary/put_supplementary_data.py +1 -1
  58. annofabcli/task/delete_tasks.py +1 -1
  59. annofabcli/task/list_all_tasks.py +1 -1
  60. annofabcli/task/list_all_tasks_added_task_history.py +2 -1
  61. annofabcli/task/list_tasks.py +1 -1
  62. annofabcli/task/list_tasks_added_task_history.py +1 -1
  63. annofabcli/task/put_tasks.py +1 -1
  64. annofabcli/task/put_tasks_by_count.py +1 -1
  65. annofabcli/task_history/list_all_task_history.py +1 -1
  66. annofabcli/task_history/list_task_history.py +1 -1
  67. annofabcli/task_history_event/list_all_task_history_event.py +1 -1
  68. annofabcli/task_history_event/list_worktime.py +2 -1
  69. {annofabcli-1.112.0.dist-info → annofabcli-1.113.0.dist-info}/METADATA +1 -1
  70. {annofabcli-1.112.0.dist-info → annofabcli-1.113.0.dist-info}/RECORD +73 -71
  71. {annofabcli-1.112.0.dist-info → annofabcli-1.113.0.dist-info}/WHEEL +0 -0
  72. {annofabcli-1.112.0.dist-info → annofabcli-1.113.0.dist-info}/entry_points.txt +0 -0
  73. {annofabcli-1.112.0.dist-info → annofabcli-1.113.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,402 @@
1
+ import argparse
2
+ import logging
3
+ import math
4
+ import sys
5
+ import tempfile
6
+ from collections.abc import Collection
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ import pandas
11
+ from annofabapi.models import InputDataType, ProjectMemberRole
12
+ from pydantic import BaseModel, ConfigDict
13
+
14
+ import annofabcli.common.cli
15
+ from annofabcli.common.annofab.annotation_zip import lazy_parse_simple_annotation_by_input_data
16
+ from annofabcli.common.cli import COMMAND_LINE_ERROR_STATUS_CODE, ArgumentParser, CommandLine, build_annofabapi_resource_and_login, get_list_from_args
17
+ from annofabcli.common.download import DownloadingFile
18
+ from annofabcli.common.enums import FormatArgument
19
+ from annofabcli.common.facade import (
20
+ AnnofabApiFacade,
21
+ TaskQuery,
22
+ match_annotation_with_task_query,
23
+ )
24
+ from annofabcli.common.utils import print_csv, print_json
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class AnnotationPolylineInfo(BaseModel):
30
+ """
31
+ ポリラインアノテーションの情報
32
+ """
33
+
34
+ model_config = ConfigDict(frozen=True)
35
+
36
+ project_id: str
37
+ task_id: str
38
+ task_status: str
39
+ task_phase: str
40
+ task_phase_stage: int
41
+
42
+ input_data_id: str
43
+ input_data_name: str
44
+
45
+ updated_datetime: str | None
46
+ """アノテーションJSONに格納されているアノテーションの更新日時"""
47
+
48
+ label: str
49
+ annotation_id: str
50
+ point_count: int
51
+ length: float
52
+ """ポリラインの総長(各線分の長さの合計)"""
53
+ start_point: dict[str, float]
54
+ """始点の座標"""
55
+ end_point: dict[str, float]
56
+ """終点の座標"""
57
+ midpoint: dict[str, float]
58
+ """中点(全頂点の座標平均)"""
59
+ bounding_box_width: float
60
+ """外接矩形の幅"""
61
+ bounding_box_height: float
62
+ """外接矩形の高さ"""
63
+ attributes: dict[str, Any]
64
+ points: list[dict[str, int]]
65
+ """ポリラインの頂点リスト。各頂点は整数座標 {"x": int, "y": int} の形式。
66
+ """
67
+
68
+
69
+ def calculate_polyline_properties(points: list[dict[str, int]]) -> tuple[float, dict[str, float], dict[str, float], dict[str, float], float, float]:
70
+ """
71
+ ポリラインの長さ、始点、終点、中点、外接矩形のサイズを計算する。
72
+
73
+ Args:
74
+ points: ポリラインの頂点リスト。各頂点は整数座標 {"x": int, "y": int} の形式。2点以上が必須。
75
+
76
+ Returns:
77
+ (長さ, 始点, 終点, 中点, 外接矩形の幅, 外接矩形の高さ) のタプル。
78
+ """
79
+ # 始点と終点
80
+ start_point = {"x": float(points[0]["x"]), "y": float(points[0]["y"])}
81
+ end_point = {"x": float(points[-1]["x"]), "y": float(points[-1]["y"])}
82
+
83
+ # 中点(全頂点の座標平均)
84
+ sum_x = sum(p["x"] for p in points)
85
+ sum_y = sum(p["y"] for p in points)
86
+ midpoint = {"x": sum_x / len(points), "y": sum_y / len(points)}
87
+
88
+ # 線の長さを計算
89
+ total_length = 0.0
90
+ for i in range(len(points) - 1):
91
+ p1 = points[i]
92
+ p2 = points[i + 1]
93
+ dx = p2["x"] - p1["x"]
94
+ dy = p2["y"] - p1["y"]
95
+ segment_length = math.hypot(dx, dy)
96
+ total_length += segment_length
97
+
98
+ # 外接矩形を計算
99
+ x_coords = [p["x"] for p in points]
100
+ y_coords = [p["y"] for p in points]
101
+ min_x = min(x_coords)
102
+ max_x = max(x_coords)
103
+ min_y = min(y_coords)
104
+ max_y = max(y_coords)
105
+ bbox_width = float(max_x - min_x)
106
+ bbox_height = float(max_y - min_y)
107
+
108
+ return total_length, start_point, end_point, midpoint, bbox_width, bbox_height
109
+
110
+
111
+ def get_annotation_polyline_info_list(simple_annotation: dict[str, Any], *, target_label_names: Collection[str] | None = None) -> list[AnnotationPolylineInfo]:
112
+ result = []
113
+ target_label_names_set = set(target_label_names) if target_label_names is not None else None
114
+ for detail in simple_annotation["details"]:
115
+ if detail["data"]["_type"] == "Points":
116
+ label = detail["label"]
117
+ # ラベル名によるフィルタリング
118
+ if target_label_names_set is not None and label not in target_label_names_set:
119
+ continue
120
+
121
+ points = detail["data"]["points"]
122
+ point_count = len(points)
123
+
124
+ # ポリラインのプロパティを計算
125
+ length, start_point, end_point, midpoint, bbox_width, bbox_height = calculate_polyline_properties(points)
126
+
127
+ result.append(
128
+ AnnotationPolylineInfo(
129
+ project_id=simple_annotation["project_id"],
130
+ task_id=simple_annotation["task_id"],
131
+ task_phase=simple_annotation["task_phase"],
132
+ task_phase_stage=simple_annotation["task_phase_stage"],
133
+ task_status=simple_annotation["task_status"],
134
+ input_data_id=simple_annotation["input_data_id"],
135
+ input_data_name=simple_annotation["input_data_name"],
136
+ label=label,
137
+ annotation_id=detail["annotation_id"],
138
+ point_count=point_count,
139
+ length=length,
140
+ start_point=start_point,
141
+ end_point=end_point,
142
+ midpoint=midpoint,
143
+ bounding_box_width=bbox_width,
144
+ bounding_box_height=bbox_height,
145
+ attributes=detail["attributes"],
146
+ points=points,
147
+ updated_datetime=simple_annotation["updated_datetime"],
148
+ )
149
+ )
150
+
151
+ return result
152
+
153
+
154
+ def get_annotation_polyline_info_list_from_annotation_path(
155
+ annotation_path: Path,
156
+ *,
157
+ target_task_ids: Collection[str] | None = None,
158
+ task_query: TaskQuery | None = None,
159
+ target_label_names: Collection[str] | None = None,
160
+ ) -> list[AnnotationPolylineInfo]:
161
+ annotation_polyline_list = []
162
+ target_task_ids = set(target_task_ids) if target_task_ids is not None else None
163
+ iter_parser = lazy_parse_simple_annotation_by_input_data(annotation_path)
164
+ logger.info(f"アノテーションZIPまたはディレクトリ'{annotation_path}'を読み込みます。")
165
+ for index, parser in enumerate(iter_parser):
166
+ if (index + 1) % 10000 == 0:
167
+ logger.info(f"{index + 1} 件目のJSONを読み込み中")
168
+ if target_task_ids is not None and parser.task_id not in target_task_ids:
169
+ continue
170
+ dict_simple_annotation = parser.load_json()
171
+ if task_query is not None and not match_annotation_with_task_query(dict_simple_annotation, task_query):
172
+ continue
173
+ sub_annotation_polyline_list = get_annotation_polyline_info_list(dict_simple_annotation, target_label_names=target_label_names)
174
+ annotation_polyline_list.extend(sub_annotation_polyline_list)
175
+ return annotation_polyline_list
176
+
177
+
178
+ def create_df(
179
+ annotation_polyline_list: list[AnnotationPolylineInfo],
180
+ ) -> pandas.DataFrame:
181
+ """
182
+ CSV出力用のDataFrameを作成する。
183
+
184
+ Notes:
185
+ points列は含めない。CSVに含めると列の長さが非常に大きくなるため。
186
+ attributes列は、キーごとに別々の列(attributes.<key>の形式)として出力する。
187
+ pandas.json_normalizeを使用してネストした辞書を自動的に展開する。
188
+
189
+ """
190
+ # 基本列の定義
191
+ base_columns = [
192
+ "project_id",
193
+ "task_id",
194
+ "task_status",
195
+ "task_phase",
196
+ "task_phase_stage",
197
+ "input_data_id",
198
+ "input_data_name",
199
+ "updated_datetime",
200
+ "label",
201
+ "annotation_id",
202
+ "point_count",
203
+ "length",
204
+ "start_point.x",
205
+ "start_point.y",
206
+ "end_point.x",
207
+ "end_point.y",
208
+ "midpoint.x",
209
+ "midpoint.y",
210
+ "bounding_box_width",
211
+ "bounding_box_height",
212
+ ]
213
+
214
+ if len(annotation_polyline_list) == 0:
215
+ # 件数が0件のときも列ヘッダを出力する
216
+ return pandas.DataFrame(columns=base_columns)
217
+
218
+ # pandas.json_normalizeを使用してネストした辞書を展開
219
+ # start_point, end_point, midpoint(辞書)とattributes(辞書)が自動的に展開される
220
+ df = pandas.json_normalize([e.model_dump() for e in annotation_polyline_list])
221
+
222
+ # attributes列を抽出してソート
223
+ attributes_columns = sorted([col for col in df.columns if col.startswith("attributes.")])
224
+ # 列の順序を設定
225
+ columns = base_columns + attributes_columns
226
+
227
+ return df[columns]
228
+
229
+
230
+ def print_annotation_polyline(
231
+ annotation_path: Path,
232
+ output_file: Path,
233
+ output_format: FormatArgument,
234
+ *,
235
+ target_task_ids: Collection[str] | None = None,
236
+ task_query: TaskQuery | None = None,
237
+ target_label_names: Collection[str] | None = None,
238
+ ) -> None:
239
+ annotation_polyline_list = get_annotation_polyline_info_list_from_annotation_path(
240
+ annotation_path,
241
+ target_task_ids=target_task_ids,
242
+ task_query=task_query,
243
+ target_label_names=target_label_names,
244
+ )
245
+
246
+ logger.info(f"{len(annotation_polyline_list)} 件のポリラインアノテーションの情報を出力します。 :: output='{output_file}'")
247
+
248
+ if output_format == FormatArgument.CSV:
249
+ df = create_df(annotation_polyline_list)
250
+ print_csv(df, output_file)
251
+
252
+ elif output_format in [FormatArgument.PRETTY_JSON, FormatArgument.JSON]:
253
+ json_is_pretty = output_format == FormatArgument.PRETTY_JSON
254
+ # Pydantic BaseModelを使用したJSON処理
255
+ print_json(
256
+ [e.model_dump() for e in annotation_polyline_list],
257
+ is_pretty=json_is_pretty,
258
+ output=output_file,
259
+ )
260
+
261
+ else:
262
+ raise ValueError(f"出力形式 '{output_format}' はサポートされていません。")
263
+
264
+
265
+ class ListAnnotationPolyline(CommandLine):
266
+ COMMON_MESSAGE = "annofabcli annotation_zip list_polyline_annotation: error:"
267
+
268
+ def validate(self, args: argparse.Namespace) -> bool:
269
+ if args.project_id is None and args.annotation is None:
270
+ print( # noqa: T201
271
+ f"{self.COMMON_MESSAGE} argument --project_id: '--annotation'が未指定のときは、'--project_id' を指定してください。",
272
+ file=sys.stderr,
273
+ )
274
+ return False
275
+ return True
276
+
277
+ def main(self) -> None:
278
+ args = self.args
279
+
280
+ if not self.validate(args):
281
+ sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
282
+
283
+ project_id: str | None = args.project_id
284
+ if project_id is not None:
285
+ super().validate_project(project_id, project_member_roles=[ProjectMemberRole.OWNER, ProjectMemberRole.TRAINING_DATA_USER])
286
+ project, _ = self.service.api.get_project(project_id)
287
+ if project["input_data_type"] != InputDataType.IMAGE.value:
288
+ print(f"project_id='{project_id}'であるプロジェクトは画像プロジェクトでないので、終了します", file=sys.stderr) # noqa: T201
289
+ sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
290
+
291
+ annotation_path = Path(args.annotation) if args.annotation is not None else None
292
+
293
+ task_id_list = get_list_from_args(args.task_id) if args.task_id is not None else None
294
+ task_query = TaskQuery.from_dict(annofabcli.common.cli.get_json_from_args(args.task_query)) if args.task_query is not None else None
295
+ label_name_list = get_list_from_args(args.label_name) if args.label_name is not None else None
296
+
297
+ output_file: Path = args.output
298
+ output_format = FormatArgument(args.format)
299
+
300
+ downloading_obj = DownloadingFile(self.service)
301
+
302
+ def download_and_print_annotation_polyline(project_id: str, temp_dir: Path, *, is_latest: bool) -> None:
303
+ local_annotation_path = temp_dir / f"{project_id}__annotation.zip"
304
+ downloading_obj.download_annotation_zip(
305
+ project_id,
306
+ dest_path=local_annotation_path,
307
+ is_latest=is_latest,
308
+ )
309
+ print_annotation_polyline(
310
+ local_annotation_path,
311
+ output_file,
312
+ output_format,
313
+ target_task_ids=task_id_list,
314
+ task_query=task_query,
315
+ target_label_names=label_name_list,
316
+ )
317
+
318
+ if project_id is not None:
319
+ if args.temp_dir is not None:
320
+ download_and_print_annotation_polyline(project_id=project_id, temp_dir=args.temp_dir, is_latest=args.latest)
321
+ else:
322
+ with tempfile.TemporaryDirectory() as str_temp_dir:
323
+ download_and_print_annotation_polyline(
324
+ project_id=project_id,
325
+ temp_dir=Path(str_temp_dir),
326
+ is_latest=args.latest,
327
+ )
328
+ else:
329
+ assert annotation_path is not None
330
+ print_annotation_polyline(
331
+ annotation_path,
332
+ output_file,
333
+ output_format,
334
+ target_task_ids=task_id_list,
335
+ task_query=task_query,
336
+ target_label_names=label_name_list,
337
+ )
338
+
339
+
340
+ def parse_args(parser: argparse.ArgumentParser) -> None:
341
+ argument_parser = ArgumentParser(parser)
342
+
343
+ group = parser.add_mutually_exclusive_group(required=True)
344
+ group.add_argument(
345
+ "--annotation",
346
+ type=str,
347
+ help="アノテーションzip、またはzipを展開したディレクトリを指定します。",
348
+ )
349
+
350
+ group.add_argument("-p", "--project_id", type=str, help="project_id。アノテーションZIPをダウンロードします。")
351
+
352
+ argument_parser.add_format(
353
+ choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON],
354
+ default=FormatArgument.CSV,
355
+ )
356
+
357
+ argument_parser.add_output()
358
+
359
+ parser.add_argument(
360
+ "-tq",
361
+ "--task_query",
362
+ type=str,
363
+ help="集計対象タスクを絞り込むためのクエリ条件をJSON形式で指定します。使用できるキーは task_id, status, phase, phase_stage です。"
364
+ " ``file://`` を先頭に付けると、JSON形式のファイルを指定できます。",
365
+ )
366
+ argument_parser.add_task_id(required=False)
367
+
368
+ parser.add_argument(
369
+ "--label_name",
370
+ type=str,
371
+ nargs="+",
372
+ help="指定したラベル名のポリラインアノテーションのみを対象にします。複数指定できます。",
373
+ )
374
+
375
+ parser.add_argument(
376
+ "--latest",
377
+ action="store_true",
378
+ help="``--annotation`` を指定しないとき、最新のアノテーションzipを参照します。このオプションを指定すると、アノテーションzipを更新するのに数分待ちます。",
379
+ )
380
+
381
+ parser.add_argument(
382
+ "--temp_dir",
383
+ type=Path,
384
+ help="指定したディレクトリに、アノテーションZIPなどの一時ファイルをダウンロードします。",
385
+ )
386
+
387
+ parser.set_defaults(subcommand_func=main)
388
+
389
+
390
+ def main(args: argparse.Namespace) -> None:
391
+ service = build_annofabapi_resource_and_login(args)
392
+ facade = AnnofabApiFacade(service)
393
+ ListAnnotationPolyline(service, facade, args).main()
394
+
395
+
396
+ def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
397
+ subcommand_name = "list_polyline_annotation"
398
+ subcommand_help = "アノテーションZIPからポリラインアノテーションの座標情報と属性情報を出力します。"
399
+ epilog = "アノテーションZIPをダウンロードする場合は、オーナロールまたはアノテーションユーザロールを持つユーザで実行してください。"
400
+ parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, epilog=epilog)
401
+ parse_args(parser)
402
+ return parser
@@ -284,7 +284,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
284
284
  parser.add_argument(
285
285
  "--label_name",
286
286
  type=str,
287
- nargs="*",
287
+ nargs="+",
288
288
  help="指定したラベル名の区間アノテーションのみを対象にします。複数指定できます。",
289
289
  )
290
290
 
@@ -43,6 +43,7 @@ class AnnotationSinglePointInfo(DataClassJsonMixin):
43
43
  label: str
44
44
  annotation_id: str
45
45
  point: dict[str, int]
46
+ attributes: dict[str, str | int | bool]
46
47
 
47
48
 
48
49
  def get_annotation_single_point_info_list(simple_annotation: dict[str, Any], *, target_label_names: Collection[str] | None = None) -> list[AnnotationSinglePointInfo]:
@@ -70,6 +71,7 @@ def get_annotation_single_point_info_list(simple_annotation: dict[str, Any], *,
70
71
  annotation_id=detail["annotation_id"],
71
72
  point=point,
72
73
  updated_datetime=simple_annotation["updated_datetime"],
74
+ attributes=detail["attributes"],
73
75
  )
74
76
  )
75
77
 
@@ -103,7 +105,7 @@ def get_annotation_single_point_info_list_from_annotation_path(
103
105
  def create_df(
104
106
  annotation_point_list: list[AnnotationSinglePointInfo],
105
107
  ) -> pandas.DataFrame:
106
- columns = [
108
+ base_columns = [
107
109
  "project_id",
108
110
  "task_id",
109
111
  "task_status",
@@ -117,26 +119,16 @@ def create_df(
117
119
  "point.x",
118
120
  "point.y",
119
121
  ]
120
- df = pandas.DataFrame(
121
- [
122
- {
123
- "project_id": e.project_id,
124
- "task_id": e.task_id,
125
- "task_status": e.task_status,
126
- "task_phase": e.task_phase,
127
- "task_phase_stage": e.task_phase_stage,
128
- "input_data_id": e.input_data_id,
129
- "input_data_name": e.input_data_name,
130
- "updated_datetime": e.updated_datetime,
131
- "label": e.label,
132
- "annotation_id": e.annotation_id,
133
- "point.x": e.point["x"],
134
- "point.y": e.point["y"],
135
- }
136
- for e in annotation_point_list
137
- ],
138
- columns=columns,
139
- )
122
+
123
+ if not annotation_point_list:
124
+ # 空のリストの場合は、base_columnsのみで空のDataFrameを返す
125
+ return pandas.DataFrame(columns=base_columns)
126
+
127
+ tmp_annotation_point_list = [e.to_dict(encode_json=True) for e in annotation_point_list]
128
+ df = pandas.json_normalize(tmp_annotation_point_list)
129
+
130
+ attribute_columns = sorted(col for col in df.columns if col.startswith("attributes."))
131
+ columns = base_columns + attribute_columns
140
132
 
141
133
  return df[columns]
142
134
 
@@ -282,7 +274,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
282
274
  parser.add_argument(
283
275
  "--label_name",
284
276
  type=str,
285
- nargs="*",
277
+ nargs="+",
286
278
  help="指定したラベル名の点アノテーションのみを対象にします。複数指定できます。",
287
279
  )
288
280
 
@@ -5,6 +5,8 @@ import argparse
5
5
  import annofabcli
6
6
  from annofabcli.annotation_zip.list_annotation_3d_bounding_box import add_parser as add_parser_list_annotation_3d_bounding_box
7
7
  from annofabcli.annotation_zip.list_annotation_bounding_box_2d import add_parser as add_parser_list_annotation_bounding_box_2d
8
+ from annofabcli.annotation_zip.list_polygon_annotation import add_parser as add_parser_list_polygon_annotation
9
+ from annofabcli.annotation_zip.list_polyline_annotation import add_parser as add_parser_list_polyline_annotation
8
10
  from annofabcli.annotation_zip.list_range_annotation import add_parser as add_parser_list_range_annotation
9
11
  from annofabcli.annotation_zip.list_single_point_annotation import add_parser as add_parser_list_single_point_annotation
10
12
 
@@ -20,6 +22,8 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
20
22
  # サブコマンドの定義
21
23
  add_parser_list_annotation_3d_bounding_box(subparsers)
22
24
  add_parser_list_annotation_bounding_box_2d(subparsers)
25
+ add_parser_list_polygon_annotation(subparsers)
26
+ add_parser_list_polyline_annotation(subparsers)
23
27
  add_parser_list_range_annotation(subparsers)
24
28
  add_parser_list_single_point_annotation(subparsers)
25
29
  # 作成中のためコメントアウト
annofabcli/common/cli.py CHANGED
@@ -6,6 +6,7 @@ import argparse
6
6
  import getpass
7
7
  import json
8
8
  import logging
9
+ import logging.config
9
10
  import os
10
11
  import pkgutil
11
12
  from pathlib import Path
@@ -14,7 +14,7 @@ from annofabapi.parser import SimpleAnnotationParser, lazy_parse_simple_annotati
14
14
  from dataclasses_json import DataClassJsonMixin
15
15
  from PIL import Image, ImageColor, ImageDraw
16
16
 
17
- import annofabcli
17
+ import annofabcli.common.cli
18
18
  from annofabcli.common.cli import (
19
19
  COMMAND_LINE_ERROR_STATUS_CODE,
20
20
  ArgumentParser,
@@ -7,7 +7,7 @@ from typing import Any
7
7
  import numpy
8
8
  import pandas
9
9
 
10
- import annofabcli
10
+ import annofabcli.common.cli
11
11
  from annofabcli.common.cli import ArgumentParser, CommandLineWithoutWebapi, get_list_from_args
12
12
  from annofabcli.common.utils import read_multiheader_csv
13
13
 
@@ -15,7 +15,7 @@ from annofabapi.parser import (
15
15
  lazy_parse_simple_annotation_zip,
16
16
  )
17
17
 
18
- import annofabcli
18
+ import annofabcli.common.cli
19
19
  from annofabcli.common.cli import (
20
20
  COMMAND_LINE_ERROR_STATUS_CODE,
21
21
  ArgumentParser,
@@ -5,7 +5,7 @@ from typing import Any
5
5
  import requests
6
6
  from annofabapi.models import ProjectMemberRole
7
7
 
8
- import annofabcli
8
+ import annofabcli.common.cli
9
9
  from annofabcli.common.cli import ArgumentParser, CommandLine, build_annofabapi_resource_and_login
10
10
  from annofabcli.common.facade import AnnofabApiFacade
11
11
 
@@ -13,7 +13,7 @@ import pandas
13
13
  from annofabapi.dataclass.input import InputData
14
14
  from annofabapi.models import ProjectMemberRole
15
15
 
16
- import annofabcli
16
+ import annofabcli.common.cli
17
17
  from annofabcli.common.cli import ArgumentParser, CommandLine, build_annofabapi_resource_and_login, print_according_to_format, print_csv
18
18
  from annofabcli.common.download import DownloadingFile
19
19
  from annofabcli.common.enums import FormatArgument
@@ -12,7 +12,7 @@ from typing import Any
12
12
  import pandas
13
13
  from annofabapi.dataclass.input import InputData
14
14
 
15
- import annofabcli
15
+ import annofabcli.common.cli
16
16
  from annofabcli.common.cli import (
17
17
  COMMAND_LINE_ERROR_STATUS_CODE,
18
18
  ArgumentParser,
@@ -7,7 +7,7 @@ import annofabapi
7
7
  import pandas
8
8
  from annofabapi.models import InputData
9
9
 
10
- import annofabcli
10
+ import annofabcli.common.cli
11
11
  from annofabcli.common.cli import ArgumentParser, CommandLine, build_annofabapi_resource_and_login, print_according_to_format, print_csv
12
12
  from annofabcli.common.enums import FormatArgument
13
13
  from annofabcli.common.facade import AnnofabApiFacade
@@ -15,7 +15,7 @@ from annofabapi.exceptions import CheckSumError
15
15
  from annofabapi.models import ProjectMemberRole
16
16
  from dataclasses_json import DataClassJsonMixin
17
17
 
18
- import annofabcli
18
+ import annofabcli.common.cli
19
19
  from annofabcli.common.cli import (
20
20
  COMMAND_LINE_ERROR_STATUS_CODE,
21
21
  PARALLELISM_CHOICES,
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
 
8
8
  from annofabapi.models import ProjectJobType, ProjectMemberRole
9
9
 
10
- import annofabcli
10
+ import annofabcli.common.cli
11
11
  from annofabcli.common.cli import (
12
12
  COMMAND_LINE_ERROR_STATUS_CODE,
13
13
  ArgumentParser,
@@ -6,7 +6,7 @@ import requests
6
6
  from annofabapi.models import ProjectMemberRole
7
7
  from pyquery import PyQuery
8
8
 
9
- import annofabcli
9
+ import annofabcli.common.cli
10
10
  from annofabcli.common.cli import CommandLine, build_annofabapi_resource_and_login
11
11
  from annofabcli.common.facade import AnnofabApiFacade
12
12
 
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  import annofabapi
7
7
  import pyquery
8
8
 
9
- import annofabcli
9
+ import annofabcli.common.cli
10
10
  from annofabcli.common.cli import (
11
11
  COMMAND_LINE_ERROR_STATUS_CODE,
12
12
  ArgumentParser,
@@ -2,7 +2,7 @@ import argparse
2
2
  import logging
3
3
  from typing import Any
4
4
 
5
- import annofabcli
5
+ import annofabcli.common.cli
6
6
  from annofabcli.common.cli import ArgumentParser, CommandLine, build_annofabapi_resource_and_login
7
7
  from annofabcli.common.enums import FormatArgument
8
8
  from annofabcli.common.facade import AnnofabApiFacade
@@ -12,7 +12,7 @@ import pyquery
12
12
  from datauri import DataURI
13
13
  from PIL import Image
14
14
 
15
- import annofabcli
15
+ import annofabcli.common.cli
16
16
  from annofabcli.common.cli import ArgumentParser, CommandLine, build_annofabapi_resource_and_login
17
17
  from annofabcli.common.facade import AnnofabApiFacade
18
18
 
@@ -4,7 +4,7 @@ import logging
4
4
  import annofabapi
5
5
  from annofabapi.models import ProjectJobType, ProjectMemberRole
6
6
 
7
- import annofabcli
7
+ import annofabcli.common.cli
8
8
  from annofabcli.common.cli import (
9
9
  ArgumentParser,
10
10
  CommandLine,
@@ -5,7 +5,7 @@ from typing import Any
5
5
 
6
6
  from annofabapi.models import ProjectJobInfo, ProjectJobType
7
7
 
8
- import annofabcli
8
+ import annofabcli.common.cli
9
9
  from annofabcli.common.cli import ArgumentParser, CommandLine, build_annofabapi_resource_and_login
10
10
  from annofabcli.common.enums import FormatArgument
11
11
  from annofabcli.common.facade import AnnofabApiFacade
@@ -5,7 +5,7 @@ from typing import Any
5
5
 
6
6
  from annofabapi.models import Project, ProjectJobInfo, ProjectJobType
7
7
 
8
- import annofabcli
8
+ import annofabcli.common.cli
9
9
  from annofabcli.common.cli import (
10
10
  COMMAND_LINE_ERROR_STATUS_CODE,
11
11
  ArgumentParser,