OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimBitMaskWriter.cpp
Go to the documentation of this file.
1 //*************************************************************************************************
2 //
3 // License: LGPL
4 //
5 // See LICENSE.txt file in the top level directory for more details.
6 //
7 // Author: Oscar Kramer
8 //
9 // Description: Contains implementation of class for computing a mask from an input image.
10 // The mask is stored in a compressed format where one byte represents 8 bit-masks for masking
11 // 8 image pixels.
12 //*************************************************************************************************
13 // $Id: ossimBitMaskWriter.cpp 3081 2011-12-22 16:34:12Z oscar.kramer $
14 
22 
23 const char *ossimBitMaskWriter::MASK_FILE_MAGIC_NUMBER = "OSSIM_BIT_MASK";
24 const char *ossimBitMaskWriter::BM_STARTING_RLEVEL_KW = "starting_rlevel";
25 
26 //*************************************************************************************************
27 // Constructor accepts handler to the source imagery being used to compute the mask.
28 //*************************************************************************************************
30  : m_flipper(0),
31  m_memoryImage(0),
32  m_startingResLevel(0)
33 {
34 }
35 
36 //*************************************************************************************************
37 // Destructor
38 //*************************************************************************************************
40 {
41  // Make sure the mask file was written out before disappearing:
42  if (isOpen())
43  close();
44 }
45 
46 //*************************************************************************************************
47 // Returns true if buffers allocated:
48 //*************************************************************************************************
50 {
51  return (m_buffers.size() != 0);
52 }
53 
54 //*************************************************************************************************
55 // Returns true if buffers allocated:
56 //*************************************************************************************************
58 {
59  // Nothing to do yet. Eventually want to open the output stream here and write the buffer after
60  // each R-level is completed, so we don't need to keep vector of buffers in memory.
61  return true;
62 }
63 
64 //*************************************************************************************************
65 // Reset
66 //*************************************************************************************************
68 {
69  // Wipe the mask buffers:
70  vector<ossim_uint8 *>::iterator iter = m_buffers.begin();
71  while (iter != m_buffers.end())
72  {
73  delete[](*iter);
74  iter++;
75  }
76  m_buffers.clear();
77  m_bufferSizes.clear();
78 }
79 
80 //*************************************************************************************************
82 //*************************************************************************************************
83 void ossimBitMaskWriter::setBogusPixel(double pixel_value)
84 {
85  if (!m_flipper.valid())
87 
88  m_flipper->setTargetValue(pixel_value);
89 }
90 
91 //*************************************************************************************************
93 //*************************************************************************************************
95 {
96  if (!m_flipper.valid())
98 
100 }
101 
102 //*************************************************************************************************
103 // Given a source's tile, derives the alpha mask and saves it in buffer for later writing to disk.
104 //*************************************************************************************************
106 {
107  static const ossim_uint8 MASK_BITS_0[] = {0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE};
108  static const ossim_uint8 MASK_BITS_1[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
109 
110  // We don't start doing anything until starting res or higher requested:
111  ossim_uint32 mask_rlevel = rLevel - m_startingResLevel;
112  if (!tile.valid())
113  return;
114 
115  // We should have had this done by now, but just in case:
116  if (!m_flipper.valid())
118 
120  m_memoryImage->setImage(tile);
122  flipTile = m_flipper->getTile(tile->getImageRectangle());
123  ossimIpt image_size = computeImageSize(rLevel, flipTile.get());
124  ossim_uint32 num_mask_cols = (image_size.x + 7) / 8; // size of mask buffer after compression
125  ossim_uint32 num_mask_rows = image_size.y;
126  ossim_uint8 *maskbuf = 0;
127 
128  // Check if mask buffer for this R-level has already been allocated:
129  if (m_buffers.size() <= mask_rlevel)
130  {
131  ossim_uint32 size_of_maskbuf = num_mask_rows * num_mask_cols;
132  maskbuf = new ossim_uint8[size_of_maskbuf];
133  memset(maskbuf, 0, size_of_maskbuf);
134  m_buffers.push_back(maskbuf);
135  m_bufferSizes.push_back(ossimIpt(num_mask_cols, num_mask_rows));
136  }
137  else
138  maskbuf = m_buffers[mask_rlevel];
139 
140  ossim_uint32 mask_index = 0, tile_index = 0, start_bit = 0;
141  ossimIrect tile_rect(flipTile->getImageRectangle());
142  ossimIpt ul(tile_rect.ul());
143  ossimIpt lr(tile_rect.lr());
144 
145  // Scan each pixel in the source tile and decide on mask value:
146  for (int y = ul.y; (y <= lr.y) && (y < image_size.y); y++)
147  {
148  mask_index = y * num_mask_cols + ul.x / 8;
149  start_bit = ul.x % 8; // may not start on even mask byte boundary
150 
151  for (int x = ul.x; (x <= lr.x); /* incremented in bit loop below */)
152  {
153  if (x < image_size.x)
154  {
155  // Process 8 samples and pack resultant mask into one byte:
156  maskbuf[mask_index] = 0;
157  for (ossim_uint32 mask_bit = start_bit; mask_bit < 8; ++mask_bit)
158  {
159  // Decide whether to mask depending on pixel flipper outputting a NULL pixel.
160  if (flipTile->isNull(tile_index++))
161  maskbuf[mask_index] &= MASK_BITS_0[mask_bit];
162  else
163  maskbuf[mask_index] |= MASK_BITS_1[mask_bit];
164 
165  // Advance the pixel column and check for end of image rect:
166  ++x;
167  if ((x >= image_size.x) || (x > lr.x))
168  break;
169  }
170  ++mask_index; // advance the mask buffer index 1 after processing 8 tile samples
171  start_bit = 0;
172  }
173  else
174  {
175  ++x;
176 
177  ++tile_index;
178  }
179  }
180  } // Finished looping over all pixels in input tile
181 
182  return;
183 }
184 
185 //*************************************************************************************************
187 //*************************************************************************************************
189 {
190  static const char *MODULE = "ossimBitMaskWriter::writeMask()";
191  static const ossimString MASK_EXTENSION("mask");
192 
193  // Open the output file. Use default name if none provided.
194  if (theOutputName.empty()) // Couldn't figure out the name
195  {
196  ossimNotify(ossimNotifyLevel_WARN) << MODULE << " -- Error encountered trying to create mask file"
197  " for writing because output file name was never initialized.";
198  return;
199  }
200 
201  ofstream maskFileStream(theOutputName.chars(), ios::out | ios::binary);
202  if (!maskFileStream.is_open())
203  {
204  ossimNotify(ossimNotifyLevel_WARN) << MODULE << " -- Error encountered trying to create mask file"
205  "<"
206  << theOutputName << "> for writing. Cannot write mask.";
207  return;
208  }
209 
210  // Write the header info:
211  ossim_uint32 num_rlevels = (ossim_uint32)m_buffers.size();
212  maskFileStream << MASK_FILE_MAGIC_NUMBER << " " << m_startingResLevel << " " << num_rlevels << " ";
213  for (ossim_uint32 r = 0; r < num_rlevels; r++)
214  maskFileStream << m_bufferSizes[r].x << " " << m_bufferSizes[r].y << " ";
215  maskFileStream << ends;
216 
217  // Loop over each res level and write buffers to disk:
218  for (ossim_uint32 rlevel = 0; rlevel < num_rlevels; ++rlevel)
219  {
220  ossim_uint32 bufsize = m_bufferSizes[rlevel].x * m_bufferSizes[rlevel].y;
221  maskFileStream.write((char *)(m_buffers[rlevel]), bufsize);
222  }
223 
224  maskFileStream.close();
225  reset();
226 
227  return;
228 }
229 
230 //*************************************************************************************************
233 //*************************************************************************************************
234 bool ossimBitMaskWriter::loadState(const ossimKeywordlist &kwl, const char *prefix)
235 {
236  // static const char* MODULE = "ossimBitMaskWriter::writeMask(kwl)";
237 
239 
240  // Fetch optional output filename:
242 
243  // Initialize the pixel flipper. This filter is used to identify pixels targeted for masking by
244  // remapping them to the null pixel:
245  m_flipper->loadState(kwl, prefix);
246 
247  // Read Starting res level:
248  m_startingResLevel = 0;
249  ossimString kw_value = kwl.find(prefix, BM_STARTING_RLEVEL_KW);
250  if (!kw_value.empty())
251  m_startingResLevel = kw_value.toUInt32();
252 
253  return true;
254 }
255 
256 //*************************************************************************************************
257 //
258 //*************************************************************************************************
260 {
261  if (!m_flipper.valid())
262  {
267  }
268  if (!m_memoryImage)
269  {
272  }
273 
274  // This method gets called when an input connection is made as well as other times, so keep an
275  // eye out for valid input connection:
276  if (theInputObjectList.size() != 0)
277  {
279  }
280 }
281 
282 //*************************************************************************************************
283 //
284 //*************************************************************************************************
286  const ossimConnectableObject *object) const
287 {
288  const ossimImageSource *ois = dynamic_cast<const ossimImageSource *>(object);
289  return (ois != NULL);
290 }
291 
292 //*************************************************************************************************
293 //
294 //*************************************************************************************************
296  bool makeOutputConnection,
297  bool createEventFlag)
298 {
299  ossimImageSource *input_source = dynamic_cast<ossimImageSource *>(inputObject);
300  if (input_source == NULL)
301  return -1;
302 
303  // Base class takes care of connection with this object:
304  ossimConnectableObject::connectMyInputTo(input_source, makeOutputConnection, createEventFlag);
305 
306  // This input should be an image handler, but may need to search the objects inputs in case
307  // it is chained:
308  ossimImageHandler *handler = dynamic_cast<ossimImageHandler *>(input_source);
309  if (handler == NULL)
310  {
311  // Need to search:
312  ossimTypeNameVisitor visitor(ossimString("ossimImageHandler"),
313  true,
315 
316  input_source->accept(visitor);
317  ossimRefPtr<ossimObject> obj = visitor.getObject();
318  if (obj.valid())
319  {
320  handler = dynamic_cast<ossimImageHandler *>(obj.get());
321  }
322  }
323 
324  // Should have a handler:
325  if (!handler)
326  {
328  return -1;
329  }
330 
331  // A handler has been identified. Need the filename:
332  ossimFilename imageFile = handler->getFilename();
333  if (imageFile.contains("ovr.tmp"))
334  {
335  // The handler is actually an overview, need some massaging to work since the overview may
336  // not have an R0 entry and R1 rect must be requested instead:
337  m_imageSize = handler->getBoundingRect(1).size() * 2;
338  imageFile = imageFile.noExtension();
339  }
340  else
341  {
342  // This is normal image handler so it is OK to request R0 rect:
343  m_imageSize = handler->getBoundingRect(0).size();
344  }
345  if (theOutputName.empty())
346  theOutputName = imageFile.setExtension("mask");
347 
348  // Check if the pixel flipper was initialized:
349  if (m_flipper.valid())
350  m_flipper->connectMyInputTo(input_source);
351  else
353 
354  return 0;
355 }
356 
357 //*************************************************************************************************
360 //*************************************************************************************************
362 {
363  if (rlevel == 0)
364  return m_imageSize;
365 
366  ossimIpt isize(m_imageSize);
367  for (ossim_uint32 r = 1; r <= rlevel; r++)
368  {
369  isize.x = (isize.x + 1) / 2;
370  isize.y = (isize.y + 1) / 2;
371  }
372 
373  // Adjust size n X direction to even mask boundary:
374  ossimIpt tile_size(tile->getWidth(), tile->getHeight());
375  isize.x = ((int)(isize.x + 7) / 8) * 8;
376 
377  return isize;
378 }
379 
380 //*************************************************************************************************
381 // For imagery that already has overviews built, but with artifact edge pixels (such as JP2-
382 // compressed data), it is preferred to build the mask overviews directly from the R0 mask.
383 // This method will build the specified number of R-levels (including R0) from the last defined
384 // mask buffer. Returns TRUE if successful.
385 // disk.
386 //*************************************************************************************************
388 {
389  // First establish the remaining number of R-levels to generate, and the last valid R-level
390  // currently in memory:
391  if (m_buffers.size() == 0)
392  return false;
393  if (m_buffers.size() == total_num_rlevels)
394  return true; // nothing to do
395  ossim_uint32 ref_rlevel = m_startingResLevel + (ossim_uint32)m_buffers.size() - 1;
396  ossim_uint32 ovr_rlevel = ref_rlevel + 1;
397 
398  ossimIpt ref_size(m_bufferSizes[ref_rlevel - m_startingResLevel]);
399  ossim_uint8 *ref_buf = m_buffers[ref_rlevel - m_startingResLevel];
400  ossim_uint32 ref_index = 0, ovr_index = 0;
401  ossim_uint8 *ovr_buf = 0;
402  ossim_uint32 size_of_refbuf = ref_size.x * ref_size.y;
403 
404  // Loop over all remaining res levels:
405  while (ovr_rlevel != total_num_rlevels)
406  {
407  // Allocate the buffer at this R-level:
408  ossimIpt ovr_size((ref_size.x + 1) / 2, (ref_size.y + 1) / 2);
409  ossim_uint32 size_of_ovrbuf = ovr_size.x * ovr_size.y;
410  if (size_of_ovrbuf == 0)
411  return false;
412  ovr_buf = new ossim_uint8[size_of_ovrbuf];
413  memset(ovr_buf, 0, size_of_ovrbuf);
414  m_buffers.push_back(ovr_buf);
415  m_bufferSizes.push_back(ovr_size);
416 
417  // Loop over each output overview pixel, considering the underlying rlevel (equivalent to
418  // nearest-neighbor resampling for overview at 2X decimation):
419  for (int y = 0; y < ovr_size.y; y++)
420  {
421  ref_index = 2 * y * ref_size.x;
422  ovr_index = y * ovr_size.x;
423 
424  for (int x = 0; x < ovr_size.x; x++)
425  {
426  ossim_uint8 a = 0;
427  ossim_uint8 b = 0;
428  if (ref_index < size_of_refbuf)
429  {
430  a = ref_buf[ref_index++];
431  if ((x < (ovr_size.x - 1)) || !(ref_size.x & 1))
432  b = ref_buf[ref_index++];
433  }
434  ovr_buf[ovr_index++] = ((a & 0x80) |
435  ((a & 0x20) << 1) |
436  ((a & 0x08) << 2) |
437  ((a & 0x02) << 3) |
438  ((b & 0x80) >> 4) |
439  ((b & 0x20) >> 3) |
440  ((b & 0x08) >> 2) |
441  ((b & 0x02) >> 1));
442  }
443  }
444 
445  // Advance to next rlevel:
446  ref_buf = ovr_buf;
447  ref_size = ovr_size;
448  size_of_refbuf = size_of_ovrbuf;
449  ++ovr_rlevel;
450  }
451 
452  return true;
453 }
virtual ossim_uint32 getWidth() const
ossim_uint32 x
ossimFilename noExtension() const
ossimIpt m_imageSize
Size of full res source image.
void setBogusPixelRange(double min, double max)
Sets the range of pixels (inclusive) to be regarded as NULL pixels when computing mask...
static const char * OUTPUT_FILE_KW
static const char * BM_STARTING_RLEVEL_KW
bool buildOverviews(ossim_uint32 total_num_rlevels)
For imagery that already has overviews built, but with artifact edge pixels (such as JP2- compressed ...
Represents serializable keyword/value map.
vector< ossimIpt > m_bufferSizes
ossim_uint32 y
ossim_uint32 m_startingResLevel
bool valid() const
Definition: ossimRefPtr.h:75
const char * find(const char *key) const
static const char * MASK_FILE_MAGIC_NUMBER
bool contains(char aChar) const
Definition: ossimString.h:58
ossimBitMaskWriter()
Default constructor typically used when reading a mask from disk.
void setImage(ossimRefPtr< ossimImageData > image)
virtual ossim_uint32 getHeight() const
ossim_uint32 toUInt32() const
vector< ossim_uint8 * > m_buffers
virtual bool loadState(const ossimKeywordlist &spec, const char *prefix=0)
Computes and writes the mask file according to the specification in the KWL.
virtual void initialize()
Initializes the state of the object from theInputConnection.
ossimIpt size() const
Definition: ossimIrect.h:510
void setReplacementMode(ossimPixelFlipper::ReplacementMode mode)
ossimString theOutputName
void initializeFlipper()
Initializes the flipper (used for identifying pixels for masking) to default values.
bool isNull(ossim_uint32 offset) const
ossimRefPtr< ossimMemoryImageSource > m_memoryImage
virtual const ossimFilename & getFilename() const
Returns the filename.
unsigned int ossim_uint32
const char * chars() const
For backward compatibility.
Definition: ossimString.h:77
virtual ossimIrect getImageRectangle() const
void setBogusPixel(double pixel_value)
Sets the NULL pixel value to consider when computing mask:
virtual ossim_int32 connectMyInputTo(ossimConnectableObject *inputObject, bool makeOutputConnection=true, bool createEventFlag=true)
Will try to connect this objects input to the passed in object.
virtual ossimRefPtr< ossimImageData > getTile(const ossimIrect &tile_rect, ossim_uint32 resLevel=0)
ossimObject * getObject(ossim_uint32 idx=0)
void generateMask(ossimRefPtr< ossimImageData > tile, ossim_uint32 rLevel)
Given a source&#39;s tile, derives the alpha mask and saves it in buffer for later writing to disk...
virtual bool isOpen() const
ossimRefPtr< ossimPixelFlipper > m_flipper
void setTargetRange(ossim_float64 target_min, ossim_float64 target_max)
Instead of a single value for a target, this method allows for specifying a range of values to flip t...
ConnectableObjectList theInputObjectList
Holds a list of input objects.
void setReplacementValue(ossim_float64 replacement_value)
This class defines an abstract Handler which all image handlers(loaders) should derive from...
void setTargetValue(ossim_float64 target_value)
virtual ossim_int32 connectMyInputTo(ossimConnectableObject *inputObject, bool makeOutputConnection=true, bool createEventFlag=true)
Will try to connect this objects input to the passed in object.
#define max(a, b)
Definition: auxiliary.h:76
ossim_int32 y
Definition: ossimIpt.h:142
Class to scan pixels and flip target dn value to a replacement dn value.
bool empty() const
Definition: ossimString.h:411
virtual void close()
Writes the mask file to path specified. Returns TRUE if successful.
ossimIpt computeImageSize(ossim_uint32 rlevel, ossimImageData *tile) const
Since overviews may not yet exist when the mask is being written, we must compute the size of the sou...
virtual bool canConnectMyInputTo(ossim_int32 myInputIndex, const ossimConnectableObject *object) const
required to be overriden by derived classes
void reset()
Deletes allocated buffers and resets all values to defaults.
virtual void accept(ossimVisitor &visitor)
We will add a visitor interface for all connectable objects.
virtual ossimIrect getBoundingRect(ossim_uint32 resLevel=0) const
Returns zero-based bounding rectangle of the image.
ossim_int32 x
Definition: ossimIpt.h:141
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=0)
Method to the load (recreate) the state of an object from a keyword list.
std::basic_ofstream< char > ofstream
Class for char output file streams.
Definition: ossimIosFwd.h:47
virtual void disconnectAllInputs()
Will disconnect all of the input objects.
ossimFilename & setExtension(const ossimString &e)
Sets the extension of a file name.
unsigned char ossim_uint8
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
#define min(a, b)
Definition: auxiliary.h:75
int ossim_int32