Metadata-Version: 2.4
Name: iris-vision-worker-core
Version: 0.3.1
Summary: Nedo Vision Worker Core Library for AI Vision Processing
Author-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
Maintainer-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://gitlab.com/sindika/research/nedo-vision/nedo-vision-worker-core-v2
Project-URL: Documentation, https://gitlab.com/sindika/research/nedo-vision/nedo-vision-worker-core-v2/-/blob/main/README.md
Project-URL: Repository, https://gitlab.com/sindika/research/nedo-vision/nedo-vision-worker-core-v2
Project-URL: Bug Reports, https://gitlab.com/sindika/research/nedo-vision/nedo-vision-worker-core-v2/-/issues
Keywords: computer-vision,machine-learning,ai,worker-core,deep-learning,object-detection,neural-networks,video-processing,pytorch,yolo
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: System :: Hardware
Classifier: Environment :: GPU
Classifier: Environment :: No Input/Output (Daemon)
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: alembic>=1.8.0
Requires-Dist: numpy>=1.21.0
Requires-Dist: opencv-python>=4.6.0
Requires-Dist: pillow>=8.0.0
Requires-Dist: psutil>=5.9.0
Requires-Dist: scipy>=1.9.0
Requires-Dist: shapely>=1.8.0
Requires-Dist: SQLAlchemy>=1.4.0
Requires-Dist: torch>=1.9.0
Requires-Dist: torchvision>=0.10.0
Requires-Dist: ultralytics>=8.0.0
Requires-Dist: rfdetr==1.6.0
Requires-Dist: pika>=1.3.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: black>=22.0.0; extra == "dev"
Requires-Dist: isort>=5.10.0; extra == "dev"
Requires-Dist: mypy>=0.950; extra == "dev"
Requires-Dist: flake8>=4.0.0; extra == "dev"
Requires-Dist: pre-commit>=2.17.0; extra == "dev"

<div align="center">

# ai-vision-worker-core

**Library inti inferensi AI Vision IRIS — mengubah aliran kamera RTSP menjadi deteksi APD & area terlarang secara real-time**

Bagian dari ekosistem **IRIS — AI Vision Platform** PT Petrokimia Gresik

![Stack](https://img.shields.io/badge/library-Python%20AI%20Inference-3776AB)
![Runtime](https://img.shields.io/badge/python-%3E%3D3.8%20(Docker%203.11)-3776AB)
![Detector](https://img.shields.io/badge/detector-YOLO%20(Ultralytics)%20%2B%20RF--DETR-00FFFF)
![Tracker](https://img.shields.io/badge/tracker-SFSORT-orange)
![Torch](https://img.shields.io/badge/torch-2.6.0%20cu124-EE4C2C)
![Stream](https://img.shields.io/badge/stream-RTSP%20%E2%86%92%20FFmpeg%20%E2%86%92%20RTMP%20:1935-red)
![DB](https://img.shields.io/badge/db-SQLite%20%2B%20Alembic-003B57)
![License](https://img.shields.io/badge/license-Proprietary-red)

</div>

---

## Daftar Isi

1. [Executive Summary](#1-executive-summary)
2. [Proses Bisnis (BPMN)](#2-proses-bisnis-bpmn)
3. [Arsitektur](#3-arsitektur)
4. [Model & Komponen ML](#4-model--komponen-ml-spesialis)
5. [Tata Kelola & Kematangan (COBIT / PMBOK / IT Maturity)](#5-tata-kelola--kematangan)
6. [Struktur Repository](#6-struktur-repository)
7. [Konfigurasi & Environment](#7-konfigurasi--environment)
8. [Local Development](#8-local-development)
9. [Deployment & CI/CD](#9-deployment--cicd)
10. [Observability](#10-observability)
11. [Documentation Index](#11-documentation-index)
12. [Contact & License](#12-contact--license)

---

## 1. Executive Summary

> **Untuk awam:** Ini adalah "mesin penglihatan" IRIS. Ia menonton video dari kamera CCTV pabrik, mengenali orang, memeriksa apakah mereka memakai Alat Pelindung Diri (APD) — helm, rompi, sepatu, kacamata, sarung tangan — dan mendeteksi bila ada orang masuk area terlarang. Setiap pelanggaran dicatat lengkap dengan fotonya. Ia bukan aplikasi berdiri sendiri, melainkan "onderdil pintar" yang dipasang di dalam agen yang berjalan dekat kamera.

**Apa ini.** `ai-vision-worker-core` (nama paket Python: `nedo-vision-worker-core`, versi **0.3.1**) adalah **library inferensi Computer Vision**. Ia menyediakan seluruh jalur pemrosesan dari frame kamera sampai stream video ber-anotasi: baca RTSP → praproses → deteksi objek (YOLO / RF-DETR) → pelacakan objek (SFSORT) → pencocokan APD/area → simpan pelanggaran → publikasi ke RTMP.

**Kenapa ada / masalah yang dipecahkan.** Pengawasan keselamatan kerja (K3) manual tidak mungkin memantau puluhan kamera 24/7. Library ini mengotomasi deteksi pelanggaran APD dan intrusi area terlarang, memberi bukti visual, dan menekan risiko kecelakaan. Dipisah menjadi *library* agar logika CV dapat diuji, di-versi, dan dipakai ulang tanpa mengubah agen edge yang membungkusnya.

**Peran dalam ekosistem IRIS.**

| Arah | Service | Hubungan |
|---|---|---|
| **Pembungkus (host)** | `ai-vision-worker-service` | Agen edge Python yang meng-*import* library ini, mengambil perintah dari RabbitMQ, dan berbicara gRPC ke manager. Keduanya berbagi `--storage-path` (SQLite IPC). |
| **Sumber konfigurasi** | `ai-vision-manager` (.NET 9) | Menulis definisi pipeline, model, area terlarang, konfigurasi APD ke DB yang disinkronkan ke edge. |
| **Sumber video** | Kamera / `ai-vision-rtsp-server` | Stream RTSP (H.264/HEVC) yang dibaca library. |
| **Hilir video** | MediaMTX (RTMP :1935 → HLS) | Menerima stream ber-anotasi dari library untuk ditonton di frontend. |

> **Catatan hubungan worker-service ↔ worker-core:** keduanya proses terpisah namun berbagi file SQLite `config.db`. worker-service *menulis* baris pipeline; worker-core *membaca* dan menjalankannya (lihat [IPC schema contract](#ipc-worker-service--worker-core)).

### Status saat ini

| Aspek | Nilai |
|---|---|
| **Versi** | 0.3.1 (`nedo_vision_worker_core.__version__`) |
| **Bahasa / runtime** | Python ≥3.8 (image produksi: `python:3.11-slim`) |
| **Ukuran kode** | ~10.500 LOC Python (`nedo_vision_worker_core/`) |
| **Detektor** | YOLO (Ultralytics ≥8) default; RF-DETR (`rfdetr==1.6.0`) opsional |
| **Tracker** | SFSORT (bawaan, `tracker/SFSORT.py`) |
| **Penyimpanan** | SQLite (3 DB) + Alembic auto-migration |
| **Port keluar utama** | RTMP **:1935** (push ber-anotasi), RTSP inbound (pull kamera, TCP) |
| **Registry image** | `ghcr.io/tekinfopg/*` (Watchtower auto-update) |
| **IT Maturity** | **Level 2 → 3** (Managed → Defined), lihat [§5](#5-tata-kelola--kematangan) |

### Stack ringkas

```
Kategori     │ Value
─────────────┼──────────────────────────────────────────────────────────
Detektor     │ Ultralytics YOLO (.pt)  ·  RF-DETR 1.6.0 (weights .pth)
Tracker      │ SFSORT (IoU + linear assignment via lap / scipy)
Geometri     │ Shapely ≥1.8 (polygon area terlarang)  ·  NumPy  ·  SciPy
Vision I/O   │ OpenCV ≥4.6 (VideoCapture RTSP/TCP)  ·  Pillow
Deep learn   │ PyTorch 2.6.0 + torchvision 0.21.0 (cu124)
Video keluar │ FFmpeg (h264_nvenc → libx264 fallback) → RTMP
Database     │ SQLite  ·  SQLAlchemy ≥1.4  ·  Alembic ≥1.8 (autogenerate)
Sistem       │ psutil (resource monitor)  ·  ffmpeg (subprocess)
Kemasan      │ Docker (amd64 + Jetson/L4T ARM64)  ·  CLI nedo-worker-core
```

---

## 2. Proses Bisnis (BPMN)

> **Untuk awam & analis:** Diagram di bawah menunjukkan siapa melakukan apa, dari operator K3 yang menggambar "garis area terlarang" di peta kamera, sampai sistem otomatis mendeteksi pelanggaran dan mengirim buktinya. Setiap belah ketupat (◇) adalah titik keputusan "ya/tidak".

```mermaid
flowchart TB
    subgraph HSE["🦺 Operator HSE / Admin"]
        A1([Mulai]) --> A2["Definisikan pipeline kamera<br/>+ area terlarang / grup APD"]
    end
    subgraph MGR["🗄️ Manager (.NET 9)"]
        A2 --> B1["Simpan konfigurasi<br/>+ model AI ke DB"]
        B1 --> B2["Sinkron ke edge<br/>(config.db)"]
    end
    subgraph CORE["🧠 worker-core (library ini)"]
        B2 --> C1["PipelineSyncThread<br/>baca pipeline aktif"]
        C1 --> C2["Buka stream RTSP<br/>+ muat model .pt"]
        C2 --> C3["Ambil frame → praproses ROI"]
        C3 --> C4["Deteksi objek<br/>(YOLO / RF-DETR)"]
        C4 --> C5["Lacak objek → UUID<br/>(SFSORT)"]
        C5 --> D1{Fitur aktif?}
        D1 -->|PPE| C6["Cocokkan APD ke orang<br/>(IoU coverage)"]
        D1 -->|Human| C7["Cek titik pusat orang<br/>di polygon area"]
        C6 --> D2{Ada pelanggaran?}
        C7 --> D2
        D2 -->|Tidak| C3
        D2 -->|Ya| C8["Simpan deteksi + foto ke DB<br/>+ emit callback / webhook"]
        C8 --> C9["Gambar anotasi → push RTMP"]
        C9 --> C3
    end
    subgraph OUT["📺 Hilir"]
        C9 --> E1["MediaMTX → HLS<br/>ditonton di frontend"]
        C8 --> E2["worker-service → manager<br/>notifikasi / dashboard"]
    end
    E2 --> Z([Selesai])
```

**Langkah proses.**

| No | Aktivitas | Aktor | Sistem / Tool | Output |
|---|---|---|---|---|
| 1 | Definisikan pipeline kamera, area terlarang (polygon), grup APD | Operator HSE | Frontend → Manager | Konfigurasi pipeline |
| 2 | Simpan konfigurasi + model AI, sinkron ke edge | Manager | PostgreSQL → SQLite `config.db` | Baris pipeline di edge |
| 3 | Baca pipeline aktif tiap 5 detik | worker-core | `PipelineSyncThread` | Daftar pipeline berjalan |
| 4 | Buka RTSP + muat model `.pt`/RF-DETR | worker-core | OpenCV, Ultralytics/RF-DETR | Stream + detektor siap |
| 5 | Ambil frame, potong ROI, resize | worker-core | `PipelinePrepocessor` (OpenCV) | Frame ternormalisasi |
| 6 | Deteksi objek (orang + atribut APD) | worker-core | `DetectionManager` (YOLO/RF-DETR) | Bounding box + label |
| 7 | Lacak objek antar-frame → UUID | worker-core | `SFSORT` / `TrackerManager` | Objek ber-ID stabil |
| 8 | Cocokkan APD (IoU) / cek area (Shapely) | worker-core | `PersonAttributeMatcher`, `PersonRestrictedAreaMatcher` | Pelanggaran / kepatuhan |
| 9 | Simpan deteksi + foto, emit callback | worker-core | SQLite, `DetectionCallbackManager` | Rekord + bukti visual |
| 10 | Gambar anotasi, push ke RTMP | worker-core | `FrameDrawer`, `RTMPStreamer` (FFmpeg) | Stream ber-anotasi |

---

## 3. Arsitektur

### 3.1 High-level component

> **Untuk awam:** kotak-kotak di bawah adalah bagian-bagian mesin. Panah menunjukkan arah data mengalir, lengkap dengan "jalur/protokol" yang dipakai (mis. RTSP untuk video kamera, RTMP untuk video hasil).

```mermaid
flowchart LR
    CAM["📹 Kamera CCTV<br/>H.264 / HEVC"]
    subgraph HOST["Proses edge (worker-service membungkus worker-core)"]
        subgraph WC["🧠 ai-vision-worker-core (library)"]
            SYNC["PipelineSyncThread<br/>+ StreamSyncThread<br/>(poll DB 5s)"]
            VS["VideoStream<br/>(OpenCV, RTSP/TCP)"]
            PP["PipelineProcessor<br/>(1 per kamera, 2 thread)"]
            DM["DetectionManager<br/>+ DetectorCache"]
            TR["TrackerManager<br/>(SFSORT)"]
            RTMP["RTMPStreamer<br/>(FFmpeg)"]
        end
        DB[("SQLite<br/>default / config / logging")]
    end
    MGR["🗄️ ai-vision-manager<br/>.NET 9"]
    MTX["📺 MediaMTX<br/>RTMP→HLS"]

    CAM -->|"RTSP :554 (TCP)"| VS
    VS --> PP
    PP --> DM
    PP --> TR
    PP --> RTMP
    RTMP -->|"RTMP :1935 (push, ber-anotasi)"| MTX
    MGR -.->|"sinkron via worker-service<br/>(gRPC :50051 + RabbitMQ :5672)"| DB
    DB <--> SYNC
    SYNC --> PP
    PP -->|"tulis deteksi + foto"| DB

    style WC fill:#e3f2fd
    style DM fill:#00bcd4,color:#fff
    style TR fill:#ff9800,color:#fff
    style RTMP fill:#f44336,color:#fff
```

> **Catatan spesialis:** library ini tidak membuka socket server sendiri. Port gRPC `:50051` dan RabbitMQ `:5672` adalah milik `worker-service` yang membungkusnya; worker-core hanya menarik RTSP (klien) dan mendorong RTMP (klien). Baris `EXPOSE` di `Dockerfile` (8554/50051/1935) bersifat deklaratif untuk konteks co-location, bukan listener yang dibuka library.

### 3.2 Alur inferensi per frame (sequence)

> **Untuk awam:** ini "resep" yang dijalankan mesin untuk setiap gambar dari kamera — mengapa satu frame bisa menjadi satu catatan pelanggaran.

```mermaid
sequenceDiagram
    autonumber
    participant VS as VideoStream<br/>(reader thread)
    participant PP as PipelineProcessor<br/>(main loop)
    participant Q as frame_queue<br/>(maxsize=1)
    participant DW as detection_worker<br/>(thread)
    participant PRE as Preprocessor (ROI)
    participant DM as DetectionManager<br/>(YOLO/RF-DETR)
    participant DP as DetectionProcessor<br/>(PPE / Human)
    participant TR as TrackerManager (SFSORT)
    participant DB as SQLite
    participant RT as RTMPStreamer

    VS->>VS: cap.read() (drain stale → frame terbaru)
    PP->>VS: get_frame()
    PP->>Q: put((frame, capture_time)) — drop bila penuh
    DW->>Q: get() (throttle: detection_interval)
    DW->>PRE: apply(frame) → crop ROI + resize
    DW->>DM: detect_objects(frame, threshold, class_thresholds)
    DM-->>DW: [{label, confidence, bbox}]
    DW->>PRE: revert_detections_bboxes() → koordinat frame asli
    DW->>DP: process(detections, dimension)
    Note over DP: PPE → PersonAttributeMatcher (IoU coverage)<br/>Human → PersonRestrictedAreaMatcher (Shapely)
    DP-->>DW: matched_results (orang + atribut)
    DW->>TR: track_objects(matched_results)
    TR-->>DW: objek ber-UUID + count atribut
    DW->>DB: save_ppe_detection / save_area_violation (+ foto)
    PP->>RT: send_frame(annotated) → FFmpeg → RTMP
```

**Poin desain kunci (terverifikasi di kode):**
- **Dua thread per pipeline.** Main loop membaca & menggambar frame; `detection_worker` melakukan inferensi. Dijembatani `frame_queue` berukuran **1** — frame lama dibuang agar preview selalu segar (`WORKER_DROP_STALE=1`).
- **Throttle inferensi.** `detection_interval = 1/fps` dari config `processing_sped`; inferensi hanya jalan bila interval terlampaui, hemat GPU/CPU.
- **capture_time dibawa bersama frame** — analyzer kecepatan memakai waktu akuisisi, bukan wall-clock proses (agar jarak/waktu tak korup).

### 3.3 Data model (SQLite)

> **Untuk awam:** tempat mesin menyimpan hasil kerjanya — daftar pelanggaran, foto, dan konfigurasi. Terpisah tiga "buku" (database) agar tulisan operasional tak mengganggu log.

Tiga file SQLite di `<storage-path>/sqlite/` (`bind_key`): `default.db`, `config.db`, `logging.db` (+ bind `auth`). Skema di-*auto-migrate* saat startup oleh `DatabaseManager.synchronize()` memakai Alembic `produce_migrations` — **operasi destruktif (DROP) diblokir** demi keamanan data (dua proses berbagi DB).

```mermaid
erDiagram
    worker_source ||--o{ worker_source_pipeline : "punya"
    worker_source_pipeline ||--o{ worker_source_pipeline_config : "dikonfigurasi"
    worker_source_pipeline ||--o{ worker_source_pipeline_detection : "menghasilkan"
    worker_source_pipeline ||--o{ ppe_detections : "PPE"
    worker_source_pipeline ||--o{ restricted_area_violation : "area"
    worker_source_pipeline ||--o{ speed_detections : "kecepatan"
    worker_source_pipeline ||--o{ clump_detections : "gumpalan"
    worker_source_pipeline ||--o{ color_anomaly_detections : "anomali warna"
    ai_model ||--o{ ppe_detection_labels : "label"
    ai_model {
        string id
        string name
        string type "yolo | rf_detr"
        string file ".pt / .pth"
        string download_status
    }
    ppe_detections {
        string pipeline_id
        string person_uuid
        json attributes
        string image_path
    }
    restricted_area_violation {
        string pipeline_id
        json polygon
        string image_path
    }
```

Tabel lain: `worker_source_pipeline_debug`, `dataset_sources`, `server_config`, `user`, `auth`, `logs`.

<a name="ipc-worker-service--worker-core"></a>
> **Spesialis — IPC contract:** `PipelineSyncThread._verify_schema_contract()` mem-*fail-fast* saat startup: ia membandingkan kolom `worker_source_pipeline` di DB nyata dengan ORM worker-core, dan melempar error jelas bila schema drift antara writer (worker-service) dan reader (worker-core). Mencegah kegagalan misterius 5 detik dalam loop polling.

### 3.4 Ports & Protokol

| Port | Protokol | Arah | Tujuan | Tool / Library |
|---|---|---|---|---|
| **554** (default RTSP) | RTSP over **TCP** | Masuk (pull) | Baca stream kamera | OpenCV `VideoCapture` + FFmpeg backend (`OPENCV_FFMPEG_CAPTURE_OPTIONS=rtsp_transport;tcp`) |
| **1935** | RTMP (FLV) | Keluar (push) | Kirim stream ber-anotasi ke MediaMTX | FFmpeg subprocess (`h264_nvenc` → `libx264`) |
| N/A (file) | SQLite | Lokal | Konfigurasi & rekord deteksi | SQLAlchemy + Alembic |
| 50051 / 5672 | gRPC / AMQP | — | *Milik worker-service* (bukan library ini) | — |

### 3.5 Tech Stack & Rationale

| Pilihan | Versi | Alasan |
|---|---|---|
| **Ultralytics YOLO** | ≥8.0.0 | Detektor default; cepat, format `.pt`, API `predict` seragam, mendukung ekspor TensorRT (roadmap) |
| **RF-DETR** | **==1.6.0** (di-pin) | Detektor transformer opsional untuk akurasi lebih. **Di-pin keras:** 1.8.x memuat checkpoint hanya sebagian → *zero detection* diam-diam; <1.6 gagal import. 1.6.0 memuat utuh |
| **SFSORT** | vendored | Tracker ringan berbasis IoU + linear assignment; menghasilkan track_id stabil yang dipetakan ke UUID |
| **Shapely** | ≥1.8.0 | Uji titik-dalam-polygon untuk area terlarang (presisi geometris) |
| **PyTorch** | 2.6.0 (cu124) | Backend inferensi. Di-pin di Dockerfile ke build cu124 — requirements tak di-pin akan menarik cu13x yang butuh driver lebih baru → fallback CPU diam-diam |
| **OpenCV** | ≥4.6.0 | Decode video, praproses, gambar |
| **FFmpeg** | sistem | Encode & push RTMP; NVENC untuk bebaskan CPU (bottleneck) |
| **SQLite + Alembic** | ≥1.4 / ≥1.8 | Zero-config, cocok edge; auto-migration tanpa file migrasi manual |

---

## 4. Model & Komponen ML (spesialis)

> **Untuk awam:** bagian ini menjelaskan tiga "kecerdasan" mesin — *melihat* (deteksi), *mengingat* siapa-siapa antar-frame (pelacakan), dan *menilai* apakah APD dipakai / area dilanggar (pencocokan).

### 4.1 Model deteksi yang didukung

| Model | Type (DB) | Format berkas | Input | Cara load | Catatan |
|---|---|---|---|---|---|
| **YOLO** (Ultralytics) | `yolo` | `.pt` | BGR langsung (tanpa cvtColor) | `YOLO(path)` | Default. Env `YOLO_HALF`, `YOLO_IMGSZ`, `YOLO_DEVICE` |
| **RF-DETR** | `rf_detr` | weights `.pth` | RGB (`cvtColor BGR→RGB`) | `RFDETRBase(pretrain_weights=...)` + `optimize_for_inference()` | class_id 0-based (sama seperti YOLO) |

Pemilihan detektor per baris `ai_model` di DB (kolom `type`). `DetectionManager` menyembunyikan keduanya di balik `BaseDetector.detect_objects()` → keluaran seragam `[{label, confidence, bbox}]`.

**Shared DetectorCache.** Pipeline yang memakai berkas model **sama** berbagi **satu** instance detektor (satu salinan bobot di VRAM), refcounted, dengan **satu lock inferensi per model** — karena forward pass torch tak aman dari dua thread sekaligus. Aktif default (`DETECTOR_SHARED_CACHE=1`); VRAM dibebaskan saat pipeline terakhir melepas.

### 4.2 Tracking — SFSORT

| Parameter | Nilai default | Fungsi |
|---|---|---|
| `high_th` | 0.6 | Ambang asosiasi tahap pertama (deteksi confidence tinggi) |
| `match_th_first` | 0.67 | Ambang IoU pencocokan tahap-1 |
| `match_th_second` | 0.2 | Ambang IoU pencocokan tahap-2 (recovery) |
| `new_track_th` | 0.7 | Ambang membuat track baru |
| `marginal_timeout` / `central_timeout` | 7 / 30 | Umur track (tepi vs tengah frame) |

**Cara kerja (`TrackerManager`):**
1. `SFSORT.update(bboxes, confidences)` mengembalikan `[bbox, track_id]` via matriks biaya IoU + linear assignment (`lap`, fallback `scipy.optimize.linear_sum_assignment`).
2. Tiap `track_id` dipetakan ke **UUID stabil** (`_assign_uuid`) — identitas objek yang bertahan antar-frame.
3. **Presence counting:** tiap atribut (mis. `no_helmet`) dihitung berapa frame berturut muncul; ini yang membuat pelanggaran "matang" (stability gate `count == 5` di sink) sebelum disimpan — meredam flicker deteksi.
4. Track basi dibersihkan setelah `track_timeout_seconds` (default 5s).

> **Spesialis — kecocokan bbox:** pencocokan hasil track ↔ deteksi asli memakai kunci koordinat piksel **dibulatkan int** (`_bbox_key`), bukan kesetaraan float; revert ROI/resize membuat box tak lagi float32-representable sehingga lookup tuple eksak akan gagal.

### 4.3 IoU matching APD (PPEDetectionProcessor)

> **Untuk awam:** "apakah helm hijau ini milik orang A atau B?" Diputuskan dari seberapa besar kotak helm tumpang-tindih dengan kotak orang.

- `PersonAttributeMatcher.match_persons_with_attributes(persons, attributes, coverage_threshold=0.5)` menghitung **coverage** (rasio irisan bbox atribut terhadap orang, `BoundingBoxMetrics.compute_coverage`). Atribut dengan coverage ≥ ambang dilekatkan ke orang tsb.
- **Exclusive groups** — pasangan saling-eksklusif `(helmet, no_helmet)`, `(vest, no_vest)`, `(gloves, no_gloves)`, `(goggles, no_goggles)`, `(boots, no_boots)`. Untuk tiap grup, hanya atribut confidence tertinggi yang menang.
- **Multi-instance classes** — `boots`, `gloves`, `goggles` boleh muncul >1 per orang (dua sepatu). Diperlakukan khusus agar tak saling menimpa.
- Label pelanggaran = prefiks `no_` (`no_helmet`, `no_vest`, …); kepatuhan = tanpa prefiks. Grup APD & ambang confidence per-grup dikonfigurasi dari `ai_model.ppe_groups` + config pipeline.

### 4.4 ROI polygon area terlarang (HumanDetectionProcessor)

> **Untuk awam:** operator menggambar zona berbahaya sebagai poligon di layar; mesin memeriksa apakah **titik pusat** kotak orang jatuh di dalam poligon itu.

- Area terlarang disimpan sebagai koordinat **ternormalisasi** `{x, y}` (0–1), lalu diskalakan ke piksel sesuai dimensi frame → `shapely.geometry.Polygon`.
- `PersonRestrictedAreaMatcher` menghitung **center point** bbox orang (`Point`) dan menguji `polygon.contains(point)`. Bila di dalam → atribut `in_restricted_area` (confidence 1.0).
- Polygon di-*cache* dan hanya dibangun ulang saat config atau dimensi frame berubah (hemat komputasi).

### 4.5 Analyzer tambahan (di luar PPE/Human)

| Analyzer | Code | Input | Deteksi | Repository |
|---|---|---|---|---|
| **ColorAnomalyAnalyzer** | `color` | piksel frame | anomali warna (kontaminasi di belt) | `ColorAnomalyRepository` |
| **ClumpAnalyzer** | `clump` | piksel frame | gumpalan/lump di conveyor | `ClumpRepository` |
| **SpeedAnalyzer** | `speed` | objek ter-*track* | kecepatan kendaraan (2 gate line) | `SpeedRepository` |

Ketiganya berjalan **independen** dari slot `detection_processor` (PPE/Human), disimpan pada *rising edge* event saja (bukan tiap frame) untuk menghindari ribuan baris duplikat per kejadian.

### 4.6 Komponen inti

| Komponen | Berkas | Peran |
|---|---|---|
| `CoreService` | `core_service.py` | Orkestrator: init DB, drawing assets, sync thread, callback manager |
| `PipelineSyncThread` | `pipeline/PipelineSyncThread.py` | Poll DB tiap 5s, start/stop pipeline sesuai status |
| `PipelineManager` | `pipeline/PipelineManager.py` | ThreadPoolExecutor (maks 50) menjalankan tiap `PipelineProcessor` |
| `PipelineProcessor` | `pipeline/PipelineProcessor.py` | Loop per kamera: baca→gambar→queue; thread inferensi terpisah |
| `DetectionManager` / `DetectorCache` | `detection/` | Muat & jalankan detektor, cache berbagi VRAM |
| `TrackerManager` / `SFSORT` | `tracker/` | Pelacakan multi-objek → UUID |
| `PersonAttributeMatcher` | `util/` | IoU coverage APD ↔ orang |
| `PersonRestrictedAreaMatcher` | `util/` | Titik-dalam-polygon Shapely |
| `VideoStream` | `streams/VideoStream.py` | Capture RTSP thread-safe, reconnect + recovery HEVC |
| `RTMPStreamer` | `streams/RTMPStreamer.py` | Encode FFmpeg (NVENC→x264) → push RTMP |
| `FrameDrawer` | `ai/FrameDrawer.py` | Anotasi bbox, ikon APD, polygon, trail |
| `DetectionCallbackManager` | `callbacks/` | Event bus: callback immediate / interval |
| `DatabaseManager` | `database/DatabaseManager.py` | Multi-DB SQLite + Alembic auto-migration |

---

## 5. Tata Kelola & Kematangan

> **Untuk manajemen & auditor:** bagian ini memetakan repo ke kerangka tata kelola TI standar. Pemetaan wajar berdasarkan fungsi nyata repo, bukan klaim sertifikasi.

### 5.1 COBIT 2019

| Objective | Bagaimana repo ini memenuhinya |
|---|---|
| **APO03 — Managed Enterprise Architecture** | Dipisah sebagai *library* di balik `BaseDetector` & `DetectionProcessor`; detektor/analyzer dapat ditukar tanpa mengubah pembungkus |
| **BAI03 — Managed Solutions Build** | CI (`ci.yml`) ruff + `compileall` + pytest; suite `tests/` untuk redaksi kredensial & metrik bbox |
| **BAI06 — Managed IT Changes** | Versi eksplisit (`__version__` 0.3.1) ditulis ke `.worker_core_version`; image ber-tag SHA di GHCR; Watchtower rollout terkontrol |
| **DSS01 — Managed Operations** | Auto-recovery stream (reconnect backoff, recovery HEVC), fail-fast schema contract, diagnostik stream |
| **DSS05 — Managed Security Services** | Redaksi kredensial URL RTSP/RTMP di log (`util/redact.py`); RTSP over TCP; guard DDL destruktif |
| **MEA01 — Performance Monitoring** | `PERF.md` (profil CPU/GPU terukur), `BoundingBoxMetrics`, `psutil` resource monitor, integrasi Prometheus via worker-service |

### 5.2 PMBOK / Knowledge Area

| Area | Deliverable konkret |
|---|---|
| **Scope** | README ini + `PERF.md`; batas jelas: library, bukan service |
| **Schedule** | Rilis di-versi via `__version__`; CI otomatis di push `develop`/`main` |
| **Quality** | CI gate (ruff/compile/pytest), pin dependensi kritis (rfdetr, torch), self-check di `tests/` |
| **Risk** | Pin `rfdetr==1.6.0` & torch cu124 (mitigasi "zero-detection" & fallback CPU diam-diam); guard destruktif DDL; recovery HEVC |
| **Integration** | IPC contract SQLite dengan worker-service (fail-fast verifikasi schema); callback/webhook untuk hilir |

### 5.3 IT Maturity (CMMI-style)

**Level saat ini: 2 → 3 (Managed menuju Defined).**

Proses inti sudah *repeatable* dan terkelola: build & deploy terotomasi (CI/CD + Watchtower), dependensi kritis di-pin dengan alasan terdokumentasi, ada suite test dan runbook performa (`PERF.md`), serta kontrak IPC yang divalidasi otomatis. Ini melampaui Level 2.

Yang menahan di ambang Level 3 penuh: cakupan test masih terbatas (logika murni tanpa GPU/infra; `ruff` masih `continue-on-error`), belum ada gate parity deteksi otomatis untuk perubahan model/backend, dan observability metrik masih bergantung pada worker-service (belum ada endpoint metrik in-library). **Untuk naik ke Level 3:** perluas test ke jalur inferensi (fixture frame + snapshot deteksi), jadikan lint gate blocking, dan tambah validasi parity FP32↔FP16/TensorRT sebelum rilis.

---

## 6. Struktur Repository

```
ai-vision-worker-core/
├── nedo_vision_worker_core/          # 📦 Paket library utama (~10.5k LOC)
│   ├── core_service.py               #    CoreService — orkestrator utama
│   ├── cli.py                        #    Entry point CLI (run / doctor)
│   ├── doctor.py                     #    Diagnostik sistem (CUDA, FFmpeg, dependensi)
│   ├── pipeline/                     # 🔄 Orkestrasi pipeline per kamera
│   │   ├── PipelineSyncThread.py     #    Poll DB, start/stop pipeline (+ schema contract)
│   │   ├── PipelineManager.py        #    ThreadPool menjalankan processor
│   │   ├── PipelineProcessor.py      #    Loop inti: baca→deteksi→track→gambar→RTMP
│   │   ├── PipelineConfigManager.py  #    Baca konfigurasi fitur per pipeline
│   │   └── PipelinePrepocessor.py    #    Rangkai praproses (ROI + resize)
│   ├── detection/                    # 🎯 Deteksi objek
│   │   ├── DetectionManager.py       #    Fasad detektor + serialisasi inferensi
│   │   ├── DetectorCache.py          #    Cache berbagi VRAM (refcount + lock)
│   │   ├── YOLODetector.py           #    Ultralytics YOLO (.pt)
│   │   ├── RFDETRDetector.py         #    RF-DETR (opsional)
│   │   ├── BaseDetector.py           #    Antarmuka detektor
│   │   ├── detection_processing/     #    PPEDetectionProcessor, HumanDetectionProcessor
│   │   ├── color/ · clump/ · speed/  #    Analyzer tambahan (pixel / tracked-object)
│   ├── tracker/                      # 🧭 SFSORT.py + TrackerManager.py (UUID)
│   ├── preprocessing/                # ✂️ ImageRoi, ImageResizer, Preprocessor
│   ├── util/                         # 🛠 PersonAttributeMatcher, PersonRestrictedAreaMatcher,
│   │                                 #    BoundingBoxMetrics, DrawingUtils, redact.py
│   ├── streams/                      # 📡 VideoStream, RTMPStreamer, VideoStreamManager,
│   │                                 #    SharedVideoDeviceManager, StreamSyncThread
│   ├── services/                     #    Video sharing daemon (akses device bersama)
│   ├── ai/                           # 🎨 FrameDrawer (anotasi), VideoDebugger
│   ├── callbacks/                    # 🔔 DetectionCallbackManager + tipe event
│   ├── repositories/                 # 🗄 Repository SQLite per domain (PPE, restricted, speed, …)
│   ├── models/                       #    Entity SQLAlchemy (bind: default/config/logging/auth)
│   ├── database/                     #    DatabaseManager (multi-DB + Alembic autogen)
│   ├── config/                       #    ConfigurationManager
│   └── icons/                        #    Ikon APD (helm/rompi/sepatu … hijau/merah)
├── tests/                            # 🧪 pytest (redact, bounding-box metrics)
├── Dockerfile                        # 🐳 amd64 (python:3.11-slim, torch cu124, ffmpeg)
├── Dockerfile.jetson                 #    ARM64 Jetson/L4T (l4t-pytorch base)
├── docker-compose.local.yml          #    Stack lokal
├── requirements.txt / pyproject.toml #    Manifest & pin dependensi
├── PERF.md                           # 📊 Runbook performa (CPU-bound, NVDEC, TensorRT)
├── install.sh / run.sh / *.bat       #    Skrip helper
└── .github/workflows/                #    ci.yml + docker-build-and-push.yml
```

---

## 7. Konfigurasi & Environment

> **Untuk awam:** "tombol pengatur" yang mengubah perilaku mesin tanpa mengubah kode — mis. server tujuan video, atau apakah memakai GPU.

| Nama | Wajib? | Default | Fungsi |
|---|---|---|---|
| `STORAGE_PATH` | Tidak | `data` | Direktori DB SQLite, berkas, & model (`<path>/model`, `/sqlite`, `/files`) |
| `RTMP_SERVER` | Tidak | `rtmp://localhost:1935/live` | Server RTMP tujuan stream ber-anotasi |
| `WORKER_DROP_STALE` | Tidak | `1` | `1` = buang frame basi (live, preview segar); `0` = baca paced source-fps |
| `YOLO_HALF` | Tidak | `0` | `1` = inferensi FP16 (~1.5–2× lebih cepat, **mengubah numerik** — validasi dulu) |
| `YOLO_IMGSZ` | Tidak | (default model) | Override ukuran inferensi |
| `YOLO_DEVICE` | Tidak | auto | Paksa device, mis. `0` untuk GPU |
| `DETECTOR_SHARED_CACHE` | Tidak | `1` | `1` = pipeline dengan model sama berbagi 1 detektor (hemat VRAM) |
| `OPENCV_FFMPEG_CAPTURE_OPTIONS` | (di-set internal) | `rtsp_transport;tcp` | Paksa RTSP over TCP (hindari drop paket HEVC) |

**CLI flags** (`nedo-worker-core run ...`): `--drawing-assets`, `--log-level {DEBUG,INFO,WARNING,ERROR}`, `--storage-path`, `--rtmp-server`, `--disable_video_sharing_daemon`.

---

## 8. Local Development

### Prasyarat

```bash
python --version     # >= 3.8 (produksi memakai 3.11)
ffmpeg -version      # WAJIB — RTMP push gagal tanpa ini
nvidia-smi           # opsional (GPU CUDA; jalan di CPU bila absen)
```

Dependensi sistem (lihat Dockerfile): `libopencv-dev`, `libomp-dev`, `liblapack-dev`, `libopenblas-dev`, `ffmpeg`, `git`.

### Setup & run

```bash
git clone <repo-url> ai-vision-worker-core
cd ai-vision-worker-core

python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate

# GPU (CUDA 12.4) — pin torch dulu agar tak tertarik build cu13x yang fallback CPU:
pip install torch==2.6.0 torchvision==0.21.0 --index-url https://download.pytorch.org/whl/cu124

pip install -e .            # atau: pip install -r requirements.txt

# Jalankan
nedo-worker-core run --log-level INFO --storage-path ./data \
    --rtmp-server rtmp://localhost:1935/live
# atau (cara image produksi):
python -m nedo_vision_worker_core.cli run

# Diagnostik sistem (CUDA / FFmpeg / dependensi)
nedo-worker-core doctor

# Test
pytest
```

> **Catatan:** worker-core biasanya di-*import* oleh `ai-vision-worker-service`, bukan dijalankan sendiri di produksi. Menjalankan CLI langsung berguna untuk pengembangan & debug pipeline. Ia butuh baris pipeline di `config.db` (ditulis worker-service/manager) untuk memproses kamera.

---

## 9. Deployment & CI/CD

> **Untuk awam:** setiap kali kode diperbarui, "pabrik otomatis" mengemasnya menjadi image, mengunggahnya, lalu server produksi menariknya sendiri — semua tanpa langkah manual.

```mermaid
flowchart LR
    Push["git push<br/>(develop / main)"]
    subgraph CI["ci.yml — ubuntu"]
        Ruff["ruff check<br/>(continue-on-error)"]
        Compile["compileall<br/>(py_compile)"]
        Pytest["pytest<br/>(logika murni)"]
    end
    subgraph CD["docker-build-and-push.yml — self-hosted macOS"]
        Build["docker build amd64<br/>(Colima + Rosetta)"]
        GHCR["push GHCR<br/>ghcr.io/tekinfopg/…<br/>:branch :sha :latest"]
        WA["Notifikasi WhatsApp<br/>(WAHA)"]
    end
    WT["Watchtower<br/>tarik image ~30s"]
    Prod["iris.petrokimia-gresik.com<br/>(dashboard /app)"]

    Push --> Ruff --> Compile --> Pytest
    Push --> Build --> GHCR --> WA
    GHCR --> WT --> Prod
```

- **CI** (`ci.yml`): ruff (non-blocking), `python -m compileall`, `pytest` — di GitHub-hosted `ubuntu-latest`.
- **Build/push** (`docker-build-and-push.yml`): runner **self-hosted macOS** (Mac mini), build **amd64 via Colima + Rosetta** (bukan buildx QEMU), auth GHCR inline. Tag: `:<branch>`, `:<branch>-<sha7>`, dan `:latest` (hanya `main`).
- **Rollout:** Watchtower menarik image baru (~30 detik) → produksi `iris.petrokimia-gresik.com`. Notifikasi status via WhatsApp (WAHA).
- **Jetson:** `Dockerfile.jetson` (base `l4t-pytorch`, ARM64) di-build native di device untuk deployment edge NVIDIA Jetson.

---

## 10. Observability

> **Untuk DevOps:** cara mengetahui mesin sehat atau bermasalah.

- **Logging:** terstruktur `%(asctime)s [%(levelname)s] %(message)s`; level via `--log-level`. Log noise sqlalchemy/pika/grpc/ffmpeg ditekan. Ditulis ke SQLite `logging.db` (bind `logging`).
- **Redaksi kredensial:** URL RTSP/RTMP di-*mask* (`util/redact.py`) sebelum masuk log — kredensial kamera tak bocor.
- **Diagnostik stream:** `_log_stream_diagnostics` melaporkan state, codec, umur frame terakhir, dan pola error HEVC (`cu_qp_delta`, `Could not find ref with POC`); recovery HEVC otomatis dengan cooldown.
- **Version reporting:** `.worker_core_version` ditulis ke storage bersama saat startup → worker-service melaporkannya ke manager (versi tampil di halaman).
- **Metrik performa:** lihat `PERF.md`. Profil terukur (RTX 3070 Ti): pipeline **CPU-bound** (~76% satu core), **GPU ~2–5% idle**, inferensi ~53 ms/frame, ~13 fps @960×540. Prioritas optimasi: NVDEC hw-decode → multi-core; TensorRT **ditunda** sampai GPU jadi bottleneck.
- **Health:** `nedo-worker-core doctor` untuk cek CUDA/FFmpeg/dependensi. Metrik Prometheus/Grafana disediakan oleh worker-service (library tak membuka endpoint metrik sendiri).

---

## 11. Documentation Index

| Audiens | Dokumen | Lokasi |
|---|---|---|
| **Awam / Manajemen** | Executive summary, proses bisnis (BPMN) | README ini §1–§2 |
| **Teknis (Engineer)** | Arsitektur, ports, struktur, dev setup | README ini §3, §6, §8 |
| **Spesialis (ML)** | Model, tracking SFSORT, IoU matching, ROI polygon | README ini §4 |
| **Spesialis (Perf)** | Runbook latency/throughput, NVDEC, TensorRT | [`PERF.md`](PERF.md) |
| **DevOps** | CI/CD, Docker (amd64 + Jetson), Watchtower | README ini §9, `Dockerfile*`, `.github/workflows/` |
| **Manajemen / Auditor** | COBIT / PMBOK / IT Maturity | README ini §5 |
| **Integrasi** | IPC contract SQLite worker-service ↔ core | README ini §3.3, `pipeline/PipelineSyncThread.py` |

---

## 12. Contact & License

- **Tech Lead:** Yafi Anshori
- **Organisasi GitHub:** [tekinfopg](https://github.com/tekinfopg)
- **Ekosistem:** IRIS — AI Vision Platform, PT Petrokimia Gresik
- **Produksi:** [iris.petrokimia-gresik.com](https://iris.petrokimia-gresik.com)

### License

**Proprietary** — © 2026 PT Petrokimia Gresik. Penggunaan internal. Tidak untuk distribusi publik.

---

<div align="center">

**Mesin penglihatan IRIS — menjaga keselamatan kerja di PT Petrokimia Gresik**

*Dipelihara oleh tim Tekinfo, dengan bantuan Claude Code AI*

</div>
