Một slide y tế được quét, một ô khảo sát trên không, một khung phim được lưu trữ ở dải động đầy đủ. Đây là những hình ảnh đến dưới dạng JPEG 2000, và chúng đến theo cách đó vì một lý do. Định dạng giữ 12 hoặc 16 bit mỗi kênh, nén bằng biến đổi wavelet thay vì DCT khối mà JPEG sử dụng, và có thể mã hóa cùng một hình ảnh không mất mát hoặc có mất mát từ một codestream. Khi một tài liệu được xây dựng từ các nguồn đó phải trở thành PDF, hình ảnh phải đi qua một bộ lọc mà thông số PDF dành riêng cho codec này
HotPDF v2.228.0 đã khôi phục engine giải mã JPEG 2000 hoạt động cho đường dẫn đó. Một bản build trước đó đã đi kèm unit với các hàm stub trả về nil, vì vậy API tồn tại nhưng không giải mã gì. Engine hiện tại ràng buộc tĩnh OpenJPEG 2.5.4 và biến nguồn JP2 hoặc J2K thành các pixel mà HotPDF có thể đặt trên trang
Bộ lọc JPXDecode trong PDF
ISO 32000-1 định nghĩa bộ lọc JPXDecode trong §7.4.9. Một image XObject PDF đặt tên nén của nó trong mục /Filter của từ điển luồng, và JPXDecode là giá trị cho biết dữ liệu luồng là một codestream JPEG 2000 thay vì JPEG cơ bản mà /DCTDecode mang. Bộ lọc là thứ cho phép PDF giữ dữ liệu hình ảnh được nén bằng wavelet với độ sâu bit cao, và nó cho phép cả hai chế độ không mất mát và có mất mát của codec, vì chế độ là thuộc tính của chính codestream chứ không phải của wrapper xung quanh nó
Điểm cuối đó là điểm đáng giữ. JPEG 2000 là một thuật toán duy nhất với trường hợp đặc biệt không mất mát, không phải hai định dạng riêng biệt. Wavelet 5/3 có thể đảo ngược tái tạo lại các mẫu gốc chính xác; wavelet 9/7 không thể đảo ngược đánh đổi sự chính xác đó để có tệp nhỏ hơn. Bộ giải mã xử lý cả hai theo cùng một cách khi đọc, đó là lý do tại sao HotPDF chỉ cần một đường dẫn giải mã để chấp nhận bất cứ điều gì mà luồng JPXDecode ném vào nó
Bộ giải mã làm gì với các pixel
Các image XObject PDF trong trường hợp thông thường mong đợi 8 bit mỗi thành phần trong DeviceGray hoặc DeviceRGB. JPEG 2000 thường xuyên vượt quá điều đó, và mô hình thành phần của nó tổng quát hơn một raster được đóng gói, vì vậy bộ giải mã có ba công việc cần làm trước khi dữ liệu có thể sử dụng được như một hình ảnh thông thường
Đầu tiên, các thành phần có độ sâu bit cao được lấy mẫu lại xuống 8 bit. Một mẫu 12-bit hoặc 16-bit được thu nhỏ xuống phạm vi 0 đến 255 để kết quả là một raster 8-bit thông thường. Các thành phần có dấu được chuyển vào phạm vi không có dấu trước. Chi tiết quan trọng vì nó tự mất mát: một lần quét xám thang độ 16-bit mất dải âm điệu sâu của nó ngay khi nó trở thành hình ảnh PDF 8-bit, đây là sự đánh đổi đúng cho đầu ra màn hình và in ấn nhưng không phải cho tái lưu trữ
Thứ hai, không gian màu YCbCr (codec gọi nó là SYCC) được chuyển đổi thành RGB. JPEG 2000 thường lưu trữ màu trong không gian luma-chroma để hiệu quả nén, cùng ý tưởng mà JPEG cơ bản sử dụng, và bộ giải mã áp dụng biến đổi ngược chuẩn để trang nhận được RGB thực sự
Thứ ba, các thành phần được lấy mẫu con được tăng mẫu bằng cách nhân rộng gần nhất. Các kênh chroma thường được lưu trữ ở độ phân giải một nửa, vì vậy bộ giải mã đọc mỗi thành phần ở kích thước và hệ số lấy mẫu riêng của nó, sau đó nhân các mẫu để đưa mọi kênh lên kích thước hình ảnh đầy đủ trước khi xen kẽ. Gần nhất giữ bước rẻ; chroma nó đang lấp đầy vốn đã là tần số thấp, vì vậy chi phí có thể nhìn thấy là nhỏ
Hộp JP2 so với codestream J2K thô
Tệp JPEG 2000 có hai hình dạng, và HotPDF phát hiện cái nào đang đọc từ các byte đầu tiên thay vì từ phần mở rộng tệp. Tệp JP2 là một container có cấu trúc hộp: nó mở với hộp chữ ký mười hai byte 00 00 00 0C 6A 50 20 20 và bọc codestream cùng với các hộp mô tả không gian màu, độ phân giải và siêu dữ liệu. Một codestream J2K thô không mang container nào cả và bắt đầu bằng marker SOC FF 4F FF 51. Bộ giải mã đọc các byte đầu đó, nhận ra chữ ký, và chọn codec OpenJPEG phù hợp cho mỗi trường hợp
Cả hai hình dạng đều được xử lý vì cả hai đều xuất hiện trong thực tế. Các thiết bị thu thập và lưu trữ cần siêu dữ liệu phụ phát ra JP2; các công cụ muốn tải trọng nhỏ nhất có thể phát ra codestream thuần. Loại định dạng được mô hình hóa như một enum, TJpeg2000FileType, với các thành viên jtInvalid, jtJP2, jtJ2K, và jtJPT. Thành viên JPT đặt tên cho biến thể streaming JPIP; bộ phát hiện chữ ký byte giải quyết hai hình dạng nó có thể giải mã, JP2 và J2K, và báo cáo bất cứ thứ gì khác là jtInvalid để đầu vào không được hỗ trợ thất bại sạch thay vì tạo ra rác
uses
HPDFJpeg2000;
var
Decoder: THPDFJpeg2000Decoder;
Pixels: TJpeg2000ByteArray;
begin
Decoder := THPDFJpeg2000Decoder.Create;
try
if Decoder.LoadFromStream(Input) then // JP2 hoặc J2K, tự động phát hiện
if Decoder.GetImageData(Pixels) then
// Pixels là dạng 8-bit xen kẽ, chiều rộng kênh ColorComponents,
// kiểu row-major từ trên xuống dưới: sẵn sàng cho một XObject DeviceGray/DeviceRGB.
ProcessRaster(Decoder.Width, Decoder.Height,
Decoder.ColorComponents, Pixels);
finally
Decoder.Free;
end;
end;
Không mất mát và có mất mát ở phía mã hóa
Bộ giải mã đọc cả hai chế độ mà không được báo cho biết cái nào. Sự lựa chọn chỉ trở thành tham số khi bạn đi theo hướng khác và tạo ra tệp JPEG 2000, mà HotPDF cũng có thể làm thông qua lớp TJpeg2000Bitmap, một con cháu TBitmap tải và lưu dữ liệu raster dưới dạng JP2. Hai thuộc tính điều chỉnh đầu ra. LosslessCompression là boolean chọn wavelet có thể đảo ngược khi true; CompressionQuality là TJpeg2000QualityRange, một số nguyên từ 1 đến 100 trong đó 1 là nhỏ và xấu và 100 là lớn và trung thực. Các giá trị mặc định nằm trong các hằng số có tên: Jpeg2000DefaultLosslessCompression là False và Jpeg2000DefaultLossyQuality là 80
Quyết định là quyết định về nội dung. Không mất mát phù hợp với bản sao gốc, lần quét y tế hoặc pháp lý, bất cứ thứ gì có thể được mã hóa lại sau và không được tích lũy mất mát thế hệ. Có mất mát ở chất lượng 80 phù hợp với hình ảnh đến màn hình hoặc in ấn, nơi sự xuống cấp nhẹ nhàng của wavelet cho tệp nhỏ hơn đáng kể mà không có artifact nào người đọc nhận thấy. Có một lưu ý CMYK cần đánh dấu: bitmap hiển thị SetCMYK để đánh dấu dữ liệu bốn kênh là CMYK thay vì RGBA, điều quan trọng đối với các pipeline in ấn giữ các tách màu nguyên vẹn
uses
HPDFJpeg2000;
var
Bmp: TJpeg2000Bitmap;
begin
Bmp := TJpeg2000Bitmap.Create;
try
Bmp.LoadFromStream(Source); // giải mã một tệp JP2/J2K có sẵn
Bmp.LosslessCompression := True; // dùng wavelet 5/3 thuận nghịch
// hoặc, cho một tệp lossy (có suy hao) kích thước nhỏ hơn:
// Bmp.LosslessCompression := False;
// Bmp.CompressionQuality := 80; // khớp với mặc định
Bmp.SaveToStream(Output); // luôn luôn viết ra định dạng tệp JP2
finally
Bmp.Free;
end;
end;
Tại sao không có pipeline bộ lọc giải mã khi tải
Một thực tế kiến trúc định hình cách bạn sử dụng bất kỳ điều nào trong số này, và dễ giả định ngược lại. HotPDF không có bộ lọc hình ảnh giải mã khi tải chung. Khi bạn mở một PDF đã chứa hình ảnh JPXDecode, engine không giải mã luồng đó. Nó giữ nguyên các byte JPEG 2000 đúng như chúng là, vì vậy sao chép trang hoặc hợp nhất tài liệu mang hình ảnh qua nguyên vẹn, byte theo byte. Bộ giải mã có một điểm vào duy nhất, và nó ở phía tạo: AddImage dựa trên tệp, được gửi đi theo phần mở rộng tệp để xử lý các nguồn .jp2, .j2k, .jpt, và .jpc
Sự phân tách đó là thiết kế đúng chứ không phải giới hạn. Giải mã luồng JPX nhúng khi tải, chỉ để mã hóa lại khi lưu, sẽ chuyển đổi một hình ảnh được lưu trữ không mất mát thành một hình ảnh có mất mát và làm phình mỗi lần hợp nhất, tất cả cho một hình ảnh bạn chỉ có ý định di chuyển từ PDF này sang PDF khác. Truyền luồng qua nguyên vẹn là một thao tác không mất mát và nhanh chóng. Giải mã bị trì hoãn đến thời điểm duy nhất thực sự cần thiết: khi bạn đưa cho engine một tệp JPEG 2000 từ đĩa và yêu cầu raster hóa hình ảnh đó để đặt trên trang mới. Tại thời điểm đó tệp phải trở thành pixel, và bộ giải mã chạy
Đăng ký hỗ trợ và đặt hình ảnh
Đăng ký hình ảnh JPEG 2000 là opt-in phía sau switch biên dịch HPDF_REGISTER_JPEG2000_PICTURE, mặc định là tắt. Lý do là xung đột thực sự, không phải thận trọng: đăng ký các định dạng tệp jp2, j2k, và jpc toàn cục với TPicture có thể can thiệp vào phát hiện định dạng BLOB mà TppDBImage của ReportBuilder dựa vào. Xác định switch khi tích hợp đó không có mặt, và các định dạng tệp đăng ký để TPicture nhận ra chúng; để không xác định và dispatch mở rộng AddImage vẫn giải mã các tệp JPEG 2000 trực tiếp, vì đường dẫn đó không đi qua TPicture chút nào
Với điều đó đã hiểu, việc đặt một hình ảnh JPEG 2000 giống như nhịp điệu ba lệnh gọi như bất kỳ hình ảnh HotPDF nào khác. Đưa AddImage một đường dẫn .jp2 và loại nén cho cách hình ảnh sẽ được lưu trữ trong đầu ra, sau đó định vị chỉ mục hình ảnh được trả về trên trang với ShowImage
var
Pdf: THotPDF;
ImgIndex: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.BeginDoc;
Pdf.AddPage;
// Nguồn .jp2 được giải mã thông qua phụ trợ OpenJPEG, sau đó
// được nhúng lại (re-embedded) với mức nén mà bạn yêu cầu ở đây.
ImgIndex := Pdf.AddImage('Scan_16bit.jp2', icJpeg);
// x, y, chiều rộng, chiều cao tính bằng điểm (points); số 0 cuối cùng là góc xoay.
Pdf.ShowImage(ImgIndex, 72, 72, 400, 300, 0);
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Nén bạn truyền cho AddImage kiểm soát cách hình ảnh đã giải mã được tái lưu trữ, không phải cách nó được đọc. Tệp JPEG 2000 được giải mã thành bitmap có thể xuất ra dưới dạng DCTDecode JPEG, raster Flate, hoặc bộ lọc được hỗ trợ khác, tùy theo điều nào phù hợp với tài liệu. Việc giải mã từ JP2 hoặc J2K xảy ra trước bất kể, vì vậy cùng một lệnh gọi chấp nhận nguồn được nén wavelet và nhúng nó ở bất kỳ dạng nào mà phần còn lại của pipeline của bạn mong đợi
Để có bức tranh rộng hơn về cách hình ảnh và phông chữ được đặt trong đầu ra được tạo ra, xem ghi chú của chúng tôi về đầu ra báo cáo với phông chữ và hình ảnh. Khi tài liệu bạn đang lắp ráp tái sử dụng nội dung từ các PDF hiện có, hành vi passthrough được mô tả ở đây kết hợp với cơ chế hợp nhất và sửa đổi trong luồng đối tượng và cập nhật gia tăng. Engine giải mã JPEG 2000 đi kèm như một phần của HotPDF Component cho Delphi và C++Builder, bên cạnh các API hình ảnh, phông chữ và tài liệu được đề cập ở nơi khác trên blog này