Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d6770e4
feat(export): add native TensorRT and INT8 engine export
Aug 16, 2026
7d639af
feat(inference): implement TensorRT engine backend
Aug 16, 2026
5d29137
feat(patchcore): add lightweight memory-bank pipeline
Aug 16, 2026
8d00358
docs: simplify README and add deployment guide
Aug 16, 2026
c135225
docs(patchcore): document public API and deployment contract
Aug 16, 2026
6a8ebab
perf(patchcore): add ultra-light pooled and chunked inference
Aug 16, 2026
5724706
docs: improve beginner README flow
Aug 16, 2026
21fbe9b
bench: align AnomaVision and Anomalib comparison
Aug 16, 2026
b25aab1
feat(export): automate PaDiM and PatchCore TensorRT conversion
Aug 16, 2026
c6faf9d
fix(detect): classify high anomaly scores correctly
Aug 16, 2026
c4b40d4
fix(export): default optional calibration settings
Aug 16, 2026
1b62e32
docs(config): include complete export defaults
Aug 16, 2026
53c18fa
fix(patchcore): calibrate thresholds and select diverse coresets
Aug 16, 2026
b36d8c6
fix(visualization): correct anomaly colors and localization masks
Aug 16, 2026
971e9b2
fix(patchcore): restore localized visualization output
Aug 16, 2026
aed5c2e
feat(autopilot): add production model selection pipeline
Aug 16, 2026
86be754
docs(autopilot): add deployment report and usage guide
Aug 16, 2026
eb10e43
feat(autopilot): add self-contained HTML deployment report
Aug 16, 2026
bf0a320
fix(autopilot): evaluate complete dataset and report AUROC
Aug 16, 2026
665fc52
fix(autopilot): report algorithm-aware localization health
Aug 16, 2026
9e6c165
docs: update autopilot and localization guidance
Aug 16, 2026
73c6d65
chore: finalize production feature validation
Aug 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
561 changes: 105 additions & 456 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions anomavision/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .datasets.mvtec_dataset import MVTecDataset
from .feature_extraction import ResnetEmbeddingsExtractor
from .padim import Padim
from .patchcore import PatchCore
from .sampling_methods.kcenter_greedy import kCenterGreedy
from .test import optimal_threshold, visualize_eval_data, visualize_eval_pair
from .utils import get_logger # Export for users
Expand Down
401 changes: 401 additions & 0 deletions anomavision/autopilot.py

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions anomavision/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def create_parser() -> argparse.ArgumentParser:
_add_export_parser(subparsers)
_add_detect_parser(subparsers)
_add_eval_parser(subparsers)
_add_autopilot_parser(subparsers)

return parser

Expand Down Expand Up @@ -133,6 +134,17 @@ def _add_eval_parser(subparsers) -> None:
).set_defaults(func=_dispatch_eval)


def _add_autopilot_parser(subparsers) -> None:
from anomavision.autopilot import create_parser as _cp

subparsers.add_parser(
"autopilot",
help="Calibrate, profile, and package a production model",
parents=[_cp(add_help=False)],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
).set_defaults(func=_dispatch_autopilot)


# ============================================================
# Dispatch functions β€” one line each, Namespace passed directly.
# No sys.argv manipulation. No double-parsing.
Expand Down Expand Up @@ -163,6 +175,12 @@ def _dispatch_eval(args: argparse.Namespace) -> None:
eval_module.main(args)


def _dispatch_autopilot(args: argparse.Namespace) -> None:
from anomavision import autopilot

autopilot.main(args)


# ============================================================
# Entry point
# ============================================================
Expand Down
39 changes: 23 additions & 16 deletions anomavision/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
from anomavision.utils import (
adaptive_gaussian_blur,
get_logger,
make_localization_mask,
merge_config,
resolve_threshold,
setup_logging,
)

Expand Down Expand Up @@ -199,6 +201,8 @@ def run_inference(args):

# Merge config with CLI args
config = edict(merge_config(args, cfg))
config.thresh = resolve_threshold(config)
algorithm_name = str(config.get("algorithm", "")).lower()

# Setup logging
setup_logging(enabled=True, log_level=config.log_level, log_to_file=True)
Expand Down Expand Up @@ -412,6 +416,17 @@ def run_inference(args):
else:
is_anomaly = np.zeros_like(image_scores)

if algorithm_name == "patchcore":
localization_masks = make_localization_mask(
score_maps, is_anomaly, quantile=0.90
)
else:
localization_masks = (
anomavision.classification(score_maps, config.thresh)
if config.thresh is not None
else np.zeros_like(score_maps)
)

# Accumulate Results (Offline only)
if not stream_mode:
results_accumulator["scores"].extend(image_scores.tolist())
Expand All @@ -432,13 +447,7 @@ def run_inference(args):
boundary_images = (
anomavision.visualization.framed_boundary_images(
images,
(
anomavision.classification(
score_maps, config.thresh
)
if config.thresh
else np.zeros_like(score_maps)
),
localization_masks,
is_anomaly,
padding=config.get("viz_padding", 40),
)
Expand All @@ -447,17 +456,15 @@ def run_inference(args):
heatmap_images = anomavision.visualization.heatmap_images(
images,
score_maps,
masks=localization_masks,
alpha=config.get("viz_alpha", 0.5),
)
highlighted_images = anomavision.visualization.highlighted_images(
[images[i] for i in range(len(images))],
# Dummy mask if threshold not set
(
anomavision.classification(score_maps, config.thresh)
if config.thresh
else np.zeros_like(score_maps)
),
color=viz_color,
highlighted_images = (
anomavision.visualization.highlighted_images(
[images[i] for i in range(len(images))],
localization_masks,
color=viz_color,
)
)

# Save/Show
Expand Down
10 changes: 7 additions & 3 deletions anomavision/eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
find_optimal_threshold,
get_logger,
merge_config,
resolve_threshold,
setup_logging,
)

Expand Down Expand Up @@ -218,6 +219,7 @@ def run_evaluation(args):
cfg = load_config(str(config_path)) if config_path.exists() else {}

config = edict(merge_config(args, cfg))
config.thresh = resolve_threshold(config)

setup_logging(enabled=True, log_level=config.log_level, log_to_file=True)
logger = get_logger("anomavision.eval")
Expand Down Expand Up @@ -316,9 +318,11 @@ def run_evaluation(args):

# Compute Metrics
if config.thresh is None:
best_thresh, _ = find_optimal_threshold(labels, scores)
best_thresh, best_f1 = find_optimal_threshold(labels, scores)
logger.info("threshold: auto-selected %.6f (F1=%.4f)", best_thresh, best_f1)
else:
best_thresh = config.thresh
logger.info("threshold: configured %.6f", best_thresh)

metrics = compute_metrics(labels, scores, thresh=best_thresh)

Expand Down Expand Up @@ -412,9 +416,9 @@ def run_evaluation(args):

for k, v in metrics.items():
if isinstance(v, float):
logger.info(f"{k.replace('_',' ').title():<28} {v:.6f}")
logger.info(f"{k.replace('_', ' ').title():<28} {v:.6f}")
else:
logger.info(f"{k.replace('_',' ').title():<28} {v}")
logger.info(f"{k.replace('_', ' ').title():<28} {v}")

logger.info("=" * 60)

Expand Down
Loading
Loading