core/vil/file_formats/vil_nitf2_image.cxx
Go to the documentation of this file.
00001 // vil_nitf2: Written by Rob Radtke (rob@) and Harry Voorhees (hlv@) of
00002 // Stellar Science Ltd. Co. (stellarscience.com) for
00003 // Air Force Research Laboratory, 2005.
00004 
00005 #include "vil_nitf2_image.h"
00006 
00007 //:
00008 // \file
00009 
00010 #include <vcl_cassert.h>
00011 #include <vcl_cstring.h> // for std::memcpy()
00012 #include <vcl_algorithm.h>
00013 #include <vcl_cstdlib.h>
00014 #include <vil/vil_stream_fstream.h>
00015 #include <vil/vil_image_view.h>
00016 #include <vil/vil_property.h>
00017 #include <vil/vil_config.h>
00018 #include "vil_nitf2_data_mask_table.h"
00019 #include "vil_nitf2_des.h"
00020 
00021 #if HAS_J2K
00022 #include "vil_j2k_image.h"
00023 #endif //HAS_J2K
00024 
00025 int debug_level = 0;
00026 
00027 //--------------------------------------------------------------------------------
00028 // class vil_nitf2_file_format
00029 
00030 static char const nitf2_string[] = "nitf";
00031 
00032 char const* vil_nitf2_file_format::tag() const
00033 {
00034   return nitf2_string;
00035 }
00036 
00037 vil_image_resource_sptr  vil_nitf2_file_format::make_input_image(vil_stream *vs)
00038 {
00039   vil_nitf2_image* im = new vil_nitf2_image( vs );
00040   if ( !im->parse_headers() ) {
00041     delete im;
00042     im = 0;
00043   }
00044   return im;
00045 }
00046 
00047 vil_image_resource_sptr
00048   vil_nitf2_file_format::make_output_image(vil_stream* /*vs*/,
00049                                            unsigned /*nx*/,
00050                                            unsigned /*ny*/,
00051                                            unsigned /*nplanes*/,
00052                                            enum vil_pixel_format /*format*/)
00053 {
00054   //write not supported
00055   return 0;
00056 }
00057 
00058 //--------------------------------------------------------------------------------
00059 // class vil_nitf2_image
00060 
00061 vil_streampos vil_nitf2_image::get_offset_to( vil_nitf2_header::section_type sec,
00062                                               vil_nitf2_header::portion_type por,
00063                                               unsigned int index ) const
00064 {
00065   vil_streampos p;
00066   if ( sec == vil_nitf2_header::enum_file_header ) {
00067     //there is no data section in the file header and
00068     //there is only one
00069     assert( por == vil_nitf2_header::enum_subheader );
00070     assert( index == 0 );
00071     //file header is the first thing
00072     p = 0;
00073   }
00074   else {
00075     vil_nitf2_header::section_type preceding_section = (vil_nitf2_header::section_type)(sec-1);
00076     p = get_offset_to( preceding_section, vil_nitf2_header::enum_subheader, 0 ) +
00077         size_to( preceding_section, vil_nitf2_header::enum_subheader, -1 ) +
00078         size_to( sec, por, index );
00079   }
00080   return p;
00081 }
00082 
00083 vil_streampos vil_nitf2_image::size_to(vil_nitf2_header::section_type sec,
00084                                        vil_nitf2_header::portion_type por,
00085                                        int index ) const
00086 {
00087   if ( sec == vil_nitf2_header::enum_file_header ) {
00088     if ( index == -1 ) {
00089       int file_header_size;
00090       m_file_header.get_property("HL", file_header_size);
00091       return (vil_streampos)file_header_size;
00092     }
00093     else {
00094       return 0;
00095     }
00096   }
00097 
00098   vil_streampos offset = 0;
00099   //if -1 specified, then we want to go past all of them... that is onto the next
00100   //section
00101   bool going_past_end = false;
00102   if ( index == -1 ) {
00103     int num_segments;
00104     m_file_header.get_property(vil_nitf2_header::section_num_tag(sec), num_segments);
00105     index = num_segments;
00106     going_past_end = true;
00107   }
00108   vcl_string sh = vil_nitf2_header::section_len_header_tag( sec );
00109   vcl_string s  = vil_nitf2_header::section_len_data_tag( sec );
00110   int i;
00111   for (i = 0 ; i < index ; i++) {
00112     int current_header_size;
00113     m_file_header.get_property(sh, i, current_header_size);
00114     offset += current_header_size;
00115     if ( sec == vil_nitf2_header::enum_image_segments ){
00116       vil_nitf2_long current_data_size;
00117       m_file_header.get_property(s, i, current_data_size);
00118       offset += current_data_size;
00119     }
00120     else {
00121       int current_data_size;
00122       m_file_header.get_property(s, i, current_data_size);
00123       offset += current_data_size;
00124     }
00125   }
00126   //we are now at the proper index's subheader... if we need to get to the data
00127   //we've got one more jump to do
00128   if ( por == vil_nitf2_header::enum_data ) {
00129     // if we've skipped past all the segments, then it doesn't make any sense
00130     // to skip to the "data" section
00131     if ( going_past_end ) { assert(!"skipped past all segments"); return 0L; }
00132     int current_header_size;
00133     m_file_header.get_property(sh, i, current_header_size);
00134     offset += current_header_size;
00135   }
00136   return offset;
00137 }
00138 
00139 vil_image_view_base_sptr ( *vil_nitf2_image::s_decode_jpeg_2000 )
00140 ( vil_stream* vs, unsigned i0, unsigned ni, unsigned j0, unsigned nj, double i_factor, double j_factor ) = 0;
00141 
00142 vil_nitf2_image::vil_nitf2_image(vil_stream* is)
00143   : m_stream(is),
00144     m_current_image_index(0)
00145 {
00146   m_stream->ref();
00147 }
00148 
00149 vil_nitf2_image::vil_nitf2_image(const vcl_string& filePath, const char* mode)
00150   : m_current_image_index(0)
00151 {
00152 #ifdef VIL_USE_FSTREAM64
00153   m_stream = new vil_stream_fstream64(filePath.c_str(), mode);
00154 #else //VIL_USE_FSTREAM64
00155   m_stream = new vil_stream_fstream(filePath.c_str(), mode);
00156 #endif //VIL_USE_FSTREAM64
00157   m_stream->ref();
00158 }
00159 
00160 void vil_nitf2_image::clear_image_headers()
00161 {
00162   for (unsigned int i = 0 ; i < m_image_headers.size() ; i++) {
00163     delete m_image_headers[i];
00164   }
00165   m_image_headers.clear();
00166 }
00167 
00168 void vil_nitf2_image::clear_des()
00169 {
00170   for (unsigned int i = 0 ; i < m_des.size() ; i++) {
00171     delete m_des[i];
00172   }
00173   m_des.clear();
00174 }
00175 
00176 
00177 vil_nitf2_image::~vil_nitf2_image()
00178 {
00179   m_stream->unref();
00180   clear_image_headers();
00181   clear_des();
00182 }
00183 
00184 unsigned int vil_nitf2_image::current_image() const
00185 {
00186   return m_current_image_index;
00187 }
00188 
00189 void vil_nitf2_image::set_current_image(unsigned int index)
00190 {
00191   assert(index < m_image_headers.size());
00192   m_current_image_index = index;
00193 }
00194 
00195 unsigned int vil_nitf2_image::nimages() const
00196 {
00197   int num_images;
00198   if (m_file_header.get_property("NUMI", num_images)) return num_images;
00199   else return 0;
00200 }
00201 
00202 vil_streampos vil_nitf2_image::get_offset_to_image_data_block_band(
00203   unsigned int image_index, unsigned int block_index_x,unsigned int block_index_y, int bandIndex) const
00204 {
00205   //band index is ignored when i_mode != "S"
00206   vcl_string i_mode;
00207   current_image_header()->get_property("IMODE", i_mode);
00208 
00209   //my image header precedes me.  Find out the offset to that, then add on the size of
00210   //that header... then you have the offset to me (the data)
00211   vil_streampos offset =
00212     get_offset_to( vil_nitf2_header::enum_image_segments, vil_nitf2_header::enum_data, image_index );
00213 
00214   //////////////////////////////////////////////////
00215   // now get the position to the desired block/band
00216   //////////////////////////////////////////////////
00217   int bits_per_pixel_per_band;
00218   current_image_header()->get_property("NBPP", bits_per_pixel_per_band);
00219 
00220 #if 0  //Not valid if blocks are partially filled (JLM 11/03/07)
00221   unsigned int bytes_per_band = ni() * nj() * bits_per_pixel_per_band / 8;
00222 #endif
00223   // New version
00224   unsigned int nbi = n_block_i(), nbj = n_block_j();
00225   unsigned int sbi = size_block_i(), sbj = size_block_j();
00226   unsigned int bytes_per_band = nbi*nbj*sbi*sbj*bits_per_pixel_per_band/8;
00227 
00228   // What we do here depends on whether we have a data_mask_table or not and
00229   // whether i_mode == "S".  The most complex case is i_mode != "S" and we have
00230   // a dataMask table.  In that case, we get some information from the table and
00231   // compute some ourselves.  Here are all the possible scenarios handled here:
00232   //   i_mode == "S" and have data_mask_table: just ask data_mask_table for the offset
00233   //   i_mode == "S" and don't have data_mask_table: compute it ourselves right here
00234   //   i_mode != "S" and have data_mask_table: ask the data_mask_table for offset to the
00235   //      block we want; then compute the offset to the band ourselves here
00236   //   i_mode != "S" and don't have data_mask_table: compute both band and block offset
00237   //      ourselves here
00238   // If it sounds complex, blame the NITF 2.1 spec for that
00239   const vil_nitf2_data_mask_table* data_mask_table = current_image_header()->data_mask_table();
00240   if (data_mask_table) {
00241     offset += data_mask_table->blocked_image_data_offset();
00242   }
00243   if (data_mask_table && data_mask_table->has_offset_table()) {
00244     //have data mask table
00245     int bI = i_mode == "S" ? bandIndex : -1;
00246     if (data_mask_table->block_band_present(block_index_x, block_index_y, bI))
00247     {
00248       return 0;
00249     }
00250     offset += data_mask_table->block_band_offset(block_index_x, block_index_y, bI);
00251   }
00252   else {
00253     unsigned int pixels_per_block = size_block_i() * size_block_j();
00254     unsigned int bits_per_band = pixels_per_block * bits_per_pixel_per_band;
00255     unsigned int bytes_per_block_per_band = bits_per_band / 8;
00256     //round up if remainder left over (this assumes that band/block boundaries
00257     //always lie on byte boundaries.
00258     if (bits_per_band % 8 != 0) bytes_per_block_per_band++;
00259     if (i_mode == "S") {
00260       //i_mode == "S" and not have data_mask_table
00261       unsigned int offset_to_desired_band = bandIndex * bytes_per_band;
00262       unsigned int offset_to_desired_block = bytes_per_block_per_band * (block_index_y * n_block_i() + block_index_x);
00263       offset += offset_to_desired_band + offset_to_desired_block;
00264     }
00265     else {
00266       //i_mode != "S" and not have data_mask_table
00267       unsigned int block_size_bytes = bytes_per_block_per_band * nplanes();
00268       unsigned int offset_to_desired_block = block_size_bytes * (block_index_y * n_block_i() + block_index_x);
00269       offset += offset_to_desired_block;
00270     }
00271   }
00272   if (i_mode != "S") {
00273     //regardless of whether we had a data_mask_table or not, we've only computed
00274     //the offset to the desired block so far.  Now, we add on the offset to
00275     //the desired band.
00276     unsigned int offset_to_desired_band = bandIndex * bytes_per_band;
00277     offset += offset_to_desired_band;
00278   }
00279   return offset;
00280 }
00281 
00282 bool vil_nitf2_image::parse_headers()
00283 {
00284   if (!m_stream->ok()) return false;
00285   //parse file header
00286   m_stream->seek(0);
00287   if (!m_file_header.read(m_stream)) {
00288     return false;
00289   }
00290   //now parse each image header
00291   clear_image_headers();
00292   m_image_headers.resize(nimages());
00293   for (unsigned int i = 0 ; i < nimages() ; i++) {
00294     vil_streampos offset = get_offset_to( vil_nitf2_header::enum_image_segments, vil_nitf2_header::enum_subheader, i);
00295     m_stream->seek(offset);
00296     m_image_headers[i] = new vil_nitf2_image_subheader(file_version());
00297     if (!m_image_headers[i]->read(m_stream)) return false;
00298   }
00299 
00300   //now parse all the DESs (if any)
00301   clear_des();
00302   int num_des;
00303   m_file_header.get_property( "NUMDES", num_des );
00304   m_des.resize( num_des );
00305   for ( int j = 0 ; j < num_des ; j++ ){
00306     vil_streampos offset = get_offset_to( vil_nitf2_header::enum_data_extension_segments, vil_nitf2_header::enum_subheader, j);
00307     m_stream->seek(offset);
00308     int data_width;
00309     m_file_header.get_property( "LD", j, data_width );
00310     m_des[j] = new vil_nitf2_des(file_version(), data_width);
00311     if (!m_des[j]->read(m_stream)) return false;
00312   }
00313   return true;
00314 }
00315 
00316 vil_nitf2_classification::file_version vil_nitf2_image::file_version() const
00317 {
00318   return m_file_header.file_version();
00319 }
00320 
00321 char const * vil_nitf2_image::file_format() const
00322 {
00323 vil_nitf2_classification::file_version v = file_version();
00324   switch (v)
00325   {
00326     case vil_nitf2_classification::V_UNKNOWN :
00327       return "unknown";
00328     case vil_nitf2_classification::V_NITF_10 :
00329       return "nitf10";
00330     case vil_nitf2_classification::V_NITF_20 :
00331       return "nitf20";
00332     case vil_nitf2_classification::V_NITF_21 :
00333       return "nitf21";
00334     default:
00335       return "unknown";
00336   }
00337 }
00338 
00339 const vil_nitf2_image_subheader* vil_nitf2_image::current_image_header() const
00340 {
00341   assert(m_current_image_index < m_image_headers.size());
00342   return m_image_headers[ m_current_image_index ];
00343 }
00344 
00345 unsigned vil_nitf2_image::nplanes() const
00346 {
00347   return current_image_header()->nplanes();
00348 }
00349 
00350 unsigned vil_nitf2_image::ni() const
00351 {
00352   //Note that we are choosing to return the number of significant columns
00353   //rather than NPPBH*NBPR which would be the total number of pixels in the
00354   //image.  if NPPBH*NBPR > NCOLS, then all the extra columns are blanked
00355   //out pad pixels.  Why would anyone want those?
00356   int num_significant_cols;
00357   if (current_image_header()->get_property("NCOLS", num_significant_cols))
00358   {
00359     return num_significant_cols;
00360   }
00361   return 0;
00362 }
00363 
00364 unsigned vil_nitf2_image::nj() const
00365 {
00366   //Note that we are choosing to return the number of significant rows
00367   //rather than NPPBV*NBPC which would be the total number of pixels in the
00368   //image.  if NPPBV*NBPC > NROWS, then all the extra columns are blanked
00369   //out pad pixels.  Why would anyone want those?
00370   int num_significant_rows;
00371   if (current_image_header()->get_property("NROWS", num_significant_rows))
00372   {
00373     return num_significant_rows;
00374   }
00375   return 0;
00376 }
00377 
00378 enum vil_pixel_format vil_nitf2_image::pixel_format () const
00379 {
00380   vcl_string pixel_type;
00381   int bits_per_pixel;
00382   if (current_image_header()->get_property("PVTYPE", pixel_type) &&
00383       current_image_header()->get_property("NBPP", bits_per_pixel))
00384   {
00385     //if bits_per_pixel isn't divisible by 8, round up to nearest byte
00386     int bytesPerPixel = bits_per_pixel / 8;
00387     if (bits_per_pixel % 8 != 0) bytesPerPixel++;
00388     bits_per_pixel = bytesPerPixel * 8;
00389     if (pixel_type == "INT") {
00390       if (bits_per_pixel == 8) {
00391         return VIL_PIXEL_FORMAT_BYTE;
00392       }
00393       else if (bits_per_pixel == 16) {
00394         return VIL_PIXEL_FORMAT_UINT_16;
00395       }
00396       else if (bits_per_pixel == 32) {
00397         return VIL_PIXEL_FORMAT_UINT_32;
00398       }
00399 #if VXL_HAS_INT_64
00400       else if (bits_per_pixel == 64) {
00401         return VIL_PIXEL_FORMAT_UINT_64;
00402       }
00403 #endif //VXL_HAS_INT_64
00404     }
00405     else if (pixel_type == "B") {
00406       return VIL_PIXEL_FORMAT_BOOL;
00407     }
00408     else if (pixel_type == "SI") {
00409       if (bits_per_pixel == 8) {
00410         return VIL_PIXEL_FORMAT_SBYTE;
00411       }
00412       else if (bits_per_pixel == 16) {
00413         return VIL_PIXEL_FORMAT_INT_16;
00414       }
00415       else if (bits_per_pixel == 32) {
00416         return VIL_PIXEL_FORMAT_INT_32;
00417       }
00418 #if VXL_HAS_INT_64
00419       else if (bits_per_pixel == 64) {
00420         return VIL_PIXEL_FORMAT_INT_64;
00421       }
00422 #endif //VXL_HAS_INT_64
00423     }
00424     else if (pixel_type == "R") {
00425       if (bits_per_pixel == 32) {
00426         return VIL_PIXEL_FORMAT_FLOAT;
00427       }
00428       else if (bits_per_pixel == 64) {
00429         return VIL_PIXEL_FORMAT_DOUBLE;
00430       }
00431     }
00432     else if (pixel_type == "C") {
00433       //two 32 bit floats (real followed by complex)
00434       if (bits_per_pixel == 64) {
00435         return VIL_PIXEL_FORMAT_COMPLEX_FLOAT;
00436       }// else if (bits_per_pixel == 64) {
00437        // return VIL_PIXEL_FORMAT_COMPLEX_DOUBLE;
00438       //}
00439     }
00440   }
00441   return VIL_PIXEL_FORMAT_UNKNOWN;
00442 }
00443 
00444 unsigned int vil_nitf2_image::size_block_i() const
00445 {
00446   return current_image_header()->get_pixels_per_block_x();
00447 }
00448 
00449 unsigned int vil_nitf2_image::size_block_j() const
00450 {
00451   return current_image_header()->get_pixels_per_block_y();
00452 }
00453 
00454 unsigned int vil_nitf2_image::n_block_i() const
00455 {
00456   return current_image_header()->get_num_blocks_x();
00457 }
00458 
00459 unsigned int vil_nitf2_image::n_block_j() const
00460 {
00461   return current_image_header()->get_num_blocks_y();
00462 }
00463 
00464 void  compute_block_and_offset(unsigned j0, unsigned long block_size,
00465                                unsigned int& block, unsigned int& offset)
00466 {
00467   block = 0;
00468   offset = 0;
00469 
00470   if (j0 != 0) {
00471     block = (j0 / block_size);
00472     if (j0 > 0 && j0 % block_size != 0) {
00473       offset = j0 - (block * block_size);
00474     }
00475   }
00476 }
00477 
00478 bool vil_nitf2_image::is_jpeg_2000_compressed() const
00479 {
00480   vcl_string compression_type;
00481   //ISO/IEC BIFF profile BPJ2k01.00 says that M8 is actually invalid
00482   //(ie. you can't use a data mask with jpeg 2000 compression)
00483   //not sure why it is an option though
00484   return ( current_image_header()->get_property("IC", compression_type) ) &&
00485          ( compression_type == "C8" || compression_type == "M8" );
00486 }
00487 
00488 vil_image_view_base_sptr vil_nitf2_image::get_copy_view_decimated_j2k(
00489   unsigned start_i, unsigned num_i, unsigned start_j, unsigned num_j, double i_factor, double j_factor ) const
00490 {
00491   // ACCORDING TO DOCUMENTATION, IF PARAMETERS ARE BAD, WE SHOULD RETURN NULL POINTER.
00492   if ((start_i + num_i > ni()) || (start_j + num_j > nj())) {
00493     return 0;
00494   }
00495   assert( is_jpeg_2000_compressed() );
00496   if ( ! s_decode_jpeg_2000 ) {
00497 #if HAS_J2K
00498     s_decode_jpeg_2000 = vil_j2k_image::s_decode_jpeg_2000;
00499 #else //HAS_J2K
00500     return 0;
00501 #endif //HAS_J2K
00502   }
00503 
00504   //it is my understanding from BIFF profile BPJ2k01.00 that JPEG compressed files
00505   //will only have on image block (ie. it will be clocked within the jp2 codestream),
00506   //so we can just pass all the work off to the vil_j2k_image class
00507   m_stream->seek(get_offset_to( vil_nitf2_header::enum_image_segments, vil_nitf2_header::enum_data, m_current_image_index));
00508   return s_decode_jpeg_2000( m_stream, start_i, num_i, start_j, num_j, i_factor, j_factor );
00509 }
00510 
00511 vil_image_view_base_sptr vil_nitf2_image::get_copy_view(unsigned start_i, unsigned num_i,
00512                                                         unsigned start_j, unsigned num_j) const
00513 {
00514   // ACCORDING TO DOCUMENTATION, IF PARAMETERS ARE BAD, WE SHOULD RETURN NULL POINTER.
00515   if ((start_i + num_i > ni()) || (start_j + num_j > nj())) {
00516     return 0;
00517   }
00518 
00519   vcl_string compression_type;
00520   if (!current_image_header()->get_property("IC", compression_type)) return 0;
00521 
00522   //right now we only plan to support uncompressed and JPEG2000
00523   if (compression_type == "NC" || compression_type == "NM") {
00524     return get_copy_view_uncompressed(start_i, num_i, start_j, num_j);
00525   }
00526   else if ( is_jpeg_2000_compressed() ) {
00527     return get_copy_view_decimated_j2k( start_i, num_i, start_j, num_j, 1.0, 1.0 );
00528   }
00529   else {
00530     return 0;
00531   }
00532 }
00533 
00534 vil_image_view_base_sptr vil_nitf2_image::get_copy_view_uncompressed(unsigned start_i, unsigned num_i,
00535                                                                      unsigned start_j, unsigned num_j) const
00536 {
00537   return vil_blocked_image_resource::get_copy_view(start_i, num_i, start_j, num_j);
00538 }
00539 
00540 template< class T >
00541 vil_memory_chunk_sptr maybe_byte_align_data(vil_memory_chunk_sptr in_data, unsigned int num_samples,
00542                                             unsigned int in_bits_per_sample, T /*dummy*/)
00543 {
00544   if (in_bits_per_sample != sizeof(T)*8) {
00545     vil_memory_chunk_sptr new_memory = new vil_memory_chunk(num_samples*sizeof(T), in_data->pixel_format());
00546     byte_align_data((T*)in_data->data(), num_samples, in_bits_per_sample, (T*)new_memory->data());
00547     return new_memory;
00548   }
00549   return in_data;
00550 }
00551 
00552 // don't do anything for float and double (bit shifting isn't allowed)
00553 template<> vil_memory_chunk_sptr maybe_byte_align_data<float> (
00554   vil_memory_chunk_sptr in_data, unsigned int /* num_samples */, unsigned int /* in_bits_per_sample */, float /*dummy*/)
00555 { return in_data; }
00556 
00557 template<> vil_memory_chunk_sptr maybe_byte_align_data<double> (
00558   vil_memory_chunk_sptr in_data, unsigned int /* num_samples */, unsigned int /* in_bits_per_sample */, double /*dummy*/)
00559 { return in_data; }
00560 
00561 template<> vil_memory_chunk_sptr maybe_byte_align_data< vcl_complex< float > > (
00562   vil_memory_chunk_sptr in_data, unsigned int /*num_samples*/, unsigned int /*in_bits_per_sample*/, vcl_complex<float> /*dummy*/)
00563 { return in_data; }
00564 
00565 
00566 //:
00567 //  This function handles the case where the actual bits per pixel per band
00568 //  is less then the actual bpppb AND where the data is vcl_left justified.  This
00569 //  shifts the data so that it is right justified.
00570 //  As of now, this function is untests as I don't have any vcl_left justified data
00571 //  (the NITF spec discourages using it -- probably because it is such a PITA)
00572 template< class T >
00573 void right_justify(T* data, unsigned int num_samples, unsigned int bitsToMove)
00574 {
00575   for (unsigned int i = 0 ; i < num_samples ; i++) {
00576     data[i] = data[i] >> bitsToMove;
00577   }
00578 }
00579 
00580 //don't do anything for bool, float and double (bit shifting isn't allowed)
00581 template<> void right_justify<bool>(bool* /* data */, unsigned int /* num_samples */, unsigned int /* bitsToMove */) {}
00582 template<> void right_justify<float>(float* /* data */, unsigned int /* num_samples */, unsigned int /* bitsToMove */) {}
00583 template<> void right_justify<double>(double* /* data */, unsigned int /* num_samples */, unsigned int /* bitsToMove */) {}
00584 template<> void right_justify< vcl_complex< float > >(vcl_complex< float >* /*data*/, unsigned int /*num_samples*/,
00585                                                       unsigned int /* bitsToMove */) {}
00586 
00587 template< class T >
00588 unsigned int get_index(T in_val)
00589 { return (T)in_val; }
00590 
00591 template<> unsigned int get_index<bool>(bool in_val)
00592 { return in_val ? 1 : 0; }
00593 
00594 template< class T >
00595 vil_image_view_base_sptr get_block_vcl_internal(vil_pixel_format pix_format, vil_memory_chunk_sptr image_memory,
00596                                                 unsigned int pixels_per_block_x, unsigned int pixels_per_block_y,
00597                                                 unsigned int nplanes,
00598                                                 unsigned int i_step, unsigned int j_step, unsigned int plane_step,
00599                                                 bool need_to_right_justify,
00600                                                 unsigned int extra_bits, unsigned int bits_per_pixel_per_band,
00601                                                 bool data_is_all_blank, const vil_nitf2_image_subheader* /*image_header*/, T dummy)
00602 {
00603   //may have to byte align data (only valid for integer type data)
00604   unsigned int num_samples = pixels_per_block_x * pixels_per_block_y * nplanes; //all bands of image
00605 
00606   if (data_is_all_blank) {
00607     //this entire block is blank
00608     T* data_ptr = reinterpret_cast<T*>(image_memory->data());
00609     for (unsigned int i = 0 ;
00610          i < pixels_per_block_x * pixels_per_block_y * nplanes ;
00611          i++)
00612     {
00613       data_ptr[i] = (T)0;
00614     }
00615   }
00616   else {
00617     //in the rare case where the actual number of bits per pixel value (ABPP) is less than the number of bits
00618     //used in the data (NBPP) AND the data is vcl_left justified... then we correct that here
00619     if (need_to_right_justify)
00620       right_justify<T>(static_cast<T*>(image_memory->data()), (unsigned int)(image_memory->size()/sizeof(T)), extra_bits);
00621     //Nitf files store data in big endian... little endian machines need to convert
00622     vil_nitf2_data_mask_table::maybe_endian_swap(static_cast< char* >(image_memory->data()), (unsigned int)(image_memory->size()), pix_format);
00623     //if the data is not byte aligned (ie. the actual bits per pixel per band is not divisible
00624     //by 8),then we need to correct that
00625     image_memory = maybe_byte_align_data(image_memory, num_samples, bits_per_pixel_per_band, dummy);
00626   }
00627 
00628   vil_image_view< T >* result =
00629     new vil_image_view< T > (image_memory, reinterpret_cast<T*>(image_memory->data()),
00630                               pixels_per_block_x, pixels_per_block_y, nplanes, i_step, j_step, plane_step);
00631 
00632   return result;
00633 }
00634 
00635 vil_image_view_base_sptr vil_nitf2_image::get_block_j2k( unsigned int blockIndexX, unsigned int blockIndexY ) const
00636 {
00637   if ( ! is_jpeg_2000_compressed() ) return 0;
00638   if ( blockIndexX >= n_block_i() ) return 0;
00639   if ( blockIndexY >= n_block_j() ) return 0;
00640 
00641   //sometimes blocks don't align nicely with the image edge.  I'm not sure
00642   //if this is a bug in the file or if we need to handle it.  Anyway,
00643   //we handle it by using vcl_min.  test file named p0_11xa,ntf exhibits
00644   //this issue
00645   unsigned int i0 = vcl_min( blockIndexX * size_block_i(), ni() );
00646   unsigned int num_i = vcl_min( size_block_i(), ni() - i0 );
00647   unsigned int j0 = vcl_min( blockIndexY * size_block_j(), nj() );
00648   unsigned int num_j = vcl_min( size_block_j(), nj() - j0 );
00649   return get_copy_view( i0, num_i, j0, num_j );
00650 }
00651 
00652 vil_image_view_base_sptr vil_nitf2_image::get_block(unsigned int block_index_x, unsigned int block_index_y) const
00653 {
00654   if (pixel_format() == VIL_PIXEL_FORMAT_UNKNOWN) return 0;
00655 
00656   if ( is_jpeg_2000_compressed() ) {
00657     return get_block_j2k( block_index_x, block_index_y );
00658   }
00659 
00660   vcl_string image_mode_type;
00661   if (!current_image_header()->get_property("IMODE", image_mode_type)) return 0;
00662 
00663   //calculate the start position of the block that we need
00664   int bits_per_pixel_per_band, actualBitsPerPixelPerBand;
00665   vcl_string bitJustification;
00666   if (!current_image_header()->get_property("NBPP", bits_per_pixel_per_band) ||
00667       !current_image_header()->get_property("ABPP", actualBitsPerPixelPerBand) ||
00668       !current_image_header()->get_property("PJUST", bitJustification)) {
00669     return 0;
00670   }
00671   int extra_bits = bits_per_pixel_per_band - actualBitsPerPixelPerBand;
00672   bool need_to_right_justify = bitJustification == "L" && (extra_bits > 0);
00673 
00674   //bytes per pixel... round up to nearest byte
00675   //unsigned int bytesPerPixelPerBand = bits_per_pixel_per_band / 8;
00676   //if (bits_per_pixel_per_band % 8 != 0) bytesPerPixelPerBand++;
00677 
00678   unsigned int pixels_per_block = size_block_i() * size_block_j();
00679   unsigned int bits_per_band = pixels_per_block * bits_per_pixel_per_band;
00680   unsigned int bytes_per_block_per_band = bits_per_band / 8;
00681   if (bits_per_band % 8 != 0) bytes_per_block_per_band++;     //round up if remainder vcl_left over
00682   unsigned int block_size_bytes = bytes_per_block_per_band * nplanes();
00683   //allocate the memory that we need
00684   vil_memory_chunk_sptr image_memory = new vil_memory_chunk(block_size_bytes, pixel_format());
00685 
00686 
00687   unsigned int i_step(0), j_step(0), plane_step(0);
00688   bool data_is_all_blank = false;
00689   if (image_mode_type == "S") {
00690 #if 0 // NOT USED
00691     unsigned int bytes_per_band = ni() * nj() * bits_per_pixel_per_band / 8;
00692     unsigned int offset_to_desired_block = bytes_per_block_per_band * (block_index_y * n_block_i() + block_index_x);
00693 #endif // 0
00694     //blocks are not contiguous... we'll have to do one read for each band
00695     for (unsigned int i = 0 ; i < nplanes() ; i++) {
00696       vil_streampos current_offset = get_offset_to_image_data_block_band(m_current_image_index, block_index_x, block_index_y, i);
00697       if (current_offset == 0) {
00698         //this block isn't in the stream (ie. it's all blank)... just blank out the memory
00699         data_is_all_blank = true;
00700       }
00701       else {
00702         m_stream->seek(current_offset);
00703         char* position_to_read_to = static_cast<char*>(image_memory->data());
00704         position_to_read_to += i*bytes_per_block_per_band;
00705         if (m_stream->read((void*)position_to_read_to, bytes_per_block_per_band) != static_cast<int>(bytes_per_block_per_band)) {
00706           return 0;
00707         }
00708       }
00709     }
00710     i_step = 1;
00711     j_step = size_block_i();
00712     plane_step = size_block_i() * size_block_j();
00713   }
00714   else {
00715     //calculate the offset we need
00716     vil_streampos current_offset = get_offset_to_image_data_block_band(m_current_image_index, block_index_x, block_index_y, 0);
00717     if (current_offset == 0) {
00718       //this block isn't in the stream (ie. it's all blank)... just blank out the memory
00719       data_is_all_blank = true;
00720     }
00721     else {
00722       //seek to the correct position in the stream
00723       m_stream->seek(current_offset);
00724       //read in the data
00725       if (m_stream->read(image_memory->data(), block_size_bytes) != static_cast<int>(block_size_bytes)) {
00726         return 0;
00727       }
00728     }
00729 
00730     //figure out the layout of the data in the memory chunk we just read in
00731     if (image_mode_type == "B") {
00732       //band interleaved by Block
00733       i_step = 1;
00734       j_step = size_block_i();
00735       plane_step = size_block_i() * size_block_j();
00736     }
00737     else if (image_mode_type == "P") {
00738       //band interleaved by Pixel
00739       i_step = nplanes();
00740       j_step = nplanes() * size_block_i();
00741       plane_step = 1;
00742     }
00743     else if (image_mode_type == "R") {
00744       //band interleaved by Row
00745       i_step = 1;
00746       j_step = nplanes() * size_block_i();
00747       plane_step = size_block_i();
00748     }
00749   }
00750 
00751   //create image view of the data
00752   vil_image_view_base_sptr view = 0;
00753   switch (vil_pixel_format_component_format(image_memory->pixel_format()))
00754   {
00755 #define GET_BLOCK_CASE(FORMAT, T)\
00756    case FORMAT:{ \
00757     T t= (T)0; \
00758     return get_block_vcl_internal(\
00759        FORMAT, image_memory, size_block_i(),size_block_j(), nplanes(),\
00760        i_step, j_step, plane_step, need_to_right_justify, extra_bits, bits_per_pixel_per_band,\
00761        data_is_all_blank, current_image_header(), t);\
00762    } break
00763 
00764     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_BYTE, vxl_byte);
00765     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_SBYTE, vxl_sbyte);
00766 
00767 #if VXL_HAS_INT_64
00768     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_UINT_64, vxl_uint_64);
00769     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_INT_64, vxl_int_64);
00770 #endif
00771     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_UINT_32, vxl_uint_32);
00772     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_INT_32, vxl_int_32);
00773     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_UINT_16, vxl_uint_16);
00774     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_INT_16, vxl_int_16);
00775     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_BOOL, bool);
00776     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_FLOAT, float);
00777     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_DOUBLE, double);
00778     GET_BLOCK_CASE(VIL_PIXEL_FORMAT_COMPLEX_FLOAT, vcl_complex<float>);
00779 #undef GET_BLOCK_CASE
00780 
00781    default:
00782     assert(!"Unknown vil data type.");
00783     break;
00784   }
00785   return view;
00786 }
00787 
00788 template<> bool* byte_align_data<bool>(bool* in_data, unsigned int num_samples, unsigned int in_bits_per_sample, bool* out_data)
00789 {
00790   switch (sizeof(bool))
00791   {
00792    case 1:
00793     byte_align_data((vxl_byte*)in_data, num_samples, in_bits_per_sample, (vxl_byte*)out_data);
00794     break;
00795    case 2:
00796     byte_align_data((vxl_uint_16*)in_data, num_samples, in_bits_per_sample, (vxl_uint_16*)out_data);
00797     break;
00798    case 4:
00799     byte_align_data((vxl_uint_32*)in_data, num_samples, in_bits_per_sample, (vxl_uint_32*)out_data);
00800     break;
00801 #if VXL_HAS_INT_64
00802    case 8:
00803     byte_align_data((vxl_uint_64*)in_data, num_samples, in_bits_per_sample, (vxl_uint_64*)out_data);
00804     break;
00805 #endif //VXL_HAS_INT_64
00806    default:
00807     assert(!"Unsupported size of bool.");
00808   }
00809 
00810 #if 0
00811   // diagnostic info
00812   vcl_cout << "\nBools: ";
00813   for (unsigned int i = 0 ; i < num_samples ; i++) {
00814     vcl_cout << (out_data[i] ?  '1' : '0');
00815   }
00816   vcl_cout << vcl_endl;
00817 #endif //0
00818   return out_data;
00819 }
00820 
00821 bool vil_nitf2_image::get_property (char const *tag, void *property_value) const
00822 {
00823   if (vcl_strcmp(vil_property_size_block_i, tag)==0)
00824   {
00825     if (property_value)
00826       *static_cast<unsigned*>(property_value) = this->size_block_i();
00827     return true;
00828   }
00829 
00830   if (vcl_strcmp(vil_property_size_block_j, tag)==0)
00831   {
00832     if (property_value)
00833       *static_cast<unsigned*>(property_value) = this->size_block_j();
00834     return true;
00835   }
00836   vcl_string result;
00837   if (m_file_header.get_property(tag, result) ||
00838       (current_image_header() && current_image_header()->get_property(tag, result)))
00839   {
00840     property_value = vcl_malloc(result.size());
00841     vcl_memcpy(property_value, result.c_str(), result.size());
00842     return true;
00843   }
00844   return false;
00845  }
00846 
00847 vil_nitf2_field::field_tree* vil_nitf2_image::get_tree( ) const
00848 {
00849   vil_nitf2_field::field_tree* t = new vil_nitf2_field::field_tree;
00850   t->columns.push_back( "NITF File" );
00851   t->children.push_back( get_header().get_tree() );
00852   unsigned int i;
00853   for ( i = 0 ; i < m_image_headers.size() ; i++ ){
00854     t->children.push_back( m_image_headers[i]->get_tree(i+1) );
00855   }
00856   for ( i = 0 ; i < m_des.size() ; i++ ){
00857     t->children.push_back( m_des[i]->get_tree(i+1) );
00858   }
00859   return t;
00860 }
00861