Teknisk artikel

HotPDF image downsampling-kernels og print-dithering

HotPDF eksponerer tre image downsampling-kernels gennem ImageDownsampleKernel-egenskaben og en separat Floyd-Steinberg-omgang gennem RenderOutputDither. Den første styrer, hvordan fotografier ser ud, efter du har formindsket dem for at ramme et størrelsesbudget, den anden styrer, hvordan de ser ud, efter en side er reduceret til sort-hvid. Ingen af dem er slået til som default, og begge er opt-in af samme grund: de koster ægte tid

Pressen, der fører folk hertil, er velkendt. En 60 MB scannet kontrakt skal ud gennem en email-gateway, der afviser alt over 10 MB, eller en bunke kontoudtog skal lande på en fax-agtig monokrom enhed, der renderer hver grå pixel som enten papir eller toner. Begge problemer er resampling-problemer, og begge har et hurtigt svar, der ser dårligt ud, og et langsomt svar, der ser rigtigt ud

Hvad de tre kernels reelt adskiller sig i

THPDFResampleKernel har tre værdier, og de sidder på reelt forskellige punkter på fart- og kvalitetskurven. rkHalftone delegerer til den historiske GDI StretchBlt-vej med HALFTONE-mode, som trods navnet er bilinear-klasse filtrering: hurtig, tilstrækkelig til line art og screenshots og tilbøjelig til de knaggede kanter, du genkender med det samme på nedskalerede fotografier. rkBicubic kører en separabel Catmull-Rom-kernel, og rkLanczos3 kører en separabel windowed sinc med tre-lobe support

Begge separable kernels kører som to passes, vandret og derefter lodret, med 6 til 12 taps per destinationspixel i ren Pascal. Det er omtrent en størrelsesorden langsommere end GDI-vejen, hvilket netop er grunden til, at rkHalftone forbliver default. På en natlig batch af tusindvis af sider er forskellen en scheduling-beslutning, ikke en præference. På et enkelt dokument, en bruger venter på, er Lanczos3 næsten gratis og synligt bedre

Vægtkurverne for de tre HotPDF downsampling-kernels: rkHalftone delegerer til den bilinear-klasse GDI HALFTONE-vej med support ét, rkBicubic kører en separabel Catmull-Rom cubic med support to, og rkLanczos3 kører en windowed sinc med support tre, og bytter omtrent en størrelsesorden fart for synligt bedre fotografier
De tre kernel-værdier sidder på reelt forskellige punkter på fart- og kvalitetskurven: en bilinear-klasse GDI-vej, en Catmull-Rom cubic og en tre-lobe windowed sinc, og de separable kernels normaliserer vægte, så intet ringer forbi sort eller hvid

To implementationsegenskaber er værd at kende, for de afgør, hvad output kan og ikke kan. Rammer clampes ved edge-replikering snarere end wrapping eller fading, og vægtene normaliseres per destinationspixel. Tilsammen betyder de to, at resultatet aldrig ringer under sort eller over hvid, så den klassiske Lanczos overshoot-halo omkring en hård kant ikke optræder som clippede artefakter i det indkodede billede

var
  Pdf: THotPDF;
  Info: THPDFLoadedResourceOptimizationInfo;
  Changed: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('scanned-contract.pdf');
    Pdf.ImageDownsampleKernel := rkLanczos3;   // sæt før kaldet
    Changed := Pdf.DownsampleLoadedImages(150, 82, 4096, Info);
    if Changed > 0 then
    begin
      Writeln('resampled images: ', Info.DownsampledImageCount);
      Writeln('kept calibrated : ', Info.PreservedCalibratedImageCount);
      Pdf.SaveToFile('scanned-contract-150dpi.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

MinimumSavingsBytes-argumentet, 4096 ovenfor, er guarden, der holder operationen ærlig. Genkodning af et billede, der allerede var effektivt komprimeret, kan producere en større stream end originalen, og en downsampler, der blindt erstatter hvert billede, vil af og til få filen, den blev bedt om at formindske, til at vokse. Tærsklen siger: commit kun erstatningen, når den sparer mindst dette antal byte. PreservedCalibratedImageCount melder den anden konservative beslutning, billeder efterladt urørte, fordi de bærer et kalibreret color space, som resampling ville kompromittere

Hvorfor er en forkert polynomiumskoefficient så svær at få øje på?

Fordi en ødelagt interpolationskernel hverken crasher eller kaster, producerer den bare et billede, der ser diskret forkert ud på en måde, ingen kan tilskrive noget. Catmull-Rom-kernelen er piecewise cubic, og dens ydre gren i indlejret Horner-form er ((-0.5t + 2.5)t - 4)t + 2. Skrives den midterste koefficient som -5 i stedet for -4, evaluerer funktionen stadig, returnerer stadig tal i et plausibelt område og producerer stadig et billede

Skaden viser sig som W(1), der evaluerer til -1, hvor den skal være 0. Negative vægte akkumulerer, summen clipper ved nul, og det synlige symptom er en gradient, hvis venstre ende går sort, og en step-kant, der mister sine mellotoner. Intet i fejlen peger på et polynomium. Tjekket, der fanger det på sekunder, er aritmetik snarere end visuelt: en interpolerende kernel skal opfylde W(0) = 1 og W(±1) = W(±2) = 0, og enhver kernel, der misser de tre punkter, har en koefficientfejl, punktum. Assert på de tre værdier i en unit test, og hele klassen af stavefejl-defekter forsvinder

Plot af HotPDF bicubic Catmull-Roms ydre gren, der viser, hvorfor en forkert koefficient gemmer sig: stavefejlen, der skriver -5 i stedet for -4 i den indlejrede Horner-form, evaluerer stadig og efterlader W(1) ved -1 og W(2) ved -2, hvor nul kræves, så at asserte W(0) lig 1 plus de to nul-betingelser fanger det på sekunder
En ødelagt kernel crasher aldrig, den returnerer bare tal, der ser plausible ud, hvilket er grunden til, at øjet ikke kan fange en koefficient-stavefejl. W(0) = 1 med nul ved plus og minus én og to er en unit test på tre linjer

Floyd-Steinberg-dithering, og hvor den hører hjemme i pipelinen

Dither-omgangen er et andet problem end resampling og bor på et andet sted i pipelinen. RenderOutputDither anvender Floyd-Steinberg error diffusion efter sidekomposition, hvilket er den eneste placering, der giver mening for et monokromt print preview eller en fax-agtig eksport: operationen handler om at reducere et færdigt raster til én bit per pixel, ikke om hvordan de enkelte billeder blev skaleret på vejen ind

Algoritmen selv er kort. Luminans tærskles ved 50 procent, og kvantiseringsfejlen diffunderes til fire naboer med de klassiske 7/16-, 3/16-, 5/16- og 1/16-vægte, mod højre, nederst-venstre, nedenfor og nederst-højre. Outputpixelen er 0 eller 255 i hver kanal. Det, det naive alternativ giver dig i stedet, en hård tærskel uden diffusion, forvandler et fotografi til en silhuet og mister hver mellotone, der bar indholdet

Render pipeline-placering af HotPDF Floyd-Steinberg-dithering: RenderOutputDither kører efter sidekomposition på det færdige 24-bit raster, tærskler luminans ved 50 procent og diffunderer hver kvantiseringsfejl til højre og nedenunder med 7/16-, 3/16-, 5/16- og 1/16-vægte gennem en rækkebuffer, der skal akkumulere, og producerer én-bit monokromt output
Dithering hører hjemme efter komposition, fordi den reducerer et færdigt raster til én bit, ikke på grund af, hvordan billeder blev skaleret. Diffusionsvægtene summer til én, og rækkebufferen skal akkumulere snarere end overskrive
// Render-tids dithering til en monokrom preview-enhed
Pdf.RenderOutputDither := True;

// Eller anvend samme omgang på en bitmap, du allerede ejer. Bitmapmen skal
// være pf24bit; funktionen returnerer False i stedet for at gætte
if not HPDFFloydSteinbergDitherBitmap(Preview) then
  raise Exception.Create('dither expects a 24-bit bitmap');

// Direkte kernel-adgang, når du resampler uden for dokumentpipelinen
Small := HPDFResizeBitmapKernel(Large, 640, 480, rkLanczos3);
try
  Small.SaveToFile('thumb.bmp');
finally
  Small.Free;
end;

Der er én implementationdetalje i error diffusion, der bider alle én gang. Række-til-række error-bufferen skal akkumulere. Hver pixel i næste række modtager bidrag fra tre forskellige pixels i den aktuelle række, 3/16-, 5/16- og 1/16-tappene, og tildeler koden i stedet for at lægge til, kasserer hver skrivning det forrige bidrag, og kun den sidste tap overlever. Billedet ser stadig ditheret ud, hvilket er det, der gør det svært at lægge mærke til, men teksturen er forkert, og den tonale gengivelse driver. Testen, der fanger det, er kvantitativ: dither et ensartet mellemgråt felt og kræv, at dækningen i det indre lander mellem 40 og 60 procent

Hvilken kombination skal en størrelsesreduktionspipeline bruge?

Match kernelen til, hvad billederne reelt er, og behandl dithering som en enhedsbekymring snarere end en komprimeringsbekymring. Til fotografiske scans, der skal overleve et størrelsesbudget, bevarer rkLanczos3 ved 150 eller 200 DPI detaljen, folk lægger mærke til, mens pixeltallet skæres ned med en faktor fire eller mere. Til screenshots, diagrammer og line art er rkHalftone reelt fint og meget hurtigere, for de billeder har få tonale gradienter at bevare. Til en blandet batch, hvor du ikke kan inspicere hvert billede, er rkBicubic det fornuftige midterste: bedre end bilinear, omtrent halvt så mange taps som Lanczos3

Downsampling er ét håndtag blandt flere, og det er ikke altid det største. Bilevel-scans responderer normalt langt bedre på encoderen dækket i native JBIG2 bilevel-komprimering i Delphi, hvor gevinsten kommer fra symbol dictionaries snarere end pixeltal. Inden du beslutter dig, hjælper det at vide, hvad der reelt er i filen, hvilket er, hvad udtrækning af billeder og deres decode filters er til: et inventar af billedobjekter og deres eksisterende komprimering fortæller dig, om resampling har noget at hente

Bygger du preview-fladen, der viser resultatet, er det den renderingsvej dokumenteret i rendering af en PDF-side til en bitmap, RenderOutputDither træder i kraft, så det ditherede preview og det ditherede output kommer fra én kodevej snarere end to implementationer, der driver fra hinanden

Det brede princip bag begge features er, at kvalitetsindstillinger bør være eksplicitte og reversible. HotPDF beholder den historiske adfærd som default, så en eksisterende applikation opgraderer uden en uventet ændring i output eller timing, og lægger de bedre udseende, langsommere veje én egenskabstildeling væk. Begge er del af HotPDF Delphi PDF-komponenten, sammen med det resource-optimiserings- og renderingsmaskineri, de bygger på