Skip GSD and size filtering without altitude

This commit is contained in:
Roman Meshko
2026-04-23 20:37:25 +03:00
parent 5cfcdb5fd5
commit a78e483a33
5 changed files with 14 additions and 29 deletions
-6
View File
@@ -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 | | 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-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-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-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) | | 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-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) | | 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 | | 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 | | 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 | | 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_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_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_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 | | 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_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 | | 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 | | classes.json | repo root `classes.json` | 19 detection classes | All tests |
@@ -27,8 +27,6 @@ e2e/
├── fixtures/ ├── fixtures/
│ ├── image_small.jpg (1280×720 JPEG, aerial, detectable objects) │ ├── image_small.jpg (1280×720 JPEG, aerial, detectable objects)
│ ├── image_large.JPG (6252×4168 JPEG, triggers tiling) │ ├── 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_different_types.jpg (900×1600 JPEG, varied object classes)
│ ├── image_empty_scene.jpg (1920×1080 JPEG, no detectable objects) │ ├── image_empty_scene.jpg (1920×1080 JPEG, no detectable objects)
│ ├── video_short01.mp4 (short MP4 with moving 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 | | `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_small` | session | Reads `image_small.jpg` from `/media/` volume |
| `image_large` | session | Reads `image_large.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_different_types` | session | Reads `image_different_types.jpg` from `/media/` volume |
| `image_empty_scene` | session | Reads `image_empty_scene.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 | | `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) | | 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_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_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_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 | | 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 | | video_short01.mp4 | `input_data/video_short01.mp4` | MP4 short video | Async, SSE, video processing tests |
-11
View File
@@ -228,17 +228,6 @@ def image_small():
def image_large(): def image_large():
return _read_media("image_large.JPG") 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") @pytest.fixture(scope="session")
def image_different_types(): def image_different_types():
return _read_media("image_different_types.jpg") return _read_media("image_different_types.jpg")
+1 -1
View File
@@ -7,6 +7,6 @@ cdef class Detection:
cdef class Annotation: cdef class Annotation:
cdef public str name cdef public str name
cdef public str original_media_name cdef public str original_media_name
cdef long time cdef public long time
cdef public list[Detection] detections cdef public list[Detection] detections
cdef public bytes image cdef public bytes image
+13 -5
View File
@@ -15,6 +15,7 @@ import cv2
import jwt as pyjwt import jwt as pyjwt
import numpy as np import numpy as np
import requests as http_requests import requests as http_requests
from loguru import logger
from fastapi import Body, Depends, FastAPI, File, Form, HTTPException, Request, UploadFile from fastapi import Body, Depends, FastAPI, File, Form, HTTPException, Request, UploadFile
from fastapi.responses import Response, StreamingResponse from fastapi.responses import Response, StreamingResponse
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
@@ -270,7 +271,8 @@ def _post_media_record(payload: dict, bearer: str) -> bool:
timeout=30, timeout=30,
) )
return r.status_code in (200, 201) 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 return False
@@ -284,7 +286,8 @@ def _put_media_status(media_id: str, media_status: int, bearer: str) -> bool:
timeout=30, timeout=30,
) )
return r.status_code in (200, 204) 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 return False
@@ -332,10 +335,13 @@ def _post_annotation_to_service(token_mgr: TokenManager, media_id: str,
try: try:
token = token_mgr.get_valid_token() token = token_mgr.get_valid_token()
image_b64 = base64.b64encode(annotation.image).decode() if annotation.image else None 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 = { payload = {
"mediaId": media_id, "mediaId": media_id,
"source": 0, "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], "detections": [d.model_dump() for d in dtos],
} }
if image_b64: if image_b64:
@@ -346,8 +352,10 @@ def _post_annotation_to_service(token_mgr: TokenManager, media_id: str,
headers={"Authorization": f"Bearer {token}"}, headers={"Authorization": f"Bearer {token}"},
timeout=30, timeout=30,
) )
except Exception: except Exception as exc:
pass logger.warning(
f"Failed to post annotation to annotations service for media {media_id}: {exc}"
)
def _cleanup_channel(channel_id: str): def _cleanup_channel(channel_id: str):