Tehnički članak

HotPDF kerneli za downsample slika i print dithering

HotPDF izlaže tri kernela za downsample slika kroz property ImageDownsampleKernel i poseban Floyd-Steinberg prolaz kroz RenderOutputDither. Prvi upravlja time kako fotografije izgledaju nakon što ih smanjite da pogodite budžet veličine, drugi time kako izgledaju nakon što se stranica svede na crno-bijelo. Nijedan nije uključen prema zadanoj postavci, i oba su opt-in iz istog razloga: koštaju stvarnog vremena

Pritisak koji ljude dovodi ovamo je poznat. 60 MB skenirani ugovor mora izaći kroz email gateway koji odbija sve preko 10 MB, ili serija izvoda mora sletjeti na fax-sličan monokromni uređaj koji svaki sivi piksel renderira kao papir ili toner. Oba problema su problemi ponovnog uzorkovanja, i oba imaju brz odgovor koji izgleda loše i spor odgovor koji izgleda dobro

U čemu se tri kernela stvarno razlikuju

THPDFResampleKernel ima tri vrijednosti, i one sjede na stvarno različitim točkama krive brzine i kvalitete. rkHalftone delegira na povijesni GDI StretchBlt put s HALFTONE modom, koji usprkos imenu je filtriranje bilinear klase: brzo, dostatno za line art i screenshotove, i sklono oštrim rubovima koje na smanjenim fotografijama odmah prepoznajete. rkBicubic vodi separabilan Catmull-Rom kernel, a rkLanczos3 vodi separabilan windowed sinc s potporom od tri režnja

Oba separabilna kernela rade kao dva prolaza, horizontalni pa vertikalni, s 6 do 12 tapova po odredišnom pikselu u čistom Pascalu. To je otprilike red veličine sporije od GDI puta, i upravo zato rkHalftone ostaje zadana postavka. Kod noćne serije od tisuća stranica razlika je odluka o rasporedu, ne preferencija. Kod jednog dokumenta na koji korisnik čeka, Lanczos3 je gotovo besplatan i vidljivo bolji

Krive težina tri HotPDF downsampling kernela: rkHalftone delegira na GDI HALFTONE put bilinear klase s potporom jedan, rkBicubic vodi separabilan Catmull-Rom kubik s potporom dva, a rkLanczos3 vodi windowed sinc s potporom tri, trgujući otprilike redom veličine brzine za vidljivo bolje fotografije
Tri vrijednosti kernela sjede na stvarno različitim točkama krive brzine i kvalitete: GDI put bilinear klase, Catmull-Rom kubik i tro-režanjski windowed sinc, a separabilni kerneli normaliziraju težine pa ništa ne zazvoni iza crno ili bijelo

Dvije implementacijske osobine vrijedi znati jer određuju što izlaz može a što ne može. Rubovi se stežu replikacijom ruba umjesto omotavanjem ili blijeđenjem, a težine se normaliziraju po odredišnom pikselu. Zajedno te dvije znače da rezultat nikad ne zazvoni ispod crne niti iznad bijele, pa se klasični Lanczos overshoot halo oko oštog ruba ne pojavljuje kao podrezani 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 prije 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, gore 4096, je guard koji operaciju drži poštenom. Ponovno kodiranje slike koja je već bila učinkovito komprimirana može proizvesti veći stream od izvornog, i downsampler koji slijepo zamjenjuje svaku sliku će povremeno uvećati datoteku koju je zamoljen da smanji. Prag kaže: obvežite zamjenu samo kad štedi barem ovoliko bajtova. PreservedCalibratedImageCount javlja drugu konzervativnu odluku, slike ostavljene na miru jer nose kalibrirani color space koji bi ponovno uzorkovanje ugrozilo

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

Zato slomljeni interpolacijski kernel ne pada niti baca, samo proizvodi sliku koja izgleda suptilno krivo na način koji nitko ne može pripisati. Catmull-Rom kernel je piecewise kubik, i njegova vanjska grana u ugniježđenom Horner obliku je ((-0.5t + 2.5)t - 4)t + 2. Napišite li taj srednji koeficijent kao -5 umjesto -4, funkcija se i dalje vrednuje, i dalje vraća brojeve u vjerojatnom rasponu, i dalje proizvodi sliku

Šteta se pokazuje kao W(1) koji se vrednuje u -1 gdje mora biti 0. Negativne težine se nakupljaju, zbroj se podrezuje na nulu, a vidljivi simptom je gradijent čiji lijevi kraj ode u crno i step rub koji izgubi prijelazne tonove. Ništa u kvaru ne ukazuje na polinom. Provjera koja to ulovi u sekundama je aritmetika, a ne oko: interpolacijski kernel mora zadovoljiti W(0) = 1 i W(±1) = W(±2) = 0, i svaki kernel koji promaši te tri točke ima grešku u koeficijentu, točka. Tvrdite te tri vrijednosti u unit testu i cijela klasa tipfeler defekata nestaje

Graf vanjske grane HotPDF bicubic Catmull-Roma pokazuje zašto se krivi koeficijent skriva: tipfeler koji piše -5 umjesto -4 u ugniježđenom Horner obliku se i dalje vrednuje i ostavlja W(1) na -1 i W(2) na -2 gdje je nula potrebna, pa tvrdnja da je W(0) jednak 1 uz dva nulte ograničenja ulovi to u sekundama
Slomljeni kernel nikad ne pada, samo vraća brojeve koji izgledaju vjerojatno, pa oko ne može uloviti koeficijentni tipfeler. W(0) = 1 s nulama na plus i minus jedan i dva je tro-linijski unit test

Floyd-Steinberg dithering i njegovo mjesto u pipelineu

Dither prolaz je drugačiji problem od ponovnog uzorkovanja i živi na drugom mjestu u pipelineu. RenderOutputDither primjenjuje Floyd-Steinberg error diffusion nakon kompozicije stranice, što je jedino smisleno mjesto za monokromni print preview ili fax-sličan izvoz: operacija je svodenje završenog rastera na jedan bit po pikselu, a ne pitanje kako su pojedinačne slike skalirane na ulazu

Sam algoritam je kratak. Luminancija se prati na 50 posto, i kvantizacijska greška se difundira na četiri susjeda s klasičnim težinama 7/16, 3/16, 5/16 i 1/16, idući desno, dolje-lijevo, dolje i dolje-desno. Izlazni piksel je 0 ili 255 u svakom kanalu. Naivna alternativa umjesto toga, oštra granica bez difuzije, pretvara fotografiju u siluetu i gubi svaki srednji ton koji je nosio sadržaj

Mjesto HotPDF Floyd-Steinberg ditheringa u render pipelineu: RenderOutputDither radi nakon kompozicije stranice na završenom 24-bitnom rasteru, prati luminanciju na 50 posto i difundira svaku kvantizacijsku grešku desno i dolje s težinama 7/16, 3/16, 5/16 i 1/16 kroz red buffer koji mora akumulirati, proizvodeći jednobitni monokromni izlaz
Dithering pripada nakon kompozicije jer svodi završeni raster na jedan bit, a ne zbog toga kako su slike skalirane. Difuzijske težine zbrajaju se u jedan, i red buffer mora akumulirati, a ne pregaziti
// Dithering u vrijeme renderiranja za monokromni preview uređaj
Pdf.RenderOutputDither := True;

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

// Izravni pristup kernelu kad ponovno uzorkujete izvan dokumentnog pipelinea
Small := HPDFResizeBitmapKernel(Large, 640, 480, rkLanczos3);
try
  Small.SaveToFile('thumb.bmp');
finally
  Small.Free;
end;

Postoji jedan implementacijski detalj u error diffusionu koji svakoga ugrize jednom. Red-po-red error buffer mora akumulirati. Svaki piksel u sljedećem redu prima doprinose od tri različita piksela u trenutnom redu, tapovi 3/16, 5/16 i 1/16, i ako kod dodjeljuje umjesto da zbraja, svaki zapis odbacuje prethodni doprinos i preživi samo zadnji tap. Slika i dalje izgleda ditherano, što otežava uočavanje, ali tekstura je kriva i tonska reprodukcija odmiče. Test koji to ulovi je kvantitativan: ditherajte ujednačeno polje srednje sive i zahtijevajte da unutrašnja pokrivenost padne između 40 i 60 posto

Koju kombinaciju treba pipeline za smanjenje veličine?

Uparenite kernel s onim što slike stvarno jesu, i tretirajte dithering kao pitanje uređaja, a ne kompresije. Za fotografske skenove koji moraju preživjeti budžet veličine, rkLanczos3 na 150 ili 200 DPI zadržava detalj koji ljudi primjete dok reže broj piksela za faktor četiri ili više. Za screenshotove, dijagrame i line art, rkHalftone je stvarno sasvim dobar i puno brži, jer te slike imaju malo tonalnih gradijenata koje treba sačuvati. Za miješanu seriju gdje ne možete pregledati svaku sliku, rkBicubic je razuman srednji put: bolji od bilinearnog, otprilike pola broja tapova Lanczos3

Downsample je jedna poluga od nekoliko, i nije uvijek najveća. Bilevel skenovi obično puno bolje odgovaraju encoderu obrađenom u native JBIG2 bilevel kompresiji u Delphiju, gdje dobit dolazi iz simbol rječnika, a ne iz broja piksela. Prije odluke pomaže znati što se stvarno nalazi u datoteci, a za to služi izvlačenje slika i njihovih decode filtara: inventura image objekata i njihove postojeće kompresije vam kaže ima li ponovno uzorkovanje što dobiti

Ako gradite preview površinu koja prikazuje rezultat, isti render put dokumentiran u renderiranju PDF stranice u bitmapu je mjesto gdje RenderOutputDither stupa na snagu, pa ditherani preview i ditherani izlaz dolaze iz jednog kodnog puta, a ne iz dvije implementacije koje se razilaze

Široko načelo iza obje mogućnosti je da quality postavke trebaju biti izričite i reverzibilne. HotPDF zadržava povijesno ponašanje kao zadanu postavku pa postojeća aplikacija nadogradnju prolazi bez iznenađujuće promjene u izlazu ili vremenu, a bolje izgledajuće sporije puteve stavlja jednu dodjelu propertyja daleko. Oboje je dio HotPDF Delphi PDF komponente, uz mašineriju optimizacije resursa i renderiranja na kojoj se grade