ภาพสแกนสไลด์ทางการแพทย์ แผ่นภาพจากการสำรวจทางอากาศ เฟรมภาพยนตร์ที่ถูกเก็บถาวรด้วยช่วงไดนามิก (dynamic range) แบบเต็มรูปแบบ ภาพเหล่านี้คือภาพที่มาในรูปแบบ JPEG 2000 และที่มันมาในรูปแบบนั้นก็มีเหตุผล รูปแบบนี้จะเก็บความละเอียด 12 หรือ 16 บิตต่อแชนเนล บีบอัดด้วยการแปลงแบบเวฟเล็ต (wavelet transform) แทนที่จะใช้ block DCT แบบที่ JPEG ใช้ และสามารถเข้ารหัสรูปภาพเดียวกันได้ทั้งแบบไม่สูญเสียข้อมูล (lossless) หรือสูญเสียข้อมูล (lossy) จากโค้ดสตรีมเดียว เมื่อเอกสารที่สร้างขึ้นจากแหล่งข้อมูลเหล่านั้นจำเป็นต้องกลายเป็น PDF รูปภาพจะต้องเดินทางผ่านตัวกรองที่ข้อกำหนดเฉพาะของ PDF สงวนไว้สำหรับตัวแปลงสัญญาณนี้โดยเฉพาะ
HotPDF v2.228.0 ได้กู้คืนเอนจินถอดรหัส JPEG 2000 ที่ใช้งานได้สำหรับเส้นทางนั้น บิลด์ก่อนหน้านี้ได้ให้ยูนิตที่มาพร้อมกับฟังก์ชันจำลอง (stub functions) ที่ส่งคืนค่า nil ดังนั้นแม้ว่าจะมี API อยู่แต่มันก็ไม่ได้ถอดรหัสอะไรเลย เอนจินปัจจุบันจะผูกกับ OpenJPEG 2.5.4 แบบสแตติกและเปลี่ยนซอร์ส JP2 หรือ J2K ให้เป็นพิกเซลที่ HotPDF สามารถนำไปวางลงบนหน้าเอกสารได้
ตัวกรอง JPXDecode ใน PDF
ISO 32000-1 กำหนดตัวกรอง JPXDecode ไว้ใน §7.4.9 ออบเจ็กต์รูปภาพ PDF แบบ XObject จะระบุชื่อการบีบอัดในรายการ /Filter ของพจนานุกรมสตรีม และ JPXDecode ก็คือค่าที่บอกว่าข้อมูลสตรีมนั้นเป็นโค้ดสตรีมของ JPEG 2000 แทนที่จะเป็น JPEG พื้นฐานที่ /DCTDecode นำมา ตัวกรองนี้เป็นสิ่งที่ช่วยให้ PDF สามารถเก็บข้อมูลรูปภาพที่ถูกบีบอัดแบบเวฟเล็ตซึ่งมีความลึกของบิตสูงไว้ได้ และมันก็รองรับทั้งโหมดที่ไม่สูญเสียข้อมูลและโหมดที่สูญเสียข้อมูลของตัวแปลงสัญญาณ เนื่องจากโหมดนั้นเป็นคุณสมบัติของตัวโค้ดสตรีมเอง ไม่ใช่ของแรปเปอร์ที่ห่อหุ้มมันอยู่
ประเด็นสุดท้ายนั้นเป็นสิ่งที่ควรค่าแก่การจดจำ JPEG 2000 เป็นอัลกอริทึมเดียวที่มีกรณีพิเศษแบบไม่สูญเสียข้อมูล ไม่ใช่รูปแบบที่แยกกันสองรูปแบบ เวฟเล็ต 5/3 แบบผันกลับได้จะสร้างตัวอย่างดั้งเดิมขึ้นมาใหม่ได้อย่างแม่นยำ ในขณะที่เวฟเล็ต 9/7 แบบผันกลับไม่ได้จะแลกความแม่นยำนั้นกับขนาดไฟล์ที่เล็กลง ตัวถอดรหัสจะจัดการกับทั้งสองรูปแบบด้วยวิธีเดียวกันในเวลาที่อ่านข้อมูล ซึ่งนี่คือเหตุผลที่ HotPDF ต้องการเพียงเส้นทางถอดรหัสเดียวเพื่อรับข้อมูลใดๆ ก็ตามที่สตรีม JPXDecode ส่งมาให้มัน
สิ่งที่ตัวถอดรหัสทำกับพิกเซล
ในกรณีทั่วไป ออบเจ็กต์รูปภาพ PDF แบบ XObject จะคาดหวังข้อมูลที่ 8 บิตต่อแชนเนลใน DeviceGray หรือ DeviceRGB แต่โดยปกติแล้ว JPEG 2000 มักจะเกินขีดจำกัดนั้น และรูปแบบแชนเนลของมันก็มีความครอบคลุมมากกว่าแรสเตอร์ที่ถูกแพ็กมา ดังนั้นตัวถอดรหัสจึงมีงานที่ต้องทำสามอย่างก่อนที่ข้อมูลจะสามารถนำไปใช้เป็นรูปภาพปกติได้
อย่างแรก แชนเนลที่มีความลึกของบิตสูงจะถูกสุ่มตัวอย่างใหม่ (resample) ให้เป็น 8 บิต ตัวอย่าง 12 บิตหรือ 16 บิตจะถูกลดขนาดลงให้อยู่ในช่วง 0 ถึง 255 เพื่อให้ผลลัพธ์กลายเป็นแรสเตอร์ 8 บิตแบบธรรมดา แชนเนลที่มีเครื่องหมาย (signed) จะถูกเลื่อนให้อยู่ในช่วงที่ไม่มีเครื่องหมาย (unsigned) ก่อน รายละเอียดนี้มีความสำคัญเพราะตัวมันเองก็ถือเป็นการสูญเสียข้อมูล ภาพสแกนระดับสีเทา 16 บิตจะสูญเสียช่วงโทนสีที่ลึกทันทีที่มันกลายเป็นรูปภาพ PDF 8 บิต ซึ่งเป็นการแลกเปลี่ยนที่ถูกต้องสำหรับผลลัพธ์บนหน้าจอและการพิมพ์ แต่ไม่เหมาะสำหรับการนำกลับไปเก็บถาวรใหม่
อย่างที่สอง ปริภูมิสี YCbCr (ซึ่งตัวแปลงสัญญาณเรียกว่า SYCC) จะถูกแปลงเป็น RGB บ่อยครั้งที่ JPEG 2000 จัดเก็บสีในรูปแบบ luma-chroma เพื่อประสิทธิภาพในการบีบอัด ซึ่งเป็นแนวคิดเดียวกับที่ใช้ใน JPEG พื้นฐาน และตัวถอดรหัสก็จะประยุกต์ใช้การแปลงผกผันตามมาตรฐานเพื่อให้หน้าเอกสารได้รับค่า RGB ที่แท้จริง
อย่างที่สาม แชนเนลที่ถูกสุ่มตัวอย่างย่อย (subsampled) จะถูกอัปแซมเปิลด้วยการจำลองแบบใกล้เคียงที่สุด (nearest-neighbor) แชนเนลสี (chroma) มักจะถูกจัดเก็บไว้ที่ความละเอียดเพียงครึ่งเดียว ดังนั้นตัวถอดรหัสจึงอ่านข้อมูลแชนเนลแต่ละส่วนตามขนาดและปัจจัยการสุ่มตัวอย่างของมันเอง จากนั้นจึงจำลองตัวอย่างเพื่อดึงทุกแชนเนลให้มีขนาดเท่ากับรูปภาพเต็มก่อนที่จะทำการสอดแทรก (interleave) วิธีการจำลองแบบใกล้เคียงที่สุดช่วยให้ขั้นตอนนี้ใช้ทรัพยากรน้อยลง สีที่นำมาเติมนั้นเป็นความถี่ต่ำตั้งแต่แรกอยู่แล้ว ดังนั้นผลกระทบที่มองเห็นได้จึงมีน้อยมาก
กล่อง JP2 กับโค้ดสตรีม J2K ดิบ
ไฟล์ JPEG 2000 มาในสองรูปแบบ และ HotPDF จะตรวจจับว่ามันกำลังอ่านรูปแบบใดอยู่จากไบต์แรกๆ แทนที่จะดูจากนามสกุลไฟล์ ไฟล์ JP2 เป็นคอนเทนเนอร์แบบโครงสร้างกล่อง มันเริ่มต้นด้วยกล่องลายเซ็นสิบสองไบต์ 00 00 00 0C 6A 50 20 20 และห่อหุ้มโค้ดสตรีมเอาไว้ควบคู่ไปกับกล่องที่อธิบายถึงปริภูมิสี ความละเอียด และข้อมูลอภิพันธุ์ (metadata) ส่วนโค้ดสตรีม J2K ดิบนั้นจะไม่มีคอนเทนเนอร์ใดๆ เลย และเริ่มต้นด้วยเครื่องหมาย SOC FF 4F FF 51 ตัวถอดรหัสจะอ่านไบต์นำหน้าเหล่านั้น จดจำลายเซ็น และเลือกตัวแปลงสัญญาณ OpenJPEG ที่ตรงกันในแต่ละกรณี
มีการจัดการทั้งสองรูปแบบเนื่องจากทั้งสองแบบมีให้พบเห็นในการใช้งานจริง อุปกรณ์จับภาพและคลังข้อมูลถาวรที่ต้องการข้อมูลอภิพันธุ์ประกอบจะปล่อยข้อมูลออกมาเป็น JP2 ส่วนเครื่องมือที่ต้องการข้อมูลเพย์โหลดที่เล็กที่สุดเท่าที่จะเป็นไปได้จะปล่อยโค้ดสตรีมเปล่าๆ ออกมา ชนิดของรูปแบบจะถูกจำลองเป็นการแจงนับ TJpeg2000FileType ที่มีสมาชิกคือ jtInvalid, jtJP2, jtJ2K และ jtJPT สมาชิก JPT ตั้งชื่อตัวแปรย่อยของการสตรีม JPIP ตัวตรวจจับลายเซ็นไบต์จะแยกแยะรูปทรงสองแบบที่มันสามารถถอดรหัสได้ นั่นคือ JP2 และ J2K และจะรายงานสิ่งอื่นใดว่าเป็น jtInvalid เพื่อให้ข้อมูลนำเข้าที่ไม่รองรับเกิดข้อผิดพลาดอย่างชัดเจนแทนที่จะสร้างข้อมูลขยะขึ้นมา
uses
HPDFJpeg2000;
var
Decoder: THPDFJpeg2000Decoder;
Pixels: TJpeg2000ByteArray;
begin
Decoder := THPDFJpeg2000Decoder.Create;
try
if Decoder.LoadFromStream(Input) then // JP2 or J2K, auto-detected
if Decoder.GetImageData(Pixels) then
// Pixels is 8-bit interleaved, ColorComponents channels wide,
// row-major top to bottom: ready for a DeviceGray/DeviceRGB XObject.
ProcessRaster(Decoder.Width, Decoder.Height,
Decoder.ColorComponents, Pixels);
finally
Decoder.Free;
end;
end;
การไม่สูญเสียข้อมูลและการสูญเสียข้อมูลในฝั่งเข้ารหัส
ตัวถอดรหัสจะอ่านทั้งสองโหมดโดยไม่ต้องมีการบอกล่วงหน้าว่าเป็นโหมดใด ตัวเลือกนี้จะกลายเป็นเพียงพารามิเตอร์เมื่อคุณทำในทางกลับกันและสร้างไฟล์ JPEG 2000 ซึ่ง HotPDF ก็สามารถทำได้ผ่านคลาส TJpeg2000Bitmap ซึ่งเป็นคลาสย่อยของ TBitmap ที่โหลดและบันทึกข้อมูลแรสเตอร์เป็น JP2 มีคุณสมบัติสองประการที่ควบคุมผลลัพธ์ LosslessCompression เป็นค่าบูลีนที่เลือกเวฟเล็ตแบบผันกลับได้เมื่อมีค่าเป็น true ส่วน CompressionQuality คือ TJpeg2000QualityRange ซึ่งเป็นจำนวนเต็มตั้งแต่ 1 ถึง 100 โดยที่ 1 จะมีขนาดเล็กและคุณภาพแย่ ส่วน 100 จะมีขนาดใหญ่และรักษาความเที่ยงตรง ค่าเริ่มต้นนั้นจะอยู่ในรูปแบบของค่าคงที่ที่มีชื่อ Jpeg2000DefaultLosslessCompression จะเป็น False และ Jpeg2000DefaultLossyQuality จะเป็น 80
การตัดสินใจดังกล่าวถือเป็นการตัดสินใจตามเนื้อหา โหมดที่ไม่สูญเสียข้อมูลจะเหมาะกับสำเนาต้นฉบับ ภาพสแกนทางการแพทย์หรือทางกฎหมาย สิ่งใดก็ตามที่อาจถูกนำไปเข้ารหัสใหม่ในภายหลังและต้องไม่มีการสะสมการสูญเสียคุณภาพตามยุคสมัย ส่วนโหมดสูญเสียข้อมูลที่คุณภาพ 80 นั้นเหมาะสำหรับรูปภาพที่เตรียมไว้สำหรับหน้าจอหรือการพิมพ์ ซึ่งการลดระดับคุณภาพอย่างนุ่มนวลของเวฟเล็ตจะให้ไฟล์ที่มีขนาดเล็กลงอย่างเห็นได้ชัดโดยที่ผู้อ่านไม่สังเกตเห็นถึงความผิดเพี้ยน มีข้อควรระวังหนึ่งข้อเกี่ยวกับ CMYK ที่ควรทราบ บิตแมปจะเปิดเผย SetCMYK เพื่อทำเครื่องหมายข้อมูลแบบสี่แชนเนลว่าเป็น CMYK แทนที่จะเป็น RGBA ซึ่งมีความสำคัญต่อขั้นตอนการทำงานของการพิมพ์ที่รักษาการแยกสีเอาไว้อย่างครบถ้วน
uses
HPDFJpeg2000;
var
Bmp: TJpeg2000Bitmap;
begin
Bmp := TJpeg2000Bitmap.Create;
try
Bmp.LoadFromStream(Source); // decode an existing JP2/J2K
Bmp.LosslessCompression := True; // reversible 5/3 wavelet
// or, for a smaller lossy file:
// Bmp.LosslessCompression := False;
// Bmp.CompressionQuality := 80; // matches the default
Bmp.SaveToStream(Output); // always writes a JP2 file
finally
Bmp.Free;
end;
end;
เหตุใดจึงไม่มีไปป์ไลน์ตัวกรองแบบถอดรหัสเมื่อโหลด
ข้อเท็จจริงทางสถาปัตยกรรมประการหนึ่งกำหนดวิธีที่คุณจะใช้งานสิ่งเหล่านี้ และมันก็เป็นเรื่องง่ายที่จะทึกทักเอาในทางตรงกันข้าม HotPDF ไม่มีตัวกรองรูปภาพทั่วไปสำหรับการถอดรหัสเมื่อโหลด เมื่อคุณเปิด PDF ที่มีรูปภาพแบบ JPXDecode อยู่แล้ว เอนจินจะไม่ถอดรหัสสตรีมนั้น มันจะเก็บไบต์ของ JPEG 2000 ไว้เหมือนเดิมทุกประการ ดังนั้นการคัดลอกหน้าหรือการผสานเอกสารจะนำพารูปภาพผ่านไปโดยไม่มีการถูกแก้ไขใดๆ แบบไบต์ต่อไบต์ ตัวถอดรหัสมีจุดเข้าใช้งานเพียงจุดเดียว และมันอยู่ฝั่งการสร้าง นั่นคือ AddImage ที่ทำงานบนพื้นฐานของไฟล์ ซึ่งจะกระจายตามนามสกุลไฟล์เพื่อจัดการกับซอร์สที่เป็น .jp2, .j2k, .jpt และ .jpc
การแยกส่วนดังกล่าวนั้นถือเป็นการออกแบบที่ถูกต้องมากกว่าที่จะเป็นข้อจำกัด การถอดรหัสสตรีม JPX ที่ฝังไว้ตอนที่โหลดเพียงเพื่อจะนำไปเข้ารหัสใหม่ตอนที่บันทึก จะเป็นการแปลงรูปภาพที่เก็บถาวรแบบไม่สูญเสียข้อมูลให้กลายเป็นแบบที่สูญเสียข้อมูล และจะทำให้ขนาดใหญ่ขึ้นในทุกการผสานเอกสาร ทั้งหมดนี้เกิดขึ้นกับรูปภาพที่คุณแค่ตั้งใจจะย้ายจาก PDF หนึ่งไปยังอีก PDF หนึ่งเท่านั้น การส่งผ่านสตรีมตามข้อความทุกตัวอักษรนั้นคือการทำงานแบบไม่สูญเสียข้อมูลและยังรวดเร็วอีกด้วย การถอดรหัสจะถูกเลื่อนออกไปจนกว่าจะถึงช่วงเวลาเดียวที่จำเป็นอย่างแท้จริงเท่านั้น นั่นคือเมื่อคุณส่งไฟล์ JPEG 2000 จากดิสก์ให้กับเอนจินและขอให้มันแปลงภาพนั้นเป็นแรสเตอร์เพื่อไปวางบนหน้าใหม่ ในจุดนั้นไฟล์จะต้องกลายเป็นพิกเซล และตัวถอดรหัสก็เริ่มทำงาน
การลงทะเบียนการสนับสนุนและการจัดวางรูปภาพ
การลงทะเบียนรูปภาพ JPEG 2000 เป็นตัวเลือกเบื้องหลังสวิตช์คอมไพล์ HPDF_REGISTER_JPEG2000_PICTURE ซึ่งปิดอยู่ตามค่าเริ่มต้น เหตุผลก็คือมันมีความขัดแย้งที่แท้จริง ไม่ใช่แค่ความระมัดระวัง การลงทะเบียนรูปแบบไฟล์ jp2, j2k และ jpc เป็นแบบโกลบอลด้วย TPicture อาจไปรบกวนการตรวจจับรูปแบบ BLOB ที่ TppDBImage ของ ReportBuilder ใช้เป็นหลัก ให้กำหนดสวิตช์นี้เมื่อการทำงานร่วมกันนั้นไม่ได้ถูกนำมาใช้ และรูปแบบไฟล์จะลงทะเบียนเพื่อให้ TPicture รู้จักมัน หากปล่อยไว้โดยไม่ได้กำหนด การกระจายนามสกุลของ AddImage จะยังคงถอดรหัสไฟล์ JPEG 2000 ได้โดยตรง เพราะเส้นทางนั้นไม่ได้ผ่าน TPicture เลย
เมื่อเข้าใจตรงกันแล้ว การจัดวางรูปภาพ JPEG 2000 ก็เป็นจังหวะการเรียกใช้งานสามขั้นตอนแบบเดียวกับรูปภาพ HotPDF อื่นๆ ส่งมอบเส้นทาง .jp2 และชนิดของการบีบอัดให้กับ AddImage สำหรับวิธีกำหนดว่ารูปภาพควรถูกเก็บไว้ในผลลัพธ์อย่างไร จากนั้นจึงจัดตำแหน่งดัชนีรูปภาพที่ส่งคืนมาไว้บนหน้ากระดาษด้วย ShowImage
var
Pdf: THotPDF;
ImgIndex: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.BeginDoc;
Pdf.AddPage;
// The .jp2 source is decoded through the OpenJPEG backend, then
// re-embedded with the compression you request here.
ImgIndex := Pdf.AddImage('Scan_16bit.jp2', icJpeg);
// x, y, width, height in points; final 0 is the rotation angle.
Pdf.ShowImage(ImgIndex, 72, 72, 400, 300, 0);
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
การบีบอัดที่คุณส่งไปยัง AddImage จะเป็นตัวควบคุมวิธีที่รูปภาพที่ถูกถอดรหัสมาจะถูกจัดเก็บอีกครั้ง ไม่ใช่วิธีที่มันถูกอ่านมา ไฟล์ JPEG 2000 ที่ถูกถอดรหัสมาเป็นบิตแมปสามารถส่งกลับออกไปเป็น DCTDecode JPEG, แรสเตอร์แบบ Flate หรือตัวกรองอื่นๆ ที่รองรับได้ แล้วแต่ว่าสิ่งใดจะเหมาะสมกับเอกสารนั้น การถอดรหัสจาก JP2 หรือ J2K จะเกิดขึ้นก่อนเสมอ ดังนั้นการเรียกใช้งานเดียวกันก็จะยอมรับแหล่งข้อมูลที่ถูกบีบอัดแบบเวฟเล็ตและนำไปฝังไว้ในรูปแบบใดก็ตามที่ขั้นตอนการทำงานที่เหลือของคุณคาดหวัง
สำหรับภาพรวมที่กว้างขึ้นเกี่ยวกับวิธีที่รูปภาพและแบบอักษรจะปรากฏในผลลัพธ์ที่สร้างขึ้น โปรดดู บันทึกของเราเกี่ยวกับผลลัพธ์รายงานที่มีแบบอักษรและรูปภาพ เมื่อเอกสารที่คุณกำลังรวบรวมมีการใช้เนื้อหาจาก PDF ที่มีอยู่ซ้ำ พฤติกรรมการส่งผ่านที่อธิบายไว้ที่นี่ก็จะจับคู่กับกลไกการผสานและการแก้ไขใน ออบเจ็กต์สตรีมและการอัปเดตแบบเพิ่มส่วน เอนจินการถอดรหัส JPEG 2000 มาพร้อมกับ HotPDF Component สำหรับ Delphi และ C++Builder ควบคู่ไปกับรูปภาพ แบบอักษร และ API ของเอกสารซึ่งได้อธิบายไว้ที่อื่นในบล็อกนี้