1 /*=========================================================================
4 Module: $RCSfile: gdcmPixelWriteConvert.cxx,v $
6 Date: $Date: 2008/04/10 12:15:36 $
7 Version: $Revision: 1.25 $
9 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10 l'Image). All rights reserved. See Doc/License.txt or
11 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
19 #include "gdcmDebug.h"
20 #include "gdcmPixelWriteConvert.h"
25 #include <stdlib.h> // abs
27 #define WITHOFFSETTABLE 1
29 namespace GDCM_NAME_SPACE
31 //-----------------------------------------------------------------------------
34 //-----------------------------------------------------------------------------
37 //-----------------------------------------------------------------------------
40 // Constructor / Destructor
44 PixelWriteConvert::PixelWriteConvert()
57 PixelWriteConvert::~PixelWriteConvert()
59 gdcmDebugMacro("PixelWriteConvert::~PixelWriteConvert()" );
67 * \brief sets Read Data (and size)
68 * @param data data (uint8_t is for prototyping. if your data is not uint8_t
69 * just cast the pointer for calling the method)
70 * @param size data size, in bytes
72 void PixelWriteConvert::SetReadData(uint8_t *data, size_t size)
79 * \brief Sets the internal pointer to the caller's inData
80 * image representation, WITHOUT COPYING THE DATA.
81 * - 'image' Pixels are presented as C-like 2D arrays : line per line.
82 * - 'volume'Pixels are presented as C-like 3D arrays : plane per plane
83 * \warning Since the pixels are not copied, it is the caller's responsability
84 * not to deallocate its data before gdcm uses them (e.g. with
85 * the Write() method )
86 * @param data data (uint8_t is for prototyping. if your data is not uint8_t
87 * just cast the pointer for calling the method)
88 * @param size size, in bytes.
90 void PixelWriteConvert::SetUserData(uint8_t *data, size_t size)
97 * \brief Get Data (UserData or ReadData)
98 * @return data (uint8_t is for prototyping. if your data is *not* uint8_t
99 * just cast the returned pointer)
101 uint8_t *PixelWriteConvert::GetData()
114 * \brief Get Data Size (UserData or ReadData)
115 * @return size, in bytes.
117 size_t PixelWriteConvert::GetDataSize()
130 typedef std::pair<size_t, uint32_t> JpegPair; //offset, jpeg size
131 typedef std::vector<JpegPair> JpegVector;
133 bool gdcm_write_JPEG2000_file (std::ostream *of, char *inputdata, size_t inputlength,
134 int image_width, int image_height, int numZ, int sample_pixel, int bitsallocated,
135 int sign, int quality);
138 void WriteDICOMItems(std::ostream *fp, JpegVector &v)
141 uint16_t group = 0xfffe;
142 uint16_t elem = 0xe000;
143 GDCM_NAME_SPACE::binary_write(*fp, group);
144 GDCM_NAME_SPACE::binary_write(*fp, elem);
146 uint32_t dummy = 0x12345678;
147 size_t offset = fp->tellp();
151 GDCM_NAME_SPACE::binary_write(*fp, dummy);
155 void EncodeWithoutBasicOffsetTable(std::ostream *fp, int numFrag)// JpegVector& v) //, uint32_t length)
157 assert( numFrag == 1);
160 uint16_t group = 0xfffe;
161 uint16_t elem = 0xe000;
162 GDCM_NAME_SPACE::binary_write(*fp, group);
163 GDCM_NAME_SPACE::binary_write(*fp, elem);
165 uint32_t item_length = 0x0000;
166 GDCM_NAME_SPACE::binary_write(*fp, item_length);
171 void EncodeWithBasicOffsetTable(std::ostream *fp, int numFrag, size_t &start)
174 uint16_t group = 0xfffe;
175 uint16_t elem = 0xe000;
176 GDCM_NAME_SPACE::binary_write(*fp, group);
177 GDCM_NAME_SPACE::binary_write(*fp, elem);
179 uint32_t item_length = numFrag*4; // sizeof(uint32_t)
180 GDCM_NAME_SPACE::binary_write(*fp, item_length);
182 // Just prepare the space
183 start = fp->tellp(); //to be able to rewind
184 for(int i=0; i<numFrag;++i)
186 uint32_t dummy = 0x0000;
187 GDCM_NAME_SPACE::binary_write(*fp, dummy);
191 void UpdateBasicOffsetTable(std::ostream *fp, JpegVector const &v, size_t pos)
193 JpegVector::const_iterator i;
195 const JpegPair &first = v[0];
196 for(i=v.begin(); i!=v.end(); ++i)
198 const JpegPair &jp = *i;
199 if(i == v.begin() ){ assert( jp.first - first.first == 0); }
200 uint32_t offset = (uint32_t)(jp.first - first.first);
201 GDCM_NAME_SPACE::binary_write(*fp, offset);
202 //std::cerr << "Updating Table:" << jp.first - first.first << std::endl;
206 void UpdateJpegFragmentSize(std::ostream *fp, JpegVector const &v)
208 JpegVector::const_iterator i;
209 for(i= v.begin(); i!=v.end(); ++i)
211 const JpegPair &jp = *i;
212 fp->seekp( jp.first );
213 uint32_t length = jp.second;
214 GDCM_NAME_SPACE::binary_write(*fp, length);
215 //std::cerr << "Updating:" << jp.first << "," << jp.second << std::endl;
219 void CloseJpeg(std::ostream *fp, JpegVector &v)
221 // sequence terminator
222 uint16_t group = 0xfffe;
223 uint16_t elem = 0xe0dd;
224 GDCM_NAME_SPACE::binary_write(*fp, group);
225 GDCM_NAME_SPACE::binary_write(*fp, elem);
227 uint32_t length = 0x0;
228 GDCM_NAME_SPACE::binary_write(*fp, length);
230 // Jpeg is done, now update the frag length
231 UpdateJpegFragmentSize(fp, v);
234 // I need to pass the File*. I do not understand how PixelWriteConvert is supposed
235 // to access this information otherwise
236 // size can now be computed from File attributes (what an API...)
237 void PixelWriteConvert::SetCompressJPEG2000UserData(uint8_t *data, size_t size, File *image)
240 //char * userData = reinterpret_cast<char*>(UserData);
242 std::ostringstream *of = new std::ostringstream();
243 int xsize = image->GetXSize();
244 int ysize = image->GetYSize();
245 int zsize = image->GetZSize();
246 int samplesPerPixel = image->GetSamplesPerPixel();
247 //std::cout << "X: " << xsize << std::endl;
248 //std::cout << "Y: " << ysize << std::endl;
249 //std::cout << "Sample: " << samplesPerPixel << std::endl;
250 int bitsallocated = image->GetBitsAllocated();
251 int sign = image->IsSignedPixelData();
252 unsigned int fragment_size = xsize*ysize*samplesPerPixel * (bitsallocated / 8);
253 //assert( fragment_size*zsize == size );
255 gdcmDebugMacro("fragment_size " << fragment_size << " zsize " << zsize << " size " << size);
256 assert( abs((long)(fragment_size*zsize-size)) <=1 );
258 JpegVector JpegFragmentSize;
259 gdcmDebugMacro("Call Encode..BasicOffsetTable " );
261 size_t bots; //basic offset table start
262 EncodeWithBasicOffsetTable(of, zsize, bots);
264 EncodeWithoutBasicOffsetTable(of, 1);
267 gdcmDebugMacro("Out of Encode..BasicOffsetTable " );
269 uint8_t *pImageData = data;
270 for(int i=0; i<zsize;i++)
272 gdcmDebugMacro("Write fragment no " << i );
273 WriteDICOMItems(of, JpegFragmentSize);
274 size_t beg = of->tellp();
275 gdcm_write_JPEG2000_file(of, (char*)pImageData,size,
276 image->GetXSize(), image->GetYSize(), image->GetZSize(), image->GetSamplesPerPixel(),
277 image->GetBitsAllocated(), sign, 100);
278 //userData, UserDataSize);
279 // CreateOneFrame(of, pImageData, fragment_size, xsize, ysize, zsize,
280 // samplesPerPixel, quality, JpegFragmentSize);
281 //assert( !(fragment_size % 2) );
282 // Update the JpegVector with offset
283 size_t end = of->tellp();
285 JpegPair &jp = JpegFragmentSize[i];
286 jp.second = (uint32_t)(end-beg);
287 if( ((end-beg) % 2) )
292 assert( !(jp.second % 2) );
293 //std::cerr << "DIFF: " << i <<" -> " << jp.second << std::endl;
295 pImageData += fragment_size;
297 CloseJpeg(of, JpegFragmentSize);
299 UpdateBasicOffsetTable(of, JpegFragmentSize, bots);
303 size_t of_size = of->str().size();
304 UserData = new uint8_t[of_size];
305 memcpy(UserData, of->str().c_str(), of_size);
306 UserDataSize = of_size;
310 bool gdcm_write_JPEG_file8 (std::ostream *fp, char *inputdata, size_t inputlength,
311 int image_width, int image_height, int numZ,
312 int sample_pixel, int bitsallocated, int quality);
313 bool gdcm_write_JPEG_file12 (std::ostream *fp, char *inputdata, size_t inputlength,
314 int image_width, int image_height, int numZ,
315 int sample_pixel, int bitsallocated, int quality);
316 bool gdcm_write_JPEG_file16 (std::ostream *fp, char *inputdata, size_t inputlength,
317 int image_width, int image_height, int numZ,
318 int sample_pixel, int bitsallocated, int quality);
320 void PixelWriteConvert::SetCompressJPEGUserData(uint8_t *data, size_t size, File *image)
326 //char * userData = reinterpret_cast<char*>(UserData);
328 std::ostringstream *of = new std::ostringstream();
329 int xsize = image->GetXSize();
330 int ysize = image->GetYSize();
331 int zsize = image->GetZSize();
333 int samplesPerPixel = image->GetSamplesPerPixel();
334 int bitsAllocated = image->GetBitsAllocated();
335 int bitsStored = image->GetBitsStored();
337 //std::cout << "X: " << xsize << std::endl;
338 //std::cout << "Y: " << ysize << std::endl;
339 //std::cout << "Sample: " << samplesPerPixel << std::endl;
341 gdcmDebugMacro( "bitsAllocated " << bitsAllocated << " bitsStored " <<bitsStored <<
345 unsigned int fragment_size = xsize*ysize*samplesPerPixel * (bitsAllocated / 8);
346 gdcmDebugMacro("fragment_size " << fragment_size << " zsize " << zsize << " size " << size);
347 assert( abs((long)(fragment_size*zsize-size)) <=1 );
349 JpegVector JpegFragmentSize;
351 size_t bots; //basic offset table start
352 EncodeWithBasicOffsetTable(of, zsize, bots);
354 EncodeWithoutBasicOffsetTable(of, 1);
357 // to avoid major troubles when BitsStored == 8 && BitsAllocated==16 !
359 if (bitsStored == 8 && bitsAllocated == 16)
362 uint8_t *pImageData = data;
363 for(int i=0; i<zsize;i++)
365 gdcmDebugMacro("Write fragment no " << i );
366 WriteDICOMItems(of, JpegFragmentSize);
367 size_t beg = of->tellp();
368 if( bitsStored == 8 )
370 gdcm_write_JPEG_file8(of, (char*)pImageData,size,
371 xsize, ysize, zsize, samplesPerPixel,
372 bitsAllocated, 100 );
374 else if (bitsStored <= 12)
376 assert( bitsStored >= 8 );
377 gdcm_write_JPEG_file12(of, (char*)pImageData, size,
378 xsize, ysize, zsize, samplesPerPixel,
381 else if (bitsStored <= 16)
383 assert( bitsStored >= 12 );
384 gdcm_write_JPEG_file16(of, (char*)pImageData,size,
385 xsize, ysize, zsize, samplesPerPixel,
388 else if (bitsStored <= 32) // if we are lucky (?), it will compress as a 2 bytes stream
389 { // (Actually it doesn't !)
390 // Just to allow ctest not to abort on 32bits per pixel image RTDOSE.dcm
391 assert( bitsStored >= 16 );
392 gdcmDebugMacro( "Warning : bitsStored>16 not supported by JPEG !" );
393 gdcm_write_JPEG_file16(of, (char*)pImageData, size,
394 xsize, ysize, zsize, samplesPerPixel,
399 std::cerr << "Major pb : bitsStored =" << bitsStored << std::endl;
402 size_t end = of->tellp();
404 JpegPair &jp = JpegFragmentSize[i];
405 jp.second = (uint32_t)(end-beg);
406 if( ((end-beg) % 2) )
411 assert( !(jp.second % 2) );
412 //std::cerr << "DIFF: " << i <<" -> " << jp.second << std::endl;
415 //JpegPair &jp = v[0];
418 //userData, UserDataSize);
419 // CreateOneFrame(of, pImageData, fragment_size, xsize, ysize, zsize,
420 // samplesPerPixel, quality, JpegFragmentSize);
421 //assert( !(fragment_size % 2) );
422 pImageData += fragment_size;
424 CloseJpeg(of, JpegFragmentSize);
426 UpdateBasicOffsetTable(of, JpegFragmentSize, bots);
429 size_t of_size = of->str().size();
430 UserData = new uint8_t[of_size];
431 memcpy(UserData, of->str().c_str(), of_size);
432 UserDataSize = of_size;
437 //-----------------------------------------------------------------------------
439 //bool PixelWriteConvert::CompressJPEG2000(uint8_t *data, size_t size)
443 //-----------------------------------------------------------------------------
446 //-----------------------------------------------------------------------------
449 //-----------------------------------------------------------------------------
450 } // end namespace gdcm