OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimJpeg12NitfReader.cpp
Go to the documentation of this file.
1 //----------------------------------------------------------------------------
2 //
3 // License: MIT
4 //
5 // See LICENSE.txt file in the top level directory for more details.
6 //
7 // Author: Refactor from Mingjie Su's ossimNitfTileSource_12.
8 //
9 // Description:
10 //
11 // Class declaration for reader of NITF images with 12 bit jpeg compressed
12 // blocks using libjpeg-turbo library compiled specifically with
13 // "--with-12bit" compiler option.
14 //
15 //----------------------------------------------------------------------------
16 // $Id
17 
18 #include "ossimJpeg12NitfReader.h"
19 #include "ossimJpegMemSrc12.h"
20 #include <ossim/base/ossimTrace.h>
23 #include <fstream>
24 #include <vector>
25 
29 #include <cstdio>
30 #include <cstdlib>
31 #include <csetjmp>
32 #include <jpeg12/jpeglib.h>
33 #include <jpeg12/jerror.h>
34 
35 static ossimTrace traceDebug("ossimJpeg12NitrReader:debug");
36 
38  "ossimJpeg12NitfReader",
40 
41 
42 struct OSSIM_DLL ossimJpegErrorMgr12
43 {
44  struct jpeg12_error_mgr pub; /* "public" fields */
45  jmp_buf setjmp_buffer; /* for return to caller */
46 };
47 typedef struct ossimJpegErrorMgr12* ossimJpegErrorPtr12;
48 
49 void ossimJpegErrorExit12 (jpeg12_common_struct* cinfo)
50 {
51  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
52  ossimJpegErrorPtr12 myerr = (ossimJpegErrorPtr12) cinfo->err;
53 
54  /* Always display the message. */
55  /* We could postpone this until after returning, if we chose. */
56  (*cinfo->err->output_message) (cinfo);
57 
58  /* Return control to the setjmp point */
59  longjmp(myerr->setjmp_buffer, 1);
60 }
61 
63 {
64  bool result = false;
65  if (hdr)
66  {
67  // C3 = jpeg. We only do 12 bit jpeg.
68  if ( (hdr->getCompressionCode() == "C3") && (hdr->getBitsPerPixelPerBand() == 12) )
69  {
70  result = true;
71  }
72  else
73  {
75  }
76  }
77  return result;
78 }
79 
81 {
82  ossim_uint32 blockNumber = getBlockNumber( ossimIpt(x,y) );
83 
84  if (traceDebug())
85  {
87  << "ossimJpeg12NitrReader::uncompressJpegBlock DEBUG:"
88  << "\nblockNumber: " << blockNumber
89  << std::endl;
90  }
91 
92  //---
93  // Logic to hold off on scanning for offsets until a block is actually needed
94  // to speed up loads for things like ossim-info that don't actually read
95  // pixel data.
96  //---
97  if ( m_jpegOffsetsDirty )
98  {
100  {
101  m_jpegOffsetsDirty = false;
102  }
103  else
104  {
106  << "ossimJpeg12NitrReader::uncompressJpegBlock scan for offsets error!"
107  << "\nReturning error..." << endl;
109  return false;
110  }
111  }
112 
113  if (traceDebug())
114  {
116  << "\noffset to block: " << theNitfBlockOffset[blockNumber]
117  << "\nblock size: " << theNitfBlockSize[blockNumber]
118  << std::endl;
119  }
120 
121  // Seek to the block.
122  theFileStr->seekg(theNitfBlockOffset[blockNumber], ios::beg);
123 
124  // Read the block into memory.
125  std::vector<ossim_uint8> compressedBuf(theNitfBlockSize[blockNumber]);
126  if (!theFileStr->read((char*)&(compressedBuf.front()),
127  theNitfBlockSize[blockNumber]))
128  {
129  theFileStr->clear();
131  << "ossimJpeg12NitrReader::uncompressJpegBlock Read Error!"
132  << "\nReturning error..." << endl;
133  return false;
134  }
135 
136  jpeg12_decompress_struct cinfo;
137 
138  ossimJpegErrorMgr12 jerr;
139 
140  cinfo.err = jpeg12_std_error(&jerr.pub);
141 
142  jerr.pub.error_exit = ossimJpegErrorExit12;
143 
144  /* Establish the setjmp return context for my_error_exit to use. */
145  if (setjmp(jerr.setjmp_buffer))
146  {
147  jpeg12_destroy_decompress(&cinfo);
148 
149  return false;
150  }
151 
152  jpeg12_CreateDecompress(&cinfo, JPEG12_LIB_VERSION, sizeof(cinfo));
153 
154  //---
155  // Step 2: specify data source. In this case we will uncompress from
156  // memory so we will use "ossimJpegMemorySrc" in place of " jpeg_stdio_src".
157  //---
158  ossimJpegMemorySrc12( &cinfo,
159  &(compressedBuf.front()),
160  static_cast<size_t>(theReadBlockSizeInBytes) );
161 
162  /* Step 3: read file parameters with jpeg_read_header() */
163 
164  jpeg12_read_header(&cinfo, TRUE);
165 
166 
167  // Check for Quantization tables.
168  if (cinfo.quant_tbl_ptrs[0] == NULL)
169  {
170  // This will load table specified in COMRAT field.
171  if (loadJpeg12QuantizationTables(cinfo) == false)
172  {
173  jpeg12_destroy_decompress(&cinfo);
174  return false;
175  }
176  }
177 
178  // Check for huffman tables.
179  if (cinfo.ac_huff_tbl_ptrs[0] == NULL)
180  {
181  // This will load default huffman tables into .
182  if (loadJpeg12HuffmanTables(cinfo) == false)
183  {
184  jpeg12_destroy_decompress(&cinfo);
185  return false;
186  }
187  }
188 
189  /* Step 4: set parameters for decompression */
190 
191  /* In this example, we don't need to change any of the defaults set by
192  * jpeg_read_header(), so we do nothing here.
193  */
194 
195  /* Step 5: Start decompressor */
196 
197  jpeg12_start_decompress(&cinfo);
198 
199  const ossim_uint32 SAMPLES = cinfo.output_width;
200 
201  //---
202  // Note: Some nitf will be tagged with a given number of lines but the last
203  // jpeg block may go beyond that to a complete block. So it you clamp to
204  // last line of the nitf you will get a libjpeg error:
205  //
206  // "Application transferred too few scanlines"
207  //
208  // So here we will always read the full jpeg block even if it is beyond the
209  // last line of the nitf.
210  //---
211  const ossim_uint32 LINES_TO_READ =
212  min(static_cast<ossim_uint32>(theCacheSize.y), cinfo.output_height);
213 
214  /* JSAMPLEs per row in output buffer */
215  const ossim_uint32 ROW_STRIDE = SAMPLES * cinfo.output_components;
216 
217 #if 0 /* Please leave for debug. drb */
218  static bool TRACED = false;
219  if ( !TRACED )
220  {
221  cout << "theCacheTile:\n" << *(theCacheTile.get())
222  << "\nSAMPLES: " << SAMPLES
223  << "\nLINES_TO_READ: " << LINES_TO_READ
224  << "\nROW_STRIDE: " << ROW_STRIDE
225  << "\nJPEG12_BITS_IN_JSAMPLE: " << JPEG12_BITS_IN_JSAMPLE
226  << "\nsizeof jsamp: " << sizeof(JSAMPLE)
227  << "\ndata_precision: " << cinfo.data_precision
228  << "\ntotal_iMCU_rows: " << cinfo.total_iMCU_rows
229  << endl;
230 
231  TRACED = true;
232  }
233 #endif
234 
235  if ( (SAMPLES < theCacheTile->getWidth() ) ||
236  (LINES_TO_READ < theCacheTile->getHeight()) )
237  {
239  }
240 
241  if ( (SAMPLES > theCacheTile->getWidth()) ||
242  (LINES_TO_READ > theCacheTile->getHeight()) )
243  {
244  jpeg12_finish_decompress(&cinfo);
245  jpeg12_destroy_decompress(&cinfo);
246 
247  return false;
248  }
249 
250  // Get pointers to the cache tile buffers.
251  std::vector<ossim_uint16*> destinationBuffer(theNumberOfInputBands);
252  ossim_uint32 band = 0;
253  for (band = 0; band < theNumberOfInputBands; ++band)
254  {
255  destinationBuffer[band] = theCacheTile->getUshortBuf(band);
256  }
257 
258  std::vector<ossim_uint16> lineBuffer(ROW_STRIDE);
259  // std::vector<ossim_uint8> lineBuffer(ROW_STRIDE);
260  JSAMPROW jbuf[1];
261  jbuf[0] = (JSAMPROW) &(lineBuffer.front());
262 
263  while (cinfo.output_scanline < LINES_TO_READ)
264  {
265  // Read a line from the jpeg file.
266  jpeg12_read_scanlines(&cinfo, jbuf, 1);
267 
268  ossim_uint32 index = 0;
269  for (ossim_uint32 sample = 0; sample < SAMPLES; ++sample)
270  {
271  for (band = 0; band < theNumberOfInputBands; ++band)
272  {
273  destinationBuffer[band][sample] = lineBuffer[index];
274  ++index;
275  }
276  }
277 
278  for (band = 0; band < theNumberOfInputBands; ++band)
279  {
280  destinationBuffer[band] += theCacheSize.x;
281  }
282  }
283 
284  jpeg12_finish_decompress(&cinfo);
285  jpeg12_destroy_decompress(&cinfo);
286 
287  return true;
288 }
289 
291  jpeg12_decompress_struct& cinfo) const
292 {
294  if (!hdr)
295  {
296  return false;
297  }
298 
299  ossimString comrat = hdr->getCompressionRateCode();
300  ossim_uint32 tableIndex = 0;
301  if (comrat.size() >= 4)
302  {
303  // COMRAT string like: "00.2" = use table 2. (between 1 and 5).
304  ossimString s;
305  s.push_back(comrat[static_cast<std::string::size_type>(3)]);
306  ossim_int32 comTbl = s.toInt32();
307  if ( (comTbl > 0) && (comTbl < 6) )
308  {
309  tableIndex = comTbl-1;
310  }
311  else
312  {
314  << "ossimJpeg12NitrReader::loadJpegQuantizationTables WARNING\n"
315  << "\nNo quantization tables specified!"
316  << endl;
317  return false;
318  }
319  }
320 
321  cinfo.quant_tbl_ptrs[0] = jpeg12_alloc_quant_table((j12_common_ptr) &cinfo);
322 
323  JQUANT_TBL* quant_ptr = cinfo.quant_tbl_ptrs[0]; // quant_ptr is JQUANT_TBL*
324 
325  for (ossim_int32 i = 0; i < 64; ++i)
326  {
327  /* Qtable[] is desired quantization table, in natural array order */
328  quant_ptr->quantval[i] = QTABLE_ARRAY[tableIndex][i];
329  }
330  return true;
331 }
332 
333 bool ossimJpeg12NitfReader::loadJpeg12HuffmanTables(jpeg12_decompress_struct& cinfo) const
334 {
335  if ( (cinfo.ac_huff_tbl_ptrs[0] != NULL) &&
336  (cinfo.dc_huff_tbl_ptrs[0] != NULL) )
337  {
338  return false;
339  }
340 
341  cinfo.ac_huff_tbl_ptrs[0] = jpeg12_alloc_huff_table((j12_common_ptr)&cinfo);
342  cinfo.dc_huff_tbl_ptrs[0] = jpeg12_alloc_huff_table((j12_common_ptr)&cinfo);
343 
344  ossim_int32 i;
345  JHUFF_TBL* huff_ptr;
346 
347  // Copy the ac tables.
348  huff_ptr = cinfo.ac_huff_tbl_ptrs[0]; /* huff_ptr is JHUFF_TBL* */
349  for (i = 0; i < 16; ++i)
350  {
351  // huff_ptr->bits is array of 17 bits[0] is unused; hence, the i+1
352  huff_ptr->bits[i+1] = AC_BITS[i];
353  }
354 
355  for (i = 0; i < 256; ++i)
356  {
357  huff_ptr->huffval[i] = AC_HUFFVAL[i];
358  }
359 
360  // Copy the dc tables.
361  huff_ptr = cinfo.dc_huff_tbl_ptrs[0]; /* huff_ptr is JHUFF_TBL* */
362  for (i = 0; i < 16; ++i)
363  {
364  // huff_ptr->bits is array of 17 bits[0] is unused; hence, the i+1
365  huff_ptr->bits[i+1] = DC_BITS[i];
366  }
367 
368  for (i = 0; i < 256; i++)
369  {
370  /* symbols[] is the list of Huffman symbols, in code-length order */
371  huff_ptr->huffval[i] = DC_HUFFVAL[i];
372  }
373  return true;
374 }
virtual ossim_uint32 getWidth() const
ossim_uint32 x
struct ossimJpegErrorMgr12 * ossimJpegErrorPtr12
virtual const ossim_uint16 * getUshortBuf() const
ossim_uint32 theNumberOfInputBands
virtual bool uncompressJpegBlock(ossim_uint32 x, ossim_uint32 y)
Uncompresses a jpeg block using the jpeg-6b library.
ossim_uint32 y
std::shared_ptr< ossim::istream > theFileStr
virtual ossimString getCompressionRateCode() const =0
void ossimJpegErrorExit12(jpeg12_common_struct *cinfo)
std::vector< ossim_uint32 > theNitfBlockSize
ossim_uint32 theReadBlockSizeInBytes
virtual ossim_uint32 getHeight() const
bool loadJpeg12HuffmanTables(jpeg12_decompress_struct &cinfo) const
Loads default huffman tables.
static const ossimErrorCode OSSIM_ERROR
void ossimJpegMemorySrc12(j12_decompress_ptr cinfo, const ossim_uint8 *buffer, std::size_t bufsize)
ossimJpeg12NitfReader class for reading NITF images with 12 bit jpeg compressed blocks using libjpeg-...
virtual bool canUncompress(const ossimNitfImageHeader *hdr) const
ossim_int32 toInt32() const
void push_back(char c)
Equivalent to insert(end(), c).
Definition: ossimString.h:905
std::string::size_type size() const
Definition: ossimString.h:405
unsigned int ossim_uint32
ossimRefPtr< ossimImageData > theCacheTile
RTTI_DEF1_INST(ossimJpeg12NitfReader, "ossimJpeg12NitfReader", ossimNitfTileSource) struct OSSIM_DLL ossimJpegErrorMgr12
Extended error handler struct for jpeg code.
const ossimNitfImageHeader * getCurrentImageHeader() const
virtual void makeBlank()
Initializes data to null pixel values.
virtual ossimString getCompressionCode() const =0
ossim_int32 y
Definition: ossimIpt.h:142
virtual bool scanForJpegBlockOffsets()
scans the file storing in offsets in "theNitfBlockOffset" and block sizes in "theNitfBlockSize".
#define OSSIM_DLL
ossim_uint32 getBlockNumber(const ossimIpt &block_origin) const
virtual ossim_int32 getBitsPerPixelPerBand() const =0
ossim_int32 x
Definition: ossimIpt.h:141
virtual bool canUncompress(const ossimNitfImageHeader *hdr) const
bool loadJpeg12QuantizationTables(jpeg12_decompress_struct &cinfo) const
Loads one of the default tables based on COMRAT value.
std::vector< std::streamoff > theNitfBlockOffset
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
#define min(a, b)
Definition: auxiliary.h:75
int ossim_int32