Coverage for scripts/annotations/coco_txt.py: 75%
75 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-07 15:22 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-07 15:22 +0000
1import os
2import json
3import numpy as np
4import argparse
6def coco_to_txt(coco_json_path, output_txt_dir):
8 if not os.path.isfile(coco_json_path):
9 print(f"Error: {coco_json_path} is not a valid file")
10 return
11 if not os.path.isdir(output_txt_dir):
12 os.makedirs(output_txt_dir, exist_ok=True)
14 # Load COCO JSON
15 try:
16 with open(coco_json_path, 'r') as f:
17 coco_data = json.load(f)
18 except Exception as e:
19 print(f"Error loading {coco_json_path}: {str(e)}")
20 return
22 # Map image IDs to image info
23 images = {img['id']: img for img in coco_data['images']}
24 print(f"Found {len(images)} images in COCO JSON")
26 # Group annotations by image
27 annotations_by_image = {}
28 for ann in coco_data['annotations']:
29 image_id = ann['image_id']
30 if image_id not in annotations_by_image:
31 annotations_by_image[image_id] = []
32 annotations_by_image[image_id].append(ann)
34 print(f"Found annotations for {len(annotations_by_image)} images")
36 # Process each image
37 processed_images = 0
38 total_txts = 0
40 for image_id, img_info in images.items():
41 try:
42 filename = img_info['file_name']
43 width, height = img_info['width'], img_info['height']
44 base_name = os.path.splitext(filename)[0]
46 # Validate image size
47 if width != height or width not in [320, 416]:
48 print(f"Warning: Image {filename} size ({width}x{height}) is not 320x320 or 416x416")
50 # Get annotations
51 annotations = annotations_by_image.get(image_id, [])
52 if not annotations:
53 print(f"No annotations for {filename}")
54 continue
56 # Collect segmentation lines
57 seg_lines = []
58 class_ids_used = set()
59 for ann in annotations:
60 cat_id = ann['category_id']
62 if 'segmentation' in ann and ann['segmentation']:
63 # Existing polygon processing (same as your code)
64 polygon = ann['segmentation'][0]
65 if len(polygon) < 4 or len(polygon) % 2 != 0:
66 print(f"Invalid polygon for annotation ID {ann['id']} in {filename}: {polygon}")
67 continue
69 normalized_coords = []
70 for i in range(0, len(polygon), 2):
71 x = polygon[i] / width
72 y = polygon[i + 1] / height
73 normalized_coords.extend([np.clip(x, 0, 1), np.clip(y, 0, 1)])
75 seg_line = [cat_id] + normalized_coords
77 elif 'bbox' in ann and ann['bbox']:
78 # bbox format in COCO is [x_min, y_min, width, height]
79 x_min, y_min, w_box, h_box = ann['bbox']
80 x_max = x_min + w_box
81 y_max = y_min + h_box
83 # normalize and create polygon as box corners (clockwise)
84 normalized_coords = [
85 np.clip(x_min / width, 0, 1), np.clip(y_min / height, 0, 1),
86 np.clip(x_max / width, 0, 1), np.clip(y_min / height, 0, 1),
87 np.clip(x_max / width, 0, 1), np.clip(y_max / height, 0, 1),
88 np.clip(x_min / width, 0, 1), np.clip(y_max / height, 0, 1)
89 ]
90 seg_line = [cat_id] + normalized_coords
92 seg_lines.append(seg_line)
93 class_ids_used.add(cat_id)
94 # Save TXT file if there are annotations
95 if seg_lines:
96 txt_path = os.path.join(output_txt_dir, f"{base_name}.txt")
97 with open(txt_path, 'w') as f:
98 for line in seg_lines:
99 # Format: class_id x1 y1 x2 y2 ... xn yn
100 f.write(' '.join(map(str, line)) + '\n')
101 print(f"Saved segmentation TXT: {txt_path}")
102 print(f"Class IDs in {base_name}: {sorted(list(class_ids_used))}")
103 total_txts += 1
104 else:
105 print(f"Skipped saving empty TXT for {filename}")
107 processed_images += 1
109 except Exception as e:
110 print(f"Error processing image ID {image_id} ({filename}): {str(e)}")
111 continue
113 print(f"Processed {processed_images} images, saved {total_txts} TXT files")
116# coco_json_path = '../../8080/train'
117# output_dir = '../../8080/labels'
119# for file in os.listdir(coco_json_path):
120# if file.endswith('.json'):
121# coco_json_path = os.path.join(coco_json_path, file)
122# print(f"Processing COCO JSON: {coco_json_path}")
124# coco_to_txt(coco_json_path, output_dir)