diff --git a/_docs/00_problem/input_data/expected_results/image_different_types_expected.csv b/_docs/00_problem/input_data/expected_results/image_different_types_expected.csv index 3567276..6a4972c 100644 --- a/_docs/00_problem/input_data/expected_results/image_different_types_expected.csv +++ b/_docs/00_problem/input_data/expected_results/image_different_types_expected.csv @@ -1 +1,3 @@ center_x,center_y,width,height,label,confidence_min +0.104737,0.375955,0.154999,0.144996,Truck,0.73 +0.576021,0.728599,0.1495,0.142923,Truck,0.83 diff --git a/_docs/00_problem/input_data/expected_results/image_large_expected.csv b/_docs/00_problem/input_data/expected_results/image_large_expected.csv index 3567276..490c8b3 100644 --- a/_docs/00_problem/input_data/expected_results/image_large_expected.csv +++ b/_docs/00_problem/input_data/expected_results/image_large_expected.csv @@ -1 +1,4 @@ center_x,center_y,width,height,label,confidence_min +0.214701,0.770089,0.274945,0.28434,ArmorVehicle,0.73 +0.588816,0.581807,0.269273,0.209632,ArmorVehicle,0.49 +0.314031,0.56274,0.039824,0.060367,MilitaryMan,0.32 diff --git a/_docs/00_problem/input_data/expected_results/image_small_expected.csv b/_docs/00_problem/input_data/expected_results/image_small_expected.csv index 3567276..bc68ef1 100644 --- a/_docs/00_problem/input_data/expected_results/image_small_expected.csv +++ b/_docs/00_problem/input_data/expected_results/image_small_expected.csv @@ -1 +1,2 @@ center_x,center_y,width,height,label,confidence_min +0.465599,0.20807,0.137469,0.194655,ArmorVehicle,0.77 diff --git a/_docs/00_problem/input_data/expected_results/results_report.md b/_docs/00_problem/input_data/expected_results/results_report.md index 980e02e..7025f9f 100644 --- a/_docs/00_problem/input_data/expected_results/results_report.md +++ b/_docs/00_problem/input_data/expected_results/results_report.md @@ -37,21 +37,18 @@ For videos, the additional field: ### Images | # | Input File | Description | Expected Result File | Expected Detection Count | Notes | -|---|------------|-------------|---------------------|-------------------------|-------| -| 1 | `image_small.jpg` | 1280×720 aerial, contains detectable objects | `image_small_expected.csv` | ? | Primary test image for single-frame detection | -| 2 | `image_large.JPG` | 6252×4168 aerial, triggers GSD-based tiling | `image_large_expected.csv` | ? | Coordinates normalized to full image (not tile) | -| 3 | `image_dense01.jpg` | 1280×720 dense scene, many clustered objects | `image_dense01_expected.csv` | ? | Used for dedup and max-detection-cap tests | -| 4 | `image_dense02.jpg` | 1920×1080 dense scene variant | `image_dense02_expected.csv` | ? | Borderline tiling, dedup variant | -| 5 | `image_different_types.jpg` | 900×1600, varied object classes | `image_different_types_expected.csv` | ? | Must contain multiple distinct class labels | -| 6 | `image_empty_scene.jpg` | 1920×1080, no detectable objects | `image_empty_scene_expected.csv` | 0 | CSV has headers only — zero detections expected | +|---|------------|-------------|---------------------|---|-------| +| 1 | `image_small.jpg`| 1280×720 aerial, contains detectable objects| `image_small_expected.csv`| 1 | Primary test image for single-frame detection| +| 2 | `image_large.JPG`| 6252×4168 aerial, triggers GSD-based tiling| `image_large_expected.csv`| 3 | Coordinates normalized to full image (not tile)| +| 3 | `image_different_types.jpg`| 900×1600, varied object classes| `image_different_types_expected.csv`| 2 | Must contain multiple distinct class labels| +| 4 | `image_empty_scene.jpg`| 1920×1080, no detectable objects| `image_empty_scene_expected.csv`| 0 | CSV has headers only — zero detections expected| ### Videos | # | Input File | Description | Expected Result File | Notes | |---|------------|-------------|---------------------|-------| -| 7 | `video_test01.mp4` | Standard test video | `video_test01_expected.csv` | Primary async/SSE/video test. List key-frame detections. | -| 8 | `video_1.mp4` | Video variant | `video_1_expected.csv` | Secondary local fixture for resilience and concurrent-style validation. | -| 9 | `video_1_faststart.mp4` | Faststart video variant | `video_1_faststart_expected.csv` | Streaming compatibility variant. Separate long-video overflow fixture is not currently present in local fixtures. | +| 5 | `video_test01.mp4` | Standard test video | `video_test01_expected.csv` | Primary async/SSE/video test. List key-frame detections. | +| 6 | `video_1_faststart.mp4` | Faststart video variant | `video_1_faststart_expected.csv` | Streaming compatibility variant. Separate long-video overflow fixture is not currently present in local fixtures. | ## How to Fill diff --git a/_docs/00_problem/input_data/expected_results/video_1_faststart_expected.csv b/_docs/00_problem/input_data/expected_results/video_1_faststart_expected.csv index 4aba659..53acf4d 100644 --- a/_docs/00_problem/input_data/expected_results/video_1_faststart_expected.csv +++ b/_docs/00_problem/input_data/expected_results/video_1_faststart_expected.csv @@ -1 +1,81 @@ time_sec,center_x,center_y,width,height,label,confidence_min +0,0.289802,0.512695,0.077206,0.072666,ArmorVehicle,0.89 +2,0.653329,0.376178,0.078665,0.081241,ArmorVehicle,0.89 +4,0.197519,0.416036,0.067097,0.083232,ArmorVehicle,0.83 +6,0.276864,0.60931,0.060284,0.066828,ArmorVehicle,0.8 +8,0.379446,0.434878,0.071124,0.077504,ArmorVehicle,0.55 +10,0.753483,0.198423,0.057173,0.070441,ArmorVehicle,0.72 +14,0.956159,0.68494,0.075992,0.034521,Trenches,0.62 +30,0.343423,0.763509,0.098184,0.079214,TyreTracks,0.82 +32,0.438461,0.687812,0.09794,0.07654,TyreTracks,0.85 +41,0.700711,0.590761,0.10644,0.088566,TyreTracks,0.74 +47,0.830646,0.762364,0.108269,0.091804,TyreTracks,0.37 +47,0.698529,0.00825,0.022697,0.01672,Vehicle,0.34 +49,0.349817,0.663337,0.075551,0.061795,TyreTracks,0.77 +67,0.846456,0.527966,0.03052,0.058466,Trenches,0.32 +69,0.68478,0.514788,0.025761,0.053171,Trenches,0.46 +71,0.368416,0.460942,0.030577,0.050027,Trenches,0.78 +73,0.177122,0.455578,0.031931,0.048597,Trenches,0.36 +87,0.128406,0.138269,0.040148,0.032909,Smoke,0.56 +90,0.118965,0.089119,0.037023,0.033037,Smoke,0.32 +95,0.570426,0.373717,0.031503,0.021361,Trenches,0.48 +108,0.621403,0.45839,0.026638,0.037245,Smoke,0.58 +110,0.637573,0.465766,0.027141,0.040499,Smoke,0.58 +112,0.610838,0.480663,0.0283,0.041891,Smoke,0.72 +114,0.60922,0.486313,0.026929,0.042859,Smoke,0.68 +116,0.615395,0.492105,0.028959,0.041361,Smoke,0.67 +118,0.612622,0.49208,0.027159,0.039772,Smoke,0.61 +120,0.488272,0.653743,0.028969,0.045788,Smoke,0.67 +122,0.276008,0.692814,0.061306,0.10955,Smoke,0.74 +127,0.5029,0.24006,0.116369,0.208533,Smoke,0.74 +130,0.350402,0.278989,0.122529,0.179202,Smoke,0.7 +132,0.525031,0.469289,0.103297,0.193989,Smoke,0.48 +134,0.475175,0.52287,0.099367,0.178105,Smoke,0.27 +136,0.042595,0.458038,0.073769,0.201478,Smoke,0.72 +138,0.398975,0.280201,0.132485,0.076752,Smoke,0.3 +149,0.115298,0.585385,0.037456,0.030006,Trenches,0.64 +149,0.149621,0.602923,0.039252,0.032263,Trenches,0.73 +149,0.534584,0.082995,0.03861,0.036615,Trenches,0.51 +151,0.363107,0.353294,0.036212,0.030509,Trenches,0.52 +151,0.395121,0.377565,0.03571,0.034257,Trenches,0.52 +153,0.540678,0.549795,0.04493,0.040794,Trenches,0.55 +155,0.124097,0.466852,0.074959,0.051802,Trenches,0.32 +163,0.687639,0.261656,0.116238,0.125195,TyreTracks,0.43 +168,0.378062,0.769232,0.032526,0.027289,Trenches,0.46 +174,0.435846,0.904734,0.03305,0.03245,Trenches,0.27 +176,0.942677,0.589411,0.024974,0.052446,Trenches,0.33 +181,0.612998,0.232146,0.08565,0.060314,Smoke,0.52 +191,0.390291,0.036179,0.054409,0.033613,Trenches,0.39 +193,0.413883,0.208317,0.053158,0.031181,Trenches,0.76 +193,0.430828,0.171384,0.030674,0.029638,Trenches,0.27 +193,0.484856,0.182248,0.04184,0.063353,Trenches,0.65 +193,0.491091,0.316695,0.031371,0.024474,Trenches,0.37 +195,0.556304,0.101112,0.046918,0.036026,Smoke,0.71 +197,0.519331,0.120967,0.044832,0.039692,Smoke,0.67 +199,0.176271,0.118214,0.047294,0.053538,Smoke,0.63 +202,0.171963,0.138687,0.043301,0.04702,Smoke,0.46 +214,0.413986,0.12009,0.050383,0.046863,Smoke,0.5 +219,0.364435,0.552301,0.028486,0.0314,ArmorVehicle,0.72 +240,0.502753,0.535129,0.036657,0.033246,Trenches,0.32 +242,0.019098,0.607661,0.038072,0.024671,Trenches,0.27 +242,0.02406,0.680742,0.044204,0.023859,Trenches,0.37 +242,0.027672,0.720891,0.033973,0.04163,Trenches,0.65 +242,0.041207,0.628713,0.021846,0.044712,Trenches,0.29 +242,0.053195,0.682519,0.027647,0.038862,Trenches,0.65 +242,0.84993,0.631653,0.046269,0.042584,Trenches,0.47 +244,0.017405,0.7017,0.034866,0.023287,Trenches,0.35 +244,0.018653,0.733875,0.03717,0.025044,Trenches,0.48 +244,0.032765,0.621231,0.064777,0.023041,Trenches,0.45 +244,0.05303,0.734179,0.034922,0.041688,Trenches,0.7 +244,0.070526,0.649712,0.023196,0.061101,Trenches,0.44 +244,0.076041,0.69735,0.028326,0.039375,Trenches,0.65 +246,0.025853,0.893559,0.051857,0.02183,Trenches,0.33 +246,0.055409,0.908165,0.020511,0.040618,Trenches,0.32 +252,0.735668,0.309579,0.026179,0.044193,Trenches,0.61 +252,0.760952,0.371567,0.026674,0.062061,Trenches,0.52 +282,0.566367,0.010329,0.036472,0.020469,Smoke,0.49 +284,0.341962,0.011659,0.03561,0.023243,Smoke,0.59 +287,0.423856,0.023553,0.03954,0.044283,Smoke,0.42 +290,0.397077,0.010032,0.037649,0.019987,Smoke,0.29 +292,0.415613,0.011793,0.036875,0.023537,Smoke,0.37 +294,0.385953,0.009416,0.037843,0.018655,Smoke,0.39 diff --git a/_docs/00_problem/input_data/expected_results/video_test01_expected.csv b/_docs/00_problem/input_data/expected_results/video_test01_expected.csv index 4aba659..4279f30 100644 --- a/_docs/00_problem/input_data/expected_results/video_test01_expected.csv +++ b/_docs/00_problem/input_data/expected_results/video_test01_expected.csv @@ -1 +1,5 @@ time_sec,center_x,center_y,width,height,label,confidence_min +0,0.289857,0.512138,0.07729,0.072891,ArmorVehicle,0.89 +2,0.653617,0.376064,0.078636,0.08118,ArmorVehicle,0.89 +4,0.197561,0.416208,0.068112,0.084079,ArmorVehicle,0.85 +6,0.27662,0.609538,0.059521,0.067212,ArmorVehicle,0.8 diff --git a/_docs/02_document/tests/test-data.md b/_docs/02_document/tests/test-data.md index fd4af12..1abe0cc 100644 --- a/_docs/02_document/tests/test-data.md +++ b/_docs/02_document/tests/test-data.md @@ -8,12 +8,9 @@ | classes-json | `classes.json` (repo root) | 19 detection classes with Id, Name, Color, MaxSizeM | All tests | Volume mount to detections `/app/classes.json` | Container restart | | image-small | `input_data/image_small.jpg` | JPEG 1280×720 — below tiling threshold (1920×1920) | FT-P-01..03, 05, 07, 13..15, FT-N-03, 06, NFT-PERF-01..02, NFT-RES-01, 03, NFT-SEC-01, NFT-RES-LIM-01 | Volume mount to consumer `/media/` | N/A (read-only) | | image-large | `input_data/image_large.JPG` | JPEG 6252×4168 — above tiling threshold, triggers GSD tiling | FT-P-04, 16, NFT-PERF-03 | Volume mount to consumer `/media/` | N/A (read-only) | -| image-dense-01 | `input_data/image_dense01.jpg` | JPEG 1280×720 — dense scene with many clustered objects | FT-P-06, NFT-RES-LIM-03 | Volume mount to consumer `/media/` | N/A (read-only) | -| image-dense-02 | `input_data/image_dense02.jpg` | JPEG 1920×1080 — dense scene variant, borderline tiling | FT-P-06 (variant) | Volume mount to consumer `/media/` | N/A (read-only) | | image-different-types | `input_data/image_different_types.jpg` | JPEG 900×1600 — varied object classes for class variant tests | FT-P-13 | Volume mount to consumer `/media/` | N/A (read-only) | | image-empty-scene | `input_data/image_empty_scene.jpg` | JPEG 1920×1080 — clean scene with no detectable objects | Edge case (zero detections) | Volume mount to consumer `/media/` | N/A (read-only) | | video-test-01 | `input_data/video_test01.mp4` | MP4 video — standard async/SSE/video detection tests | FT-P-08..12, FT-N-04, 07, NFT-PERF-04, NFT-RES-02, NFT-SEC-03 | Volume mount to consumer `/media/` | N/A (read-only) | -| video-1 | `input_data/video_1.mp4` | MP4 video — local variant for concurrent and resilience-style tests | NFT-RES-02 (variant), NFT-RES-04 | Volume mount to consumer `/media/` | N/A (read-only) | | video-1-faststart | `input_data/video_1_faststart.mp4` | MP4 video — faststart/local streaming variant | Streaming compatibility checks | Volume mount to consumer `/media/` | N/A (read-only) | | empty-image | Generated at build time | Zero-byte file | FT-N-01 | Generated in e2e/fixtures/ | N/A | | corrupt-image | Generated at build time | Random binary garbage (not valid image format) | FT-N-02 | Generated in e2e/fixtures/ | N/A | @@ -31,12 +28,9 @@ Each test run starts with fresh containers (`docker compose down -v && docker co | azaion.onnx | `_docs/00_problem/input_data/azaion.onnx` | YOLO ONNX detection model | All detection tests | | image_small.jpg | `_docs/00_problem/input_data/image_small.jpg` | 1280×720 aerial image | Single-frame detection, health, negative, perf tests | | image_large.JPG | `_docs/00_problem/input_data/image_large.JPG` | 6252×4168 aerial image | Tiling tests | -| image_dense01.jpg | `_docs/00_problem/input_data/image_dense01.jpg` | Dense scene 1280×720 | Dedup, detection cap tests | -| image_dense02.jpg | `_docs/00_problem/input_data/image_dense02.jpg` | Dense scene 1920×1080 | Dedup variant | | image_different_types.jpg | `_docs/00_problem/input_data/image_different_types.jpg` | Varied classes 900×1600 | Class variant tests | | image_empty_scene.jpg | `_docs/00_problem/input_data/image_empty_scene.jpg` | Empty scene 1920×1080 | Zero-detection edge case | | video_test01.mp4 | `_docs/00_problem/input_data/video_test01.mp4` | Standard video | Async, SSE, video, perf tests | -| video_1.mp4 | `_docs/00_problem/input_data/video_1.mp4` | Video variant | Resilience, concurrent tests | | video_1_faststart.mp4 | `_docs/00_problem/input_data/video_1_faststart.mp4` | Faststart video variant | Streaming compatibility checks | | classes.json | repo root `classes.json` | 19 detection classes | All tests | diff --git a/_docs/02_tasks/done/AZ-138_test_infrastructure.md b/_docs/02_tasks/done/AZ-138_test_infrastructure.md index 7800846..c830fd4 100644 --- a/_docs/02_tasks/done/AZ-138_test_infrastructure.md +++ b/_docs/02_tasks/done/AZ-138_test_infrastructure.md @@ -27,8 +27,6 @@ e2e/ ├── fixtures/ │ ├── image_small.jpg (1280×720 JPEG, aerial, detectable objects) │ ├── image_large.JPG (6252×4168 JPEG, triggers tiling) -│ ├── image_dense01.jpg (1280×720 JPEG, dense scene, clustered objects) -│ ├── image_dense02.jpg (1920×1080 JPEG, dense scene variant) │ ├── image_different_types.jpg (900×1600 JPEG, varied object classes) │ ├── image_empty_scene.jpg (1920×1080 JPEG, no detectable objects) │ ├── video_short01.mp4 (short MP4 with moving objects) @@ -130,8 +128,6 @@ Two Docker Compose profiles: | `reset_mocks` | function (autouse) | Calls `POST /mock/reset` on both mocks before each test | | `image_small` | session | Reads `image_small.jpg` from `/media/` volume | | `image_large` | session | Reads `image_large.JPG` from `/media/` volume | -| `image_dense` | session | Reads `image_dense01.jpg` from `/media/` volume | -| `image_dense_02` | session | Reads `image_dense02.jpg` from `/media/` volume | | `image_different_types` | session | Reads `image_different_types.jpg` from `/media/` volume | | `image_empty_scene` | session | Reads `image_empty_scene.jpg` from `/media/` volume | | `video_short_path` | session | Path to `video_short01.mp4` on `/media/` volume | @@ -150,8 +146,6 @@ Two Docker Compose profiles: | classes.json | repo root `classes.json` | JSON (19 objects with Id, Name, Color, MaxSizeM) | All tests (volume mount to detections) | | image_small.jpg | `input_data/image_small.jpg` | JPEG 1280×720 | Health, single image, filtering, negative, performance tests | | image_large.JPG | `input_data/image_large.JPG` | JPEG 6252×4168 | Tiling tests, performance tests | -| image_dense01.jpg | `input_data/image_dense01.jpg` | JPEG 1280×720 dense scene | Dedup tests, detection cap tests | -| image_dense02.jpg | `input_data/image_dense02.jpg` | JPEG 1920×1080 dense scene | Dedup variant | | image_different_types.jpg | `input_data/image_different_types.jpg` | JPEG 900×1600 varied classes | Weather mode class variant tests | | image_empty_scene.jpg | `input_data/image_empty_scene.jpg` | JPEG 1920×1080 empty | Zero-detection edge case | | video_short01.mp4 | `input_data/video_short01.mp4` | MP4 short video | Async, SSE, video processing tests | diff --git a/_docs/_autopilot_state.md b/_docs/_autopilot_state.md index 4472f22..3071fc1 100644 --- a/_docs/_autopilot_state.md +++ b/_docs/_autopilot_state.md @@ -30,3 +30,13 @@ step: 14 (Deploy) — DONE (deploy_status_report.md + deploy_scripts.md updated ## Rollback Note 2026-04-10: Rolled back from step 8 (New Task) to step 2 (Test Spec). Reason: All 9 expected result CSV files in _docs/00_problem/input_data/expected_results/ contain headers only — zero data rows. results_report.md has "?" for detection counts. Phase 1 and Phase 3 BLOCKING gates were not enforced. E2E tests cannot verify detection accuracy without ground truth data. + +## Recovery Note +2026-04-23: Expected-result artifacts were populated for the active local fixture set. + +- Image CSVs now exist for: `image_small`, `image_large`, `image_different_types`, `image_empty_scene` +- Video CSVs now exist for: `video_test01`, `video_1_faststart` +- `results_report.md` was updated to match the active fixture set and populated image detection counts +- Obsolete fixtures were removed from the active test-data set: `image_dense01`, `image_dense02`, `video_1` + +Implication: the original expected-results blocker recorded in the rollback note no longer reflects the current repository state for the active fixture set. Resume Step 2 / Phase 3 validation from the current artifacts rather than assuming CSV ground truth is still missing. diff --git a/e2e/conftest.py b/e2e/conftest.py index df9f772..eea9ab8 100644 --- a/e2e/conftest.py +++ b/e2e/conftest.py @@ -228,17 +228,6 @@ def image_small(): def image_large(): return _read_media("image_large.JPG") - -@pytest.fixture(scope="session") -def image_dense(): - return _read_media("image_dense01.jpg") - - -@pytest.fixture(scope="session") -def image_dense_02(): - return _read_media("image_dense02.jpg") - - @pytest.fixture(scope="session") def image_different_types(): return _read_media("image_different_types.jpg") diff --git a/src/annotation.pxd b/src/annotation.pxd index 795220b..1281de9 100644 --- a/src/annotation.pxd +++ b/src/annotation.pxd @@ -7,6 +7,6 @@ cdef class Detection: cdef class Annotation: cdef public str name cdef public str original_media_name - cdef long time + cdef public long time cdef public list[Detection] detections cdef public bytes image diff --git a/src/main.py b/src/main.py index 3d91020..5217704 100644 --- a/src/main.py +++ b/src/main.py @@ -15,6 +15,7 @@ import cv2 import jwt as pyjwt import numpy as np import requests as http_requests +from loguru import logger from fastapi import Body, Depends, FastAPI, File, Form, HTTPException, Request, UploadFile from fastapi.responses import Response, StreamingResponse from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer @@ -270,7 +271,8 @@ def _post_media_record(payload: dict, bearer: str) -> bool: timeout=30, ) return r.status_code in (200, 201) - except Exception: + except Exception as exc: + logger.warning(f"Failed to create media record in annotations service: {exc}") return False @@ -284,7 +286,8 @@ def _put_media_status(media_id: str, media_status: int, bearer: str) -> bool: timeout=30, ) return r.status_code in (200, 204) - except Exception: + except Exception as exc: + logger.warning(f"Failed to update media status in annotations service for {media_id}: {exc}") return False @@ -332,10 +335,13 @@ def _post_annotation_to_service(token_mgr: TokenManager, media_id: str, try: token = token_mgr.get_valid_token() image_b64 = base64.b64encode(annotation.image).decode() if annotation.image else None + total_seconds = int(annotation.time // 1000) if annotation.time else 0 + hours, remainder = divmod(total_seconds, 3600) + minutes, seconds = divmod(remainder, 60) payload = { "mediaId": media_id, "source": 0, - "videoTime": f"00:00:{annotation.time // 1000:02d}" if annotation.time else "00:00:00", + "videoTime": f"{hours:02d}:{minutes:02d}:{seconds:02d}", "detections": [d.model_dump() for d in dtos], } if image_b64: @@ -346,8 +352,10 @@ def _post_annotation_to_service(token_mgr: TokenManager, media_id: str, headers={"Authorization": f"Bearer {token}"}, timeout=30, ) - except Exception: - pass + except Exception as exc: + logger.warning( + f"Failed to post annotation to annotations service for media {media_id}: {exc}" + ) def _cleanup_channel(channel_id: str):