chinmay-1302 commited on
Commit
25b8a6e
·
1 Parent(s): e040872

add inference scripts

Browse files
usage/inference_damo_yolo.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ # Example usage:
4
+ # python tools/inference_dir.py \
5
+ # --model_path path/to/model.pth \
6
+ # --config path/to/config.py \
7
+ # --image_dir path/to/image_dir \
8
+ # --output_json path/to/output.json \
9
+ # --infer_size 640 640 \
10
+ # --device cuda
11
+
12
+ import argparse
13
+ import json
14
+ import os
15
+ from pathlib import Path
16
+
17
+ if 'PYTORCH_CUDA_ALLOC_CONF' in os.environ:
18
+ alloc_conf = os.environ['PYTORCH_CUDA_ALLOC_CONF']
19
+ if 'expandable_segments' in alloc_conf:
20
+ # Remove expandable_segments option
21
+ new_conf = ','.join([opt for opt in alloc_conf.split(',') if 'expandable_segments' not in opt])
22
+ if new_conf:
23
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = new_conf
24
+ else:
25
+ os.environ.pop('PYTORCH_CUDA_ALLOC_CONF', None)
26
+
27
+ import cv2
28
+ import numpy as np
29
+ import torch
30
+ from loguru import logger
31
+ from PIL import Image
32
+ from tqdm import tqdm
33
+
34
+ from damo.base_models.core.ops import RepConv
35
+ from damo.config.base import parse_config
36
+ from damo.detectors.detector import build_local_model
37
+ from damo.utils import postprocess
38
+ from damo.utils.demo_utils import transform_img
39
+ from damo.structures.image_list import ImageList
40
+ from damo.structures.bounding_box import BoxList
41
+
42
+
43
+ def pad_image(img, target_size):
44
+ """Pad image to target size."""
45
+ n, c, h, w = img.shape
46
+ assert n == 1
47
+ assert h <= target_size[0] and w <= target_size[1]
48
+ target_size = [n, c, target_size[0], target_size[1]]
49
+ pad_imgs = torch.zeros(*target_size)
50
+ pad_imgs[:, :c, :h, :w].copy_(img)
51
+
52
+ img_sizes = [img.shape[-2:]]
53
+ pad_sizes = [pad_imgs.shape[-2:]]
54
+
55
+ return ImageList(pad_imgs, img_sizes, pad_sizes)
56
+
57
+
58
+ def get_image_files(image_dir):
59
+ """Get all image files from directory."""
60
+ image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.webp'}
61
+ image_dir = Path(image_dir)
62
+ image_files = []
63
+ for ext in image_extensions:
64
+ image_files.extend(image_dir.glob(f'*{ext}'))
65
+ image_files.extend(image_dir.glob(f'*{ext.upper()}'))
66
+ return sorted(image_files)
67
+
68
+
69
+ class BatchInfer:
70
+ def __init__(self, config, infer_size=[640, 640], device='cuda', ckpt=None):
71
+ """Initialize inference engine."""
72
+ self.ckpt_path = ckpt
73
+ suffix = ckpt.split('.')[-1]
74
+ if suffix == 'onnx':
75
+ self.engine_type = 'onnx'
76
+ elif suffix == 'trt':
77
+ self.engine_type = 'tensorRT'
78
+ elif suffix in ['pt', 'pth']:
79
+ self.engine_type = 'torch'
80
+ else:
81
+ raise ValueError(f'Unknown checkpoint format: {suffix}')
82
+
83
+ if torch.cuda.is_available() and device == 'cuda':
84
+ self.device = 'cuda'
85
+ else:
86
+ self.device = 'cpu'
87
+ logger.warning('CUDA not available, using CPU')
88
+
89
+ if "class_names" in config.dataset:
90
+ self.class_names = config.dataset.class_names
91
+ else:
92
+ self.class_names = []
93
+ for i in range(config.model.head.num_classes):
94
+ self.class_names.append(str(i))
95
+ self.class_names = tuple(self.class_names)
96
+
97
+ self.infer_size = infer_size
98
+ config.dataset.size_divisibility = 0
99
+ self.config = config
100
+ self.model = self._build_engine(self.config, self.engine_type)
101
+
102
+ def _build_engine(self, config, engine_type):
103
+ """Build inference engine."""
104
+ logger.info(f'Inference with {engine_type} engine!')
105
+ if engine_type == 'torch':
106
+ model = build_local_model(config, self.device)
107
+ ckpt = torch.load(self.ckpt_path, map_location=self.device)
108
+ model.load_state_dict(ckpt['model'], strict=True)
109
+ for layer in model.modules():
110
+ if isinstance(layer, RepConv):
111
+ layer.switch_to_deploy()
112
+ model.eval()
113
+ return model
114
+ elif engine_type == 'tensorRT':
115
+ raise NotImplementedError('TensorRT inference not implemented in this script. Use demo.py instead.')
116
+ elif engine_type == 'onnx':
117
+ raise NotImplementedError('ONNX inference not implemented in this script. Use demo.py instead.')
118
+ else:
119
+ raise NotImplementedError(f'{engine_type} is not supported yet! Please use one of [onnx, torch, tensorRT]')
120
+
121
+ def preprocess(self, origin_img):
122
+ """Preprocess image for inference."""
123
+ img = transform_img(origin_img, 0,
124
+ **self.config.test.augment.transform,
125
+ infer_size=self.infer_size)
126
+ oh, ow, _ = origin_img.shape
127
+ img = pad_image(img.tensors, self.infer_size)
128
+ img = img.to(self.device)
129
+ return img, (ow, oh)
130
+
131
+ def forward(self, origin_image):
132
+ """Run inference on image."""
133
+ image, origin_shape = self.preprocess(origin_image)
134
+ with torch.no_grad():
135
+ output = self.model(image)
136
+ return output, image, origin_shape
137
+
138
+ def postprocess_to_coco(self, preds, image, origin_shape):
139
+ """Postprocess predictions to COCO format."""
140
+ output = preds[0]
141
+ output = output.resize(origin_shape)
142
+ output = output.convert('xywh') # Convert to xywh format for COCO
143
+
144
+ # Handle empty predictions
145
+ if len(output) == 0:
146
+ return []
147
+
148
+ bboxes = output.bbox.cpu().detach().numpy()
149
+ scores = output.get_field('scores').cpu().detach().numpy()
150
+ labels = output.get_field('labels').cpu().detach().numpy()
151
+
152
+ # Model outputs 0-indexed labels (0 to num_classes-1)
153
+ # COCO category_id is 1-indexed (1 to num_classes)
154
+ category_ids = labels + 1
155
+
156
+ coco_results = []
157
+ for k in range(len(bboxes)):
158
+ coco_results.append({
159
+ 'image_id': None, # Will be set later
160
+ 'category_id': int(category_ids[k]),
161
+ 'bbox': bboxes[k].tolist(), # [x, y, width, height]
162
+ 'score': float(scores[k]),
163
+ })
164
+
165
+ return coco_results
166
+
167
+
168
+ def main():
169
+ parser = argparse.ArgumentParser('DAMO-YOLO Directory Inference')
170
+ parser.add_argument(
171
+ '--model_path',
172
+ required=True,
173
+ type=str,
174
+ help='Path to model checkpoint (.pth, .pt)'
175
+ )
176
+ parser.add_argument(
177
+ '--config',
178
+ required=True,
179
+ type=str,
180
+ help='Path to config file'
181
+ )
182
+ parser.add_argument(
183
+ '--image_dir',
184
+ required=True,
185
+ type=str,
186
+ help='Path to directory containing images'
187
+ )
188
+ parser.add_argument(
189
+ '--output_json',
190
+ required=True,
191
+ type=str,
192
+ help='Path to output JSON file (COCO format)'
193
+ )
194
+ parser.add_argument(
195
+ '--infer_size',
196
+ nargs='+',
197
+ type=int,
198
+ default=[640, 640],
199
+ help='Inference image size [height width]'
200
+ )
201
+ parser.add_argument(
202
+ '--device',
203
+ default='cuda',
204
+ type=str,
205
+ help='Device for inference (cuda or cpu)'
206
+ )
207
+ parser.add_argument(
208
+ '--conf_threshold',
209
+ default=None,
210
+ type=float,
211
+ help='Confidence threshold (uses config default if not specified)'
212
+ )
213
+
214
+ args = parser.parse_args()
215
+
216
+ # Parse config
217
+ config = parse_config(args.config)
218
+
219
+ # Override confidence threshold if provided
220
+ if args.conf_threshold is not None:
221
+ config.model.head.nms_conf_thre = args.conf_threshold
222
+
223
+ # Parse inference size
224
+ if len(args.infer_size) == 1:
225
+ infer_size = [args.infer_size[0], args.infer_size[0]]
226
+ elif len(args.infer_size) == 2:
227
+ infer_size = args.infer_size
228
+ else:
229
+ raise ValueError('infer_size should be 1 or 2 values')
230
+
231
+ # Initialize inference engine
232
+ logger.info(f'Loading model from {args.model_path}')
233
+ infer_engine = BatchInfer(
234
+ config,
235
+ infer_size=infer_size,
236
+ device=args.device,
237
+ ckpt=args.model_path
238
+ )
239
+
240
+ # Get all image files
241
+ image_files = get_image_files(args.image_dir)
242
+ if len(image_files) == 0:
243
+ logger.error(f'No image files found in {args.image_dir}')
244
+ return
245
+
246
+ logger.info(f'Found {len(image_files)} images')
247
+
248
+ # Process images
249
+ all_results = []
250
+
251
+ for img_id, image_path in enumerate(tqdm(image_files, desc='Processing images')):
252
+ # Load image
253
+ origin_img = cv2.imread(str(image_path))
254
+ if origin_img is None:
255
+ logger.warning(f'Failed to load image: {image_path}')
256
+ continue
257
+
258
+ origin_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2RGB)
259
+
260
+ # Run inference
261
+ preds, image, origin_shape = infer_engine.forward(origin_img)
262
+
263
+ # Postprocess to COCO format
264
+ coco_results = infer_engine.postprocess_to_coco(preds, image, origin_shape)
265
+
266
+ # Use image filename (without extension) as image_id
267
+ image_id = image_path.stem
268
+
269
+ for result in coco_results:
270
+ result['image_id'] = image_id
271
+ all_results.append(result)
272
+
273
+ # Save results
274
+ output_dir = Path(args.output_json).parent
275
+ output_dir.mkdir(parents=True, exist_ok=True)
276
+
277
+ with open(args.output_json, 'w') as f:
278
+ json.dump(all_results, f, indent=2)
279
+
280
+ logger.info(f'Saved {len(all_results)} detections to {args.output_json}')
281
+ logger.info(f'Processed {len(image_files)} images')
282
+
283
+
284
+ if __name__ == '__main__':
285
+ main()
286
+
287
+
usage/inference_rtdetrv2.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Copyright(c) 2023 lyuwenyu. All Rights Reserved.
2
+ """
3
+
4
+ # Example usage:
5
+ # python references/deploy/rtdetrv2_torch.py \
6
+ # -c path/to/model_config.yml \
7
+ # -r path/to/model.pth \
8
+ # --im-dir path/to/images_dir \
9
+ # -d cuda \
10
+ # -o path/to/output.json
11
+
12
+ import torch
13
+ import torch.nn as nn
14
+ import torchvision.transforms as T
15
+
16
+ import numpy as np
17
+ import json
18
+ import os
19
+ from pathlib import Path
20
+ from PIL import Image, ImageDraw
21
+ import sys
22
+
23
+ # Ensure repository root is on sys.path so `src` package can be imported
24
+ REPO_ROOT = str(Path(__file__).resolve().parents[2])
25
+ if REPO_ROOT not in sys.path:
26
+ sys.path.insert(0, REPO_ROOT)
27
+
28
+ from src.core import YAMLConfig
29
+
30
+ def save_coco_format(results, output_file='detections.json'):
31
+ """Save detection results in COCO format
32
+
33
+ Args:
34
+ results: List of detection dictionaries
35
+ output_file: Path to save JSON file
36
+ """
37
+ with open(output_file, 'w') as f:
38
+ json.dump(results, f, indent=2)
39
+ print(f'Saved COCO format results to {output_file}')
40
+
41
+
42
+ def get_image_files(path):
43
+ """Get all image files from a path (file or directory)
44
+
45
+ Args:
46
+ path: Path to image file or directory
47
+
48
+ Returns:
49
+ List of image file paths
50
+ """
51
+ image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif'}
52
+ path = Path(path)
53
+
54
+ if path.is_file():
55
+ return [path]
56
+ elif path.is_dir():
57
+ image_files = []
58
+ for ext in image_extensions:
59
+ image_files.extend(path.glob(f'*{ext}'))
60
+ image_files.extend(path.glob(f'*{ext.upper()}'))
61
+ return sorted(image_files)
62
+ else:
63
+ raise ValueError(f"Path {path} is neither a file nor a directory")
64
+
65
+
66
+ def main(args, ):
67
+ """main
68
+ """
69
+ cfg = YAMLConfig(args.config, resume=args.resume)
70
+
71
+ if args.resume:
72
+ checkpoint = torch.load(args.resume, map_location='cpu')
73
+ if 'ema' in checkpoint:
74
+ state = checkpoint['ema']['module']
75
+ else:
76
+ state = checkpoint['model']
77
+ else:
78
+ raise AttributeError('Only support resume to load model.state_dict by now.')
79
+
80
+ # NOTE load train mode state -> convert to deploy mode
81
+ cfg.model.load_state_dict(state)
82
+
83
+ class Model(nn.Module):
84
+ def __init__(self, ) -> None:
85
+ super().__init__()
86
+ self.model = cfg.model.deploy()
87
+ self.postprocessor = cfg.postprocessor.deploy()
88
+
89
+ def forward(self, images, orig_target_sizes):
90
+ outputs = self.model(images)
91
+ outputs = self.postprocessor(outputs, orig_target_sizes)
92
+ return outputs
93
+
94
+ model = Model().to(args.device)
95
+ model.eval() # Ensure model is in eval mode
96
+
97
+ # Get image files from either single file or directory
98
+ if args.im_dir:
99
+ image_files = get_image_files(args.im_dir)
100
+ elif args.im_file:
101
+ image_files = get_image_files(args.im_file)
102
+ else:
103
+ raise ValueError("Either --im-file or --im-dir must be provided")
104
+
105
+ print(f'Processing {len(image_files)} image(s)...')
106
+
107
+ # Prepare transforms
108
+ transforms = T.Compose([
109
+ T.Resize((640, 640)),
110
+ T.ToTensor(),
111
+ ])
112
+
113
+ # Store results for COCO format
114
+ coco_results = []
115
+
116
+ # Process each image with memory-efficient approach
117
+ with torch.no_grad(): # Disable gradient computation to save memory
118
+ for idx, image_path in enumerate(image_files):
119
+ image_name = image_path.name
120
+ print(f'Processing {image_name} ({idx+1}/{len(image_files)})...')
121
+
122
+ # Load and prepare image
123
+ im_pil = Image.open(image_path).convert('RGB')
124
+ w, h = im_pil.size
125
+ orig_size = torch.tensor([w, h], dtype=torch.int64)[None].to(args.device)
126
+
127
+ # Transform and run inference
128
+ im_data = transforms(im_pil)[None].to(args.device)
129
+ output = model(im_data, orig_size)
130
+ labels, boxes, scores = output
131
+
132
+ # Move to CPU immediately to free GPU memory
133
+ labels_cpu = labels[0].cpu()
134
+ boxes_cpu = boxes[0].cpu()
135
+ scores_cpu = scores[0].cpu()
136
+
137
+ # Delete GPU tensors immediately
138
+ del im_data, orig_size, output, labels, boxes, scores
139
+ if args.device != 'cpu':
140
+ torch.cuda.empty_cache() # Clear CUDA cache after each image
141
+
142
+ # Convert to COCO format
143
+ for label, box, score in zip(labels_cpu, boxes_cpu, scores_cpu):
144
+ # bbox format in COCO: [x, y, width, height]
145
+ x1, y1, x2, y2 = box.tolist()
146
+ bbox = [x1, y1, x2 - x1, y2 - y1]
147
+
148
+ coco_result = {
149
+ "image_id": image_name,
150
+ "category_id": int(label.item()),
151
+ "bbox": bbox,
152
+ "score": float(score.item())
153
+ }
154
+ coco_results.append(coco_result)
155
+
156
+ # Delete CPU tensors (they're already converted to Python objects)
157
+ del labels_cpu, boxes_cpu, scores_cpu
158
+
159
+ # Periodically clear cache for large batches
160
+ if (idx + 1) % 50 == 0 and args.device != 'cpu':
161
+ torch.cuda.empty_cache()
162
+ print(f' Cleared GPU cache after {idx+1} images')
163
+
164
+ # Save COCO format JSON
165
+ save_coco_format(coco_results, args.output_json)
166
+
167
+ if __name__ == '__main__':
168
+ import argparse
169
+ parser = argparse.ArgumentParser(description='RT-DETR PyTorch Inference')
170
+ parser.add_argument('-c', '--config', type=str, required=True, help='Path to config file')
171
+ parser.add_argument('-r', '--resume', type=str, required=True, help='Path to checkpoint file')
172
+ parser.add_argument('-f', '--im-file', type=str, default=None, help='Path to single image file')
173
+ parser.add_argument('--im-dir', type=str, default=None, help='Path to directory containing images')
174
+ parser.add_argument('-d', '--device', type=str, default='cpu', help='Device to run inference on (cpu/cuda)')
175
+ parser.add_argument('-o', '--output-json', type=str, default='detections.json', help='Path to save COCO format JSON')
176
+ parser.add_argument('--output-dir', type=str, default='results', help='Directory to save visualization images')
177
+ args = parser.parse_args()
178
+ main(args)
179
+
usage/inference_yolov11.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Example usage:
5
+ python inference.py \
6
+ --model_path path/to/model.pth \
7
+ --image_dir path/to/image_dir \
8
+ --output_json path/to/output.json \
9
+ """
10
+
11
+ import argparse
12
+ import json
13
+ import os
14
+ from pathlib import Path
15
+ from ultralytics import YOLO
16
+
17
+
18
+ def convert_bbox_to_coco_format(bbox):
19
+ """
20
+ Convert YOLO bbox format [x_min, y_min, x_max, y_max] to COCO format [x_min, y_min, width, height].
21
+
22
+ Args:
23
+ bbox: List or tensor [x_min, y_min, x_max, y_max]
24
+
25
+ Returns:
26
+ List [x_min, y_min, width, height]
27
+ """
28
+ x_min, y_min, x_max, y_max = bbox[:4]
29
+ width = x_max - x_min
30
+ height = y_max - y_min
31
+ return [float(x_min), float(y_min), float(width), float(height)]
32
+
33
+
34
+ def run_inference(model_path, image_dir, output_json_path):
35
+ """
36
+ Run YOLOv11 inference on images in a directory and save results in COCO format.
37
+
38
+ Args:
39
+ model_path: Path to the YOLOv11 model file (.pt)
40
+ image_dir: Directory containing images to process
41
+ output_json_path: Path where output JSON will be saved
42
+ """
43
+ # Load the model
44
+ print(f"Loading model from {model_path}...")
45
+ model = YOLO(model_path)
46
+
47
+ # Get all image files from the directory
48
+ image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif'}
49
+ image_dir_path = Path(image_dir)
50
+ image_files = [
51
+ f for f in image_dir_path.iterdir()
52
+ if f.suffix.lower() in image_extensions
53
+ ]
54
+ image_files.sort() # Sort for consistent ordering
55
+
56
+ if not image_files:
57
+ raise ValueError(f"No image files found in {image_dir}")
58
+
59
+ print(f"Found {len(image_files)} images to process...")
60
+
61
+ # Run inference
62
+ coco_results = []
63
+ image_id_map = {} # Map filename to image_id
64
+
65
+ for idx, image_file in enumerate(image_files):
66
+ image_id = image_file.stem # Use filename without extension as image_id
67
+ image_id_map[str(image_file)] = image_id
68
+
69
+ # Run inference on the image (use GPU if available)
70
+ results = model(str(image_file), device='cuda', verbose=False)
71
+
72
+ # Process results for this image
73
+ result = results[0] # Get first (and only) result
74
+
75
+ if result.boxes is not None and len(result.boxes) > 0:
76
+ boxes = result.boxes
77
+ for i in range(len(boxes)):
78
+ # Get box coordinates, class, and confidence
79
+ box = boxes.xyxy[i].cpu().numpy() # [x_min, y_min, x_max, y_max]
80
+ cls = int(boxes.cls[i].cpu().numpy()) # class_id
81
+ conf = float(boxes.conf[i].cpu().numpy()) # confidence score
82
+
83
+ # Convert to COCO bbox format
84
+ coco_bbox = convert_bbox_to_coco_format(box)
85
+
86
+ # Add to results
87
+ coco_results.append({
88
+ "image_id": image_id,
89
+ "category_id": cls,
90
+ "bbox": coco_bbox,
91
+ "score": conf
92
+ })
93
+ else:
94
+ # No detections for this image
95
+ print(f"No detections in {image_file.name}")
96
+
97
+ # Save results to JSON file
98
+ output_path = Path(output_json_path)
99
+ output_path.parent.mkdir(parents=True, exist_ok=True)
100
+
101
+ with open(output_path, 'w') as f:
102
+ json.dump(coco_results, f, indent=2)
103
+
104
+ print(f"\nInference complete!")
105
+ print(f"Total images processed: {len(image_files)}")
106
+ print(f"Total detections: {len(coco_results)}")
107
+ print(f"Results saved to: {output_json_path}")
108
+
109
+
110
+ if __name__ == "__main__":
111
+ parser = argparse.ArgumentParser(
112
+ description="Run YOLOv11 inference on images and output results in COCO format"
113
+ )
114
+ parser.add_argument(
115
+ "--model_path",
116
+ type=str,
117
+ required=True,
118
+ help="Path to the YOLOv11 model file (.pt)"
119
+ )
120
+ parser.add_argument(
121
+ "--image_dir",
122
+ type=str,
123
+ required=True,
124
+ help="Directory containing images to process"
125
+ )
126
+ parser.add_argument(
127
+ "--output_json_path",
128
+ type=str,
129
+ required=True,
130
+ help="Path where output JSON file will be saved"
131
+ )
132
+
133
+ args = parser.parse_args()
134
+
135
+ # Validate inputs
136
+ if not os.path.exists(args.model_path):
137
+ raise FileNotFoundError(f"Model file not found: {args.model_path}")
138
+
139
+ if not os.path.isdir(args.image_dir):
140
+ raise NotADirectoryError(f"Image directory not found: {args.image_dir}")
141
+
142
+ run_inference(args.model_path, args.image_dir, args.output_json_path)
143
+
144
+