Zawartość: - Koncepcja 0.1 oraz rozszerzona Koncepcja 0.2 (model domenowy, architektura, mapowanie cel→metryki→usługi AI, wymienialność modeli, model danych) - Katalog 15 celów analiz kognitywnych - Specyfikacja procesu AI + opis procesów źródłowych - Pytania do koncepcji - Materiały źródłowe: info.md, n8n.json Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
392 lines
16 KiB
Markdown
392 lines
16 KiB
Markdown
# Specyfikacja procesu: analiza uwagi wzrokowej kreacji reklamowych
|
||
|
||
> Opis **logiki procesu** niezależny od narzędzia orkiestrującego, przeznaczony do
|
||
> implementacji jako komponent oprogramowania (np. usługa backendowa / pipeline).
|
||
> Definiuje: dane wejściowe, kontrakty zewnętrznych usług AI, kolejność i równoległość
|
||
> wywołań, model danych wynikowych oraz logikę prezentacji.
|
||
|
||
---
|
||
|
||
## 1. Cel i zakres
|
||
|
||
System przyjmuje **pojedynczy obraz** (kreację reklamową / grafikę) i zwraca zestaw analiz:
|
||
|
||
1. **Rozumienie zawartości** — jakie obiekty są na obrazie i gdzie się znajdują.
|
||
2. **Predykcja uwagi wzrokowej (saliency)** — gdzie skupi się wzrok odbiorcy; trzy
|
||
niezależne modele dla walidacji krzyżowej.
|
||
|
||
Wynikiem są dane (lista obiektów z ramkami, mapy uwagi) oraz pochodne wizualizacje.
|
||
Proces jest **bezstanowy** — jedno wejście (obraz) → jeden komplet wyników.
|
||
|
||
---
|
||
|
||
## 2. Architektura logiczna
|
||
|
||
```
|
||
┌────────────────────┐
|
||
│ Wejście: obraz │
|
||
└─────────┬──────────┘
|
||
▼
|
||
┌────────────────────┐
|
||
│ Preprocessing │ bytes + base64 + mime + (w,h)
|
||
└─────────┬──────────┘
|
||
│
|
||
┌──────────────┼───────────────┬───────────────┬───────────────┐
|
||
▼ (łańcuch A) ▼ (B) ▼ (C) ▼ (D)
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ LLaVA │ │ DeepGaze │ │ UNISAL │ │ ASM │
|
||
│ (etykiety)│ │(saliency)│ │(saliency)│ │(2× saliency)
|
||
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
|
||
▼ │ │ │
|
||
┌──────────────┐ │ │ │
|
||
│ Grounding │ │ │ │
|
||
│ DINO (boxy) │ │ │ │
|
||
└────┬─────────┘ │ │ │
|
||
│ ▼ ▼ ▼
|
||
▼ ┌──────────────────────────────────────┐
|
||
┌──────────────┐ │ Agregacja wyników saliency │
|
||
│ Wynik: │ └──────────────────┬───────────────────┘
|
||
│ detekcja │ ▼
|
||
└──────────────┘ ┌──────────────────────────┐
|
||
│ Artefakty wyjściowe: │
|
||
│ • overlay boxów │
|
||
│ • nakładka heatmapy │
|
||
│ • porównanie 2×2 │
|
||
└──────────────────────────┘
|
||
```
|
||
|
||
**Kluczowe zależności:**
|
||
- **Łańcuch A jest sekwencyjny:** Grounding DINO potrzebuje etykiet z LLaVA.
|
||
- **Zadania B, C, D oraz cały łańcuch A są względem siebie niezależne** → wykonywane równolegle.
|
||
- Punkt agregacji czeka na zakończenie wymaganych zadań przed złożeniem danego artefaktu.
|
||
|
||
---
|
||
|
||
## 3. Dane wejściowe i preprocessing
|
||
|
||
**Wejście:** plik graficzny (PNG lub JPEG).
|
||
|
||
**Preprocessing** — z jednego uploadu przygotuj reprezentację współdzieloną przez wszystkie usługi:
|
||
|
||
```
|
||
ImageInput {
|
||
bytes : binary // oryginalne bajty pliku (do multipart/form-data)
|
||
base64 : string // base64 bez prefiksu data: (do osadzania i obliczeń)
|
||
mime : "image/png" | "image/jpeg"
|
||
width : int
|
||
height : int
|
||
}
|
||
```
|
||
|
||
- `mime`, `width`, `height` ustal **standardową biblioteką** do obrazów
|
||
(np. Pillow / `image-size` / `sharp`).
|
||
> Uwaga: pierwotna implementacja parsowała nagłówki ręcznie (PNG: `width@offset16`,
|
||
> `height@offset20` jako uint32 BE; JPEG: skan markerów `SOF0 0xC0` / `SOF2 0xC2`).
|
||
> W docelowym kodzie użyj gotowej biblioteki — wymiary i tak są potrzebne tylko do
|
||
> pozycjonowania ramek i renderowania (sekcja 7).
|
||
- `base64` służy do: (a) osadzania oryginału w wizualizacjach, (b) blendu pikselowego.
|
||
- `bytes` służy do wszystkich wywołań usług (pole multipart `image` lub `file`).
|
||
|
||
---
|
||
|
||
## 4. Wewnętrzny model danych (wyników)
|
||
|
||
```
|
||
DetectionResult {
|
||
objects : [ { object_type: string,
|
||
bbox_xyxy: [x1, y1, x2, y2], // piksele, układ XYXY
|
||
score: float } ] // 0..1
|
||
model_id? : string
|
||
threshold? : float
|
||
}
|
||
|
||
SaliencyMap {
|
||
source : "deepgaze" | "unisal" | "asm_detailed" | "asm_general"
|
||
image_base64 : string
|
||
mime : string
|
||
}
|
||
|
||
AnalysisResult {
|
||
image : ImageInput
|
||
detection : DetectionResult
|
||
saliency : SaliencyMap[] // 4 pozycje: deepgaze, unisal, asm_detailed, asm_general
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Zewnętrzne usługi AI — kontrakty API
|
||
|
||
Wszystkie usługi to **HTTP POST** z ciałem `multipart/form-data` zawierającym obraz.
|
||
Adresy hostów i token pochodzą z konfiguracji / magazynu sekretów (sekcja 8).
|
||
Każde wywołanie powinno mieć skończony **timeout** i być **odporne na błędy** (sekcja 9).
|
||
|
||
Kształty odpowiedzi oznaczone „(wnioskowane)" odtworzono na podstawie sposobu konsumpcji
|
||
danych — przy implementacji potwierdź je z dokumentacją/realnymi odpowiedziami usług.
|
||
|
||
### 5.1. LLaVA — ekstrakcja typów obiektów
|
||
|
||
Multimodalny model wizualno-językowy; zwraca listę kategorii obiektów widocznych na obrazie.
|
||
|
||
```
|
||
POST http://{LLAVA_HOST}/llava-api/v1/get-object-types
|
||
Headers:
|
||
accept: application/json
|
||
Authorization: Bearer {TOKEN}
|
||
Body (multipart/form-data):
|
||
temperature = 0.2 // niska → powtarzalność
|
||
top_p = 0.9
|
||
image = <plik>
|
||
|
||
Odpowiedź (wnioskowane):
|
||
{ "labels": ["person", "bottle", "logo", ...] }
|
||
```
|
||
|
||
**Transformacja po stronie klienta:** połącz `labels` w jeden ciąg rozdzielony przecinkami
|
||
(`"person, bottle, logo"`) — wejście dla Grounding DINO.
|
||
|
||
### 5.2. Grounding DINO — detekcja obiektów (bounding boxy)
|
||
|
||
Otwarto-zbiorowa (zero-shot) detekcja sterowana tekstem: dla podanych etykiet zwraca ramki.
|
||
|
||
```
|
||
POST http://{GROUNDING_DINO_HOST}/grounding-dino-api/v1/get-objects-bboxes
|
||
Headers:
|
||
accept: application/json
|
||
Authorization: Bearer {TOKEN}
|
||
Body (multipart/form-data):
|
||
object_types = "{labels_z_LLaVA}" // np. "person, bottle, logo"
|
||
max_objects = 50
|
||
base64 = false
|
||
image = <plik>
|
||
|
||
Odpowiedź (wnioskowane):
|
||
{
|
||
"objects_bboxes": [
|
||
{ "object_type": "logo",
|
||
"bbox_coords_xyxy": [x1, y1, x2, y2],
|
||
"score": 0.87 }
|
||
],
|
||
"model_id": "...", // opcjonalnie
|
||
"threshold": 0.3, // opcjonalnie
|
||
"width": 839, // opcjonalnie (fallback wymiarów)
|
||
"height": 1190
|
||
}
|
||
```
|
||
|
||
### 5.3. DeepGaze — mapa uwagi (saliency)
|
||
|
||
Predykcja rozkładu uwagi wzrokowej; zwraca heatmapę zakodowaną base64.
|
||
|
||
```
|
||
POST http://{DEEPGAZE_HOST}/v1/predict-heatmap
|
||
Headers:
|
||
accept: application/json
|
||
Authorization: Bearer {TOKEN}
|
||
Body (multipart/form-data):
|
||
image = <plik>
|
||
|
||
Odpowiedź (wnioskowane):
|
||
{ "heatmap_b64": "<base64>",
|
||
"heatmap_mime": "image/png",
|
||
"width": 839, "height": 1190,
|
||
"model": "deepgaze..." }
|
||
```
|
||
|
||
→ mapuj na `SaliencyMap{ source:"deepgaze", image_base64:heatmap_b64, mime:heatmap_mime }`.
|
||
|
||
### 5.4. UNISAL — mapa uwagi (saliency, model porównawczy)
|
||
|
||
```
|
||
POST http://{UNISAL_HOST}/unisal/process-image?as_base64=true
|
||
Headers:
|
||
accept: application/json
|
||
X-API-Key: {TOKEN}
|
||
Body (multipart/form-data):
|
||
file = <plik>
|
||
|
||
Odpowiedź (wnioskowane):
|
||
{ "data": "<base64>", "mimetype": "image/jpeg" }
|
||
```
|
||
|
||
→ `SaliencyMap{ source:"unisal", image_base64:data, mime:mimetype }`.
|
||
|
||
### 5.5. ASM (asmModel) — mapy uwagi z warunkowaniem promptem
|
||
|
||
Model centralny: zwraca **dwie** mapy uwagi (szczegółową i ogólną), warunkowane tekstowym
|
||
promptem osadzającym kontekst marketingowy.
|
||
|
||
```
|
||
POST http://{ASM_HOST}/asmModel/process-image
|
||
?prompt=Describe the image in the style of a polished marketing ad.
|
||
&as_raw_output=false
|
||
&max_new_tokens=100
|
||
Headers:
|
||
accept: application/json
|
||
X-API-Key: {TOKEN}
|
||
Body (multipart/form-data):
|
||
file = <plik>
|
||
|
||
Odpowiedź (wnioskowane):
|
||
{ "image_detailed": "<base64>",
|
||
"image_general": "<base64>",
|
||
"mimetype": "image/jpeg" }
|
||
```
|
||
|
||
→ dwa wpisy: `SaliencyMap{source:"asm_detailed",...}` oraz `SaliencyMap{source:"asm_general",...}`.
|
||
|
||
> **Uwaga:** architektura ASM nie wynika z kontraktu — to wewnętrzny mikroserwis.
|
||
> `prompt` i `max_new_tokens` sugerują model multimodalny warunkowany językiem.
|
||
> Prompt potraktuj jako **parametr konfigurowalny**, nie stałą.
|
||
|
||
---
|
||
|
||
## 6. Orkiestracja
|
||
|
||
Logika sterująca (zastępuje przepływ narzędzia) — uruchom niezależne gałęzie równolegle,
|
||
zachowując jedyną zależność LLaVA → Grounding DINO.
|
||
|
||
```python
|
||
async def analyze(image_bytes) -> AnalysisResult:
|
||
img = preprocess(image_bytes) # bytes, base64, mime, w, h
|
||
|
||
async def detection_chain():
|
||
labels = await llava_object_types(img.bytes) # 5.1
|
||
labels_str = ", ".join(labels)
|
||
return await grounding_dino(img.bytes, labels_str) # 5.2
|
||
|
||
# gałęzie niezależne — równolegle
|
||
detection, deepgaze, unisal, asm = await gather(
|
||
detection_chain(), # łańcuch A (sekwencyjny wewnątrz)
|
||
deepgaze_heatmap(img.bytes), # B
|
||
unisal_saliency(img.bytes), # C
|
||
asm_saliency(img.bytes, PROMPT), # D
|
||
return_exceptions=True, # patrz sekcja 9
|
||
)
|
||
|
||
saliency = [
|
||
SaliencyMap("deepgaze", deepgaze.heatmap_b64, deepgaze.heatmap_mime),
|
||
SaliencyMap("unisal", unisal.data, unisal.mimetype),
|
||
SaliencyMap("asm_detailed", asm.image_detailed, asm.mimetype),
|
||
SaliencyMap("asm_general", asm.image_general, asm.mimetype),
|
||
]
|
||
return AnalysisResult(img, detection, saliency)
|
||
```
|
||
|
||
**Zasady:**
|
||
- Cztery gałęzie startują jednocześnie; całkowity czas ≈ czas najwolniejszej gałęzi.
|
||
- Punkt agregacji łączy wyniki dopiero, gdy potrzebne gałęzie się zakończą.
|
||
- Każdy artefakt wyjściowy zależy od konkretnego podzbioru wyników (sekcja 7) — można je
|
||
generować, gdy tylko jego zależności są gotowe.
|
||
|
||
---
|
||
|
||
## 7. Warstwa prezentacji (artefakty wyjściowe)
|
||
|
||
Trzy artefakty. Można je renderować **po stronie klienta** (HTML/Canvas — jak w oryginale)
|
||
lub **serwerowo** komponować obrazy (Pillow / sharp / canvas). Poniżej logika niezależna od wyboru.
|
||
|
||
### 7.1. Wizualizacja detekcji obiektów
|
||
**Zależności:** oryginał + `DetectionResult`.
|
||
- Dla każdego obiektu narysuj ramkę z `bbox_xyxy`, podpis `"{object_type} {score%}"`.
|
||
- Kolor per typ obiektu (paleta cykliczna; ten sam typ = ten sam kolor).
|
||
- Pozycje przeliczaj na **procenty** wymiarów obrazu (`x1/width`, `y1/height`, …),
|
||
aby skalowały się responsywnie.
|
||
- Opcjonalnie: filtry pokazujące/ukrywające typy, legenda, licznik obiektów.
|
||
|
||
### 7.2. Nakładka pojedynczej mapy uwagi
|
||
**Zależności:** oryginał + jedna `SaliencyMap` (np. DeepGaze).
|
||
- Heatmapa nałożona na oryginał z **regulowaną przezroczystością** (suwak 0–100%).
|
||
|
||
### 7.3. Porównanie map uwagi (siatka 2×2)
|
||
**Zależności:** oryginał + 4 mapy (`deepgaze`, `unisal`, `asm_detailed`, `asm_general`).
|
||
- Cztery kafelki, wspólny suwak intensywności, technika „inwersji" (7.4).
|
||
|
||
### 7.4. Algorytm „inwersji" heatmapy (blend luminancji)
|
||
|
||
Wspólna logika nakładek. Dla parametru `t ∈ [0,1]` (intensywność / pozycja suwaka):
|
||
|
||
```
|
||
# Z heatmapy licz luminancję (jasność uwagi) 0..1:
|
||
lum = (0.299*R + 0.587*G + 0.114*B) / 255
|
||
|
||
# Blend per kanał (RGB), per piksel:
|
||
out = orig * (1 - t) + (lum * 255) * t
|
||
```
|
||
|
||
- `t = 0` → czysty oryginał; `t = 1` → maska uwagi w skali szarości
|
||
(biel = wysoka uwaga, czerń = niska) → efekt „reflektora".
|
||
- Wymaga zgodności rozdzielczości oryginału i heatmapy (w razie potrzeby przeskaluj heatmapę).
|
||
|
||
---
|
||
|
||
## 8. Konfiguracja i sekrety
|
||
|
||
Wynieś poza kod (zmienne środowiskowe / vault):
|
||
|
||
| Klucz | Opis |
|
||
|---|---|
|
||
| `LLAVA_HOST` | host usługi LLaVA |
|
||
| `GROUNDING_DINO_HOST` | host usługi Grounding DINO |
|
||
| `DEEPGAZE_HOST` | host usługi DeepGaze |
|
||
| `UNISAL_HOST` | host usługi UNISAL (w oryginale `1.208.108.242:58951`) |
|
||
| `ASM_HOST` | host usługi ASM (w oryginale ten sam host co UNISAL) |
|
||
| `TOKEN` | poświadczenie (Bearer dla LLaVA/DINO/DeepGaze; `X-API-Key` dla UNISAL/ASM) |
|
||
| `ASM_PROMPT` | prompt warunkujący ASM (domyślnie marketingowy) |
|
||
| `MAX_OBJECTS` | limit detekcji (domyślnie 50) |
|
||
|
||
> Dwa schematy autoryzacji: **`Authorization: Bearer`** (LLaVA, Grounding DINO, DeepGaze)
|
||
> vs **`X-API-Key`** (UNISAL, ASM). Zaimplementuj per usługa.
|
||
|
||
---
|
||
|
||
## 9. Obsługa błędów i odporność
|
||
|
||
W oryginale węzły LLaVA, DeepGaze, UNISAL i ASM były ustawione na **kontynuację mimo błędu** —
|
||
awaria pojedynczego modelu nie przerywała całości. Odtwórz to:
|
||
|
||
- **Degradacja stopniowa:** błąd jednej gałęzi → pomiń jej artefakt, zwróć pozostałe
|
||
(`return_exceptions` / per-task `try/except`). Nie przerywaj całej analizy.
|
||
- **Twarda zależność:** jeśli LLaVA zawiedzie, Grounding DINO nie ma etykiet → pomiń detekcję
|
||
(lub fallback: pusta lista / domyślne etykiety), reszta map uwagi działa dalej.
|
||
- **Timeouty i retry:** skończony timeout na każde wywołanie; ewentualny retry z backoffem
|
||
dla błędów przejściowych (5xx / timeout).
|
||
- **Walidacja odpowiedzi:** sprawdzaj obecność kluczy (`labels`, `objects_bboxes`,
|
||
`heatmap_b64`, `data`, `image_detailed`/`image_general`) przed użyciem.
|
||
- **Zgodność wymiarów:** przy blendzie/overlayu pilnuj zgodności rozdzielczości oryginału i map.
|
||
|
||
---
|
||
|
||
## 10. Proponowana dekompozycja modułów
|
||
|
||
| Moduł | Odpowiedzialność |
|
||
|---|---|
|
||
| `preprocess` | walidacja pliku, base64, mime, wymiary |
|
||
| `clients/` | po jednym kliencie na usługę (LLaVA, GroundingDINO, DeepGaze, UNISAL, ASM); auth, timeout, parsowanie odpowiedzi |
|
||
| `orchestrator` | równoległe uruchomienie gałęzi, agregacja, obsługa błędów (sekcja 6, 9) |
|
||
| `models` | typy danych (sekcja 4) |
|
||
| `render/` | generowanie artefaktów (overlay boxów, nakładka heatmapy, siatka 2×2 + blend) |
|
||
| `api`/`cli` | punkt wejścia: przyjmij obraz → zwróć `AnalysisResult` + artefakty |
|
||
|
||
**Sugerowany stack:** dowolny z natywną asynchronicznością i obsługą `multipart` oraz obrazów —
|
||
np. Python (FastAPI + `httpx.AsyncClient` + Pillow) lub Node (Fastify/Express + `undici`/`fetch` + `sharp`).
|
||
Renderowanie wizualizacji: serwerowo (kompozycja obrazu) albo zwrot danych + szablon kliencki.
|
||
|
||
---
|
||
|
||
## 11. Rozszerzenia (roadmapa)
|
||
|
||
Pierwotny projekt przewidywał dalsze etapy analizy uwagi (jeszcze niezaimplementowane):
|
||
|
||
- **Mapa cieplna w interwałach czasowych** (np. „30 s, 6 obrazów", „20 s → 0,5 s") —
|
||
saliency rozłożona na przedziały czasu.
|
||
- **Ścieżki fiksacji (scanpath)** — przewidywana sekwencja ruchów oka.
|
||
- **Mapa emocji** — predykcja reakcji emocjonalnej na kreację.
|
||
|
||
Architektura z sekcji 6 jest na to przygotowana: dokładaj kolejne niezależne gałęzie do
|
||
równoległej orkiestracji i nowe artefakty do warstwy prezentacji.
|
||
|
||
---
|
||
|
||
*Specyfikacja odtworzona z istniejącego przepływu przetwarzania; kontrakty odpowiedzi oznaczone*
|
||
*„wnioskowane" wymagają potwierdzenia z dokumentacją docelowych usług AI.*
|