HotPDF는 ImageDownsampleKernel 프로퍼티로 세 가지 이미지 다운샘플링 커널을 노출하고, 별도의 Floyd-Steinberg 패스는 RenderOutputDither로 노출합니다. 첫 번째는 크기 예산을 맞추려 사진을 줄였을 때 사진이 어떻게 보이는지를, 두 번째는 페이지가 흑백으로 환원된 뒤 어떻게 보이는지를 제어합니다. 둘 다 기본값은 꺼져 있고 같은 이유로 옵트인입니다. 진짜 시간이 드니까요
사람들을 여기로 몰아오는 압박은 익숙합니다. 60MB짜리 스캔 계약서가 10MB를 넘기면 거부하는 메일 게이트웨이로 나가야 하거나, 명세서 배치가 모든 회색 픽셀을 종이나 토너로 렌더링하는 팩스식 모노크롬 장치에 떨어져야 하거나. 둘 다 리샘플링 문제이고, 둘 다 나쁘게 보이는 빠른 답과 올바르게 보이는 느린 답을 갖고 있습니다
세 커널이 실제로 다른 점
THPDFResampleKernel은 값이 셋이고 속도와 품질 곡선의 실제로 다른 지점들에 놓여 있습니다. rkHalftone은 HALFTONE 모드의 역사적인 GDI StretchBlt 경로에 위임하는데, 이름과 달리 바이리니어급 필터링입니다. 빠르고, 라인 아트와 스크린샷에는 충분하고, 축소된 사진에서 즉시 알아볼 수 있는 거친 엣지가 생기기 쉽습니다. rkBicubic은 분리형 Catmull-Rom 커널을 돌리고, rkLanczos3는 3로브 서포트의 분리형 windowed sinc를 돌립니다
두 분리형 커널은 가로 다음 세로의 두 패스로 실행되며, 순수 Pascal로 목적지 픽셀당 6~12개 탭을 계산합니다. GDI 경로보다 대략 한 자릿수 느린데, rkHalftone이 기본값으로 남아 있는 정확한 이유입니다. 수천 페이지짜리 야간 배치에서 이 차이는 선호의 문제가 아니라 스케줄링 결정입니다. 사용자 한 명이 기다리는 단일 문서에서는 Lanczos3가 거의 공짜이고 눈에 띄게 좋습니다
출력이 무엇을 할 수 있고 없는지를 결정하므로 알아 둘 가치가 있는 구현 특성이 두 가지 있습니다. 경계는 래핑이나 페이딩이 아니라 에지 복제로 클램프하고, 가중치는 목적지 픽셀별로 정규화합니다. 이 둘을 합치면 결과가 black 아래나 white 위로 링잉하지 않으므로, 하드 엣지 주변의 전형적인 Lanczos 오버슈트 헤일로가 인코딩된 이미지에 클리핑 아티팩트로 나타나지 않습니다
var
Pdf: THotPDF;
Info: THPDFLoadedResourceOptimizationInfo;
Changed: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('scanned-contract.pdf');
Pdf.ImageDownsampleKernel := rkLanczos3; // 호출 전에 설정
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;
MinimumSavingsBytes 인수, 위에서는 4096,은 이 연산을 정직하게 유지하는 가드입니다. 이미 효율적으로 압축된 이미지를 재인코딩하면 원본보다 큰 스트림이 나올 수 있고, 모든 이미지를 맹목적으로 교체하는 다운샘플러는 줄이라고 받은 파일을 가끔 키웁니다. 임계값의 말은 이것입니다. 최소한 이만큼의 바이트를 아낄 때만 교체를 확정하라. PreservedCalibratedImageCount는 다른 보수적 결정을 보고합니다. 리샘플링이 훼손할 보정된 색 공간을 실은 이미지는 손대지 않고 남겨집니다
틀린 다항식 계수는 왜 찾기 어려운가?
망가진 보간 커널은 크래시하지도 예외를 던지지도 않고, 아무도 귀속시킬 수 없는 방식으로 미묘하게 잘못 보이는 이미지를 만들어 낼 뿐이기 때문입니다. Catmull-Rom 커널은 조각별 3차이고, 중첩 Horner 형식의 바깥 분기는 ((-0.5t + 2.5)t - 4)t + 2입니다. 그 가운데 계수를 -4가 아니라 -5로 적어도 함수는 여전히 평가되고, 그럴듯한 범위의 숫자를 여전히 돌려주고, 이미지도 여전히 만들어 냅니다
피해는 W(1)이 0이어야 하는데 -1로 평가되는 형태로 드러납니다. 음의 가중치가 쌓이고, 합이 0에서 클리프되고, 눈에 보이는 증상은 왼쪽 끝이 검게 변하는 그라디언트와 중간 톤을 잃는 스텝 엣지입니다. 실패의 어디도 다항식을 가리키지 않습니다. 몇 초 안에 잡아내는 검사는 시각이 아니라 산술입니다. 보간 커널은 W(0) = 1과 W(±1) = W(±2) = 0을 만족해야 하고, 이 세 점 중 하나라도 빗나가는 커널에는 계수 에러가 있습니다. 이의 없이요. 유닛 테스트에서 이 세 값을 단언하면 이 부류의 오타 결함 전체가 사라집니다
Floyd-Steinberg 디더링, 그리고 파이프라인에서의 자리
디더 패스는 리샘플링과 다른 문제이고 파이프라인의 다른 지점에 삽니다. RenderOutputDither는 페이지 합성 후에 Floyd-Steinberg 오차 확산을 적용하는데, 모노크롬 인쇄 미리 보기나 팩스식 익스포트에 맞는 유일한 배치입니다. 이 연산은 완성된 래스터를 픽셀당 1비트로 줄이는 것에 관한 것이지, 들어가는 길에 개별 이미지가 어떻게 스케일됐는지에 관한 것이 아닙니다
알고리즘 자체는 짧습니다. 휘도를 50퍼센트에서 임계 처리하고, 양자화 오차를 오른쪽, 아래왼쪽, 아래, 아래오른쪽 이웃에 클래식 7/16, 3/16, 5/16, 1/16 가중치로 확산합니다. 출력 픽셀은 모든 채널에서 0 또는 255입니다. 순진한 대안이 주는 것, 즉 확산 없는 하드 임계는 사진을 실루엣으로 만들고 콘텐츠를 실어 나르던 모든 중간 톤을 잃습니다
// 모노크롬 미리 보기 장치를 위한 렌더 시점 디더링
Pdf.RenderOutputDither := True;
// 이미 소유한 비트맵에 같은 패스를 적용할 수도 있습니다. 비트맵은
// pf24bit이어야 하며, 함수는 추측 대신 False를 돌려줍니다
if not HPDFFloydSteinbergDitherBitmap(Preview) then
raise Exception.Create('dither expects a 24-bit bitmap');
// 문서 파이프라인 밖에서 리샘플할 때의 직접 커널 접근
Small := HPDFResizeBitmapKernel(Large, 640, 480, rkLanczos3);
try
Small.SaveToFile('thumb.bmp');
finally
Small.Free;
end;
오차 확산에는 모두가 한 번씩 물리는 구현 디테일이 하나 있습니다. 행에서 행으로 이어지는 오차 버퍼는 누적해야 합니다. 다음 행의 각 픽셀은 현재 행의 서로 다른 세 픽셀, 즉 3/16, 5/16, 1/16 탭에서 기여를 받는데, 코드가 더하는 대신 할당하면 각 쓰기가 이전 기여를 버리고 마지막 탭만 살아남습니다. 이미지는 여전히 디더링된 것처럼 보이는데, 그래서 알아차리기 어렵습니다. 하지만 텍스처는 틀리고 톤 재현은 흘러갑니다. 잡아내는 테스트는 정량적입니다. 균일한 중간 회색 필드를 디더링하고 내부 커버리지가 40~60퍼센트 사이에 떨어지도록 요구하세요
크기 축소 파이프라인은 어떤 조합을 써야 하나?
커널은 이미지가 실제로 무엇인지에 맞추고, 디더링은 압축 문제가 아니라 장치 문제로 취급하세요. 크기 예산을 넘기면 안 되는 사진 스캔에는 150이나 200 DPI의 rkLanczos3가 픽셀 수를 4분의 1 이상으로 깎으면서 사람들이 알아채는 디테일을 지켜 줍니다. 스크린샷, 다이어그램, 라인 아트에는 rkHalftone이 실제로 충분하고 훨씬 빠릅니다. 지킬 톤 그라디언트가 거의 없는 이미지들이니까요. 하나하나 들여다볼 수 없는 혼합 배치에는 rkBicubic이 합리적인 중간입니다. 바이리니어보다 낫고, 탭 수는 Lanczos3의 절반 정도입니다
다운샘플링은 여러 레버 중 하나이며 항상 가장 큰 것은 아닙니다. 바이레벨 스캔은 보통 Delphi에서 네이티브 JBIG2 바이레벨 압축이 다루는 인코더에 훨씬 잘 반응합니다. 이득은 픽셀 수가 아니라 심볼 딕셔너리에서 나오니까요. 결정하기 전에 파일에 실제로 무엇이 들어 있는지 아는 것이 도움이 되는데, 이미지와 디코드 필터 추출하기가 바로 그 용도입니다. 이미지 오브젝트와 기존 압축의 목록이 리샘플링에 얻을 것이 있는지 알려 줍니다
결과를 보여 주는 미리 보기 표면을 만들고 있다면, PDF 페이지를 비트맵으로 렌더링하기에서 문서화된 같은 렌더링 경로가 RenderOutputDither가 효과를 발휘하는 자리입니다. 디더링된 미리 보기와 디더링된 출력이 흘러가다 어긋나는 두 구현이 아니라 하나의 코드 경로에서 나옵니다
두 기능 뒤에 있는 넓은 원칙은 품질 설정은 명시적이고 되돌릴 수 있어야 한다는 것입니다. HotPDF는 기존 애플리케이션이 출력이나 타이밍의 기습적 변화 없이 업그레이드되도록 역사적 동작을 기본값으로 유지하고, 더 예쁘고 느린 경로들은 프로퍼티 할당 한 번 거리에 둡니다. 둘 다 그것들이 쌓여 있는 리소스 최적화 및 렌더링 장치와 함께 HotPDF Delphi PDF 컴포넌트의 일부입니다