Hsyshasuzh/longmemory-geo / project /evaluation /analyze_precision_errors.py
Hsyshasuzh's picture
download
raw
3.45 kB
"""Inspect high-confidence development false positives against labeled boxes."""
import argparse
from collections import Counter, defaultdict
import json
from pathlib import Path
from shapely.geometry import Polygon
from evaluation.metrics import _match_class
def geometry(points):
return Polygon(list(zip(points[::2], points[1::2]))).buffer(0)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("records", type=Path)
parser.add_argument("comparison", type=Path)
parser.add_argument("output", type=Path)
args = parser.parse_args()
rows = json.loads(args.records.read_text())["candidate"]
previous = json.loads(args.comparison.read_text())["results"]["initial"]["selection_precision_99_frontier"]
targets = {row["sample_id"]: row for row in rows["targets"]}
ignored = {sid: row.get("ignored_annotations", []) for sid, row in targets.items()}
result = {}
for cls in (4, 5):
threshold = previous[str(cls)]["best"]["threshold"]
truth = {sid: [a for a in row["annotations"] if int(a["category_id"]) == cls]
for sid, row in targets.items()}
pool = [(row["sample_id"], a) for row in rows["predictions"] for a in row["annotations"]
if int(a["category_id"]) == cls]
scores, _, _ = _match_class(pool, truth, ignored, .5, ignore_prediction_fraction=.5)
by_score = defaultdict(list)
for sid, prediction in pool:
by_score[float(prediction["confidence"])].append((sid, prediction))
errors, ambiguous = [], 0
for score, positive in scores:
if score < threshold or positive:
continue
if len(by_score[score]) != 1:
ambiguous += 1
continue
sid, prediction = by_score[score][0]
poly = geometry(prediction["obb_polygon"])
nearest = {"same_class_iou": 0.0, "other_class_iou": 0.0, "other_class_id": None}
for target in targets[sid]["annotations"]:
other = geometry(target["obb_polygon"])
union = poly.union(other).area
iou = poly.intersection(other).area / union if union else 0.0
if int(target["category_id"]) == cls:
nearest["same_class_iou"] = max(nearest["same_class_iou"], iou)
elif iou > nearest["other_class_iou"]:
nearest["other_class_iou"] = iou
nearest["other_class_id"] = target["category_id"]
errors.append({"sample_id": sid, "confidence": score, "obb_polygon": prediction["obb_polygon"], **nearest})
buckets = Counter("same_class_iou>=0.5" if e["same_class_iou"] >= .5 else
"same_class_iou>=0.1" if e["same_class_iou"] >= .1 else
"other_class_iou>=0.1" if e["other_class_iou"] >= .1 else "no_labeled_overlap>=0.1"
for e in errors)
result[str(cls)] = {"threshold_from_initial_selection": threshold, "false_positive_count": len(errors),
"ambiguous_tied_scores": ambiguous, "categories": dict(buckets), "errors": errors}
with args.output.open("x") as stream:
json.dump(result, stream, indent=2)
stream.write("\n")
print(json.dumps({key: {k: v for k, v in value.items() if k != "errors"} for key, value in result.items()}))
if __name__ == "__main__":
main()

Xet Storage Details

Size:
3.45 kB
·
Xet hash:
b54cfa55ef8b95320bf618493ba837cb5cec48aaa169552d55209147ff679d58

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.