Teknisk artikel

Formindsk PDF'er ved at sammenlægge næsten identiske billeder i Delphi

PDF Library for Delphi kan sammenlægge billeder inde i en PDF, der ser ens ud, men ikke er byte-identiske, via DeduplicateSimilarImages. Eksakt deduplikering sammenlægger kun billeder, hvis dictionaries og kodede payloads matcher; denne sammenligner i stedet, hvordan billederne faktisk ser ud, ved brug af et perceptuelt hash plus to pixelfejl-tærskler, og omdirigerer hver reference til ét enkelt kanonisk objekt

De filer, hvor dette betaler sig, er dem, der er vokset ved et uheld. En scannet batch, hvor hver side bærer det samme logo, genkodet af en anden scanner. En rapport samlet fra kilder, der hver indlejrede det samme diagram i en lidt anden JPEG-kvalitet. Et katalog, hvis miniaturebilleder blev regenereret af to værktøjer. I hvert tilfælde indeholder filen mange kopier af ét billede, og eksakt deduplikering kan ikke røre nogen af dem

Hvad "tilstrækkeligt ens" skal betyde

Tre tærskler skal alle være opfyldt, før to billeder sammenlægges, og hver fanger en forskellig slags forskel. Den perceptuelle hash-distance afgrænser strukturel forskel: et 64-bit forskels-hash beregnes ud fra en 64 x 64-prøve af det afkodede billede, og den tilladte Hamming-distance løber fra 0 til 7. Den gennemsnitlige kanalfejl afgrænser overordnet drift, hvilket er, hvad genkodning giver. Den maksimale enkeltkanalfejl afgrænser lokaliseret forskel, hvilket er, hvad en lille redigering giver

At kræve alle tre er, hvad der gør sammenlægningen forsvarlig. Et billede, der adskiller sig ved et vandmærke i ét hjørne, har en lav gennemsnitsfejl og en høj maksimumfejl, så maksimumfejl-porten afviser det. Et billede, der er blevet genkomprimeret, har en lille fejl overalt, så det består. Det er præcis den skelnen, en størrelsesoptimering skal drage

Tre tærskler porter sammenlægningen af næsten identiske billeder: hash-distance, gennemsnitlig kanalfejl og maksimal enkeltkanalfejl
Alle tre porte bruger afkodede pixels — et vandmærkepar dør ved maksimumfejl-porten, mens en genkodet tvilling sejler igennem
uses
  PDFlibrary;

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

    // Hash-distance 2, gennemsnitsfejl 2, enkeltkanalfejl 4
    // Options kan bede om en tørkørsel, så intet ændres
    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;

Semantisk identitet kommer før visuel lighed

Før nogen pixels sammenlignes, skal kandidatbilleder stemme overens semantisk. Biblioteket bygger en stabil signatur ud fra hele billed-dictionary'en, undtaget /Length, /Filter og /DecodeParms, så dimensioner, farverum, bitdybde, masker, /Decode-arrayet, interpolation, medlemskab af optional content og metadata alle skal matche

Denne rækkefølge forhindrer en hel klasse af forkerte sammenlægninger. To billeder kan se identiske ud i en 64 x 64-prøve og opføre sig forskelligt på siden: ét med /Interpolate sat til true og ét uden vil blive udjævnet forskelligt, når det skaleres, og ét, der tilhører en optional content-gruppe, vil forsvinde, når det lag skjules. At sammenlægge på tværs af de grænser giver et dokument, der er mindre og forkert

Kun indirekte Image XObjects scannes. Inline-billeder er en anden konstruktion med en anden økonomi, og billeder, der ikke kan afkodes, springes konservativt over og tælles frem for at blive sammenlagt på tro og love — ingen pixel-bevis, ingen sammenlægning

Sådan holdes søgningen billig

At sammenligne hvert billede mod hvert andet billede er kvadratisk, hvilket er uacceptabelt på et dokument med tusindvis af billeder. Hashet opdeles derfor i otte 8-bit bånd med et ordnet indeks over hver

Dueslagsprincippet klarer resten: hvis to hashes adskiller sig med højst 7 bit, skal de stemme nøjagtigt overens på mindst ét af de otte bånd. At slå kandidater op bånd for bånd finder derfor hvert tilladt match uden en udtømmende parvis scanning, og den offentliggjorte grænse på 7 findes netop, fordi garantien kun holder op til den distance

Det 64-bit forskels-hash opdeles i otte 8-bit bånd, og dueslagsprincippet garanterer, at ét bånd stadig matcher efter syv bit-flip
Syv flippede bit fordelt på otte bånd må efterlade mindst ét bånd intakt, hvilket forvandler en kvadratisk scanning til otte ordnede opslag

Kandidater og bånd-hits ordnes deterministisk efter objektnummer og generation, og det laveste objektnummer bliver kanonisk. Det gør outputtet reproducerbart: at køre optimeringen to gange på det samme input giver de samme kanoniske valg, hvilket betyder noget, når resultatet sammenlignes eller signeres

Tørkørsler og ærlig rapportering

Fordi dette er en lossy optimering i den forstand, at de sammenlagte billeder ikke var identiske, er det fornuftige arbejdsforløb at se sig for, før man springer. Tørkørsel-indstillingen rapporterer, hvad der ville blive sammenlagt, og hvor meget payload der ville blive frigjort, uden at ændre objektgrafen, så et batch-job kan logge effekten, og et menneske kan godkende tærsklerne én gang frem for pr. fil

To ærlige bemærkninger i rapporteringen fortjener nævnelse. Det genindvindelige tal estimerer kun den kodede payload af duplikerede streams; det tæller ikke dictionary-, indirekte objekt- og cross-reference-overhead med, så den faktiske filstørrelsesreduktion er lidt større end rapporteret frem for mindre. Og rapporten eksponerer den maksimalt accepterede fejl og sammenligningsarbejdsmængden, så du kan se, hvor tæt sammenlægningerne kom på dine tærskler

Det faste 64 x 64-prøvegitter er en bevidst approksimation, og det har en konsekvens, det er værd at sige ligeud: en forskel mindre end én prøvecelle kan gå ubemærket hen, selv ved en nul-tærskel. Ét ændret ord på en scannet side overlever muligvis ikke den prøvetagning. Når byte-niveau-bevis er påkrævet, brug den eksakte deduplikeringssti i stedet — den sammenlægger kun genuint identiske ressourcer og fælder slet ingen visuel bedømmelse

Hvor det hører hjemme i en størrelsesreduktionspipeline

Ordn passeringerne efter risiko. Kør eksakt ressourcededuplikering først, da den er tabsfri og gratis. Så skrifttype-delmængdedannelse, som også er tabsfri og som regel den største enkeltstående gevinst på teksttunge dokumenter, som dækket i optimering af PDF-filstørrelse og skrifttype-delmængdedannelse. Så billedgenkomprimering eller nedskalering, hvis målmediet tillader det. Perceptuel deduplikering hører hjemme efter dem, fordi det er det eneste trin, hvis resultat afhænger af en bedømmelse

Anbefalet pipeline-rækkefølge fra tabsfri eksakt deduplikering og skrifttype-delmængdedannelse til betinget billedgenkomprimering, perceptuel deduplikering og verifikation
Hvert trin tilføjer risiko, så tabsfrie gevinster kommer først, og bedømmelsessammenlægningen kører sidst, efterfulgt af et gengiv-og-sammenlign-tjek

Vælg tærskler efter dokumentklasse frem for globalt. For scannede batches, hvor hver side bærer det samme brevhoved, sammenlægger en hash-distance på 2 til 3 med en gennemsnitsfejl omkring 2 brevhovedet pålideligt. For dokumenter, hvor billeder bærer data — diagrammer, kort, medicinske billeder — brug nul eller nær-nul-tærskler, eller spring passeringen helt over, fordi to diagrammer, der adskiller sig i ét plottet punkt, er to forskellige diagrammer, uanset hvor ens de ser ud ved 64 x 64

Efter optimering, verificér. Gengiv en stikprøve af sider før og efter, og sammenlign dem, og tjek, at side- og billedantallet ændrede sig, som du forventede. Inspektionsvejen for, hvad et dokument faktisk indeholder, er beskrevet i audit af PDF-filstørrelse efter kategori, hvilket også er den hurtigste måde at finde ud af, om billeder overhovedet er dit problem

Perceptuel deduplikering, eksakt ressourcededuplikering og størrelsesauditeringen kører på den samme objektgraf til Delphi, C++Builder og Free Pascal; den komplette funktionsliste findes på PDF Library for Delphi-siden