HotPDF exponerar tre downsample-kärnor för bilder via egenskapen ImageDownsampleKernel och ett separat Floyd-Steinberg-pass via RenderOutputDither. Den första styr hur fotografier ser ut efter att du krympt dem för att träffa en storleksbudget, den andra styr hur de ser ut när en sida reducerats till svartvitt. Ingen av dem är på som standard, och båda är opt-in av samma anledning: de kostar verklig tid
Pressen som leder folk hit är välbekant. Ett 60 MB inskannat kontrakt måste ut genom en e-postgateway som avvisar allt över 10 MB, eller en sats av utdrag måste landa på en faxliknande monokrom enhet som renderar varje grå pixel som antingen papper eller toner. Båda problemen är resamplingsproblem, och båda har ett snabbt svar som ser dåligt ut och ett långsamt svar som ser rätt ut
Vad de tre kärnorna faktiskt skiljer sig i
THPDFResampleKernel har tre värden, och de sitter på genuint olika punkter på hastighets- och kvalitetskurvan. rkHalftone delegerar till den historiska GDI-vägen StretchBlt med HALFTONE-läget, som trots namnet är bilinearklassfiltrering: snabb, adekvat för linjekonst och skärmdumpar, och benägen att ge de knorriga kanter du känner igen direkt på nedskalade fotografier. rkBicubic kör en separerbar Catmull-Rom-kärna, och rkLanczos3 kör en separerbar fönstersatt sinc med tre-lobigt stöd
Båda separerbara kärnorna körs som två pass, horisontellt sedan vertikalt, med 6 till 12 taps per destinationspixel i ren Pascal. Det är grovt sagt en storleksordning långsammare än GDI-vägen, vilket är exakt varför rkHalftone förblir standard. På en nattlig sats av tusentals sidor är skillnaden ett schemaläggningsbeslut, inte en preferens. På ett enskilt dokument en användare väntar på är Lanczos3 nästan gratis och synligt bättre
Två implementationsegenskaper är värda att känna till eftersom de bestämmer vad utmatningen kan och inte kan göra. Kanter kläms av kantrepplikering i stället för wrapning eller toning ut, och vikterna normaliseras per destinationspixel. Tillsammans betyder de två att resultatet aldrig ringer under svart eller över vitt, så den klassiska Lanczos-överskjutningshalon runt en hård kant uppträder inte som klippta artefakter i den kodade bilden
var
Pdf: THotPDF;
Info: THPDFLoadedResourceOptimizationInfo;
Changed: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('scanned-contract.pdf');
Pdf.ImageDownsampleKernel := rkLanczos3; // sätts före anropet
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;
Argumentet MinimumSavingsBytes, 4096 ovan, är vakten som håller operationen ärlig. Att koda om en bild som redan var effektivt komprimerad kan ge en större ström än originalet, och en downsampler som blint ersätter varje bild kommer ibland att växa filen den bads krympa. Tröskeln säger: committa endast ersättningen när den sparar åtminstone så här många byte. PreservedCalibratedImageCount rapporterar det andra konservativa beslutet, bilder lämnade orörda eftersom de bär ett kalibrerat färgrum som resampling skulle kompromettera
Varför är en felaktig polynomkoefficient så svår att upptäcka?
Därför att en sönder interpolationskärna inte kraschar eller kastar, den producerar bara en bild som ser subtilt fel på ett sätt ingen kan tillskriva något. Catmull-Rom-kärnan är styckvis kubisk, och dess yttre gren i nästlad Horner-form är ((-0.5t + 2.5)t - 4)t + 2. Skriv den mellankoefficienten som -5 i stället för -4 och funktionen evaluerar ändå, returnerar ändå tal i ett plausibelt intervall och producerar ändå en bild
Skadan visar sig som W(1) som evaluerar till -1 där den måste vara 0. Negativa vikter ackumuleras, summan klipps vid noll, och det synliga symptomet är en gradient vars vänstra ände blir svart och en stegkant som tappar sina mellanliggande toner. Ingenting i felet pekar på en polynom. Kontrollen som fångar det på sekunder är aritmetisk snarare än visuell: en interpolerande kärna måste uppfylla W(0) = 1 och W(±1) = W(±2) = 0, och varje kärna som missar de tre punkterna har ett koefficientfel, punkt slut. Hävda om de tre värdena i ett enhetstest och hela klassen av stavfelsdefekter försvinner
Floyd-Steinberg-dithering, och var den hör hemma i pipelinen
Dither-passet är ett annat problem än resampling och bor på en annan punkt i pipelinen. RenderOutputDither tillämpar Floyd-Steinberg error diffusion efter sidokompositionen, vilket är den enda placering som är vettig för en monokrom utskriftsförhandsvisning eller en faxlik export: operationen handlar om att reducera en färdig raster till en bit per pixel, inte om hur enskilda bilder skalades på vägen in
Algoritmen i sig är kort. Luminansen trösklas vid 50 procent, och kvantiseringsfelet diffunderas till fyra grannar med de klassiska vikterna 7/16, 3/16, 5/16 och 1/16, åt höger, nedre-vänster, nedåt och nedre-höger. Utmatningspixeln är 0 eller 255 i varje kanal. Vad det naiva alternativet ger dig i stället, en hård tröskel utan diffusion, förvandlar ett fotografi till en siluett och tappar varje mellanton som bar innehållet
// Dithering vid rendertid för en monokrom förhandsvisningsenhet
Pdf.RenderOutputDither := True;
// Eller tillämpa samma pass på en bitmapp du redan äger. Bitmappen måste
// vara pf24bit; funktionen returnerar False i stället för att gissa
if not HPDFFloydSteinbergDitherBitmap(Preview) then
raise Exception.Create('dither expects a 24-bit bitmap');
// Direkt kärnåtkomst när du resamplar utanför dokumentpipelinen
Small := HPDFResizeBitmapKernel(Large, 640, 480, rkLanczos3);
try
Small.SaveToFile('thumb.bmp');
finally
Small.Free;
end;
Det finns en implementationsdetalj i error diffusion som biter alla en gång. Rad-till-rad-felbufferten måste ackumulera. Varje pixel i nästa rad tar emot bidrag från tre olika pixlar i den aktuella raden, tapserna 3/16, 5/16 och 1/16, och om koden tilldelar i stället för att addera kastar varje skrivning bort det föregående bidraget och bara den sista tapseen överlever. Bilden ser ändå ditherad ut, vilket är det som gör det svårt att märka, men texturen är fel och den tonala återgivningen driver. Testet som fångar det är kvantitativt: dithra ett enhetligt mellangrått fält och kräv att den inre täckningen landar mellan 40 och 60 procent
Vilken kombination bör en storleksreduceringspipeline använda?
Matcha kärnan mot vad bilderna faktiskt är, och behandla dithering som en enhetsfråga snarare än en komprimeringsfråga. För fotografiska inskanningar som måste klara en storleksbudget håller rkLanczos3 vid 150 eller 200 DPI detaljerna folk märker samtidigt som pixelantalet skärs med en faktor fyra eller mer. För skärmdumpar, diagram och linjekonst är rkHalftone genuint fint och mycket snabbare, för de bilderna har få tonala gradienter att bevara. För en blandad sats där du inte kan granska varje bild är rkBicubic det rimliga mitten: bättre än bilineär, grovt hälften av tapantalet i Lanczos3
Downsampling är en spak bland flera, och den är inte alltid den största. Två-nivå-inskanningar svarar vanligen mycket bättre på encodern som tas upp i inbyggd JBIG2 bilevel-komprimering i Delphi, där vinsten kommer från symboldictionaries snarare än pixelantal. Innan du bestämmer dig hjälper det att veta vad som faktiskt finns i filen, vilket är vad att extrahera bilder och deras decode-filter är till för: en inventering av bildobjekt och deras befintliga komprimering talar om för dig huruvida resampling har något att vinna
Om du bygger förhandsvisningsytan som visar resultatet är det samma renderingsväg som dokumenteras i att rendera en PDF-sida till en bitmapp där RenderOutputDither slår igenom, så den ditherade förhandsvisningen och den ditherade utmatningen kommer från en kodväg i stället för två implementationer som driver isär
Den breda principen bakom båda funktionerna är att kvalitetsinställningar ska vara explicita och reversibla. HotPDF behåller det historiska beteendet som standard så att en befintlig applikation uppgraderar utan en överraskande förändring i utmatning eller timing, och lägger de bättre seende, långsammare vägarna en egenskapstilldelning bort. Båda ingår i HotPDF Delphi PDF component, tillsammans med resursoptimeringen och renderingsmaskineriet de bygger på