Artikel Teknis

Kernel Downsampling Gambar dan Dithering Cetak HotPDF

HotPDF menyediakan tiga kernel downsampling gambar lewat property ImageDownsampleKernel dan satu pass Floyd-Steinberg terpisah lewat RenderOutputDither. Yang pertama mengendalikan tampilan foto setelah Anda mengecilnya demi mengejar anggaran ukuran, yang kedua mengendalikan tampilannya setelah halaman direduksi menjadi hitam-putih. Tak satu pun aktif secara bawaan, dan keduanya opt-in karena alasan yang sama: semuanya berbiaya waktu nyata

Tekanan yang mengarahkan orang ke sini itu akrab. Kontrak hasil scan 60 MB harus keluar lewat gateway email yang menolak apa pun di atas 10 MB, atau sekumpulan statement harus mendarat di device monokrom bergaya faks yang merender setiap piksel abu-abu sebagai kertas atau toner. Kedua masalah itu masalah resampling, dan keduanya punya jawaban cepat yang tampak jelek dan jawaban lambat yang tampak benar

Apa yang sebenarnya membedakan ketiga kernel

THPDFResampleKernel punya tiga nilai, dan ketiganya duduk di titik yang benar-benar berbeda pada kurva kecepatan-kualitas. rkHalftone mendelegasikan ke jalur StretchBlt GDI historis dengan mode HALFTONE, yang meski bernama demikian adalah penyaringan kelas bilinear: cepat, cukup untuk line art dan screenshot, dan rawan tepi kasar yang langsung Anda kenali pada foto hasil pengecilan. rkBicubic menjalankan kernel Catmull-Rom yang separabel, dan rkLanczos3 menjalankan windowed sinc separabel dengan support tiga lobus

Kedua kernel separabel berjalan sebagai dua pass, horizontal lalu vertikal, dengan 6 sampai 12 tap per piksel tujuan dalam Pascal murni. Itu kira-kira satu ordo magnitudo lebih lambat daripada jalur GDI, dan persis itulah sebabnya rkHalftone tetap menjadi bawaan. Pada batch malam ribuan halaman, selisihnya adalah keputusan penjadwalan, bukan preferensi. Pada satu dokumen yang ditunggu pengguna, Lanczos3 nyaris gratis dan lebih baik secara terlihat

Kurva bobot ketiga kernel downsampling HotPDF: rkHalftone mendelegasikan ke jalur GDI HALFTONE kelas bilinear dengan support satu, rkBicubic menjalankan kubik Catmull-Rom separabel dengan support dua, dan rkLanczos3 menjalankan windowed sinc dengan support tiga, menukar kira-kira satu ordo magnitudo kecepatan demi foto yang lebih baik secara terlihat
Ketiga nilai kernel itu duduk di titik yang benar-benar berbeda pada kurva kecepatan-kualitas: jalur GDI kelas bilinear, kubik Catmull-Rom, dan windowed sinc tiga lobus, dan kernel separabel menormalkan bobot agar tak ada yang ringing melampaui hitam atau putih

Dua properti implementasi layak diketahui karena keduanya menentukan apa yang bisa dan tidak bisa dilakukan output. Border di-clamp dengan replikasi tepi alih-alih wrapping atau memudar, dan bobotnya dinormalkan per piksel tujuan. Keduanya bersama-sama berarti hasil tak pernah ringing di bawah hitam atau di atas putih, sehingga overshoot halo Lanczos klasik di sekitar tepi tajam tidak muncul sebagai artefak terpotong di gambar yang dienkode

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

Argumen MinimumSavingsBytes — 4096 di atas — adalah guard yang menjaga operasi itu jujur. Mengenkode ulang gambar yang sudah terkompresi efisien bisa menghasilkan stream yang lebih besar daripada aslinya, dan downsampler yang mengganti setiap gambar secara membabi buta sesekali membesarkan file yang diminta untuk dikecilkan. Threshold itu berkata: komitkan penggantian hanya kalau menghemat sekian byte minimal. PreservedCalibratedImageCount melaporkan keputusan konservatif lainnya, gambar yang dibiarkan utuh karena membawa color space terkalibrasi yang akan dikompromikan resampling

Mengapa koefisien polinomial yang salah begitu sulit terlihat?

Karena kernel interpolasi yang rusak tidak crash atau melempar exception, ia hanya menghasilkan gambar yang tampak halus salahnya dengan cara yang tak bisa diatribusikan siapa pun. Kernel Catmull-Rom itu piecewise cubic, dan cabang luarnya dalam bentuk Horner bersarang adalah ((-0.5t + 2.5)t - 4)t + 2. Tulis koefisien tengah itu -5 alih-alih -4, fungsinya tetap berevaluasi, tetap mengembalikan angka dalam rentang yang masuk akal, dan tetap menghasilkan gambar

Kerusakannya muncul sebagai W(1) yang berevaluasi -1 padahal harus 0. Bobot negatif menumpuk, jumlahnya ter-clip di nol, dan gejala terlihatnya adalah gradien yang ujung kirinya menghitam dan tepi tangga yang kehilangan nada tengahnya. Tak ada yang mengarah ke polinomial. Pemeriksaan yang menangkapnya dalam hitungan detik adalah aritmetika, bukan visual: kernel interpolasi wajib memenuhi W(0) = 1 dan W(±1) = W(±2) = 0, dan kernel mana pun yang meleset dari tiga titik itu punya kesalahan koefisien, titik. Assert tiga nilai itu dalam unit test dan seluruh kelas cacat salah ketik hilang

Plot cabang luar bicubic Catmull-Rom HotPDF yang menunjukkan mengapa koefisien yang salah bersembunyi: salah ketik yang menulis -5 alih-alih -4 pada bentuk Horner bersarang tetap berevaluasi dan meninggalkan W(1) di -1 dan W(2) di -2 padahal nol diharuskan, sehingga assert W(0) sama dengan 1 plus dua constraint nol menangkapnya dalam hitungan detik
Kernel yang rusak tak pernah crash, ia hanya mengembalikan angka yang tampak masuk akal, dan itulah kenapa mata tak bisa menangkap salah ketik koefisien. W(0) = 1 dengan nol di plus dan minus satu serta dua adalah unit test tiga baris

Dithering Floyd-Steinberg, dan di mana posisinya dalam pipeline

Pass dither adalah masalah yang berbeda dari resampling dan tinggal di titik yang berbeda dalam pipeline. RenderOutputDither menerapkan difusi error Floyd-Steinberg setelah komposisi halaman, satu-satunya penempatan yang masuk akal untuk preview cetak monokrom atau ekspor bergaya faks: operasinya soal mereduksi raster yang sudah jadi menjadi satu bit per piksel, bukan soal bagaimana gambar-gambar individual diskalakan di jalur masuk

Algoritmanya sendiri pendek. Luminance di-threshold di 50 persen, dan error kuantisasinya didifusikan ke empat tetangga dengan bobot klasik 7/16, 3/16, 5/16 dan 1/16 — ke kanan, bawah-kiri, bawah, dan bawah-kanan. Piksel outputnya 0 atau 255 di semua kanal. Alternatif naifnya — threshold keras tanpa difusi — mengubah foto menjadi siluet dan membuang setiap nada tengah yang membawa konten

Penempatan dalam pipeline render untuk dithering Floyd-Steinberg HotPDF: RenderOutputDither berjalan setelah komposisi halaman pada raster 24-bit yang sudah jadi, meng-threshold luminance di 50 persen dan mendifusikan tiap error kuantisasi ke kanan dan ke bawah dengan bobot 7/16, 3/16, 5/16 dan 1/16 melalui row buffer yang wajib mengakumulasi, menghasilkan output monokrom satu bit
Dithering menempati posisi setelah komposisi karena ia mereduksi raster jadi menjadi satu bit, bukan karena cara gambar diskalakan. Bobot difusinya berjumlah satu, dan row buffer harus mengakumulasi alih-alih menimpa
// Dithering saat render untuk device preview monokrom
Pdf.RenderOutputDither := True;

// Atau terapkan pass yang sama ke bitmap yang sudah Anda miliki. Bitmap-nya
// wajib pf24bit; fungsinya mengembalikan False alih-alih menebak
if not HPDFFloydSteinbergDitherBitmap(Preview) then
  raise Exception.Create('dither expects a 24-bit bitmap');

// Akses kernel langsung saat Anda resample di luar pipeline dokumen
Small := HPDFResizeBitmapKernel(Large, 640, 480, rkLanczos3);
try
  Small.SaveToFile('thumb.bmp');
finally
  Small.Free;
end;

Ada satu detail implementasi di difusi error yang menggigit semua orang tepat satu kali. Buffer error antarbaris harus mengakumulasi. Tiap piksel di baris berikutnya menerima kontribusi dari tiga piksel berbeda di baris saat ini — tap 3/16, 5/16 dan 1/16 — dan kalau kodenya menugaskan alih-alih menjumlahkan, tiap penulisan membuang kontribusi sebelumnya dan hanya tap terakhir yang bertahan. Gambarnya masih tampak ter-dither, dan itulah yang membuatnya sulit disadari, tapi teksturnya salah dan reproduksi tonalnya menyimpang. Test yang menangkapnya kuantitatif: dither medan abu-abu tengah yang seragam dan syaratkan coverage interiornya mendarat antara 40 dan 60 persen

Kombinasi mana yang sebaiknya dipakai pipeline pengurangan ukuran?

Cocokkan kernelnya dengan apa adanya gambar-gambarnya, dan perlakukan dithering sebagai urusan device, bukan urusan kompresi. Untuk scan fotografis yang harus selamat dari anggaran ukuran, rkLanczos3 pada 150 atau 200 DPI mempertahankan detail yang diperhatikan orang sambil memangkas jumlah piksel empat kali lipat atau lebih. Untuk screenshot, diagram dan line art, rkHalftone benar-benar cukup dan jauh lebih cepat, karena gambar-gambar itu punya sedikit gradien tonal untuk dipertahankan. Untuk batch campuran yang tak bisa Anda periksa satu per satu, rkBicubic adalah titik tengah yang masuk akal: lebih baik dari bilinear, sekitar separuh jumlah tap Lanczos3

Downsampling adalah satu tuas di antara beberapa, dan belum tentu yang terbesar. Scan bilevel biasanya jauh lebih responsif terhadap encoder yang dibahas di kompresi bilevel JBIG2 native di Delphi, tempat keuntungannya datang dari symbol dictionary alih-alih jumlah piksel. Sebelum memutuskan, membantu mengetahui apa yang sebenarnya ada di dalam file, dan itulah gunanya mengekstrak gambar dan decode filter-nya: inventaris objek gambar dan kompresi eksistingnya memberi tahu apakah resampling punya sesuatu untuk didapat

Kalau Anda membangun permukaan preview yang menampilkan hasilnya, jalur render yang sama yang didokumentasikan di merender halaman PDF ke bitmap adalah tempat RenderOutputDither berefek, sehingga preview yang ter-dither dan output yang ter-dither datang dari satu jalur kode, bukan dua implementasi yang saling menjauh

Prinsip luas di balik kedua fitur itu: setting kualitas seharusnya eksplisit dan reversible. HotPDF mempertahankan perilaku historis sebagai bawaan agar aplikasi yang sudah ada ter-upgrade tanpa perubahan output atau timing yang mengejutkan, dan meletakkan jalur yang lebih bagus tapi lebih lambat satu penugasan property jauhnya. Keduanya bagian dari komponen PDF Delphi HotPDF, berdampingan dengan mesin optimisasi resource dan rendering yang menjadi fondasinya