Artykuł techniczny

Zmniejszanie PDF przez scalanie niemal identycznych obrazów w Delphi

PDF Library for Delphi potrafi scalić obrazy wewnątrz PDF, które wyglądają tak samo, ale nie są identyczne bajtowo, za pomocą DeduplicateSimilarImages. Dokładna deduplikacja scala tylko obrazy, których słowniki i zakodowane ładunki się zgadzają; ta porównuje to, jak obrazy faktycznie wyglądają, wykorzystując hash percepcyjny plus dwa progi błędu pikseli, i przekierowuje każde odwołanie do jednego kanonicznego obiektu

Pliki, w których to się opłaca, to te, które urosły przypadkiem. Zeskanowana partia, w której każda strona niesie to samo logo ponownie zakodowane przez inny skaner. Raport złożony ze źródeł, z których każde osadziło ten sam wykres w nieco innej jakości JPEG. Katalog, którego miniatury zostały ponownie wygenerowane przez dwa narzędzia. W każdym przypadku plik zawiera wiele kopii jednego obrazu, a dokładna deduplikacja nie może dotknąć żadnej z nich

Co musi znaczyć „wystarczająco podobne”

Muszą być spełnione wszystkie trzy progi, zanim dwa obrazy zostaną scalone, a każdy wyłapuje inny rodzaj różnicy. Odległość hasha percepcyjnego ogranicza różnicę strukturalną: 64-bitowy hash różnicowy jest obliczany z próbki 64 na 64 zdekodowanego obrazu, a dozwolona odległość Hamminga wynosi od 0 do 7. Średni błąd kanału ogranicza ogólny dryf, który jest efektem ponownego kodowania. Maksymalny błąd pojedynczego kanału ogranicza zlokalizowaną różnicę, będącą efektem drobnej edycji

To wymóg wszystkich trzech sprawia, że scalenie jest uzasadnione. Obraz różniący się znakiem wodnym w jednym rogu ma niski średni błąd i wysoki błąd maksymalny, więc bramka błędu maksymalnego go odrzuca. Obraz, który został ponownie skompresowany, ma niewielki błąd wszędzie, więc przechodzi. To dokładnie to rozróżnienie, które musi wyznaczyć optymalizator rozmiaru

Trzy progi bramkują scalanie niemal identycznych obrazów: odległość hasha, średni błąd kanału i maksymalny błąd pojedynczego kanału
Wszystkie trzy bramki korzystają ze zdekodowanych pikseli — para ze znakiem wodnym ginie na bramce błędu maksymalnego, podczas gdy ponownie zakodowany bliźniak przechodzi bez problemu
uses
  PDFlibrary;

var
  Lib: TPDFlib;
  Stats: TPDFlibImageSimilarityDedupStats;
  Merged: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('scanned-batch.pdf', '');

    // Odległość hasha 2, średni błąd 2, błąd pojedynczego kanału 4.
    // Opcje mogą zażądać przebiegu próbnego, dzięki czemu nic nie jest modyfikowane
    Merged := Lib.DeduplicateSimilarImages(2, 2, 4, 0, Stats);
    if Merged < 0 then
      raise Exception.Create('deduplication failed');

    Log(Format('%d scanned, %d merged, %d refs repointed, %d bytes duplicated',
      [Stats.ImagesScanned, Stats.ImagesDeduplicated,
       Stats.ReferencesRepointed, Stats.DuplicatePayloadBytes]));
    Log(Format('worst accepted: hash %d, mean %.2f, channel %d',
      [Stats.MaxAcceptedHashDistance, Stats.MaxAcceptedMeanError,
       Stats.MaxAcceptedChannelError]));
    Lib.SaveToFile('scanned-batch-optimised.pdf');
  finally
    Lib.Free;
  end;
end;

Tożsamość semantyczna przed podobieństwem wizualnym

Zanim porówna się jakiekolwiek piksele, obrazy kandydujące muszą się zgadzać semantycznie. Biblioteka buduje stabilną sygnaturę z pełnego słownika obrazu z wyłączeniem /Length, /Filter i /DecodeParms, więc wymiary, przestrzeń kolorów, głębia bitowa, maski, tablica /Decode, interpolacja, przynależność do treści opcjonalnej i metadane muszą się wszystkie zgadzać

Ta kolejność zapobiega całej klasie błędnych scaleń. Dwa obrazy mogą wyglądać identycznie w próbce 64 na 64, a zachowywać się różnie na stronie: jeden z /Interpolate ustawionym na true, a drugi bez tego będą inaczej wygładzane przy skalowaniu, a ten należący do grupy treści opcjonalnej zniknie, gdy ta warstwa zostanie ukryta. Scalanie ponad tymi granicami daje dokument mniejszy i błędny

Skanowane są tylko pośrednie obiekty Image XObject. Obrazy osadzone w tekście (inline) to inna konstrukcja o innej ekonomii, a obrazy, których nie da się zdekodować, są ostrożnie pomijane i liczone, a nie scalane na wiarę — bez dowodu na poziomie pikseli nie ma scalenia

Jak przeszukiwanie pozostaje tanie

Porównywanie każdego obrazu z każdym innym jest kwadratowe, co jest nieakceptowalne w dokumencie zawierającym tysiące obrazów. Hash jest więc dzielony na osiem 8-bitowych pasm, z uporządkowanym indeksem dla każdego z nich

Resztę robi zasada szufladkowa: jeśli dwa hashe różnią się co najwyżej o 7 bitów, muszą się dokładnie zgadzać na co najmniej jednym z ośmiu pasm. Wyszukiwanie kandydatów pasmo po paśmie znajduje więc każde dopuszczalne dopasowanie bez wyczerpującego przeszukiwania parami, a opublikowany limit 7 istnieje właśnie dlatego, że gwarancja obowiązuje tylko do tej odległości

64-bitowy hash różnicowy dzieli się na osiem 8-bitowych pasm, a zasada szufladkowa gwarantuje, że jedno pasmo wciąż się zgadza po siedmiu odwróceniach bitów
Siedem odwróconych bitów rozłożonych na osiem pasm musi pozostawić jedno pasmo nietknięte, co zamienia przeszukiwanie kwadratowe w osiem uporządkowanych wyszukiwań

Kandydaci i trafienia pasm są uporządkowani deterministycznie według numeru obiektu i generacji, a najniższy numer obiektu staje się kanoniczny. To sprawia, że wynik jest odtwarzalny: dwukrotne uruchomienie optymalizatora na tym samym wejściu daje te same kanoniczne wybory, co ma znaczenie, gdy wynik jest porównywany albo podpisywany

Przebiegi próbne i uczciwe raportowanie

Ponieważ jest to optymalizacja stratna w tym sensie, że scalane obrazy nie były identyczne, sensownym sposobem pracy jest najpierw spojrzeć, zanim się skoczy. Opcja przebiegu próbnego zgłasza, co zostałoby scalone i ile ładunku zostałoby odzyskane, bez modyfikowania grafu obiektów, więc zadanie wsadowe może logować efekt, a człowiek może zatwierdzić progi raz, zamiast dla każdego pliku z osobna

Warto wspomnieć o dwóch uczciwych uwagach w raportowaniu. Liczba możliwa do odzyskania szacuje wyłącznie zakodowany ładunek duplikatów strumieni; nie liczy narzutu słownika, obiektu pośredniego i odsyłaczy krzyżowych, więc rzeczywista redukcja rozmiaru pliku jest nieco większa niż zgłoszona, a nie mniejsza. A raport ujawnia maksymalny zaakceptowany błąd i nakład porównań, więc widać, jak blisko progów znalazły się scalenia

Stała siatka próbkowania 64 na 64 to celowe przybliżenie, i ma konsekwencję wartą jasnego wypowiedzenia: różnica mniejsza niż jedna komórka próbki może pozostać niezauważona nawet przy zerowym progu. Pojedyncze zmienione słowo na zeskanowanej stronie może nie przetrwać takiego próbkowania. Gdy wymagany jest dowód na poziomie bajtów, użyj zamiast tego ścieżki dokładnej deduplikacji — scala ona wyłącznie zasoby naprawdę identyczne i w ogóle nie dokonuje oceny wizualnej

Gdzie to pasuje w potoku redukcji rozmiaru

Uporządkuj przebiegi według ryzyka. Uruchom najpierw dokładną deduplikację zasobów, ponieważ jest bezstratna i darmowa. Potem tworzenie podzbioru czcionek, które również jest bezstratne i zwykle stanowi pojedynczy największy zysk w dokumentach bogatych w tekst, co opisano w optymalizacji rozmiaru pliku PDF i tworzeniu podzbiorów czcionek. Potem ponowna kompresja obrazów albo zmniejszenie rozdzielczości, jeśli medium docelowe na to pozwala. Deduplikacja percepcyjna należy do etapu po tych, ponieważ jest jedynym krokiem, którego wynik zależy od oceny wymagającej osądu

Zalecana kolejność potoku od bezstratnej dokładnej deduplikacji i tworzenia podzbiorów czcionek, przez warunkową ponowną kompresję obrazów, deduplikację percepcyjną, aż po weryfikację
Każdy krok dodaje ryzyko, więc bezstratne zyski są pierwsze, a scalenie wymagające osądu działa jako ostatnie, po nim następuje kontrola renderowania i porównania

Wybieraj progi według klasy dokumentu, a nie globalnie. Dla zeskanowanych partii, w których każda strona niesie ten sam papier firmowy, odległość hasha 2 do 3 ze średnim błędem około 2 niezawodnie scala papier firmowy. Dla dokumentów, w których obrazy niosą dane — wykresy, mapy, obrazowanie medyczne — użyj progów zerowych albo bliskich zeru, albo całkowicie pomiń ten przebieg, ponieważ dwa wykresy różniące się jednym naniesionym punktem to dwa różne wykresy, bez względu na to, jak podobne wyglądają przy próbkowaniu 64 na 64

Po optymalizacji zweryfikuj. Wyrenderuj próbkę stron przed i po i porównaj je, a także sprawdź, czy liczba stron i liczba obrazów zmieniły się tak, jak oczekiwałeś. Ścieżkę inspekcji tego, co dokument faktycznie zawiera, opisano w audytowaniu rozmiaru pliku PDF według kategorii, co jest też najszybszym sposobem, by w ogóle sprawdzić, czy problemem są obrazy

Deduplikacja percepcyjna, dokładna deduplikacja zasobów i audyt rozmiaru działają na tym samym grafie obiektów dla Delphi, C++Builder i Free Pascal; pełną listę funkcji znajdziesz na stronie PDF Library for Delphi