Files
flywms/flywms_pipeline_spec.rtf
2026-05-15 09:54:10 +02:00

422 lines
9.9 KiB
Plaintext

{\rtf1\ansi\deff0
{\fonttbl{\f0 Calibri;}{\f1 Consolas;}}
\fs24
\b Specifica pipeline realtime flywms\par\b0
\par
\b Obiettivo\par\b0
Costruire una pipeline realtime modulare per video/camera, con tre blocchi principali:\par
\par
{\f1 Capture/Preview\par
YOLO Detection\par
OCR\par}
\par
I blocchi lavorano in parallelo, ognuno al proprio ritmo. Se un blocco e' lento, non deve bloccare gli altri. I frame/ROI vecchi possono essere scartati per mantenere bassa la latenza.\par
\par
\b Principio chiave\par\b0
Non elaboriamo necessariamente tutti i frame. Elaboriamo il piu' recente disponibile quando un worker e' libero.\par
\par
{\f1 meglio saltare frame vecchi\par
che accumulare ritardo\par}
\par
\b Strutture dati\par\b0
{\f1 FramePacket\par
frame_id: intero crescente\par
timestamp: tempo acquisizione\par
frame: immagine OpenCV BGR\par
width: larghezza\par
height: altezza\par
source: video/camera\par
\par
Detection\par
class_id\par
class_name\par
confidence\par
bbox: x1, y1, x2, y2\par
\par
DetectionResult\par
frame_id\par
timestamp\par
detections: lista Detection\par
inference_ms\par
source_width\par
source_height\par
\par
RoiPacket\par
roi_id\par
source_frame_id\par
timestamp\par
class_name\par
confidence\par
bbox\par
roi_image: crop BGR copiato\par
width\par
height\par
\par
OcrResult\par
roi_id\par
source_frame_id\par
timestamp\par
text\par
raw_text\par
confidence opzionale\par
bbox\par
ocr_ms\par}
\par
\b Buffer\par\b0
Usiamo buffer thread-safe "latest", non FIFO classici.\par
\par
{\f1 LatestBuffer\par
max_size\par
put(item)\par
get_latest_blocking(stop_event)\par
get_latest_nowait()\par
stats()\par}
\par
Comportamento:\par
\par
{\f1 put(item):\par
se pieno, elimina elementi vecchi\par
inserisce item nuovo\par
notifica i worker in attesa\par
\par
get_latest_blocking():\par
se vuoto, aspetta\par
quando c'e' almeno un item:\par
prende il piu' recente\par
scarta gli elementi piu' vecchi\par
restituisce item\par}
\par
Buffer previsti:\par
\par
{\f1 frame_buffer: FramePacket\par
roi_buffer: RoiPacket\par}
\par
Risultati condivisi:\par
\par
{\f1 SharedState\par
latest_detection_result\par
latest_ocr_results\par
counters/statistiche\par}
\par
SharedState va protetto con lock breve.\par
\par
\b Thread\par\b0
{\f1 Main/Capture Thread\par
legge frame\par
genera FramePacket\par
mette FramePacket in frame_buffer\par
visualizza preview\par
opzionalmente disegna ultimi risultati disponibili\par
\par
YOLO Worker Thread\par
aspetta ultimo FramePacket\par
copia il frame fuori lock\par
esegue detection\par
salva DetectionResult in SharedState\par
crea RoiPacket per le etichette utili\par
mette RoiPacket in roi_buffer\par
aggiorna statistiche\par
\par
OCR Worker Thread\par
aspetta ultimo RoiPacket\par
analizza ROI\par
salva OcrResult in SharedState/log\par
aggiorna statistiche\par}
\par
\b Thread safety\par\b0
Il lock deve proteggere solo operazioni piccole:\par
\par
{\f1 inserire riferimento nel buffer\par
leggere riferimento dal buffer\par
aggiornare contatori\par
aggiornare latest result\par
notify/wait\par}
\par
Il lock non deve coprire:\par
\par
{\f1 cap.read()\par
frame.copy()\par
YOLO forward\par
OCR\par
cv2.imshow\par
conversioni immagine\par}
\par
\b Politica copie\par\b0
Prima versione, robusta:\par
\par
{\f1 Capture:\par
non copia ogni frame\par
mette il frame letto nel FramePacket\par
\par
YOLO:\par
prende FramePacket\par
fa frame.copy() solo del frame che analizzera'\par
\par
OCR:\par
riceve ROI gia' copiata\par}
\par
Questo evita corruzione dati e mantiene copie limitate.\par
\par
\b Politica realtime\par\b0
Se YOLO e' lento:\par
\par
{\f1 non analizza frame arretrati\par
prende il piu' recente disponibile\par}
\par
Se OCR e' lento:\par
\par
{\f1 non legge ROI vecchie\par
prende la ROI piu' recente disponibile\par}
\par
Questo riduce la latenza.\par
\par
\b Algoritmo pseudocodice - main/capture\par\b0
{\f1 inizializza config\par
carica labels\par
carica modello YOLO\par
apri video/camera\par
\par
crea stop_event\par
crea frame_buffer max_size=N\par
crea roi_buffer max_size=M\par
crea shared_state\par
\par
avvia thread yolo_worker\par
avvia thread ocr_worker\par
\par
frame_id = 0\par
\par
loop main:\par
grabbed, frame = cap.read()\par
\par
se non grabbed:\par
stop_event.set()\par
break\par
\par
frame_id += 1\par
\par
packet = FramePacket(\par
frame_id=frame_id,\par
timestamp=now(),\par
frame=frame,\par
width=frame.width,\par
height=frame.height,\par
source=source_name\par
)\par
\par
frame_buffer.put(packet)\par
\par
detection_result = shared_state.get_latest_detection()\par
ocr_results = shared_state.get_recent_ocr_results()\par
\par
display_frame = frame.copy()\par
draw detections compatibili su display_frame\par
draw OCR texts compatibili su display_frame\par
\par
cv2.imshow("Capture", display_frame)\par
\par
se premuto q:\par
stop_event.set()\par
break\par
\par
attendi chiusura worker\par
rilascia video\par
chiudi finestre\par}
\par
\b Algoritmo pseudocodice - YOLO worker\par\b0
{\f1 function yolo_worker():\par
while not stop_event:\par
packet = frame_buffer.get_latest_blocking(stop_event)\par
\par
se packet e' None:\par
continue\par
\par
local_frame = packet.frame.copy()\par
\par
t0 = now()\par
detections = run_yolo(local_frame)\par
inference_ms = now() - t0\par
\par
result = DetectionResult(\par
frame_id=packet.frame_id,\par
timestamp=now(),\par
detections=detections,\par
inference_ms=inference_ms,\par
source_width=packet.width,\par
source_height=packet.height\par
)\par
\par
shared_state.set_latest_detection(result)\par
\par
per ogni detection in detections:\par
se detection.class_name non e' "etichetta":\par
continue\par
\par
se bbox troppo piccola:\par
continue\par
\par
se OCR cooldown per questa zona non e' scaduto:\par
continue\par
\par
roi = crop local_frame usando bbox espansa\par
roi_copy = roi.copy()\par
\par
roi_packet = RoiPacket(\par
roi_id=next_roi_id(),\par
source_frame_id=packet.frame_id,\par
timestamp=now(),\par
class_name=detection.class_name,\par
confidence=detection.confidence,\par
bbox=detection.bbox,\par
roi_image=roi_copy,\par
width=roi_copy.width,\par
height=roi_copy.height\par
)\par
\par
roi_buffer.put(roi_packet)\par
\par
aggiorna statistiche yolo\par}
\par
\b Algoritmo pseudocodice - OCR worker\par\b0
{\f1 function ocr_worker():\par
while not stop_event:\par
roi_packet = roi_buffer.get_latest_blocking(stop_event)\par
\par
se roi_packet e' None:\par
continue\par
\par
t0 = now()\par
processed = preprocess_for_ocr(roi_packet.roi_image)\par
text, raw_text = run_ocr(processed)\par
ocr_ms = now() - t0\par
\par
result = OcrResult(\par
roi_id=roi_packet.roi_id,\par
source_frame_id=roi_packet.source_frame_id,\par
timestamp=now(),\par
text=text,\par
raw_text=raw_text,\par
bbox=roi_packet.bbox,\par
ocr_ms=ocr_ms\par
)\par
\par
shared_state.add_ocr_result(result)\par
\par
se text valido:\par
logga risultato\par
\par
aggiorna statistiche ocr\par}
\par
\b LatestBuffer pseudocodice\par\b0
{\f1 class LatestBuffer:\par
items = deque(maxlen=max_size)\par
condition = Condition()\par
pushed = 0\par
popped = 0\par
dropped = 0\par
\par
put(item):\par
with condition:\par
before = len(items)\par
\par
se before == max_size:\par
dropped += 1\par
\par
items.append(item)\par
pushed += 1\par
condition.notify_all()\par
\par
get_latest_blocking(stop_event):\par
with condition:\par
while items e' vuoto and not stop_event.is_set():\par
condition.wait(timeout=0.1)\par
\par
se stop_event.is_set():\par
return None\par
\par
latest = items[-1]\par
skipped = len(items) - 1\par
dropped += skipped\par
items.clear()\par
popped += 1\par
\par
return latest\par}
\par
\b SharedState pseudocodice\par\b0
{\f1 class SharedState:\par
lock\par
latest_detection_result\par
ocr_results_deque maxlen=100\par
stats\par
\par
set_latest_detection(result):\par
with lock:\par
latest_detection_result = result\par
\par
get_latest_detection():\par
with lock:\par
return latest_detection_result\par
\par
add_ocr_result(result):\par
with lock:\par
ocr_results_deque.append(result)\par
\par
get_recent_ocr_results():\par
with lock:\par
return copia lista risultati recenti\par}
\par
\b Statistiche minime\par\b0
Da stampare ogni N secondi:\par
\par
{\f1 capture_fps\par
yolo_fps\par
ocr_fps\par
frame_buffer pushed/popped/dropped\par
roi_buffer pushed/popped/dropped\par
avg_yolo_ms\par
avg_ocr_ms\par
last_frame_id\par
last_yolo_frame_id\par
last_ocr_frame_id\par
latency_yolo_ms\par
latency_ocr_ms\par}
\par
\b Configurazione iniziale\par\b0
{\f1 --video testhd.mp4\par
--frame-buffer-size 10\par
--roi-buffer-size 20\par
--min-confidence 0.30\par
--label-class etichetta\par
--max-roi-per-frame 2\par
--ocr-min-digits 2\par
--preview-width 1280\par
--backend cpu|cuda|cuda-fp16\par
--debug-yolo-window\par
--debug-ocr-window\par}
\par
\b Prima versione: cosa evitare\par\b0
Per la prima versione eviterei:\par
\par
{\f1 Dear PyGui\par
PaddleOCR\par
modello YOLO moderno\par
tracking complesso\par}
\par
Prima validiamo la pipeline con OpenCV window + YOLO attuale + OCR attuale. Poi sostituiamo i motori uno alla volta.\par
\par
\b Decisione architetturale\par\b0
Questa pipeline privilegia:\par
\par
{\f1 bassa latenza\par
fluidita' realtime\par
uso controllato della memoria\par
misurabilita'\par}
\par
e sacrifica:\par
\par
{\f1 analisi completa di ogni frame\par
ordine perfetto di output per ogni frame\par}
\par
Per un sistema operativo realtime e' il compromesso giusto.\par
}