comparison Framework/Outputs/HierarchicalTiffWriter.cpp @ 0:4a7a53257c7d

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 22 Oct 2016 21:48:33 +0200
parents
children 7a88c614be04
comparison
equal deleted inserted replaced
-1:000000000000 0:4a7a53257c7d
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU Affero General Public License
8 * as published by the Free Software Foundation, either version 3 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20
21 #include "HierarchicalTiffWriter.h"
22
23 #include "../Orthanc/Core/Logging.h"
24 #include "../Orthanc/Core/OrthancException.h"
25 #include "../Orthanc/Core/Uuid.h"
26
27 namespace OrthancWSI
28 {
29 class HierarchicalTiffWriter::PendingTile
30 {
31 private:
32 HierarchicalTiffWriter& that_;
33 unsigned int level_;
34 unsigned int tileX_;
35 unsigned int tileY_;
36 Orthanc::Toolbox::TemporaryFile file_;
37
38 public:
39 PendingTile(HierarchicalTiffWriter& that,
40 unsigned int level,
41 unsigned int tileX,
42 unsigned int tileY,
43 const std::string& tile) :
44 that_(that),
45 level_(level),
46 tileX_(tileX),
47 tileY_(tileY)
48 {
49 file_.Write(tile);
50 }
51
52 unsigned int GetLevel() const
53 {
54 return level_;
55 }
56
57 unsigned int GetTileX() const
58 {
59 return tileX_;
60 }
61
62 unsigned int GetTileY() const
63 {
64 return tileY_;
65 }
66
67 void Store(TIFF* tiff)
68 {
69 std::string tile;
70 file_.Read(tile);
71 that_.StoreTile(tile, tileX_, tileY_);
72 }
73 };
74
75
76 struct HierarchicalTiffWriter::Comparator
77 {
78 inline bool operator() (PendingTile* const& a,
79 PendingTile* const& b)
80 {
81 if (a->GetLevel() < b->GetLevel())
82 {
83 return true;
84 }
85
86 if (a->GetLevel() > b->GetLevel())
87 {
88 return false;
89 }
90
91 if (a->GetTileY() < b->GetTileY())
92 {
93 return true;
94 }
95
96 if (a->GetTileY() > b->GetTileY())
97 {
98 return false;
99 }
100
101 return a->GetTileX() < b->GetTileX();
102 }
103 };
104
105
106 static uint8_t GetUint8(const std::string& tile,
107 size_t index)
108 {
109 if (index >= tile.size())
110 {
111 return 0;
112 }
113 else
114 {
115 return static_cast<uint8_t>(tile[index]);
116 }
117 }
118
119
120 #if 0
121 static uint16_t GetUint16(const std::string& tile,
122 size_t index)
123 {
124 if (index + 1 >= tile.size())
125 {
126 return 0;
127 }
128 else
129 {
130 return static_cast<uint16_t>(tile[index]) * 256 + static_cast<uint16_t>(tile[index + 1]);
131 }
132 }
133 #endif
134
135
136 static void CheckJpegTile(const std::string& tile,
137 Orthanc::PixelFormat pixelFormat)
138 {
139 // Check the sampling by accessing the "Start of Frame" header of JPEG
140
141 if (tile.size() < 3 ||
142 static_cast<uint8_t>(tile[0]) != 0xff ||
143 static_cast<uint8_t>(tile[1]) != 0xd8 ||
144 static_cast<uint8_t>(tile[2]) != 0xff)
145 {
146 LOG(WARNING) << "The source image does not contain JPEG tiles";
147 return;
148 }
149
150 // Look for the "Start of Frame" header (FF C0)
151 for (size_t i = 2; i + 1 < tile.size(); i++)
152 {
153 if (static_cast<uint8_t>(tile[i]) == 0xff &&
154 static_cast<uint8_t>(tile[i + 1]) == 0xc0)
155 {
156 uint8_t numberOfComponents = GetUint8(tile, i + 9);
157
158 switch (pixelFormat)
159 {
160 case Orthanc::PixelFormat_Grayscale8:
161 if (numberOfComponents != 3)
162 {
163 LOG(WARNING) << "The source image does not contain a grayscale image as expected";
164 }
165 break;
166
167 case Orthanc::PixelFormat_RGB24:
168 {
169 if (numberOfComponents != 3)
170 {
171 LOG(WARNING) << "The source image does not contain a RGB24 color image as expected";
172 }
173
174 // Read the header corresponding to the first component
175 const size_t component = 0;
176 const size_t offset = i + 10 + component * 3;
177 const uint8_t sampling = GetUint8(tile, offset + 1);
178 const int samplingH = sampling / 16;
179 const int samplingV = sampling % 16;
180
181 LOG(WARNING) << "The source image uses chroma sampling " << samplingH << ":" << samplingV;
182
183 if (samplingH != 2 ||
184 samplingV != 2)
185 {
186 LOG(WARNING) << "The source image has not a chroma sampling of 2:2, "
187 << "you should consider using option \"--reencode\"";
188 }
189
190 break;
191 }
192
193 default:
194 break;
195 }
196 }
197 }
198 }
199
200
201
202 void HierarchicalTiffWriter::StoreTile(const std::string& tile,
203 unsigned int tileX,
204 unsigned int tileY)
205 {
206 // Get the index of the tile
207 ttile_t index = TIFFComputeTile(tiff_, tileX * GetTileWidth(), tileY * GetTileHeight(), 0 /*z*/, 0 /*sample*/);
208
209 if (TIFFWriteRawTile(tiff_, index, tile.size() ? const_cast<char*>(&tile[0]) : NULL, tile.size()) !=
210 static_cast<tsize_t>(tile.size()))
211 {
212 throw Orthanc::OrthancException(Orthanc::ErrorCode_CannotWriteFile);
213 }
214
215 if (isFirst_ &&
216 GetImageCompression() == ImageCompression_Jpeg)
217 {
218 CheckJpegTile(tile, GetPixelFormat());
219 }
220
221 isFirst_ = false;
222 }
223
224
225 void HierarchicalTiffWriter::ConfigureLevel(const Level& level,
226 bool createLevel)
227 {
228 if (createLevel && TIFFWriteDirectory(tiff_) != 1)
229 {
230 throw Orthanc::OrthancException(Orthanc::ErrorCode_CannotWriteFile);
231 }
232
233 if (TIFFFlush(tiff_) != 1)
234 {
235 throw Orthanc::OrthancException(Orthanc::ErrorCode_CannotWriteFile);
236 }
237
238 currentLevel_ = level.z_;
239 nextX_ = 0;
240 nextY_ = 0;
241
242 switch (GetImageCompression())
243 {
244 case ImageCompression_Jpeg:
245 {
246 uint16_t c = COMPRESSION_JPEG;
247
248 if (TIFFSetField(tiff_, TIFFTAG_COMPRESSION, c) != 1)
249 {
250 Close();
251 throw Orthanc::OrthancException(Orthanc::ErrorCode_CannotWriteFile);
252 }
253
254 break;
255 }
256
257 default:
258 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
259 }
260
261
262 switch (GetPixelFormat())
263 {
264 case Orthanc::PixelFormat_RGB24:
265 {
266 uint16_t samplesPerPixel = 3;
267 uint16_t photometric = PHOTOMETRIC_YCBCR;
268 uint16_t planar = PLANARCONFIG_CONTIG; // Interleaved RGB
269 uint16_t bpp = 8;
270 uint16_t subsampleHorizontal = 2;
271 uint16_t subsampleVertical = 2;
272
273 if (TIFFSetField(tiff_, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel) != 1 ||
274 TIFFSetField(tiff_, TIFFTAG_PHOTOMETRIC, photometric) != 1 ||
275 TIFFSetField(tiff_, TIFFTAG_BITSPERSAMPLE, bpp) != 1 ||
276 TIFFSetField(tiff_, TIFFTAG_PLANARCONFIG, planar) != 1 ||
277 TIFFSetField(tiff_, TIFFTAG_YCBCRSUBSAMPLING, subsampleHorizontal, subsampleVertical) != 1)
278 {
279 Close();
280 throw Orthanc::OrthancException(Orthanc::ErrorCode_CannotWriteFile);
281 }
282
283 break;
284 }
285
286 default:
287 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
288 }
289
290 uint32_t w = level.width_;
291 uint32_t h = level.height_;
292 uint32_t tw = GetTileWidth();
293 uint32_t th = GetTileHeight();
294
295 if (TIFFSetField(tiff_, TIFFTAG_IMAGEWIDTH, w) != 1 ||
296 TIFFSetField(tiff_, TIFFTAG_IMAGELENGTH, h) != 1 ||
297 TIFFSetField(tiff_, TIFFTAG_TILEWIDTH, tw) != 1 ||
298 TIFFSetField(tiff_, TIFFTAG_TILELENGTH, th) != 1)
299 {
300 throw Orthanc::OrthancException(Orthanc::ErrorCode_CannotWriteFile);
301 }
302 }
303
304
305 void HierarchicalTiffWriter::AdvanceToNextTile()
306 {
307 assert(currentLevel_ < levels_.size());
308
309 nextX_ += 1;
310 if (nextX_ >= levels_[currentLevel_].countTilesX_)
311 {
312 nextX_ = 0;
313 nextY_ += 1;
314
315 if (nextY_ >= levels_[currentLevel_].countTilesY_)
316 {
317 currentLevel_ += 1;
318
319 if (currentLevel_ < levels_.size())
320 {
321 ConfigureLevel(levels_[currentLevel_], true);
322 }
323 }
324 }
325 }
326
327
328 void HierarchicalTiffWriter::ScanPending()
329 {
330 std::sort(pending_.begin(), pending_.end(), Comparator());
331
332 while (currentLevel_ < levels_.size() &&
333 !pending_.empty() &&
334 pending_.front()->GetLevel() == currentLevel_ &&
335 pending_.front()->GetTileX() == nextX_ &&
336 pending_.front()->GetTileY() == nextY_)
337 {
338 pending_.front()->Store(tiff_);
339 delete pending_.front();
340 pending_.pop_front();
341 AdvanceToNextTile();
342 }
343 }
344
345
346 void HierarchicalTiffWriter::WriteRawTileInternal(const std::string& tile,
347 const Level& level,
348 unsigned int tileX,
349 unsigned int tileY)
350 {
351 boost::mutex::scoped_lock lock(mutex_);
352
353 if (level.z_ == currentLevel_ &&
354 tileX == nextX_ &&
355 tileY == nextY_)
356 {
357 StoreTile(tile, tileX, tileY);
358 AdvanceToNextTile();
359 ScanPending();
360 }
361 else
362 {
363 pending_.push_back(new PendingTile(*this, level.z_, tileX, tileY, tile));
364 }
365 }
366
367
368 void HierarchicalTiffWriter::AddLevelInternal(const Level& level)
369 {
370 if (level.z_ == 0)
371 {
372 // Configure the finest level on initialization
373 ConfigureLevel(level, false);
374 }
375
376 levels_.push_back(level);
377 }
378
379
380 HierarchicalTiffWriter::HierarchicalTiffWriter(const std::string& path,
381 Orthanc::PixelFormat pixelFormat,
382 ImageCompression compression,
383 unsigned int tileWidth,
384 unsigned int tileHeight) :
385 PyramidWriterBase(pixelFormat, compression, tileWidth, tileHeight),
386 currentLevel_(0),
387 isFirst_(true)
388 {
389 tiff_ = TIFFOpen(path.c_str(), "w");
390 if (tiff_ == NULL)
391 {
392 throw Orthanc::OrthancException(Orthanc::ErrorCode_CannotWriteFile);
393 }
394 }
395
396
397 HierarchicalTiffWriter::~HierarchicalTiffWriter()
398 {
399 if (pending_.size())
400 {
401 LOG(ERROR) << "Some tiles (" << pending_.size() << ") were not written to the TIFF file";
402 }
403
404 for (size_t i = 0; i < pending_.size(); i++)
405 {
406 if (pending_[i])
407 {
408 delete pending_[i];
409 }
410 }
411
412 Close();
413 }
414
415
416 void HierarchicalTiffWriter::Flush()
417 {
418 boost::mutex::scoped_lock lock(mutex_);
419 ScanPending();
420 }
421 }