Tehnički članak

HotPDF kerneli za downsample slika i print dithering

HotPDF izlaže tri kernela za downsample slika kroz svojstvo ImageDownsampleKernel i zaseban Floyd-Steinberg prolaz kroz RenderOutputDither. Prvo kontroliše kako fotografije izgledaju pošto ih smanjite da pogodite budžet veličine, drugo kontroliše kako izgledaju pošto se strana svede na crno i belo. Nijedno nije uključeno po defaultu, i oba su opt-in iz istog razloga: koštaju stvarno vreme

Pritisak koji vas dovodi ovde je poznat. Skenirani ugovor od 60 MB mora da izađe kroz email gateway koji odbija sve preko 10 MB, ili serija izvoda mora da sleti na fax stilski monohromni uređaj koji svaki sivi piksel renderuje kao papir ili toner. Oba problema su problemi resamplovanja, i oba imaju brz odgovor koji izgleda loše i spor odgovor koji izgleda ispravno

U čemu se tri kernela stvarno razlikuju

THPDFResampleKernel ima tri vrednosti, i one sede na stvarno različitim tačkama krive brzine i kvaliteta. rkHalftone delegira na istorijsku GDI StretchBlt putanju sa HALFTONE režimom, koja je uprkos imenu filtriranje bilinear klase: brzo, dovoljno za line art i snimke ekrana, i sklono krupnim ivicama koje na smanjenim fotografijama prepoznajete odmah. rkBicubic radi separabilan Catmull-Rom kernel, a rkLanczos3 radi separabilan windowed sinc sa podrškom od tri režnja

Oba separabilna kernela rade kao dva prolaza, prvo horizontalni pa vertikalni, sa 6 do 12 tapova po odredišnom pikselu u čistom Pascal-u. To je otprilike red veličine sporije od GDI putanje, i upravo zato rkHalftone ostaje podrazumevani. Na noćnom batch-u od hiljada strana razlika je odluka o rasporedu, ne preferenca. Na jednom dokumentu na kog korisnik čeka, Lanczos3 je skoro besplatan i vidljivo bolji

Krive težina tri HotPDF downsampling kernela: rkHalftone delegira na GDI HALFTONE putanju bilinear klase sa podrškom jedan, rkBicubic radi separabilan Catmull-Rom kubik sa podrškom dva, a rkLanczos3 radi windowed sinc sa podrškom tri, menjajući otprilike red veličine brzine za vidljivo bolje fotografije
Tri kernel vrednosti sede na stvarno različitim tačkama krive brzine i kvaliteta: GDI putanja bilinear klase, Catmull-Rom kubik i windowed sinc sa tri režnja, a separabilni kerneli normalizuju težine pa ništa ne zvoni preko crnog ili belog

Dve implementacione osobine vredi znati jer određuju šta izlaz može, a šta ne može. Ivice se stežu replikacijom po ivici umesto umotavanjem ili bledljenjem, a težine se normalizuju po odredišnom pikselu. Zajedno te dve znače da rezultat nikada ne zvoni ispod crnog ni iznad belog, pa se klasični Lanczos overshoot halo oko oštre ivice ne pojavljuje kao isečeni artefakti u kodiranoj slici

var
  Pdf: THotPDF;
  Info: THPDFLoadedResourceOptimizationInfo;
  Changed: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('scanned-contract.pdf');
    Pdf.ImageDownsampleKernel := rkLanczos3;   // postavi pre poziva
    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;

Argument MinimumSavingsBytes, 4096 gore, je guard koji operaciju drži poštenom. Ponovno kodiranje slike koja je već bila efikasno komprimovana može proizvesti veći tok od originala, i downsampler koji slepo zamenjuje svaku sliku će povremeno uvećati datoteku koju je traženo da smanji. Prag kaže: potvrdi zamenu samo kad štedi barem ovoliko bajtova. PreservedCalibratedImageCount prijavljuje drugu konzervativnu odluku, slike ostavljene netaknutim jer nose kalibrisan color space koji bi resamplovanje kompromitovalo

Zašto je pogrešan polinomski koeficijent tako teško uočiti?

Zato što pokvaren interpolacioni kernel ne pada i ne baca izuzetak, samo proizvodi sliku koja izgleda suptilno pogrešno na način koji niko ne ume da pripše. Catmull-Rom kernel je parčasto kubik, i njegova spoljašnja grana u ugnježđenom Horner obliku je ((-0.5t + 2.5)t - 4)t + 2. Napišite taj srednji koeficijent kao -5 umesto -4 i funkcija se i dalje izračunava, i dalje vraća brojeve u verodostojnom opsegu, i dalje proizvodi sliku

Šteta se pokazuje kao W(1) koje se izračunava u -1 tamo gde mora biti 0. Negativne težine se gomilaju, zbir se iseče na nuli, i vidljiv simptom je gradijent čiji levi kraj pocrni i stepenasta ivica koja gubi svoje srednje tonove. Ništa u otkazu ne pokazuje na polinom. Provera koja ga hvata za sekunde je aritmetička, a ne vizuelna: interpolacioni kernel mora zadovoljiti W(0) = 1 i W(±1) = W(±2) = 0, i svaki kernel koji promaši te tri tačke ima grešku u koeficijentu, tačka. Asertujte te tri vrednosti u unit testu i cela ta klasa defekata od prekucaja nestaje

Graf spoljašnje grane HotPDF bicubic Catmull-Rom kernela koji pokazuje zašto se pogrešan koeficijent krije: prekucaj koji napiše -5 umesto -4 u ugnježđenom Horner obliku se i dalje izračunava i ostavlja W(1) na -1 i W(2) na -2 tamo gde je nula potrebna, pa asert W(0) jednak 1 plus dva nulta ograničenja hvata ga za sekunde
Pokvaren kernel nikada ne pada, samo vraća brojeve koji izgledaju verodostojno, po čemu oko ne može da uhvati prekucaj koeficijenta. W(0) = 1 sa nulama na plus i minus jedan i dva je unit test od tri linije

Floyd-Steinberg dithering i gde mu je mesto u pipeline-u

Dither prolaz je drugačiji problem od resamplovanja i živi na drugoj tački pipeline-a. RenderOutputDither primenjuje Floyd-Steinberg difuziju greške posle kompozicije strane, što je jedino mesto koje ima smisla za monohromni print pregled ili fax stilski izvoz: operacija je o smanjivanju završenog rastera na jedan bit po pikselu, ne o tome kako su pojedinačne slike skalirane na ulazu

Sam algoritam je kratak. Luminansa se praguje na 50 procenata, i greška kvantizacije difunduje se ka četiri suseda sa klasičnim težinama 7/16, 3/16, 5/16 i 1/16, idući desno, dole-levo, dole i dole-desno. Izlazni piksel je 0 ili 255 u svakom kanalu. Naivna alternativa umesto toga daje oštar prag bez difuzije, što od fotografije pravi siluetu i gubi svaki srednji ton koji je nosio sadržaj

Mesto u render pipeline-u HotPDF Floyd-Steinberg dithering-a: RenderOutputDither radi posle kompozicije strane nad završenim 24-bitnim rasterom, praguje luminansu na 50 procenata i difunduje svaku grešku kvantizacije desno i dole sa težinama 7/16, 3/16, 5/16 i 1/16 kroz redni bafer koji mora akumulirati, proizvodeći jedno-bitni monohromni izlaz
Dithering pripada posle kompozicije jer smanjuje završeni raster na jedan bit, a ne zbog toga kako su slike skalirane. Težine difuzije se sabiraju u jedinicu, i redni bafer mora da akumulira, a ne da pregazi
// Dithering u vremenu rendera za monohromni uređaj za pregled
Pdf.RenderOutputDither := True;

// Ili primenite isti prolaz na bitmapu koju već posedujete. Bitmapa mora
// biti pf24bit; funkcija vraća False umesto da nagađa
if not HPDFFloydSteinbergDitherBitmap(Preview) then
  raise Exception.Create('dither expects a 24-bit bitmap');

// Direktan pristup kernelu kad resamplujete van pipeline-a dokumenta
Small := HPDFResizeBitmapKernel(Large, 640, 480, rkLanczos3);
try
  Small.SaveToFile('thumb.bmp');
finally
  Small.Free;
end;

Postoji jedan implementacioni detalj u difuziji greške koji svakoga jednom ugrize. Bafer greške iz reda u red mora da akumulira. Svaki piksel u sledećem redu prima doprinose od tri različita piksela u trenutnom redu — tapovi 3/16, 5/16 i 1/16 — i ako kod dodeljuje umesto da sabira, svaki upis odbacuje prethodni doprinos i samo poslednji tap preživi. Slika i dalje izgleda dither-ovano, i to je ono što otežava uočavanje, ali tekstura je pogrešna i tonalna reprodukcija driftuje. Test koji to hvata je kvantitativan: dither-ujte ujednačeno polje srednje sive i zahtevajte da unutrašnja pokrivenost padne između 40 i 60 procenata

Koju kombinaciju treba da koristi pipeline za smanjenje veličine?

Upotite kernel sa onim što slike stvarno jesu, i dithering tretirajte kao brigu uređaja, a ne kompresije. Za fotografske skenove koji moraju preživeti budžet veličine, rkLanczos3 na 150 ili 200 DPI čuva detalje koje ljudi primeće dok seže za smanjenjem broja piksela četiri puta ili više. Za snimke ekrana, dijagrame i line art, rkHalftone je stvarno sasvim dobar i mnogo brži, jer te slike imaju malo tonalnih gradijenata za čuvanje. Za mešoviti batch gde ne možete pregledati svaku sliku, rkBicubic je razuman srednji put: bolji od bilinear, otprilike pola tapova Lanczos3

Downsample je jedna poluga od više njih, i nije uvek najveća. Bilevel skenovi obično mnogo bolje odgovaraju encoder-u pokrivenom u članku o nativnoj JBIG2 bilevel kompresiji u Delphi-ju, gde dobit dolazi iz rečnika simbola, a ne iz broja piksela. Pre nego što odlučite, pomaže znati šta je stvarno u datoteci, a za to služi izvlačenje slika i njihovih decode filtera: inventar objekata slika i njihove postojeće kompresije vam govori ima li resamplovanje šta da dobije

Ako gradite površinu pregleda koja prikazuje rezultat, ista render putanja dokumentovana u članku o renderovanju PDF strane u bitmapu je mesto gde RenderOutputDither deluje, pa dither-ovan pregled i dither-ovan izlaz potiču iz jednog kodnog puta umesto iz dve implementacije koje razlaze

Širi princip iza obe mogućnosti je da podešavanja kvaliteta treba da budu eksplicitna i obrativa. HotPDF zadržava istorijsko ponašanje kao podrazumevano da postojeća aplikacija nadogradi bez iznenađujuće promene izlaza ili vremena, a lepše izgledajuće, sporije putanje stavlja na jednu dodelu svojstva dalje. Oboje je deo HotPDF Delphi PDF komponente, uz mašineriju optimizacije resursa i renderovanja na kojoj počivaju