103 lines
3.6 KiB
Python
103 lines
3.6 KiB
Python
"""
|
|
Benchmark Suite for Supermarket Product Classification
|
|
Compares SURF, ORB, BRISK, and Hybrid methods following
|
|
MDPI Electronics 2023, 12(17), 3640.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import cv2
|
|
import numpy as np
|
|
from typing import Dict, List, Any
|
|
|
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
if current_dir not in sys.path:
|
|
sys.path.insert(0, current_dir)
|
|
|
|
from feature_matcher import ProductFeatureMatcher
|
|
|
|
|
|
def run_benchmark(stock_dir: str = "stock", num_runs: int = 3):
|
|
print("=" * 70)
|
|
print("RETAIL SHELF PRODUCT RECOGNITION - ALGORITHM BENCHMARK")
|
|
print("Methods Evaluated: SURF | ORB | BRISK | HYBRID (Paper Method)")
|
|
print("=" * 70)
|
|
|
|
matcher = ProductFeatureMatcher(stock_dir=stock_dir)
|
|
templates = matcher.db.templates
|
|
|
|
if not templates:
|
|
print("[Error] No templates found in stock directory.")
|
|
return
|
|
|
|
methods = ["surf", "orb", "brisk", "hybrid"]
|
|
results = {m: {"latencies": [], "inliers": [], "keypoints_query": []} for m in methods}
|
|
|
|
# Test images: We will evaluate using crops synthesized with slight transformations
|
|
# (scale, rotation, noise) as well as the template images themselves
|
|
print(f"\nEvaluating across {len(templates)} catalog SKUs (repeat count: {num_runs})...\n")
|
|
|
|
for sku_id, tmpl in templates.items():
|
|
original_img = tmpl["image"]
|
|
h, w = original_img.shape[:2]
|
|
|
|
# Apply synthetic shelf condition (rotation by 10 deg, resize by 0.85)
|
|
M = cv2.getRotationMatrix2D((w / 2, h / 2), 10, 0.85)
|
|
query_img = cv2.warpAffine(original_img, M, (w, h))
|
|
|
|
for method in methods:
|
|
times = []
|
|
for _ in range(num_runs):
|
|
t0 = time.perf_counter()
|
|
res = matcher.classify_crop(query_img, method=method)
|
|
t1 = time.perf_counter()
|
|
times.append((t1 - t0) * 1000.0) # ms
|
|
|
|
avg_latency = np.mean(times)
|
|
results[method]["latencies"].append(avg_latency)
|
|
|
|
# Record inliers/score
|
|
if res.get("status") == "success":
|
|
top_sku = res["best_sku"]
|
|
score = res["score"]
|
|
results[method]["inliers"].append(score)
|
|
|
|
# Summarize metrics
|
|
summary_rows = []
|
|
for method in methods:
|
|
latencies = results[method]["latencies"]
|
|
scores = results[method]["inliers"]
|
|
|
|
avg_lat = np.mean(latencies) if latencies else 0.0
|
|
p95_lat = np.percentile(latencies, 95) if latencies else 0.0
|
|
avg_score = np.mean(scores) if scores else 0.0
|
|
|
|
# Paper characteristics notes
|
|
notes = {
|
|
"surf": "Scale & rotation robust; Hessian gradient features",
|
|
"orb": "Ultra fast binary descriptor; FAST + BRIEF",
|
|
"brisk": "Scale-space AGAST; circular pattern descriptor",
|
|
"hybrid": "Paper fusion (SURF+ORB+BRISK); optimal accuracy/speed"
|
|
}[method]
|
|
|
|
summary_rows.append([
|
|
method.upper(),
|
|
f"{avg_lat:.2f} ms",
|
|
f"{p95_lat:.2f} ms",
|
|
f"{avg_score:.1f}",
|
|
notes
|
|
])
|
|
|
|
headers = ["Method", "Avg Latency (ms)", "P95 Latency (ms)", "Avg Match Score", "Characteristics / Paper Findings"]
|
|
print("\n" + "-" * 105)
|
|
print(f"{headers[0]:<10} | {headers[1]:<18} | {headers[2]:<18} | {headers[3]:<16} | {headers[4]}")
|
|
print("-" * 105)
|
|
for row in summary_rows:
|
|
print(f"{row[0]:<10} | {row[1]:<18} | {row[2]:<18} | {row[3]:<16} | {row[4]}")
|
|
print("-" * 105)
|
|
print("\n[Benchmark Complete] All methods operational.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
run_benchmark()
|