HotPDF vystavuje tři jádra downsamplingu obrázků přes vlastnost ImageDownsampleKernel a samostatný průchod Floyd-Steinberg přes RenderOutputDither. První řídí, jak fotografie vypadají poté, co je zmenšíte, aby se vešly na rozpočet velikosti, druhý řídí, jak vypadají poté, co se stránka redukuje na černobílou. Ani jedno není zapnuté defaultně a obě jsou opt-in ze stejného důvodu: stojí reálný čas
Tlak, který sem lidi přivádí, je známý. 60 MB naskenovaná smlouva musí ven přes e-mailovou bránu odmítající cokoliv nad 10 MB, nebo dávka výpisů musí dopadnout na faxově pojaté monochromatické zařízení, které každý šedý pixel vykreslí jako papír nebo toner. Oba problémy jsou problémy resamplingu a oba mají rychlou odpověď, která vypadá špatně, a pomalou odpověď, která vypadá správně
V čem se ta tři jádra doopravdy liší
THPDFResampleKernel má tři hodnoty a sedí na skutečně různých místech křivky rychlost a kvalita. rkHalftone deleguje na historickou GDI cestu StretchBlt s módem HALFTONE, který přes svůj název je filtr bilineární třídy: rychlý, dostačující pro line art a screenshoty a náchylný k těm drsným hranám, které na zmenšených fotografiích poznáte okamžitě. rkBicubic běží se separabilním jádrem Catmull-Rom a rkLanczos3 běží se separabilním okénkovaným sincem s podporou tří laloků
Obě separabilní jádra běží jako dva průchody, vodorovný pak svislý, se 6 až 12 taps na cílový pixel v čistém Pascalu. To je zhruba řád velikosti pomalejší než GDI cesta, což je přesně důvod, proč rkHalftone zůstává defaultem. Na noční dávce tisíců stránek je ten rozdíl schedulingové rozhodnutí, ne preference. U jediného dokumentu, na kterého uživatel čeká, je Lanczos3 téměř zadarmo a viditelně lepší
Dvě implementační vlastnosti stojí za znalost, protože určují, co výstup dokáže a nedokáže. Okraje se upnou replikací hrany místo obalení nebo vyprchání a váhy se normalizují per cílový pixel. Spolu to znamená, že výsledek nikdy nezvoní pod černou ani nad bílou, takže klasická přesstřelová halo Lanczosu kolem tvrdé hrany se v zakódovaném obrázku neobjeví jako ořezané artefakty
var
Pdf: THotPDF;
Info: THPDFLoadedResourceOptimizationInfo;
Changed: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('scanned-contract.pdf');
Pdf.ImageDownsampleKernel := rkLanczos3; // nastavte před voláním
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, výše 4096, je hlídka držící operaci při zemi. Znovu zakódování obrázku, který už byl efektivně komprimovaný, může produkovat větší stream než originál a downsampler slepě nahrazující každý obrázek občas zvětší soubor, který měl zmenšit. Práh říká: potvrď nahrazení jen tehdy, když ušetří aspoň tolik bajtů. PreservedCalibratedImageCount hlásí druhé konzervativní rozhodnutí, obrázky ponechané nedotčené, protože nesou kalibrovaný barevný prostor, který by resampling kompromitoval
Proč je špatný polynomiální koeficient tak těžké odhalit?
Protože rozbité interpolační jádro nepadá a nehodí výjimku, prostě produkuje obrázek, který vypadá subtilně špatně způsobem, jenž nikdo nedokáže přiřadit. Jádro Catmull-Rom je po částech kubické a jeho vnější větev v zanořeném Hornerově tvaru je ((-0.5t + 2.5)t - 4)t + 2. Napíšete-li ten prostřední koeficient jako -5 místo -4, funkce pořád vyhodnocuje, pořád vrací čísla v plausibilním rozsahu a pořád produkuje obrázek
Škoda se projeví jako W(1) vyhodnocující se na -1, kde musí být 0. Záporné váhy se kumulují, součet se klipne na nule a viditelným symptomem je gradient, jehož levý konec zčerná, a skoková hrana, která ztratí své mezilehlé tóny. Nic v tom selhání neukazuje na polynom. Kontrola, která to chytne za sekundy, je aritmetická, ne vizuální: interpolační jádro musí splňovat W(0) = 1 a W(±1) = W(±2) = 0 a jakékoli jádro, které u těch tří bodů selže, má chybu koeficientu, tečka. Asertujte ty tři hodnoty v unit testu a celá třída překlepových defektů zmizí
Dithering Floyd-Steinberg a kam v pipeline patří
Dither průchod je jiný problém než resampling a bydlí na jiném místě pipeline. RenderOutputDither aplikuje error diffusion Floyd-Steinberg po skládání stránky, což je jediné umístění dávající smysl pro monochromatický tiskový náhled nebo faxový export: operace je o redukci hotového rastru na jeden bit na pixel, ne o tom, jak byly jednotlivé obrázky cestou dovnitř škálovány
Samotný algoritmus je krátký. Luminance se prahuje na 50 procentech a kvantizační chyba se difunduje na čtyři sousedy s klasickými váhami 7/16, 3/16, 5/16 a 1/16, doprava, dole-vlevo, dole a dole-vpravo. Výstupní pixel je 0 nebo 255 v každém kanálu. Co vám místo toho dá naivní alternativa, tvrdý práh bez difuse, promění fotografii v siluetu a ztratí každý mezitón, který nesl obsah
// Dithering při vykreslování pro monochromatické náhledové zařízení
Pdf.RenderOutputDither := True;
// Nebo aplikujte týž průchod nad bitmapou, kterou už vlastníte. Bitmapa
// musí být pf24bit; funkce vrátí False místo hádání
if not HPDFFloydSteinbergDitherBitmap(Preview) then
raise Exception.Create('dither expects a 24-bit bitmap');
// Přímý přístup k jádru, když resamplujete mimo dokumentovou pipeline
Small := HPDFResizeBitmapKernel(Large, 640, 480, rkLanczos3);
try
Small.SaveToFile('thumb.bmp');
finally
Small.Free;
end;
V error diffusion je jeden implementační detail, který kousne každého jednou. Chybový buffer mezi řádky musí akumulovat. Každý pixel dalšího řádku dostává příspěvky od tří různých pixelů aktuálního řádku, taps 3/16, 5/16 a 1/16, a jestliže kód přiřazuje místo sčítání, každý zápis zahodí předchozí příspěvek a přežije jen poslední tap. Obrázek pořád vypadá ditherovaně, což ztěžuje všimnutí, ale textura je špatná a tónová reprodukce driftuje. Test, který to chytne, je kvantitativní: ditherněte rovnoměrné pole střední šedi a vyžadujte, aby vnitřní pokrytí dopadlo mezi 40 a 60 procenty
Jakou kombinaci má zvolit pipeline na zmenšování velikosti?
Přiřaďte jádro k tomu, čím obrázky doopravdy jsou, a berte dithering jako starost zařízení, ne komprese. Pro fotografické skeny, které musí přežít rozpočet velikosti, drží rkLanczos3 na 150 nebo 200 DPI detail, který si lidé všímají, a přitom sekne počet pixelů na čtvrtinu nebo víc. Pro screenshoty, diagramy a line art je rkHalftone upřímně v pořádku a mnohem rychlejší, protože ty obrázky mají málo tónových gradientů k uchování. Pro smíšenou dávku, kterou nemůžete prohlížet obrázek po obrázku, je rkBicubic rozumný střed: lepší než bilineární, zhruba polovina tap countu Lanczosu3
Downsampling je jedna páka z několika a není vždy ta největší. Bilevel skeny obvykle reagují daleko lépe na encoder pokrytý v článku nativní JBIG2 bilevel komprese v Delphi, kde zisk pochází ze symbolových slovníků, ne z počtů pixelů. Než se rozhodnete, pomáhá vědět, co je v souboru doopravdy, k čemu slouží článek extrakce obrázků a jejich decode filtrů: inventura obrazových objektů a jejich stávající komprese vám řekne, zda má resampling vůbec co získat
Stavíte-li náhledovou plochu, která výsledek ukazuje, tatáž renderovací cesta zdokumentovaná v článku vykreslení PDF stránky do bitmapy je ta, kde RenderOutputDither naskočí, takže ditherovaný náhled i ditherovaný výstup pocházejí z jediné kódové cesty, ne ze dvou implementací rozbíhajících se od sebe
Široký princip za oběma featurami je, že kvalitativní nastavení má být explicitní a reverzibilní. HotPDF nechává historické chování jako default, takže stávající aplikace upgraduje bez překvapivé změny výstupu nebo času, a lepší a pomalejší cesty dává jediné přiřazení vlastnosti daleko. Obojí je částí HotPDF Delphi PDF komponenty, spolu s optimalizací resources a renderovací mechanikou, na nichž stojí