sonny-eyes
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sonny-eyesAnalyze this video clip and give me exposure, color, sharpness, noise, and motion metrics."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
🇵🇱 Polski | 🇬🇧 English
sonny-eyes
Pomiar percepcyjny ujęć wideo: bierze klip z kamery i zamienia go w liczby (tonalność, kolor, ostrość, szum, ruch kamery, przebieg jasności), zapisuje raport JSON per klip i stronę przeglądu HTML, a agentom AI udostępnia te dane przez serwer MCP. Dla twórców wideo, którzy pracują z modelami AI, i dla agentów, które mają "obejrzeć" materiał, a fizycznie nie potrafią.
Problem: jak AI ogląda film
Model nie widzi pikseli. Widzi siatkę kwadratów. Obraz jest cięty na patche (u Claude 28x28 px), każdy patch spłaszczany do wektora liczb, a oryginalne piksele znikają - nie ma do nich powrotu, nie da się "przybliżyć". Klatka 4K nigdy nie dociera w 4K: jest skalowana do 2576 px dłuższego boku, czyli siatki 92 x 52 = 4784 patche. To sufit, więcej nie będzie, choćbyś dał 8K. Do tego zero metadanych: żadnego EXIF, żadnego ICC, żadnej informacji, że to F-Log albo 10-bit.
Enkoder wizyjny jest trenowany tak, żeby dopasować obraz do TEKSTU (trening kontrastywny obraz-opis), a nie żeby zachować wierność fotometryczną. Nikt tej sieci nigdy nie uczył trzymać wartości luminancji. Efekt jest przewidywalny: model uczciwie powie "ciepłe", "płaskie", "przepalone", ale nie powie "czerń na 6 IRE" ani "300 K za ciepło". Sonny, agent, dla którego to narzędzie powstało, podsumował to tak: "widzę treść i nastrój kadru, nie widzę wartości, a filmu nie widzę w ogóle, bo dostaję garść nieruchomych zdjęć."
Z filmem jest gorzej, bo nikt nie ogląda filmu. W API Claude nie ma bloku "video" - obsługiwane są JPEG, PNG, GIF (bez animacji) i WebP, więc jedyna droga to wyciąć klatki ffmpegiem i wysłać jako sekwencję obrazów. Gemini przyjmuje plik wideo, ale dokumentacja opisuje to wprost jako "sequence of frames" próbkowane domyślnie z częstotliwością 1 klatki na sekundę, bez dedykowanego kanału optical flow. Modele open-weight robią to samo, tylko jawniej.
Konkret z prawdziwego materiału: kamera nagrywa 50 klatek na sekundę, okno 8 sekund to 400 klatek. Przy próbkowaniu 1 fps model dostaje z tego 8 klatek. 392 klatki, w których żyje całe drganie, po prostu nie istnieją. Nie "są słabo widoczne" - nie ma ich. A stabilność, judder, duplikaty klatek i tempo montażu są właśnie własnością relacji MIĘDZY klatkami, nie pojedynczej klatki.
Benchmarki mówią to samo liczbami. ColorBench: podanie dokładnego koloru punktu to najtrudniejsze zadanie percepcyjne w całym zbiorze, najlepszy wynik 57.3%; rozpoznanie dominującego koloru idzie znacznie lepiej (najlepszy 82.9% przy człowieku 92.0%) - czyli kategoria tak, wartość nie. ShotBench (3.5k eksperckich pytań z ponad 200 filmów, 24 modele): najlepszy poniżej 60% średniej, a ruch kamery to najtrudniejszy wymiar - GPT-4o 48.3%, Qwen2.5-VL-72B 48.9%, Gemini-2.5-flash 43.5%. CameraBench testuje wprost skalę stabilności (static, no shaking, minimal shaking, unsteady, very unsteady) i większość open-source VLM wypada na poziomie przypadku albo poniżej. MotionBench: najlepsze video-LLM poniżej 0.60, a w kategorii liczenia powtórzeń ruchu "all models scored near random".
To nie jest problem akademicki, tylko rachunek za czas. Przy serii Canarian Tweety plik DCTL o nazwie WARMTH był no-opem przez cały odcinek EP02 i nikt tego nie zauważył - ani agent, ani autor. Jeden pomiar delty przed i po wyłapałby to w sekundę; oglądanie nie wyłapało przez cały odcinek. Wcześniej, przy EP01, AI wyprodukowało 36 renderów TIFF i 3 kontaktówki do porównania looków A/B/C, na których delty były praktycznie niewidoczne: 30 minut w błoto. Werdykty autora na kolejnych iteracjach brzmiały: "nie widzę żadnej różnicy, za mało mordo", potem "kompletnie nie widzę tego warmtha nigdzie", po podbiciu "za żółto". Każda z tych rund to był przejazd człowieka w viewerze, bo pomiaru nie było.
Related MCP server: Enhanced Multimedia Analysis MCP
Rozwiązanie: mierz, nie patrz
Zasada jest jedna: narzędzia mierzą, model interpretuje. To narzędzie robi tylko pierwszą połowę.
Cały stack percepcji ma trzy warstwy i one nie są wymienne - każda odpowiada na inne pytanie i kłamie, gdy użyć jej do cudzego:
Fizyka (liczby). Percentyle luminancji, saturacja, cast, wariancja laplasjanu, wektor przesunięcia między klatkami. Tanie, powtarzalne, obiektywne. Odpowiadają na "ile", nigdy na "czy dobrze". To jest warstwa, którą robi
sonny-eyes.Kontrola wzrokowa (skopy). Waveform, vectorscope, wykres przebiegu i pojedyncze klatki jako PNG. Tutaj człowiek albo model faktycznie patrzy, ale na wykres, nie na obraz. Wykres czyta się pewniej niż tabelę liczb i kosztuje mniej tokenów niż pełna klatka.
sonny-eyeste obrazy generuje.Semantyka (model). Plan, ruch kamery, treść, rola montażowa, "czy jest ciekawie". Jedyna warstwa, która umie powiedzieć "ciekawie", i jedyna, która potrafi się pomylić z pełnym przekonaniem. Dlatego idzie na końcu i zawsze na klatkach wybranych przez warstwę 1. Tego
sonny-eyesNIE robi.
Fundamentem jest raport ujęcia: jeden JSON na klip, plus ścieżki do klatek i skopów. Kontrakt tego raportu:
nullzamiast zgadywania. Metryka, której nie dało się policzyć (sekunda bez wystarczającej faktury do trackingu, szacunek temperatury barwowej, który wyszedł poza sensowny zakres), zapisuje się jakonull, nie jako fikcyjne zero.Jawna niepewność. Pole
niepewnoscmówi wprost, co poszło nie tak przy tym konkretnym pomiarze: czy klip Fuji zmierzono bez LUT, czy kamera jest nierozpoznana, czy GPU spadło na CPU, czy okno pomiarowe było krótsze niż zamówione.werdyktyzawszenull. Ocenę wpisuje człowiek, nigdy skrypt. Automat jest klasyfikatorem, nie wykonawcą: flaguje i sortuje, decyduje człowiek.Progi jadą razem z danymi. Pole
progi_prowizorycznezapisuje w KAŻDYM raporcie komplet progów użytych do flag i klasyfikacji ruchu, więc stary pomiar da się odróżnić od nowego i zinterpretować po zmianie progów. To samo robiwersja_metryk.
Co mierzy
Tonalność. Percentyle jasności p1 / p5 / p50 / p95 / p99 (luma BT.709 w skali 0-1), procent pikseli wypalonych w dole i pod sufitem, kontrast RMS, rozrzut mediany między klatkami, plus profil jasności co sekundę (p5 / p50 / p99) na całej długości klipu.
Kolor. Nasycenie średnie i p95, cast R-G i B-G, szacunek temperatury barwowej (McCamy 1992, przez
colour-science).Ostrość. Wariancja laplasjanu: średnia, minimum i maksimum po klatkach okna, plus siatka 3x3 z najostrzejszej klatki (żeby wiedzieć, czy ostry jest obiekt, czy całe pole).
Szum. Estymata sigma z najostrzejszej klatki (
skimage.restoration.estimate_sigma).Ruch kamery. Cechy (
goodFeaturesToTrack) + optical flow (calcOpticalFlowPyrLK) + transformacja afiniczna z RANSAC (estimateAffinePartial2D) między kolejnymi próbkami. RANSAC odrzuca punkty na ruchomych obiektach (liście, przechodnie) jako outliery, zostawiając ruch samej kamery. Wynik: profil co sekundę (ruch zamierzony, jitter, pewność pomiaru), odcinki stabilne i w ruchu, najdłuższy odcinek stabilny, osobna informacja o pierwszych i ostatnich 2 sekundach, oraz klasa ujęcia (postawiona/static/handheld/shaky, plus niezależniepan).Płynność. Flaga VFR (rozjazd
r_frame_rateiavg_frame_rate), liczba duplikatów klatek, średnia różnica między kolejnymi klatkami.Montaż. Długość klipu. Wykrywanie cięć jeszcze nie jest zaimplementowane (
montaz.segmentyjest zawsze puste).Obrazy. Pierwsza klatka, najostrzejsza klatka, waveform, vectorscope i wykres przebiegu ujęcia (trzy panele: jasność po LUT, drganie, ruch zamierzony) - wszystko PNG.
Metryki tonalności, koloru, ostrości i szumu liczą się z krótkiego okna (domyślnie 5-25 s; klip krótszy niż 30 s mierzony jest w całości od 0), próbkowanego 2 klatki na sekundę przy szerokości 480 px, po LUT. Ruch i profil jasności to osobna próbka: cały klip od 0 s, 10 klatek na sekundę do 60 s długości i 5 klatek na sekundę powyżej, twardy limit 180 sekund materiału.
Czego NIE robi
Zero AI w samym pomiarze. To ffmpeg, numpy, OpenCV, scikit-image i colour-science. Żaden model nie jest ładowany, nic nie leci do żadnego API.
Zero werdyktów. Narzędzie nie powie, czy ujęcie jest dobre. Pole
werdyktyw raporcie jest zawszenull.Zero renderów i eksportów. Nie generuje wersji materiału, nie dotyka DaVinci Resolve ani żadnego NLE.
Nic nie modyfikuje. Pliki źródłowe są czytane i nigdy nie zapisywane. Narzędzie pisze wyłącznie do własnego katalogu wyjściowego. Raporty są regenerowalne: skasowanie JSON-a i ponowny pomiar odtwarza go od zera. Nic poza samymi klipami z kamery nie jest tu źródłem prawdy.
Nie wykrywa cięć (na razie) i nie robi warstwy semantycznej.
Szybki start
Wymagania
Python 3.12 lub nowszy (rozwijane i testowane na 3.12.10).
ffmpegiffprobew PATH. Rozwijane na buildzie 8.0.1; niektóre obejścia w kodzie są podyktowane zachowaniem tej wersji (patrz "Ograniczenia i pułapki").Opcjonalnie karta NVIDIA. Każde wywołanie ffmpeg próbuje najpierw
-hwaccel cuda, a przy błędzie automatycznie ponawia na CPU. Fallback jest wbudowany i odnotowany w raporcie (niepewnosc.gpu_fallback_cpu), więc narzędzie działa bez GPU. Można też wymusić CPU flagą--no-gpu.Testowane wyłącznie na Windows 11 z RTX 4080 Laptop. macOS i Linux nie były sprawdzone. Kod nie ma jawnych zależności od Windows poza ścieżkami w przykładach i obejściami ffmpeg, ale nikt tego nie odpalił na innym systemie
jeśli spróbujesz, zgłoszenie z wynikiem jest mile widziane.
Instalacja
git clone https://github.com/JDHole/sonny-eyes.git
cd sonny-eyes
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS / Linux
pip install -r requirements.txtrequirements.txt to minimalny zestaw dziewięciu pakietów potrzebnych do
pomiaru i serwera MCP (instalacja około 1-2 minut, około 0,6 GB). Pełne
środowisko deweloperskie autora siedzi w requirements.lock i nie jest
potrzebne do uruchomienia narzędzia. Sprawdzenie instalacji:
python -m eyes --version.
Pierwszy pomiar
python -m eyes measure --project demo --files clip.mp4Wynik ląduje pod ./eyes-out względem bieżącego katalogu:
eyes-out/demo/reports/clip.json <- raport
eyes-out/demo/clip/frames/*.png <- pierwsza i najostrzejsza klatka
eyes-out/demo/clip/scopes/*.png <- waveform, vectorscope, przebieg
eyes-out/demo/_log.jsonl <- log: jedna linia na klip na uruchomienieKilka plików naraz: --files a.MOV b.MP4 (albo z powtórzoną flagą:
--files a.MOV --files b.MP4); tak samo działa --folder w batch. Domyślnie klip, który ma już raport, jest
pomijany - --force wymusza ponowny pomiar. Okno pomiarowe zmieniają
--window-start i --window-len.
Przemiał folderu
python -m eyes batch --project demo --folder "D:/kamera/dzien-1"Skanuje folder rekurencyjnie i mierzy wszystko, co pasuje. Uwaga: w tej
wersji filtr nazw jest zaszyty pod dwie kamery autora - DSCF*.MOV (Fuji)
oraz GX*.MP4, GH*.MP4, GOPR*.MP4 (GoPro). Reszta plików w folderze jest
pomijana. Pojedyncze pliki o dowolnej nazwie mierzy measure --files.
Przerwanie w dowolnym momencie jest bezpieczne: ponowne uruchomienie pomija
klipy, które mają już raport. Postęp z paskiem i ETA leci do
eyes-out/demo/_postep.json (zapis atomowy po każdym pliku, więc czytelnik
nigdy nie zobaczy połowy JSON-a).
Przemiał w tle, bez trzymania terminala:
python -m eyes batch --project demo --folder "D:/kamera/dzien-1" --detach
python -m eyes status --project demo # albo --json
python -m eyes stop --project demo # ubija całe drzewo procesów, z ffmpegiemInne przydatne flagi batch: --limit N (tylko pierwsze N plików, do testu
porcji), --porcja "Dzień 1" (etykieta zapisywana w _postep.json).
Strona przeglądu
python -m eyes review --project demoBuduje eyes-out/demo/_przeglad.html: jedna strona z cegłami (najostrzejsza
klatka każdego klipu), liczbami, flagami, sortowaniem i filtrowaniem. Obrazy
wchodzą jako data URI, więc plik działa bez dostępu do katalogu wyjściowego i
można go wysłać jednym plikiem. --out-file sciezka.html dokłada kopię pod
wskazaną ścieżką, --compact zamienia pełne PNG na małe JPEG-i (pod limity
rozmiaru artefaktów), --status "tekst" wstawia baner na górze strony,
--lang pl|en ustawia język strony (domyślnie en; patrz też klucz lang w
config.toml i zmienna EYES_LANG w sekcji "Konfiguracja").
Duplikaty
python -m eyes dupes --project demo
python -m eyes dupes --project demo --verifyPodczas pomiaru pliki o identycznym hash_4mb (sha1 pierwszych 4 MB) jak
wcześniejszy plik na liście są rozpoznawane jako duplikat treści i mierzone
tylko raz. dupes wypisuje te pary z logu jako listę do decyzji, a --verify
liczy sha1 CAŁYCH plików obu stron i oznacza pary jako "identyczne" albo
"RÓŻNE" (różne = zgadzały się tylko pierwsze 4 MB, taki plik nie jest
kandydatem do kasacji). Nigdy nic nie kasuje - to decyzja człowieka.
Przebieg ujęcia
python -m eyes profile --project demo
python -m eyes profile --project demo --ids DSCF0934 DSCF0935Rysuje wykres przebiegu z ISTNIEJĄCEGO raportu, bez ponownego dekodowania.
Trzy panele na wspólnej osi czasu: jasność po LUT (p5 / p50 / p99), drganie
(jitter jako procent szerokości kadru) i ruch zamierzony (procent szerokości
kadru na sekundę), z zaznaczonymi progami i odcinkami stabilnymi. Raport bez
pola tonalnosc.profil (sprzed tej funkcji) jest pomijany z komunikatem.
--lang pl|en ustawia język napisów na wykresie (domyślnie en; to samo
measure/batch przyjmują, bo one też rysują ten wykres przy pomiarze -
patrz --lang w sekcji "Konfiguracja").
Test dymny
python tests/test_smoke.pyBramka bez pytest: pliki w tests/ uruchamia się jako zwykłe skrypty. test_smoke.py sprawdza raporty z materiału autora, więc u siebie zacznij od tests/test_config.py, tests/test_cli.py i tests/test_mcp.py.
Dla agentów AI
Serwer MCP startuje jedną komendą:
python -m eyes mcpClaude Code:
claude mcp add sonny-eyes -- /sciezka/do/.venv/bin/python -m eyes mcpClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"sonny-eyes": {
"command": "C:/sciezka/do/sonny-eyes/.venv/Scripts/python.exe",
"args": ["-m", "eyes", "mcp"],
"env": {
"EYES_OUT": "C:/sciezka/do/eyes-out"
}
}
}
}Serwer wystawia osiem narzędzi:
Narzędzie | Co robi |
| Zwraca parametry strumienia z ffprobe (kodek, rozdzielczość, fps, pix_fmt, zakres, długość) bez mierzenia czegokolwiek. |
| Mierzy jeden klip i zapisuje raport ujęcia. |
| Zwraca gotowy raport JSON konkretnego klipu. |
| Wypisuje raporty istniejące w projekcie. |
| Startuje przemiał folderu jako proces w tle. |
| Zwraca postęp przemiału (zrobione, błędy, ETA). |
| Zatrzymuje przemiał w tle. |
| Buduje stronę przeglądu HTML dla projektu. |
Do tego skill: katalog skill/sonny-eyes/ kopiuje się do .claude/skills/
projektu (albo do ~/.claude/skills/). Skill uczy agenta, kiedy sięgnąć po
pomiar zamiast po patrzenie, i jak czytać pola raportu.
Szczegóły konfiguracji, uprawnień i przykładowe wywołania: docs/mcp.md.
Wynik
Układ folderów
Wszystkie ścieżki są konfigurowalne (patrz "Konfiguracja"); poniżej wersja
domyślna, czyli wszystko pod ./eyes-out względem bieżącego katalogu.
eyes-out/
_luts/ kopie LUT-ów użytych do pomiaru
<projekt>/
reports/<id>.json raport ujęcia, jeden na klip
<id>/frames/<id>_pierwsza.png
<id>/frames/<id>_najostrzejsza.png
<id>/scopes/<id>_waveform.png
<id>/scopes/<id>_vectorscope.png
<id>/scopes/<id>_przebieg.png
_log.jsonl jedna linia JSON per klip per uruchomienie
_postep.json postęp przemiału (tylko batch)
_runner.json pid i komenda procesu w tle (tylko --detach)
_batch.log stdout i stderr procesu w tle
_przeglad.html strona przeglądu (tylko review)id to nazwa pliku bez rozszerzenia (DSCF3236). Kolizja nazw w jednym
uruchomieniu daje nazwa@folder_nadrzedny. Nazwa raportu to zawsze
{id}.json.
_log.jsonl ma jedną linię na klip na uruchomienie: id, nazwa pliku, status
(ok / pominieto / duplikat / blad), czas, wersja metryk, a przy
błędzie treść błędu. _postep.json jest nadpisywany atomowo po każdym pliku i
zawiera: projekt, porcję, listę folderów, status (w toku / zakonczony /
zakonczony z bledami / przerwany), liczniki (plików, zrobione, ok,
pominięte, duplikaty, błędy), średni czas, ETA i ostatni przetworzony plik.
Kształt raportu
Skrót prawdziwego raportu (klip Fuji 4K50, 12 s, ujęcie z ręki w ruchu; tablice
profil, odcinki i siatka_3x3 skrócone):
{
"wersja_metryk": "0.2",
"id": "DSCF0934",
"projekt": "Canarian Tweety EP03",
"zrodlo": {
"plik": "DSCF0934.MOV", "kamera": "Fuji", "profil": "F-Log",
"lut_pomiarowy": "FLog_to_Rec709_65pt.cube",
"hash_4mb": "e69685b4caec2a965ef4a861f5c71da49bd3e853",
"rozmiar_b": 307694592, "czas_s": 12.0
},
"stream": {
"codec": "hevc", "w": 3840, "h": 2160, "fps": 50.0, "fps_avg": 50.0,
"pix_fmt": "yuv420p10le", "range": "pc", "transfer": "smpte170m",
"primaries": "bt709", "nb_frames": 600
},
"okno_pomiaru": {
"start_s": 0.0, "koniec_s": 12.0, "fps_probki_a": 2, "fps_probki_b": 10.0,
"szerokosc_px": 480, "liczba_klatek_a": 24, "liczba_klatek_b": 120
},
"tonalnosc": {
"p1": 0.001, "p5": 0.001, "p50": 0.008, "p95": 0.066, "p99": 0.144,
"clip_lo_pct": 61.168, "clip_hi_pct": 0.029,
"kontrast_rms": 0.042, "p50_rozrzut": 0.005,
"profil": [{"t_s": 0, "p5": 0.0, "p50": 0.004, "p99": 0.072}]
},
"kolor": {"sat_mean": 0.499, "sat_p95": 1.0, "cast_rg": 0.003, "cast_bg": 0.007, "cct_est_k": 7743.439},
"ostrosc": {"lapvar": 52.285, "lapvar_min": 21.624, "lapvar_max": 124.618,
"siatka_3x3": [47.258, 209.048, 185.412], "skala_pomiaru_px": 480},
"szum": {"sigma": 0.002},
"ruch": {
"ruch_zamierzony_pct_s": 30.144, "jitter_rms_pct": 1.733,
"mediana_jitter_pct": 1.016, "p90_jitter_pct": 2.416,
"profil": [{"t_s": 0, "ruch_pct": 36.611, "jitter_pct": 1.236, "conf": 0.857}],
"odcinki": [{"od_s": 0, "do_s": 11, "typ": "ruch"}],
"srodek": null, "brzegi": {"poczatek_ruch": false, "koniec_ruch": false},
"pary_niepewne": 31, "klasa": ["shaky", "pan"],
"zrodlo": "lk_ransac_affine", "obcieto_s": null
},
"plynnosc": {"vfr": false, "duplikaty": 0, "tdiff": 0.01},
"montaz": {"dlugosc_s": 12.0, "segmenty": []},
"flagi_prowizoryczne": ["czern_zabita", "trzesie"],
"werdykty": null,
"niepewnosc": {"pomiar_bez_lut": false, "kamera_nieznana": false,
"gpu_fallback_cpu": false, "okno_krotsze_niz_zadane": true},
"pomiar": {"czas_dekodowania_s": 5.841, "czas_calkowity_s": 7.734, "gpu": true,
"ffmpeg": "8.0.1-full_build-www.gyan.dev", "opencv": "5.0.0"}
}Nazwy pól są po polsku i to jest świadoma decyzja: raport jest kontraktem danych, który już czytają inne narzędzia autora, więc nie zmienia się go dla kosmetyki. Pełny słownik pól po angielsku - typ, znaczenie, pułapki - leży w docs/report-fields.md.
Strona przeglądu
review składa wszystkie raporty projektu w jedną stronę HTML: kafelek na klip
z najostrzejszą klatką (przełączaną na waveform i wykres przebiegu), sześć
najważniejszych liczb, flagi, klasa ruchu, sortowanie (po numerze, po czerni,
po przepale, po ostrości, po drganiu) i filtry po fladze i kamerze. Na górze
jest sekcja "Jak czytać liczby" z opisem każdej metryki w języku, a nie w
matematyce.
Konfiguracja
Bez żadnej konfiguracji narzędzie działa od razu i pisze wszystko pod
./eyes-out względem bieżącego katalogu. Żeby to zmienić, skopiuj
config.example.toml do config.toml obok repo i odkomentuj to, czego
potrzebujesz. config.toml jest w .gitignore - to plik z lokalnymi
ścieżkami, nie wchodzi do repo.
# Główny katalog wyjściowy, gdy nic innego nie jest ustawione.
out_root = "./eyes-out"
# Cache per klip: klatki, skopy, _log.jsonl, _postep.json, _luts/.
# Domyślnie to samo co out_root.
cache_root = "./eyes-out"
# Gdzie lądują gotowe raporty JSON. MUSI zawierać znacznik "{project}",
# podstawiany nazwą z --project. Domyślnie "{cache_root}/{project}/reports".
reports_root = "./eyes-out/{project}/reports"
# LUT per kamera. Klucz = nazwa kamery jak w polu zrodlo.kamera raportu,
# małymi literami ("fuji", "gopro"); wartość = ścieżka do pliku .cube,
# nakładanego przed pomiarem klipów tej kamery.
[lut]
fuji = "C:/sciezka/do/FLog_to_Rec709.cube"
# Język interfejsu strony przeglądu (`review`) i wykresu przebiegu
# (`profile`, a także `measure`/`batch`, bo one też rysują wykres przebiegu
# przy pomiarze). Tylko "pl" albo "en".
# lang = "en"Kamera spoza tabeli [lut] (albo brak tabeli) jest mierzona bez LUT, co
odnotowuje niepewnosc.pomiar_bez_lut w raporcie. Flaga --lut sciezka.cube
nadpisuje całą tabelę i wymusza jeden LUT na każdym klipie, niezależnie od
kamery.
Zmienne środowiskowe:
EYES_OUT- główny katalog wyjściowy, to samo coout_root.EYES_CONFIG- jawna ścieżka do plikuconfig.toml.EYES_LANG- język interfejsu, to samo colang("pl" albo "en").
Kolejność nadpisań (wygrywa pierwsze): flagi CLI (--out, --config,
--lut, --lang) > zmienne środowiskowe (EYES_OUT, EYES_CONFIG,
EYES_LANG) > config.toml > wbudowane domyślne. --out X jest twardsze
niż wygląda: ustawia naraz out_root, cache_root i reports_root,
nadpisując także jawny reports_root z configu - inaczej --out do
katalogu tymczasowego nadal pisałby raporty w miejscu z configu.
Ograniczenia i pułapki
Progi są prowizoryczne. Progi flag i klasyfikacji ruchu
(jitter_stabilny_max 0.4, ruch_stabilny_max 3.0, jitter_shaky_min 0.6 i
reszta) zostały skalibrowane ręcznie na TRZECH klipach z werdyktem autora
(kamera położona na murku, ujęcie hero z ręki, ujęcie ze statywu), nie na
zróżnicowanej próbce. Startowały z przybliżenia literaturowego i zostały
podniesione, bo na prawdziwym materiale fragmentowały jeden długi stabilny
odcinek na kilkanaście krótkich. Traktuj je jak punkt startowy do kalibracji na
własnym materiale, nie jak standard. Dlatego jadą w każdym raporcie jako
progi_prowizoryczne - da się przeliczyć klasyfikację po zmianie progów bez
ponownego pomiaru.
Czego metryka ruchu v0.2 nie łapie:
Rotacja i skala są liczone z macierzy afinicznej, ale nieużywane. Rolka kamery i zoom podczas ujęcia nie mają dziś wpływu na klasę.
"Pan" liczy się ze średniej z modułu wygładzonego przesunięcia, nie z uśrednionego WEKTORA, więc bardzo silny chaotyczny jitter może w skrajnym przypadku też dopisać "pan".
Sceny o niskiej fakturze (czyste niebo, wypalona ściana) dają za mało cech do trackingu - pary albo całe sekundy kończą jako
null, nie jako fikcyjne zero.ruch.brzegipatrzy sztywno na pierwsze i ostatnie 2 sekundy. Odkładanie kamery trwające wyraźnie dłużej może częściowo wyciec poza to okno.Rozpad trackingu (ręka zasłaniająca obiektyw przy starcie albo stopie nagrania) potrafi dać pojedynczą sekundę z fizycznie bezsensownym
ruch_pctprzy niskimconf. Agregaty i bramka brzegów są przed tym zabezpieczone (medianą i minimalną pewnością), ale surowyruch.profilnadal takie wartości pokazuje.Duży obiekt poruszający się spójnie w kadrze (sylwetka balansująca pod wiatr) może częściowo przeciekać do
ruch_pct, mimo że RANSAC odrzuca większość jego punktów.Wykrywania cięć nie ma w ogóle -
montaz.segmentyjest zawsze puste.
Czas pomiaru długich klipów. Ruch jest liczony z całego klipu (do 180 s),
nie z krótkiego okna, i to dominuje koszt. Dekodowanie 4K 10-bit HEVC chodzi w
praktyce w okolicach 200-300 klatek na sekundę efektywnego throughputu
NIEZALEŻNIE od docelowego fps próbkowania, bo filtr fps odrzuca klatki PO
zdekodowaniu, nie przed - cały zakres czasowy i tak jest w pełni dekodowany.
Zysk z GPU jest umiarkowany (rzędu 20-50% na dekodowaniu), bo najdroższym
krokiem bywa CPU-owy lut3d, nie sam dekoding. Czas każdego pomiaru jest
zapisany w raporcie (pomiar.czas_calkowity_s, pomiar.czas_dekodowania_s),
więc łatwo zmierzyć to u siebie.
Windows: LUT i dwukropek dysku. Wstawienie ścieżki Windows jako wartości
file= w filtrze lut3d psuje parser filtergraphu tego builda ffmpeg, i to
niezależnie od tego, czy dwukropek jest escapowany, czy wartość jest w
apostrofach - parser gubi się na literze dysku. Obejście jest wbudowane: LUT
jest kopiowany raz do {cache_root}/_luts/, ffmpeg odpalany z cwd
ustawionym na ten katalog, a referencja do LUT-a to sama nazwa pliku, bez
dwukropka w ogóle.
Windows: kodowanie konsoli. Konsola Windows domyślnie nie wypisze polskich
znaków w niektórych trybach (cp1252) i przewraca się na UnicodeEncodeError.
Każde wejście CLI woła na starcie sys.stdout.reconfigure(encoding="utf-8")
(i to samo dla stderr), a wszystkie pliki są zapisywane i czytane jako UTF-8.
Ścieżki ze znakiem # i spacjami działają bez escapowania, bo wywołania
ffmpeg i ffprobe idą przez subprocess.run z listą argumentów, bez powłoki.
Skąd to się wzięło
To jest wycinek JDHole OS, systemu życia i produkcji jednego twórcy wideo,
w którym pracuje dwunastu agentów AI o wydzielonych domenach. Sonny odpowiada
za kolor i postprodukcję. Przez cztery odcinki serii Canarian Tweety (około
1111 klipów, 1 TB surówki z Fuji i GoPro) każde "czy to dobrze wygląda" szło
przez oko autora, bo agent fizycznie nie widział materiału - i za każdym razem,
gdy udawał, że widzi, kosztowało to godziny (rewert 516 instancji grade'u,
36 renderów bez wartości, WARMTH no-op przez cały odcinek). sonny-eyes
powstał jako odpowiedź: nie kolejny model, tylko warstwa pomiaru pod modelem.
Wychodzi jako open source, bo research pod ten projekt pokazał realną dziurę -
pomiar (a nie naprawa) trzęsienia kamery praktycznie nie ma repozytoriów, a
najlepsze skorery jakości obrazu mają licencje niekomercyjne, więc nie da się
ich użyć w monetyzowanej produkcji. Jeśli komuś to oszczędzi tych samych
godzin, robota się zwróciła drugi raz.
Licencja
MIT. Patrz LICENSE.
Contributing
Zgłoszenia są mile widziane - szczególnie wyniki z macOS i Linuksa oraz
kalibracja progów na innym materiale niż Fuji i GoPro. Pull request powinien
mieć test w tests/ napisany jako zwykły skrypt uruchamiany przez
python tests/test_cos.py (w repo nie ma pytest). Nowych zależności pip nie
dodajemy poza tym, co siedzi w locku - jeśli czegoś naprawdę brakuje, opisz to
najpierw w issue.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Video analysis AI: transcripts, summaries, visual scenes/shots, clips, answers in natural language.
Preflight QA for AI-agent deliverables with structured verdicts and repair guidance.
Video, audio, and image processing for AI agents: convert, transcribe, upscale - 150+ operations.
Give AI random access to video: timestamped contact sheets + zoom into any start/end range.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI agents to analyze images, extract text, compare images, and analyze video through any OpenAI-compatible vision model.411920MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to analyze images and videos, and generate optimized prompts for AI video generation systems.MIT
- AlicenseNot gradedqualityAmaintenanceExtracts ffprobe metadata, subtitles, scenes, and timelines from video files without frame-by-frame LLM vision, providing evidence-first reading for AI agents.253MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to scan code for security and quality issues and receive machine-readable reports with suggested fixes and verification criteria.722MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/JDHole/sonny-eyes'
If you have feedback or need assistance with the MCP directory API, please join our Discord server