Jak usunąć lub ujednolicić półprzezroczyste piksele w wyrenderowanych klatkach postaci
Aby usunąć półprzezroczyste piksele z wyrenderowanych klatek postaci, zmień ich wartości alfa na całkowicie przezroczyste albo całkowicie nieprzezroczyste. Wybierz, czy usunąć wszystkie częściowo przezroczyste piksele, ujednolicić wszystkie widoczne piksele, czy zastosować próg zachowujący wyraźniejsze fragmenty konturu.
Ten proces jest przeznaczony dla twórców przygotowujących klatki postaci PNG z przezroczystym tłem i ostrymi krawędziami. Opisuje metodę wizualną w GIMP-ie oraz powtarzalną metodę w Pythonie do przetwarzania całego folderu z klatkami, wraz ze sprawdzeniem, czy wyeksportowane obrazy nie zawierają pośrednich wartości alfa.
Co dokładnie trzeba zmienić w klatce?
W obrazie RGBA o 8-bitowej głębi każdy piksel ma składowe czerwieni, zieleni, błękitu i alfa. Alfa 0 oznacza pełną przezroczystość, a alfa 255 — pełną nieprzezroczystość. Wartości od 1 do 254 oznaczają częściową przezroczystość. Dokumentacja Pillow opisuje RGBA jako cztery 8-bitowe kanały z maską przezroczystości. Pillow: Pojęcia
Klatka może więc wyglądać na niemal całkowicie nieprzezroczystą, a mimo to zawierać wiele częściowo przezroczystych pikseli. Przed edycją obejrzyj kopię w docelowym rozmiarze wyświetlania oraz w powiększeniu, piksel po pikselu. Podłóż pod nią tymczasowo jasne i ciemne tła. Poszukaj słabo widocznych pikseli na zewnętrznej krawędzi, brakujących wąskich detali i kolorowych obwódek.
Zdecyduj, czy częściowa przezroczystość ma pozostać w gotowej grafice. Półprzezroczysty dodatek lub miękki cień znacznie się zmieni, jeśli zostanie sprowadzony do maski nieprzezroczystości i przezroczystości. Jeśli korekty wymaga tylko kontur postaci, przed zastosowaniem globalnej operacji wydziel ten obszar lub warstwę.
Zachowaj oryginalne wyrenderowane klatki. Progowanie sprowadza wiele wartości alfa do dwóch skrajnych wartości, dlatego przetworzony plik nie pozwala ustalić, jak przezroczyste były te piksele pierwotnie.
Czy usunąć częściowo przezroczyste piksele, czy je ujednolicić?
W całej sekwencji klatek stosuj jedną, jasno określoną regułę. W poniższej tabeli C oznacza minimalną wartość alfa, którą należy zachować, a każdy zachowany piksel staje się całkowicie nieprzezroczysty. Te reguły dotyczą danych wejściowych 8-bitowych.
| Oczekiwany wynik | Reguła | Główny kompromis wizualny |
|---|---|---|
| Usuń wszystkie półprzezroczyste piksele | Zachowaj tylko alfę 255 | Wąskie detale złożone wyłącznie z częściowo przezroczystych pikseli znikną |
| Ujednolić wszystkie widoczne piksele | Zachowaj alfę 1–255 | Słabo widoczne piksele na zewnętrznej krawędzi staną się częścią nieprzezroczystej sylwetki |
| Wybierz pośredni kontur | Zachowaj alfę ≥ C; odrzuć alfę < C | Próg określa, które piksele krawędzi zostaną zachowane |
Załóżmy dla przykładu, że wartości alfa to 0, 1, 64, 127, 128, 254, 255. Przy C = 128 wynik to 0, 0, 0, 0, 255, 255, 255. Przy C = 255 zostanie tylko ostatni piksel. Przy C = 1 każdy pierwotnie widoczny piksel stanie się nieprzezroczysty.
Zacznij od C = 128 jako punktu odniesienia, a następnie przyjrzyj się samej postaci. To wygodna wartość środkowa, a nie sprawdzona najlepsza wartość. Podnieś ją, jeśli pozostają niepożądane, słabo widoczne piksele; obniż, jeśli znikają przydatne detale. Przy każdym porównaniu wracaj do oryginalnej klatki.
Jeśli podniesienie progu usuwa obwódkę, ale zarazem przerywa istotny detal, jeden globalny próg nie spełni obu celów. Popraw ten detal lokalnie lub wydziel go do osobnej maski przed przetworzeniem pozostałej części konturu.
Jak progować alfę w GIMP-ie?
GIMP 3 udostępnia opcję Warstwa → Przezroczystość → Próg alfa…. Działa ona na jednej warstwie, wymaga kanału alfa i nie daje efektu, gdy włączona jest opcja Zablokuj kanał alfa. Próg przyjmuje wartość od 0 do 1: wartości powyżej progu stają się nieprzezroczyste, a wartości równe progowi lub niższe — przezroczyste. GIMP: Próg alfa
- Otwórz kopię klatki i zaznacz warstwę postaci.
- Sprawdź, czy ma kanał alfa, i wyłącz opcję Zablokuj kanał alfa.
- Otwórz Warstwa → Przezroczystość → Próg alfa….
- Ustaw próg 0.5 jako wartość pośrednią lub 0, aby ujednolicić każdy piksel o niezerowej alfie.
- Przed zastosowaniem operacji sprawdź drobne detale i zewnętrzny kontur.
- Wyeksportuj wynik jako osobny plik PNG.
W przypadku danych wejściowych 8-bitowych próg 254/255, czyli około 0.996078, zachowuje tylko piksele, których pierwotna alfa wynosiła 255. Nie ustawiaj w tym celu progu na 1: zgodnie z udokumentowanym ścisłym porównaniem nawet alfa 1 jest równa progowi i staje się przezroczysta. GIMP: Próg alfa
Nie dołączaj do eksportowanego pliku postaci tymczasowych teł użytych do kontroli. Otwórz później plik PNG ponownie, aby ocenić zarówno wyeksportowany wynik, jak i edytowalny dokument.
Jak zastosować tę samą regułę do wszystkich klatek?
W przypadku wielu klatek zastosuj stałe mapowanie alfa zamiast zmieniać każdą klatkę niezależnie. Poniższy kompletny program w Pythonie przetwarza bezpośrednio pliki PNG znajdujące się w jednym folderze, zachowuje ich nazwy i wymiary oraz zapisuje wyniki w nowym folderze.
Zainstaluj Pillow poleceniem python -m pip install Pillow, a następnie zapisz program jako alpha_clean.py. Użyj osobnych, statycznych plików PNG RGBA o 8-bitowej głębi. Obrazy indeksowane, renderingi o większej precyzji lub obrazy w innych formatach przed użyciem tego procesu należy celowo wyeksportować do tego formatu.
Program pobiera alfę za pomocą getchannel, mapuje ją przy użyciu tablicy przeglądowej z point i zastępuje wyłącznie alfę za pomocą putalpha. Te operacje opisano w dokumentacji modułu Image biblioteki Pillow.
import argparse
from pathlib import Path
from PIL import Image
def threshold_alpha(image, cutoff):
result = image.copy()
table = [255 if value >= cutoff else 0 for value in range(256)]
result.putalpha(image.getchannel("A").point(table))
return result
def partial_count(image):
return sum(image.getchannel("A").histogram()[1:255])
def main():
parser = argparse.ArgumentParser()
parser.add_argument("source", type=Path)
parser.add_argument("destination", type=Path)
parser.add_argument("--cutoff", type=int, default=128)
parser.add_argument("--demo", action="store_true")
args = parser.parse_args()
if not 1 <= args.cutoff <= 255:
parser.error("--cutoff must be between 1 and 255")
if args.demo:
values = [0, 1, 64, 127, 128, 254, 255]
sample = Image.new("RGBA", (len(values), 1))
sample.putdata([(80, 160, 220, a) for a in values])
result = threshold_alpha(sample, args.cutoff)
print([result.getpixel((x, 0))[3] for x in range(len(values))])
return
if not args.source.is_dir():
parser.error("Source must be an existing folder")
if args.destination.exists():
parser.error("Destination must be a new folder")
files = sorted(
p for p in args.source.iterdir()
if p.is_file() and p.suffix.lower() == ".png"
)
if not files:
parser.error("No PNG frames found directly inside source")
args.destination.mkdir(parents=True)
for path in files:
with Image.open(path) as image:
if image.format != "PNG" or image.mode != "RGBA":
raise ValueError(f"{path.name}: expected RGBA PNG")
if getattr(image, "n_frames", 1) != 1:
raise ValueError(f"{path.name}: expected a single frame")
before = partial_count(image)
result = threshold_alpha(image, args.cutoff)
target = args.destination / path.name
result.save(target, format="PNG")
with Image.open(target) as saved:
after = partial_count(saved)
if saved.size != result.size or after != 0:
raise RuntimeError(f"{target.name}: verification failed")
print(f"{path.name}: partial pixels {before} -> {after}")
if __name__ == "__main__":
main()
Najpierw uruchom samodzielny przykład. W trybie demonstracyjnym oba argumenty folderów są przyjmowane, ale żaden folder nie jest odczytywany ani tworzony:
python alpha_clean.py unused_input unused_output --demo --cutoff 128
Program wypisuje [0, 0, 0, 0, 255, 255, 255]. Mapowania dla progów 1, 128 i 255, niezmienione wartości RGB oraz zachowanie przy zapisie i ponownym otwarciu pliku PNG sprawdzono na próbce w pamięci z użyciem Pillow 13.0.0.
Aby przetworzyć właściwe klatki, umieść wejściowe pliki PNG w folderze o nazwie frames, a następnie uruchom:
python alpha_clean.py frames frames_clean --cutoff 128
Użyj --cutoff 255, aby usunąć wszystkie półprzezroczyste piksele, lub --cutoff 1, aby ujednolicić wszystkie widoczne piksele. Do każdego porównania wybierz nowy folder docelowy.
Program zatrzymuje się w przypadku nieobsługiwanych danych wejściowych lub nieudanego sprawdzenia. Pliki zapisane przed wystąpieniem błędu pozostają w folderze docelowym; taki przebieg jest niekompletny. Popraw dane wejściowe i uruchom program ponownie, wskazując inny, nowy folder. Program nie przeszukuje podfolderów, nie zachowuje czasu animacji i nie służy do ogólnego zachowywania metadanych.
Dlaczego ujednolicona krawędź nadal może wyglądać na ciemną lub bladą?
Czyszczenie alfy zmienia pokrycie. Kolor krawędzi wymaga osobnej kontroli. Microsoft odróżnia alfę prostą, w której RGB i alfa są niezależne, od alfy premnożonej, w której RGB uwzględnia już mnożenie przez alfę. Microsoft Learn: Alfa premnożona
Jako przykładowe obliczenie: kolor czysto czerwony (200, 0, 0) przy alfie 0.25 odpowiada premnożonemu RGB (50, 0, 0). Jeśli te premnożone wartości kolorów zostaną błędnie potraktowane jak zwykłe RGB, a alfa zostanie po prostu zwiększona do 1, wynikiem będzie ciemnoczerwony piksel. Wynika to bezpośrednio z udokumentowanej zależności przeliczeniowej. Microsoft Learn: Alfa premnożona
Proces w Pythonie zakłada prawidłowo zakodowane wejściowe pliki PNG RGBA z alfą prostą. Nie wykryje nieprawidłowo zakodowanych kolorów ani nie odtworzy zamierzonego koloru konturu. Jeśli ujednolicenie pikseli ujawni obwódkę, najpierw sprawdź sposób interpretacji alfy podczas pierwotnego renderowania lub eksportu. Jeśli kolor został zanieczyszczony przez wcześniejsze nałożenie obrazu na tło, wróć do czystego renderu z przezroczystym tłem lub przemaluj krawędź.
Porównaj oczyszczoną klatkę na jasnym i ciemnym tle. Zerowa liczba częściowo przezroczystych pikseli potwierdza, że alfa ma wartości binarne; nie oznacza to, że kolory krawędzi pasują do postaci.
Jak zachować efekt czyszczenia po eksporcie i podczas odtwarzania?
Zmiany rozmiaru wykonaj przed końcowym czyszczeniem alfy. Interpolacja przy resamplingu może wprowadzić wartości pośrednie między przezroczystymi a nieprzezroczystymi pikselami. Dokumentacja Pillow opisuje resampling biliniowy jako obliczanie wartości przez interpolację, podczas gdy najbliższy sąsiad wybiera istniejący piksel wejściowy. W efekcie zmiana rozmiaru binarnego kanału alfa metodą najbliższego sąsiada zachowuje jego dwie skrajne wartości. Pillow: Pojęcia — filtry
Użyj metody najbliższego sąsiada, gdy zamierzony wygląd wymaga wyraźnej siatki pikseli. W przypadku innego stylu wizualnego najpierw zmień rozmiar wybranym filtrem, a potem progowuj uzyskane klatki i ponownie sprawdź ich kontury. Po późniejszym skalowaniu lub nałożeniu obrazu na inne tło sprawdź ponownie wynik końcowy.
Obejrzyj całą sekwencję, stosując ten sam próg. Wąski element może przekroczyć próg między sąsiednimi klatkami i migotać, nawet jeśli każda klatka z osobna wygląda dobrze. Jeśli tak się stanie, sprawdź oryginalne klatki w okolicy tej zmiany i popraw element spójnie we wszystkich tych klatkach.
Przed użyciem sekwencji sprawdź następujące konkretne warunki:
- Wszystkie oczekiwane klatki są obecne i mają oryginalne nazwy plików oraz wymiary płótna.
- Każda zapisana klatka zawiera zero pikseli z alfą od 1 do 254.
- Drobne detale pozostają czytelne w docelowym rozmiarze wyświetlania.
- Podglądy na jasnym i ciemnym tle nie ujawniają niepożądanej obwódki.
- Podczas odtwarzania sylwetka pozostaje spójna w całym ruchu.
Wybierz regułę alfa, która zachowuje zamierzony kształt postaci, stosuj ją konsekwentnie i sprawdź wyeksportowaną sekwencję. Zachowaj oryginalne rendery obok oczyszczonych klatek, aby późniejsze poprawki można było rozpocząć z pełną informacją o przezroczystości.
