Evaluate regional detector ensembles
This commit is contained in:
@@ -52,6 +52,31 @@ def metrics(tp: int, fp: int, fn: int) -> dict[str, float | int]:
|
||||
}
|
||||
|
||||
|
||||
def ensemble_predictions(
|
||||
primary: list[tuple[tuple[float, float, float, float], float]],
|
||||
secondary: list[tuple[tuple[float, float, float, float], float]],
|
||||
*,
|
||||
match_iou: float,
|
||||
mode: str,
|
||||
) -> list[tuple[tuple[float, float, float, float], float]]:
|
||||
unmatched = set(range(len(secondary)))
|
||||
combined = []
|
||||
unmatched_primary = []
|
||||
for box, score in primary:
|
||||
candidates = [(iou(box, secondary[index][0]), index) for index in unmatched]
|
||||
overlap, index = max(candidates, default=(0.0, -1))
|
||||
if overlap < match_iou:
|
||||
unmatched_primary.append((box, score))
|
||||
continue
|
||||
other_box, other_score = secondary[index]
|
||||
unmatched.remove(index)
|
||||
combined.append((tuple((left + right) / 2 for left, right in zip(box, other_box)), (score + other_score) / 2))
|
||||
if mode == "union":
|
||||
combined.extend(unmatched_primary)
|
||||
combined.extend(secondary[index] for index in unmatched)
|
||||
return combined
|
||||
|
||||
|
||||
def scale_box(
|
||||
box: tuple[float, float, float, float], factor: float, offset_x: float = 0.0, offset_y: float = 0.0
|
||||
) -> tuple[float, float, float, float]:
|
||||
@@ -106,6 +131,9 @@ def main() -> int:
|
||||
parser.add_argument("--box-scale", type=float, default=1.0)
|
||||
parser.add_argument("--box-offset-x", type=float, default=0.0)
|
||||
parser.add_argument("--box-offset-y", type=float, default=0.0)
|
||||
parser.add_argument("--additional-model", type=Path)
|
||||
parser.add_argument("--ensemble-mode", choices=("consensus", "union"), default="consensus")
|
||||
parser.add_argument("--ensemble-match-iou", type=float, default=0.3)
|
||||
parser.add_argument("--proposal-classifier", type=Path)
|
||||
parser.add_argument("--proposal-classifier-threshold", type=float, default=0.5)
|
||||
parser.add_argument("--proposal-crop-scale", type=float, default=1.4)
|
||||
@@ -147,8 +175,14 @@ def main() -> int:
|
||||
max_det=args.max_det,
|
||||
verbose=False,
|
||||
)
|
||||
additional_results = None
|
||||
if args.additional_model:
|
||||
additional_results = YOLO(str(args.additional_model)).predict(
|
||||
image_paths, conf=min(args.thresholds), device=args.device, augment=args.augment,
|
||||
imgsz=args.imgsz, max_det=args.max_det, verbose=False,
|
||||
)
|
||||
observations: list[dict[str, Any]] = []
|
||||
for tile, result in zip(tiles, results, strict=True):
|
||||
for result_index, (tile, result) in enumerate(zip(tiles, results, strict=True)):
|
||||
height, width = result.orig_shape
|
||||
predictions = [
|
||||
(
|
||||
@@ -159,6 +193,20 @@ def main() -> int:
|
||||
)
|
||||
for box, score in zip(result.boxes.xyxy.cpu().tolist(), result.boxes.conf.cpu().tolist(), strict=True)
|
||||
]
|
||||
if additional_results is not None:
|
||||
secondary = [
|
||||
(
|
||||
scale_box(tuple(map(float, box)), args.box_scale, args.box_offset_x, args.box_offset_y),
|
||||
float(score),
|
||||
)
|
||||
for box, score in zip(
|
||||
additional_results[result_index].boxes.xyxy.cpu().tolist(),
|
||||
additional_results[result_index].boxes.conf.cpu().tolist(), strict=True,
|
||||
)
|
||||
]
|
||||
predictions = ensemble_predictions(
|
||||
predictions, secondary, match_iou=args.ensemble_match_iou, mode=args.ensemble_mode
|
||||
)
|
||||
if proposal_classifier is not None:
|
||||
from PIL import Image
|
||||
import torch
|
||||
@@ -229,6 +277,9 @@ def main() -> int:
|
||||
"box_scale": args.box_scale,
|
||||
"box_offset_x": args.box_offset_x,
|
||||
"box_offset_y": args.box_offset_y,
|
||||
"additional_model": str(args.additional_model) if args.additional_model else None,
|
||||
"ensemble_mode": args.ensemble_mode if args.additional_model else None,
|
||||
"ensemble_match_iou": args.ensemble_match_iou if args.additional_model else None,
|
||||
"proposal_classifier": str(args.proposal_classifier) if args.proposal_classifier else None,
|
||||
"proposal_classifier_threshold": args.proposal_classifier_threshold if args.proposal_classifier else None,
|
||||
"proposal_crop_scale": args.proposal_crop_scale if args.proposal_classifier else None,
|
||||
|
||||
Reference in New Issue
Block a user