Blog / Inżynieria

Przezroczyste PNG psują upscalery AI: błąd ciemnego halo

· studium błędu, z pomiarem poprawki · obiekt: produkcyjny potok ×4

Podaj PNG z przezroczystością (wycięte zdjęcie, eksport naklejki) niemal dowolnemu upscalerowi AI, a pojawi się konkretna wada: ciemne halo wpełzające kilka pikseli w głąb obiektu od każdej przezroczystej krawędzi, z bladym jasnym pierścieniem przestrzelenia za nim. Kryje się na ciemnych podglądach i razi na białych stronach. Odtworzyliśmy je na własnym potoku, zmierzyliśmy jego dokładny kształt (rów −94 luminancji, szeroki na trzy piksele modelu), wyśledziliśmy je do obiegu przez wstępnie przemnożoną alfę platformy webowej i zmierzyliśmy poprawkę, którą wdrażamy: zalewowe wypełnianie O(n), które usuwa 98% błędu, zanim model w ogóle ruszy.

−93,9błąd luminancji, pierwszy pierścień pikseliścieżka naiwna wobec czystego odniesienia
≈3 pxszerokość rowu w skali modelupotem pierścień przestrzelenia +17
63×redukcja błędu dzięki poprawce−62,9 → 1,1 średnio, pas 0–3 px
O(n)koszt poprawkijeden przebieg BFS, bez dodatkowego wnioskowania

Każda liczba w tej notatce została zmierzona na kodzie produkcyjnego potoku: te same modele, ta sama matematyka. Surowe rekordy dla każdego obrazu: e3_alpha.json.

1 · Skąd bierze się czerń

Płótno 2D w przeglądarce przechowuje piksele wstępnie przemnożone: nie (r, g, b, α), lecz (rα, gα, bα, α). Kompozycja uwielbia ten format (operatory Portera–Duffa[1] stają się pojedynczymi mnożeniami z dodawaniem), ale jest on stratny w dokładnie jednym miejscu. Odczyt pikseli oznacza podzielenie koloru przez α:

r = ⌊ rα / α ⌉, nieokreślone dla α = 0(1)

Przy α = 0 to 0/0, a odpowiedź platformy[2] brzmi, że nie ma już czego odzyskać: kolor został pomnożony przez zero w chwili, gdy dotknął płótna. getImageData zwraca RGB (0, 0, 0) pod każdym w pełni przezroczystym pikselem. Przezroczyste pole Twojego wycinka po cichu stało się jeziorem czystej czerni.

Człowiek nigdy nie widzi tego jeziora: alfa je ukrywa. Sieć superrozdzielczości nie jest człowiekiem: pobiera trzy kanały RGB i żadnej alfy, więc widzi fotografię osadzoną w vantablacku, a każdy piksel wyjścia blisko granicy jest liczony z pola recepcyjnego, które zanurza się w jeziorze. Czerń nie zostaje na miejscu; dyfunduje do środka przez stos splotów[3].

2 · Pomiar halo

naiwna (RGB płótna = 0)rozlewanie alfa (nasza)
-100-75-50-2502513581216odległość L1 w głąb obiektu (px, skala modelu)Δ luminancji wobec odniesieniaidealnie
Rys. 1. Halo, sprofilowane. Ścieżka naiwna (fiolet): rów −94 luminancji w pierwszym pierścieniu pikseli wewnątrz obiektu, −90 przy 2 px, −54 przy 3 px, potem pierścień przestrzelenia +17 przy 5 px, gasnący do zera około 13 px; ciemne skażenie przez skończone pole recepcyjne, wyostrzone przez GAN w krawędź. Ścieżka produkcyjna (zieleń): nigdy gorzej niż 1,5 poziomu luminancji. Odległości w skali modelu; ×4 dla pikseli wyjścia.
Trzy panele: wycięte wejście pokazane na szachownicy, naiwne powiększenie z ciemną obwódką wokół owalu i czyste powiększenie z rozlaną alfą
Rys. 2. Wejście z przezroczystością (po lewej), ścieżka naiwna (w środku), ścieżka produkcyjna (po prawej), złożone na jasnym tle. Obwódka środkowego panelu wygląda w rozmiarze miniatury jak subtelna „brudna krawędź”, i dokładnie dlatego ten błąd tak często trafia do produkcji: kryje się, dopóki użytkownik nie położy naklejki na jasnym tle.

Jeden naprawdę sprzeczny z intuicją pomiar: spodziewaliśmy się, że model będzie halucynował śmieci wewnątrz przezroczystego obszaru, „ulepszał pustkę”. Nie robi tego. Płaska czerń na wejściu, płaska czerń na wyjściu (energia gradientu 0,1). Szkodą nie jest to, co model wymyśla w pustce; jest nią to, co pustka robi obiektowi przez granicę. To ma znaczenie, bo oznacza, że maskowanie wyjścia Cię nie uratuje: uszkodzone piksele są wewnątrz widocznego obszaru, pod α > 0.

3 · Poprawka: zalej pustkę wiarygodnym kolorem

Jeśli model musi widzieć RGB pod α = 0, niech to będzie RGB, które kontynuuje obiekt. Przed wnioskowaniem uruchamiamy rozlewanie alfa: wieloźródłowe zalewanie wszerz z każdego nieprzezroczystego piksela, w którym każdy przezroczysty piksel przyjmuje średni kolor swoich już pokolorowanych 4-sąsiadów:

c(p) = mean{ c(q) : q ∈ N₄(p), q pokolorowane }, p w kolejności BFS(2)

Zasianie kolejki pierścieniem brzegowym i przetwarzanie FIFO gwarantuje, że każdy zdejmowany z kolejki piksel ma co najmniej jednego pokolorowanego sąsiada (ten, kto go dodał, skończył pierwszy), więc jeden przebieg O(n) koloruje całą pustkę: bez limitu odległości, z zamkniętymi dziurami włącznie. Bajty alfy nigdy nie są dotykane; półprzezroczyste piksele zachowują własny kolor. Model widzi wtedy gładką kontynuację zamiast urwiska, a alfa (powiększana osobno) jest nakładana ponownie na wyjściu. Zmierzony efekt: błąd w paśmie 0–3 px spada z -62,9 do 1,1 poziomu luminancji, redukcja 63×, bez mierzalnego kosztu czasu działania.

Detal krawędzi rekonstrukcji naiwnej i z rozlewaniem oraz wzmocniona mapa cieplna różnicy pokazująca halo przylegające do granicy alfy
Rys. 3. Detal krawędzi: ścieżka naiwna (po lewej) obok ścieżki z rozlewaniem (w środku), a po prawej |różnica| między obiema rekonstrukcjami, wzmocniona ×8. Halo śledzi granicę alfy i nic więcej; z dala od krawędzi wyjścia są identyczne, czyli poprawka zachowuje się dokładnie tak, jak zaprojektowano.

4 · Co to oznacza dla Twoich obrazów

Jeśli używasz tej strony: nic; przezroczyste PNG idą zmierzoną wyżej ścieżką zarówno w potoku chmurowym, jak i w zapasowym silniku przeglądarkowym, bo oba dzielą tę samą czystą bibliotekę. Jeśli budujesz narzędzia do obrazów: traktuj każdy model wyłącznie RGB jako uczulony na wstępnie przemnożone zera: rozlewaj przed wnioskowaniem (powyższe zalewanie to ~30 linii bez zależności) albo nakładaj na jednolity kolor i zaakceptuj jego odchylenie. A jeśli diagnozujesz tajemniczą ciemną krawędź w czyimś wyjściu z ulepszacza, znasz już odcisk palca: rów szeroki na kilka pikseli, pierścień przestrzelenia za nim i idealna korelacja z granicą alfy. Trzy liczby do zapamiętania: −94, 3 px, 63×.

Źródła

  1. Porter, T., & Duff, T. (1984). Compositing Digital Images. SIGGRAPH '84, Computer Graphics 18(3), 253–259. (The premultiplied-alpha compositing algebra.) dl.acm.org
  2. WHATWG HTML Living Standard, the 2D canvas element: premultiplied backing stores and the lossy getImageData round-trip for non-opaque pixels. html.spec.whatwg.org
  3. Wang, X., Xie, L., Dong, C., & Shan, Y. (2021). Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data. ICCV Workshops. arXiv:2107.10833. arxiv.org
  4. Lange, D. (1936). “Migrant Mother” (Destitute pea pickers in California), Farm Security Administration. Public domain, via Wikimedia Commons. commons.wikimedia.org
  5. ONNX Runtime: the inference engine both our server (CPU) and in-browser (WebGPU/WASM) engines run on. onnxruntime.ai