Dataczwartek, 13 sierpnia 2026 Czas18:01:56
← Grafika i multimedia

Lekcja 4. Kompresja danych — dlaczego JPG waży mniej niż BMP

Średni

Po co się tego uczymy?

To samo zdjęcie zapisane jako BMP może ważyć 20 MB, a jako JPG — 500 KB, przy niemal identycznym wyglądzie na ekranie. Ta 40-krotna różnica to nie magia, tylko efekt ALGORYTMÓW KOMPRESJI — matematycznych sposobów zapisywania tej samej informacji przy użyciu mniejszej liczby bajtów. To temat teoretyczny, ale regularnie sprawdzany na pisemnej części matury z informatyki.

Teoria

Kompresja bezstratna (lossless) — zmniejsza rozmiar pliku bez utraty JAKIEJKOLWIEK informacji — po dekompresji otrzymujemy DOKŁADNIE taki sam plik jak oryginał, bit po bicie. Przykłady formatów: PNG, ZIP, FLAC (dźwięk). Działa dobrze na danych z powtórzeniami (np. duże jednolite obszary koloru).

Kompresja stratna (lossy) — TRWALE usuwa część informacji, której brak jest mało zauważalny dla ludzkiego oka/ucha, uzyskując znacznie mniejszy plik niż kompresja bezstratna. Po dekompresji NIE odzyskamy oryginału — to jednokierunkowa operacja. Przykłady: JPG, MP3, MP4. Im wyższy stopień kompresji stratnej, tym mniejszy plik, ale i więcej widocznych "artefaktów" (zamazań, kwadratowych zniekształceń).

Kodowanie długości serii (RLE — Run-Length Encoding) — jeden z najprostszych algorytmów kompresji BEZSTRATNEJ: zamiast zapisywać powtarzający się ciąg identycznych wartości jeden po drugim, zapisuje się PARĘ (wartość, liczba powtórzeń). Np. ciąg AAAAAAAA (8 znaków) zapisuje się jako 8A (2 znaki) — idealne dla obrazów z dużymi, jednolitymi obszarami koloru.

Kodowanie Huffmana — bardziej zaawansowany algorytm bezstratny: znaki, które WYSTĘPUJĄ CZĘŚCIEJ w danych, dostają KRÓTSZY kod binarny, a znaki rzadkie — dłuższy kod. Dzięki temu średnia długość zapisu całego pliku maleje. To fundament działania formatów takich jak ZIP czy JPEG (w wewnętrznej fazie kompresji bezstratnej).

Dlaczego JPG jest mały — JPG wykorzystuje właściwość ludzkiego oka, które jest znacznie mniej czułe na subtelne różnice KOLORU niż na różnice JASNOŚCI — algorytm celowo "zaokrągla"/upraszcza informację o kolorze (nie o jasności), tracąc dane niezauważalne dla większości widzów, ale drastycznie zmniejszając rozmiar pliku.

Współczynnik kompresji — stosunek rozmiaru pliku oryginalnego do rozmiaru pliku skompresowanego (np. plik 20 MB skompresowany do 500 KB ma współczynnik kompresji 40:1).

Kiedy unikać kompresji stratnej — pliki, które będą WIELOKROTNIE edytowane i zapisywane ponownie (np. plik roboczy grafika), NIGDY nie powinny być trzymane w formacie stratnym — każdy kolejny zapis JPG dokłada NOWE straty jakości (efekt kumulacyjny), dlatego prace w toku zapisuje się w formatach bezstratnych (PNG, XCF, PSD), a JPG dopiero na sam koniec, do dystrybucji.

Schemat

RLE (Run-Length Encoding) - przykład na wierszu pikseli:

DANE ORYGINALNE (16 znaków):
BBBBBBBB WWWWWWWW
(8 czarnych, 8 białych)

PO KOMPRESJI RLE (4 znaki):
8B 8W
             ↑
   zamiast zapisywać każdy piksel osobno,
   zapisujemy PARĘ (kolor, liczba powtórzeń)

Współczynnik kompresji: 16 znaków → 4 znaki = 4:1

Przykład z życia

Serwis streamingowy wideo (np. platforma z filmami) MUSI używać silnej kompresji stratnej (H.264/H.265) — bez niej przesłanie filmu w jakości 4K wymagałoby internetu o przepustowości nieosiągalnej dla przeciętnego domu. Niewielka, niemal niezauważalna utrata jakości obrazu jest akceptowalnym kompromisem za możliwość oglądania filmu w ogóle.

kompresja_rle.py

# Prosty algorytm kompresji RLE (Run-Length Encoding) w Pythonie

def kompresuj_rle(dane):
    wynik = ""
    i = 0
    while i < len(dane):
        znak = dane[i]
        licznik = 1
        # Licz, ile razy z rzędu powtarza się ten sam znak
        while i + licznik < len(dane) and dane[i + licznik] == znak:
            licznik += 1
        wynik += str(licznik) + znak
        i += licznik
    return wynik


def dekompresuj_rle(dane_skompresowane):
    wynik = ""
    i = 0
    while i < len(dane_skompresowane):
        # Wczytaj liczbę (może mieć więcej niż jedną cyfrę)
        start_liczby = i
        while dane_skompresowane[i].isdigit():
            i += 1
        licznik = int(dane_skompresowane[start_liczby:i])
        znak = dane_skompresowane[i]
        wynik += znak * licznik
        i += 1
    return wynik


oryginal = "BBBBBBBBWWWWWWWW"
skompresowane = kompresuj_rle(oryginal)
print(f"Oryginał ({len(oryginal)} znaków): {oryginal}")
print(f"Skompresowane ({len(skompresowane)} znaków): {skompresowane}")
print(f"Odtworzone: {dekompresuj_rle(skompresowane)}")

Komentarz i wyjaśnienie kodu

To ten sam Python, którego uczysz się w dziale "Python — programowanie" — kompresja danych to nie abstrakcyjna teoria, tylko konkretny ALGORYTM, który można zaimplementować w kilku liniach kodu i przetestować na dowolnym tekście. Zwróć uwagę na pętlę while i + licznik < len(dane) and dane[i + licznik] == znak — to klasyczny wzorzec "licz powtórzenia", który pojawia się też w innych algorytmach przetwarzania sekwencji.

Warto zauważyć, że RLE działa świetnie dla danych z DŁUGIMI powtórzeniami (jak w przykładzie), ale dla danych bez powtórzeń (np. losowy tekst) może wręcz ZWIĘKSZYĆ rozmiar pliku — dlatego różne algorytmy kompresji nadają się do różnych typów danych.

Ćwiczenie samodzielne

Uruchom powyższy kod (możesz skopiować go do Plac zabaw poniżej lekcji) i sprawdź działanie na własnym przykładowym tekście, np. "AAABBBCCCCD". Policz ręcznie współczynnik kompresji.

Plac zabaw — wypróbuj online

Poniższy kod wykonuje się od razu w Twojej przeglądarce — nic nie trzeba instalować. Zmień kod i kliknij „Uruchom”.

PYTHON

Zadania do pracy własnej

  1. Wykonaj ręcznie (na kartce) kompresję RLE dla ciągu znaków "WWWWWWCCCCCCCCWW" i podaj wynikowy, skompresowany zapis oraz współczynnik kompresji.

  2. Zmodyfikuj powyższy kod Pythona, dodając funkcję procent_oszczednosci(oryginal, skompresowane), która zwraca, o ile procent zmniejszył się rozmiar danych po kompresji. Przetestuj na 3 różnych przykładowych ciągach znaków (jeden z dużą liczbą powtórzeń, jeden bez powtórzeń, jeden pośredni) i porównaj wyniki.

  3. Rozbuduj algorytm RLE tak, aby POPRAWNIE obsługiwał sytuację, gdy pojedynczy, niepowtarzający się znak WYSTĘPUJE w danych (np. "ABCCCCD" — sprawdź, czy Twoja implementacja nie "psuje się" ani nie marnuje miejsca, zapisując 1A1B4C1D). Napisz też krótkie uzasadnienie tekstowe, dlaczego kompresja RLE NIE nadaje się dobrze do kompresji zdjęć fotograficznych (w przeciwieństwie do prostej grafiki płaskiej, jak zrzuty ekranu z dużymi jednolitymi obszarami).

Typowe błędy

Wielokrotny zapis tego samego pliku w formacie stratnym (JPG) podczas kolejnych sesji edycji — każdy zapis dokłada NOWE straty jakości, sumujące się z poprzednimi (efekt kumulacyjnej degradacji) — pliki robocze zawsze trzymaj w formacie bezstratnym.

Mylenie kompresji bezstratnej ze stratną przy wyborze formatu — zapisanie zdjęcia z gradientami jako PNG (bezstratny) zamiast JPG daje dużo WIĘKSZY plik bez realnej korzyści wizualnej, bo oko i tak nie odróżni subtelnej straty jakości JPG.

Zakładanie, że kompresja zawsze zmniejsza rozmiar — dla danych bez powtórzeń algorytm RLE może wręcz POWIĘKSZYĆ plik (każdy pojedynczy znak zamienia się w parę "1+znak", czyli 2 znaki zamiast 1).

Nawiązanie do egzaminu zawodowego

Zrozumienie kompresji przygotowuje Cię do ostatnich dwóch lekcji tego działu — prezentacji multimedialnych (gdzie dobór formatu obrazu/wideo bezpośrednio wpływa na rozmiar pliku prezentacji) oraz wizualizacji 3D, gdzie skala danych jest jeszcze większym wyzwaniem.