1 #include <algorithm>
2 #include <cmath>
3 #include <limits>
4 #include <cstdlib>
5 #include "vil_j2k_image.h"
6 //:
7 // \file
8 // vil_j2k: Written by Rob Radtke (rob@) and Harry Voorhees (hlv@) of
9 // Stellar Science Ltd. Co. (stellarscience.com) for
10 // Air Force Research Laboratory, 2005.
11 // Write capability added by J. Mundy, April 2009
12 // Do not remove the following notice
13 // Modifications approved for public Release, distribution unlimited
14 // DISTAR Case 14074
15 //
16 
17 #include <NCSFile.h>
18 
19 #include <cassert>
20 #ifdef _MSC_VER
21 #  include "vcl_msvc_warnings.h"
22 #endif
23 #include "vil/vil_memory_chunk.h"
24 #include "vil/vil_image_view.h"
25 #include "vil/vil_load.h"
26 #include "vil/vil_open.h"
27 #include "vil/vil_new.h"
28 #include "NCSJPCVilIOStream.h"
29 #include <NCSTypes.h>
30 
31 // Fix me - need to add UINT64 and INT64 - JLM
32 // Also note, float and double are defined but not handled by SDK 3.3
33 // they are reinterpreted as INT32 and INT64
34 NCSFileBandInfo
bandInfo(const vil_pixel_format & vilType)35 bandInfo(const vil_pixel_format & vilType)
36 {
37   NCSFileBandInfo info;
38   switch (vil_pixel_format_component_format(vilType))
39   {
40     case VIL_PIXEL_FORMAT_UINT_32: {
41       info.nBits = sizeof(vxl_uint_32) * 8;
42       info.bSigned = std::numeric_limits<vxl_uint_32>::is_signed;
43       info.szDesc = 0;
44       return info;
45     }
46     case VIL_PIXEL_FORMAT_INT_32: {
47       info.nBits = sizeof(vxl_int_32) * 8;
48       info.bSigned = std::numeric_limits<vxl_int_32>::is_signed;
49       info.szDesc = 0;
50       return info;
51     }
52     case VIL_PIXEL_FORMAT_UINT_16: {
53       info.nBits = sizeof(vxl_uint_16) * 8;
54       info.bSigned = std::numeric_limits<vxl_uint_16>::is_signed;
55       info.szDesc = 0;
56       return info;
57     }
58     case VIL_PIXEL_FORMAT_INT_16: {
59       info.nBits = sizeof(vxl_int_16) * 8;
60       info.bSigned = std::numeric_limits<vxl_int_16>::is_signed;
61       info.szDesc = 0;
62       return info;
63     }
64     case VIL_PIXEL_FORMAT_BYTE: {
65       info.nBits = sizeof(vxl_byte) * 8;
66       info.bSigned = std::numeric_limits<vxl_byte>::is_signed;
67       info.szDesc = 0;
68       return info;
69     }
70     case VIL_PIXEL_FORMAT_SBYTE: {
71       info.nBits = sizeof(vxl_sbyte) * 8;
72       info.bSigned = std::numeric_limits<vxl_sbyte>::is_signed;
73       info.szDesc = 0;
74       return info;
75     }
76     case VIL_PIXEL_FORMAT_FLOAT: {
77       info.nBits = sizeof(float) * 8;
78       info.bSigned = std::numeric_limits<float>::is_signed;
79       info.szDesc = 0;
80       return info;
81     }
82     case VIL_PIXEL_FORMAT_DOUBLE: {
83       info.nBits = sizeof(double) * 8;
84       info.bSigned = std::numeric_limits<double>::is_signed;
85       info.szDesc = 0;
86       return info;
87     }
88     case VIL_PIXEL_FORMAT_BOOL:
89     case VIL_PIXEL_FORMAT_COMPLEX_FLOAT:
90     case VIL_PIXEL_FORMAT_COMPLEX_DOUBLE:
91     case VIL_PIXEL_FORMAT_UINT_64:
92     case VIL_PIXEL_FORMAT_INT_64:
93     case VIL_PIXEL_FORMAT_UNKNOWN:
94     default: {
95       assert(0);
96       info.nBits = 0;
97       info.bSigned = false;
98       info.szDesc = 0;
99       return info;
100     }
101   }
102 }
103 
104 //--------------------------------------------------------------------------------
105 // class vil_j2k_file_format
106 
107 static char const j2k_string[] = "j2k";
108 
109 char const *
tag() const110 vil_j2k_file_format::tag() const
111 {
112   return j2k_string;
113 }
114 
115 vil_image_resource_sptr
make_input_image(vil_stream * vs)116 vil_j2k_file_format::make_input_image(vil_stream * vs)
117 {
118   vil_j2k_image * im = new vil_j2k_image(vs);
119   if (!im->is_valid())
120   {
121     delete im;
122     im = 0;
123   }
124   return im;
125 }
126 
127 
128 vil_image_resource_sptr
make_output_image(vil_stream * vs,unsigned ni,unsigned nj,unsigned nplanes,enum vil_pixel_format format)129 vil_j2k_file_format::make_output_image(vil_stream * vs,
130                                        unsigned ni,
131                                        unsigned nj,
132                                        unsigned nplanes,
133                                        enum vil_pixel_format format)
134 {
135   vil_j2k_image * j2k_img = new vil_j2k_image(vs, ni, nj, nplanes, format, compression_ratio_);
136   if (j2k_img->is_valid())
137     return static_cast<vil_image_resource *>(j2k_img);
138   return 0;
139 }
140 
141 NCSEcwCellType
convertType(const vil_pixel_format & vilType)142 convertType(const vil_pixel_format & vilType)
143 {
144   switch (vil_pixel_format_component_format(vilType))
145   {
146     case VIL_PIXEL_FORMAT_UINT_64:
147       return NCSCT_UINT64;
148     case VIL_PIXEL_FORMAT_INT_64:
149       return NCSCT_INT64;
150     case VIL_PIXEL_FORMAT_UINT_32:
151       return NCSCT_UINT32;
152     case VIL_PIXEL_FORMAT_INT_32:
153       return NCSCT_INT32;
154     case VIL_PIXEL_FORMAT_UINT_16:
155       return NCSCT_UINT16;
156     case VIL_PIXEL_FORMAT_INT_16:
157       return NCSCT_INT16;
158     case VIL_PIXEL_FORMAT_BYTE:
159       return NCSCT_UINT8;
160     case VIL_PIXEL_FORMAT_SBYTE:
161       return NCSCT_INT8;
162     case VIL_PIXEL_FORMAT_FLOAT:
163       return NCSCT_IEEE4;
164     case VIL_PIXEL_FORMAT_DOUBLE:
165       return NCSCT_IEEE8;
166     case VIL_PIXEL_FORMAT_BOOL:
167     case VIL_PIXEL_FORMAT_COMPLEX_FLOAT:
168     case VIL_PIXEL_FORMAT_COMPLEX_DOUBLE:
169     case VIL_PIXEL_FORMAT_UNKNOWN:
170     default:
171       assert(0);
172       return NCSCT_UINT8;
173   }
174 }
175 
176 // Note the J2K SDK defines IEEE4 and IEEE8 but they are not
177 // handled as float or double, they are reinterpreted as INT32 and INT64
178 vil_pixel_format
convertType(const NCSEcwCellType & ecwType)179 convertType(const NCSEcwCellType & ecwType)
180 {
181   switch (ecwType)
182   {
183     case NCSCT_UINT64:
184       return VIL_PIXEL_FORMAT_UINT_64;
185     case NCSCT_INT64:
186       return VIL_PIXEL_FORMAT_INT_64;
187     case NCSCT_UINT32:
188       return VIL_PIXEL_FORMAT_UINT_32;
189     case NCSCT_INT32:
190       return VIL_PIXEL_FORMAT_INT_32;
191     case NCSCT_UINT16:
192       return VIL_PIXEL_FORMAT_UINT_16;
193     case NCSCT_INT16:
194       return VIL_PIXEL_FORMAT_INT_16;
195     case NCSCT_UINT8:
196       return VIL_PIXEL_FORMAT_BYTE;
197     case NCSCT_INT8:
198       return VIL_PIXEL_FORMAT_SBYTE;
199     case NCSCT_IEEE4:
200       return VIL_PIXEL_FORMAT_FLOAT;
201     case NCSCT_IEEE8:
202       return VIL_PIXEL_FORMAT_DOUBLE;
203     default:
204       assert(0);
205       return VIL_PIXEL_FORMAT_UNKNOWN;
206   }
207 }
208 
209 ////////////////////////////////////////////////////
210 //                vil_j2k_image
211 ////////////////////////////////////////////////////
212 
vil_j2k_image(const std::string & fileOrUrl)213 vil_j2k_image::vil_j2k_image(const std::string & fileOrUrl)
214   : vil_image_resource()
215   , mFileResource(new CNCSFile())
216   , mStr(0)
217   , mMaxLocalDimension(5000)
218   , // default value
219   mMaxRemoteDimension(640)
220   , // default value
221   mRemoteFile(false)
222   , mFinfo(0)
223   , mBandinfo(0)
224   , line_index_(0)
225 {
226   if (mFileResource->Open((char *)fileOrUrl.c_str(), false, false) != NCS_SUCCESS)
227   {
228     mFileResource = 0;
229     return;
230   }
231   if (fileOrUrl.substr(0, 7) == "ecwp://" || fileOrUrl.substr(0, 7) == "ECWP://")
232   {
233     mRemoteFile = true;
234   }
235 }
236 
vil_j2k_image(vil_stream * is)237 vil_j2k_image::vil_j2k_image(vil_stream * is)
238   : vil_image_resource()
239   , mFileResource(new CNCSFile())
240   , mStr(new CNCSJPCVilIOStream())
241   , mMaxLocalDimension(5000)
242   , // default value
243   mMaxRemoteDimension(640)
244   , // default value
245   mRemoteFile(false)
246   , mFinfo(0)
247   , mBandinfo(0)
248   , line_index_(0)
249 {
250   mStr->Open(is);
251 
252   if ((static_cast<CNCSJP2FileView *>(mFileResource))->Open(mStr) != NCS_SUCCESS)
253   {
254     mFileResource = 0;
255     return;
256   }
257 }
258 
vil_j2k_image(vil_stream * vs,unsigned ni,unsigned nj,unsigned nplanes,enum vil_pixel_format format,unsigned compression_ratio)259 vil_j2k_image::vil_j2k_image(vil_stream * vs,
260                              unsigned ni,
261                              unsigned nj,
262                              unsigned nplanes,
263                              enum vil_pixel_format format,
264                              unsigned compression_ratio)
265   : vil_image_resource()
266   , mFileResource(new CNCSFile())
267   , mStr(new CNCSJPCVilIOStream())
268   , mMaxLocalDimension(5000)
269   , // default value
270   mMaxRemoteDimension(640)
271   , // default value
272   mRemoteFile(false)
273   , mFinfo(new NCSFileViewFileInfoEx())
274   , line_index_(0)
275 {
276   mBandinfo = new NCSFileBandInfo[nplanes];
277   CNCSError Error;
278   for (unsigned i = 0; i < nplanes; ++i)
279     mBandinfo[i] = bandInfo(format);
280 
281   // String names for each band, should specialize according to color space
282   mBandinfo[0].szDesc = "grey";
283   if (nplanes == 3)
284   {
285     mBandinfo[0].szDesc = "Red";
286     mBandinfo[1].szDesc = "Green";
287     mBandinfo[2].szDesc = "Blue";
288   }
289   NCSEcwCellType t = convertType(format);
290   NCSFileViewFileInfoEx finfo = *mFinfo;
291   finfo.pBands = mBandinfo;
292   finfo.nSizeX = ni;
293   finfo.nSizeY = nj;
294   finfo.nBands = nplanes;
295   finfo.eCellType = t;
296   finfo.nCompressionRate = compression_ratio;
297   finfo.eCellSizeUnits = ECW_CELL_UNITS_METERS;
298   finfo.fCellIncrementX = 1.0;
299   finfo.fCellIncrementY = 1.0;
300   finfo.fOriginX = 0.0;
301   finfo.fOriginY = 0.0;
302   finfo.szDatum = "RAW";
303   finfo.szProjection = "RAW";
304   finfo.fCWRotationDegrees = 0.0;
305   if (nplanes == 1)
306     finfo.eColorSpace = NCSCS_GREYSCALE;
307   else if (nplanes == 3)
308     finfo.eColorSpace = NCSCS_sRGB;
309   else
310   {
311     delete mFileResource;
312     mFileResource = 0;
313   }
314   Error = mFileResource->SetFileInfo(finfo);
315   if (Error != NCS_SUCCESS)
316   {
317     if (mFileResource)
318       delete mFileResource;
319     mFileResource = 0;
320   }
321   Error = mStr->Open(vs, true);
322   if (Error != NCS_SUCCESS)
323   {
324     if (mFileResource)
325       delete mFileResource;
326     mFileResource = 0;
327   }
328   CNCSJP2FileView * fview = static_cast<CNCSJP2FileView *>(mFileResource);
329   Error = fview->Open(static_cast<CNCSJPCIOStream *>(mStr));
330   if (Error != NCS_SUCCESS)
331   {
332     if (mFileResource)
333       delete mFileResource;
334     mFileResource = 0;
335     return;
336   }
337 }
338 
~vil_j2k_image()339 vil_j2k_image::~vil_j2k_image()
340 {
341   if (mFileResource)
342   {
343     mFileResource->Close(true);
344   }
345   if (mStr)
346     delete mStr;
347   if (mBandinfo)
348     delete mBandinfo;
349   if (mFinfo)
350     delete mFinfo;
351 }
352 
353 unsigned
nplanes() const354 vil_j2k_image::nplanes() const
355 {
356   assert(mFileResource);
357   return mFileResource->GetFileInfo()->nBands;
358 }
359 
360 unsigned
ni() const361 vil_j2k_image::ni() const
362 {
363   assert(mFileResource);
364   return mFileResource->GetFileInfo()->nSizeX;
365 }
366 
367 unsigned
nj() const368 vil_j2k_image::nj() const
369 {
370   assert(mFileResource);
371   return mFileResource->GetFileInfo()->nSizeY;
372 }
373 
374 enum vil_pixel_format
pixel_format() const375 vil_j2k_image::pixel_format() const
376 {
377   assert(mFileResource);
378   return convertType(mFileResource->GetFileInfo()->eCellType);
379 }
380 
381 char const *
file_format() const382 vil_j2k_image::file_format() const
383 {
384   return "j2k";
385 }
386 
387 vil_image_view_base_sptr
get_copy_view_decimated(unsigned sample0,unsigned num_samples,unsigned line0,unsigned numLines,double i_factor,double j_factor) const388 vil_j2k_image::get_copy_view_decimated(unsigned sample0,
389                                        unsigned num_samples,
390                                        unsigned line0,
391                                        unsigned numLines,
392                                        double i_factor,
393                                        double j_factor) const
394 {
395   return get_copy_view_decimated_by_size(sample0,
396                                          num_samples,
397                                          line0,
398                                          numLines,
399                                          (unsigned int)(((double)num_samples) / i_factor),
400                                          (unsigned int)(((double)numLines) / j_factor));
401 }
402 
403 vil_image_view_base_sptr
get_copy_view_decimated_by_size(unsigned sample0,unsigned num_samples,unsigned line0,unsigned numLines,unsigned int output_width,unsigned int output_height) const404 vil_j2k_image::get_copy_view_decimated_by_size(unsigned sample0,
405                                                unsigned num_samples,
406                                                unsigned line0,
407                                                unsigned numLines,
408                                                unsigned int output_width,
409                                                unsigned int output_height) const
410 {
411   if (!(mFileResource) || !((sample0 + num_samples - 1) < ni() && (line0 + numLines - 1) < nj()))
412   {
413     return 0;
414   }
415 
416   // we want all bands mapped in the same order as they come in the input file
417   // eg. bandMap = {0,1,2,3...nBands}
418   INT32 nBands = nplanes();
419   INT32 * bandMap = (INT32 *)std::malloc(sizeof(UINT32) * nBands);
420   for (int i = 0; i < nBands; i++)
421   {
422     bandMap[i] = i;
423   }
424 
425   // this guards us from returning an image that is too big for the computer's memory
426   //(or would take too long to download in the remote case).
427   // We don't want infinite hangs or application crashes.
428   unsigned int maxDim = mRemoteFile ? mMaxRemoteDimension : mMaxLocalDimension;
429   if (output_width > maxDim || output_height > maxDim)
430   {
431     unsigned int biggestDim = (std::max)(output_width, output_height);
432     double zoomFactor = ((double)maxDim) / ((double)biggestDim);
433     output_width = (unsigned int)(((double)output_width) * zoomFactor);
434     output_height = (unsigned int)(((double)output_height) * zoomFactor);
435   }
436 
437   // set the view to be that specified by the function's input parameters
438   // note that we don't want ECW to do any scaling for us.  That's why
439   // the box created by (sample0,line0) and (sample0+num_samples-1,line0+numLines-1) is made to be exactly
440   // num_samplesXnumLines.
441 
442   NCSError setViewError = mFileResource->SetView(nBands,
443                                                  bandMap,
444                                                  output_width,
445                                                  output_height,
446                                                  (INT32)sample0,
447                                                  (INT32)line0,
448                                                  (INT32)(sample0 + num_samples - 1),
449                                                  (INT32)(line0 + numLines - 1));
450   if (setViewError != NCS_SUCCESS)
451   {
452     free(bandMap);
453     return 0;
454   }
455 
456   // number of samples times the bytes per sample in each band
457   double bitsPerSample = mFileResource->GetFileInfo()->pBands[0].nBits;
458   unsigned int bytesPerSample = (unsigned int)std::ceil(bitsPerSample / 8.0);
459   unsigned int singleBandLineSizeBytes = output_width * bytesPerSample;
460   unsigned int allBandLineSizeBytes = singleBandLineSizeBytes * nBands;
461   unsigned int dataPtrSizeBytes = allBandLineSizeBytes * output_height;
462   // void* data_ptr = std::malloc( dataPtrSizeBytes );
463   vil_memory_chunk_sptr data_ptr =
464     new vil_memory_chunk(dataPtrSizeBytes, convertType(mFileResource->GetFileInfo()->eCellType));
465   void ** linePtrPtr =
466     (void **)std::malloc(nBands * sizeof(int * /*all pointers have same size, so eg char* would work too*/));
467   // now read all the lines that we want
468   for (unsigned int currLine = 0; currLine < output_height; currLine++)
469   {
470     for (int currBand = 0; currBand < nBands; currBand++)
471     {
472       linePtrPtr[currBand] =
473         (void *)(((char *)data_ptr->data()) + currLine * allBandLineSizeBytes + currBand * singleBandLineSizeBytes);
474     }
475     NCSEcwReadStatus readStatus =
476       mFileResource->ReadLineBIL(mFileResource->GetFileInfo()->eCellType, nBands, linePtrPtr, 0);
477     if (readStatus != NCSECW_READ_OK)
478     {
479       free(bandMap);
480       free(linePtrPtr);
481       return 0;
482     }
483   }
484 
485   // free our temp resources
486   free(bandMap);
487   free(linePtrPtr);
488 
489   vil_image_view_base_sptr view = 0;
490   // now create our vil_image_view
491   // note that float and double are defaulted since the J2K SDK doesn't
492   // implement these types properly
493   switch (vil_pixel_format_component_format(data_ptr->pixel_format()))
494   {
495 #define macro(F, T)                                                                                                    \
496   case F:                                                                                                              \
497     view = new vil_image_view<T>(data_ptr,                                                                             \
498                                  reinterpret_cast<T *>(data_ptr->data()),                                              \
499                                  output_width,                                                                         \
500                                  output_height,                                                                        \
501                                  nBands,                                                                               \
502                                  1,                                                                                    \
503                                  output_width * nBands,                                                                \
504                                  output_width);                                                                        \
505     break
506     macro(VIL_PIXEL_FORMAT_BYTE, vxl_byte);
507     macro(VIL_PIXEL_FORMAT_SBYTE, vxl_sbyte);
508     macro(VIL_PIXEL_FORMAT_UINT_64, vxl_uint_64);
509     macro(VIL_PIXEL_FORMAT_INT_64, vxl_int_64);
510     macro(VIL_PIXEL_FORMAT_UINT_32, vxl_uint_32);
511     macro(VIL_PIXEL_FORMAT_INT_32, vxl_int_32);
512     macro(VIL_PIXEL_FORMAT_UINT_16, vxl_uint_16);
513     macro(VIL_PIXEL_FORMAT_INT_16, vxl_int_16);
514     macro(VIL_PIXEL_FORMAT_BOOL, bool);
515 #undef macro
516     default:
517       std::cerr << "Pixel format not supported by ERMapper SDK\n";
518       assert(0);
519       break;
520   }
521 
522   return view;
523 }
524 
525 vil_image_view_base_sptr
get_copy_view(unsigned sample0,unsigned num_samples,unsigned line0,unsigned numLines) const526 vil_j2k_image::get_copy_view(unsigned sample0, unsigned num_samples, unsigned line0, unsigned numLines) const
527 {
528   return get_copy_view_decimated(sample0, num_samples, line0, numLines, 1.0, 1.0);
529 }
530 
531 void
unsetMaxImageDimension(bool remote)532 vil_j2k_image::unsetMaxImageDimension(bool remote)
533 {
534 #undef max
535   setMaxImageDimension(std::numeric_limits<unsigned int>::max(), remote);
536 }
537 
538 void
setMaxImageDimension(unsigned int widthOrHeight,bool remote)539 vil_j2k_image::setMaxImageDimension(unsigned int widthOrHeight, bool remote)
540 {
541   if (remote)
542   {
543     mMaxRemoteDimension = widthOrHeight;
544   }
545   else
546   {
547     mMaxLocalDimension = widthOrHeight;
548   }
549 }
550 
551 vil_image_view_base_sptr
s_decode_jpeg_2000(vil_stream * vs,unsigned i0,unsigned ni,unsigned j0,unsigned nj,double i_factor,double j_factor)552 vil_j2k_image::s_decode_jpeg_2000(vil_stream * vs,
553                                   unsigned i0,
554                                   unsigned ni,
555                                   unsigned j0,
556                                   unsigned nj,
557                                   double i_factor,
558                                   double j_factor)
559 {
560   vil_j2k_image * j2k_image = new vil_j2k_image(vs);
561   // remove limit by default, since vil is not typically used remotely
562   // but more commonly with large image files - JLM Jan 07, 2012
563   j2k_image->unsetMaxImageDimension();
564   vil_image_view_base_sptr view = j2k_image->get_copy_view_decimated(i0, ni, j0, nj, i_factor, j_factor);
565   delete j2k_image;
566   return view;
567 }
568 
569 vil_image_view_base_sptr
s_decode_jpeg_2000_by_size(vil_stream * vs,unsigned i0,unsigned ni,unsigned j0,unsigned nj,unsigned int output_width,unsigned int output_height)570 vil_j2k_image::s_decode_jpeg_2000_by_size(vil_stream * vs,
571                                           unsigned i0,
572                                           unsigned ni,
573                                           unsigned j0,
574                                           unsigned nj,
575                                           unsigned int output_width,
576                                           unsigned int output_height)
577 {
578   vil_j2k_image * j2k_image = new vil_j2k_image(vs);
579   vil_image_view_base_sptr view =
580     j2k_image->get_copy_view_decimated_by_size(i0, ni, j0, nj, output_width, output_height);
581   delete j2k_image;
582   return view;
583 }
584 
585 template <class T>
586 static bool
write_line_BIL(vil_memory_chunk_sptr & chunk,unsigned ni,unsigned nplanes,unsigned istep,unsigned planestep,unsigned bytes_per_pixel,CNCSFile * f_resource,NCSEcwCellType t)587 write_line_BIL(vil_memory_chunk_sptr & chunk,
588                unsigned ni,
589                unsigned nplanes,
590                unsigned istep,
591                unsigned planestep,
592                unsigned bytes_per_pixel,
593                CNCSFile * f_resource,
594                NCSEcwCellType t)
595 {
596   T * cdata = reinterpret_cast<T *>(chunk->data());
597   T ** line_ptr = new T *[nplanes];
598   for (unsigned p = 0; p < nplanes; ++p)
599     line_ptr[p] = new T[ni * bytes_per_pixel];
600 
601   for (unsigned p = 0; p < nplanes; ++p)
602   {
603     T * wline = line_ptr[p];
604     for (unsigned i = 0; i < ni; ++i)
605     {
606       *(wline + i) = *(cdata + i * istep + p * planestep);
607     }
608   }
609   bool good = true;
610   void ** outbuf = reinterpret_cast<void **>(line_ptr);
611   CNCSError writeError = f_resource->WriteLineBIL(t, nplanes, outbuf);
612   if (writeError != NCS_SUCCESS)
613     good = false;
614 
615   for (unsigned p = 0; p < nplanes; ++p)
616     delete[] line_ptr[p];
617   delete[] line_ptr;
618   return good;
619 }
620 
621 
622 //: JPEG2K compress by inserting an image row (line) at a time
623 //  When the full image has been inserted, call put_line with
624 // image_row == nj(). This call causes the resource to be closed
625 // and is no longer valid. The lines must be inserted in strict row order.
626 bool
put_line(const vil_image_view_base & im)627 vil_j2k_image::put_line(const vil_image_view_base & im)
628 {
629   if (!mFileResource)
630     return false;
631   vil_pixel_format format = this->pixel_format();
632   unsigned ni = this->ni(), nj = this->nj(), nplanes = this->nplanes();
633   if (line_index_ >= nj)
634   {
635     mFileResource->Close(true);
636     if (mFileResource)
637       delete mFileResource;
638     mFileResource = 0;
639     return true;
640   }
641   unsigned bytes_per_pixel = 0;
642   NCSEcwCellType t = convertType(format);
643   vil_memory_chunk_sptr chunk;
644   // now write out the image line
645   // note that float and double are defaulted since the J2K SDK doesn't
646   // implement these types properly
647   switch (vil_pixel_format_component_format(format))
648   {
649 #define macro(F, T)                                                                                                    \
650   case F: {                                                                                                            \
651     bytes_per_pixel = sizeof(T);                                                                                       \
652     const vil_image_view<T> & view = static_cast<const vil_image_view<T> &>(im);                                       \
653     chunk = view.memory_chunk();                                                                                       \
654     if (!write_line_BIL<T>(chunk, ni, nplanes, view.istep(), view.planestep(), bytes_per_pixel, mFileResource, t))     \
655       return false;                                                                                                    \
656   }                                                                                                                    \
657   break
658     macro(VIL_PIXEL_FORMAT_BYTE, vxl_byte);
659     macro(VIL_PIXEL_FORMAT_SBYTE, vxl_sbyte);
660     macro(VIL_PIXEL_FORMAT_UINT_32, vxl_uint_32);
661     macro(VIL_PIXEL_FORMAT_INT_32, vxl_int_32);
662     macro(VIL_PIXEL_FORMAT_UINT_16, vxl_uint_16);
663     macro(VIL_PIXEL_FORMAT_INT_16, vxl_int_16);
664     macro(VIL_PIXEL_FORMAT_BOOL, bool);
665 #undef macro
666     default:
667       std::cerr << "Pixel format not supported by ERMapper SDK\n";
668       assert(0);
669       break;
670   }
671   ++line_index_;
672   return true;
673 }
674 
675 //: JPEG2K compress the view and of the full image and insert in resource
676 //  The file is closed after putting the view into the resource
677 //  and becomes an invalid resource.
678 bool
put_view(const vil_image_view_base & im)679 vil_j2k_image::put_view(const vil_image_view_base & im)
680 {
681   if (!this->view_fits(im, 0, 0))
682     return false;
683   if (!mFileResource)
684     return false;
685   unsigned ni = im.ni(), nj = im.nj();
686   vil_image_resource_sptr mem_res = vil_new_image_resource_of_view(im);
687   vil_image_view_base_sptr view;
688   for (unsigned j = 0; j < nj; ++j)
689   {
690     view = mem_res->get_copy_view(0, ni, j, 1);
691     if (!this->put_line(*view))
692       return false;
693   }
694   return true;
695 }
696 
697 //: Check that a view will fit into the data at the given offset.
698 bool
view_fits(const vil_image_view_base & im,unsigned i0,unsigned j0)699 vil_j2k_image::view_fits(const vil_image_view_base & im, unsigned i0, unsigned j0)
700 {
701   unsigned ni_view = im.ni(), nj_view = im.nj(), nplanes_view = im.nplanes();
702   return i0 + 1 < ni_view && j0 + 1 < nj_view && ni_view <= this->ni() && nj_view <= this->nj() &&
703          nplanes_view <= this->nplanes();
704 }
705 
706 //:
707 //  Encode an entire image by loading the input resource from stream
708 //  and compressing the input line by line by extracting an image view
709 //  of a block of lines at a time, thus works for arbitrarily large images.
710 //  The num_lines_block parameter is the number of image rows in the
711 //  block which is read into memory from the resource
712 bool
s_encode_jpeg2000(vil_stream * vs,const char * out_filename,unsigned compression_ratio,unsigned num_lines_block,bool verbose)713 vil_j2k_image::s_encode_jpeg2000(vil_stream * vs,
714                                  const char * out_filename,
715                                  unsigned compression_ratio,
716                                  unsigned num_lines_block,
717                                  bool verbose)
718 {
719   vil_image_resource_sptr in_res = vil_load_image_resource_raw(vs);
720   if (!in_res)
721     return false;
722   unsigned ni = in_res->ni(), nj = in_res->nj(), nplanes = in_res->nplanes();
723   vil_pixel_format format = in_res->pixel_format();
724   vil_stream * os = vil_open(out_filename, "w");
725   if (!vs)
726     return false;
727   vil_j2k_file_format fmt;
728   fmt.set_compression_ratio(compression_ratio);
729   vil_image_resource_sptr res = fmt.make_output_image(os, ni, nj, nplanes, format);
730   if (!res)
731     return false;
732   vil_j2k_image * j2k_img = reinterpret_cast<vil_j2k_image *>(res.ptr());
733 
734   // number of full blocks in image height
735   unsigned n_blocks = nj / num_lines_block;
736   unsigned jb = 0;
737   for (unsigned b = 0; b < n_blocks; b++, jb += num_lines_block)
738   {
739     // read a block from the file: width = ni, height = num_lines_block
740     vil_image_view_base_sptr block_view = in_res->get_view(0, ni, jb, num_lines_block);
741     if (!block_view)
742       return false;
743 
744     // wrap the view in a memory resident resource
745     vil_image_resource_sptr block_res = vil_new_image_resource_of_view(*block_view);
746 
747     // compress the block, line by line
748     for (unsigned j = 0; j < num_lines_block; ++j)
749     {
750       vil_image_view_base_sptr line_view = block_res->get_copy_view(0, ni, j, 1);
751       if (!j2k_img->put_line(*line_view))
752         return false;
753       if (verbose)
754         if (j % 100 == 0) // output a dot every 100 lines
755           std::cout << '.';
756     }
757   }
758   // output the remaining lines left over after loading block-sized chunks
759   unsigned remaining_lines = nj - jb;
760   if (remaining_lines)
761   {
762     vil_image_view_base_sptr residual_view = in_res->get_view(0, ni, jb, remaining_lines);
763     vil_image_resource_sptr residual_res = vil_new_image_resource_of_view(*residual_view);
764     vil_image_view_base_sptr view;
765     for (unsigned j = 0; j < remaining_lines; ++j)
766     {
767       view = residual_res->get_copy_view(0, ni, j, 1);
768       if (!j2k_img->put_line(*view))
769         return false;
770       if (verbose)
771         if (j % 100 == 0) // output a dot every 100 lines
772           std::cout << '.';
773     }
774   }
775   if (verbose)
776     std::cout << '\n';
777   return true;
778 }
779 
780 bool
s_encode_jpeg2000(const char * in_filename,const char * out_filename,unsigned compression_ratio,unsigned num_lines_block,bool verbose)781 vil_j2k_image::s_encode_jpeg2000(const char * in_filename,
782                                  const char * out_filename,
783                                  unsigned compression_ratio,
784                                  unsigned num_lines_block,
785                                  bool verbose)
786 {
787   vil_stream * vs = vil_open(in_filename);
788   vs->ref();
789   bool success = vil_j2k_image::s_encode_jpeg2000(vs, out_filename, compression_ratio, num_lines_block, verbose);
790   vs->unref();
791   return success;
792 }
793