comparison Framework/Inputs/HierarchicalTiff.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 "HierarchicalTiff.h"
22
23 #include "../Orthanc/Core/Logging.h"
24 #include "../Orthanc/Core/OrthancException.h"
25
26 #include <iostream>
27 #include <algorithm>
28 #include <cassert>
29 #include <string.h>
30
31 namespace OrthancWSI
32 {
33 HierarchicalTiff::Level::Level(TIFF* tiff,
34 tdir_t directory,
35 unsigned int width,
36 unsigned int height) :
37 directory_(directory),
38 width_(width),
39 height_(height)
40 {
41 // Read the JPEG headers shared at that level, if any
42 uint8_t *tables = NULL;
43 uint32_t size;
44 if (TIFFGetField(tiff, TIFFTAG_JPEGTABLES, &size, &tables) &&
45 size > 0 &&
46 tables != NULL)
47 {
48 // Look for the EOI (end-of-image) tag == FF D9
49 // https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format
50
51 bool found = false;
52
53 for (size_t i = 0; i + 1 < size; i++)
54 {
55 if (tables[i] == 0xff &&
56 tables[i + 1] == 0xd9)
57 {
58 headers_.assign(reinterpret_cast<const char*>(tables), i);
59 found = true;
60 }
61 }
62
63 if (!found)
64 {
65 headers_.assign(reinterpret_cast<const char*>(tables), size);
66 }
67 }
68 }
69
70 struct HierarchicalTiff::Comparator
71 {
72 bool operator() (const HierarchicalTiff::Level& a,
73 const HierarchicalTiff::Level& b) const
74 {
75 return a.width_ > b.width_;
76 }
77 };
78
79
80 void HierarchicalTiff::Finalize()
81 {
82 if (tiff_)
83 {
84 TIFFClose(tiff_);
85 tiff_ = NULL;
86 }
87 }
88
89
90 void HierarchicalTiff::CheckLevel(unsigned int level) const
91 {
92 if (level >= levels_.size())
93 {
94 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
95 }
96 }
97
98
99 bool HierarchicalTiff::GetCurrentCompression(ImageCompression& compression)
100 {
101 uint16_t c;
102 if (!TIFFGetField(tiff_, TIFFTAG_COMPRESSION, &c))
103 {
104 return false;
105 }
106
107 switch (c)
108 {
109 case COMPRESSION_NONE:
110 compression = ImageCompression_None;
111 return true;
112
113 case COMPRESSION_JPEG:
114 compression = ImageCompression_Jpeg;
115 return true;
116
117 default:
118 return false;
119 }
120 }
121
122
123 bool HierarchicalTiff::GetCurrentPixelFormat(Orthanc::PixelFormat& pixelFormat,
124 ImageCompression compression)
125 {
126 // http://www.awaresystems.be/imaging/tiff/tifftags/baseline.html
127
128 uint16_t channels, photometric, bpp, planar;
129 if (!TIFFGetField(tiff_, TIFFTAG_SAMPLESPERPIXEL, &channels) ||
130 channels == 0 ||
131 !TIFFGetField(tiff_, TIFFTAG_PHOTOMETRIC, &photometric) ||
132 !TIFFGetField(tiff_, TIFFTAG_BITSPERSAMPLE, &bpp) ||
133 !TIFFGetField(tiff_, TIFFTAG_PLANARCONFIG, &planar))
134 {
135 return false;
136 }
137
138 if (compression == ImageCompression_Jpeg &&
139 channels == 3 && // This is a color image
140 bpp == 8 &&
141 photometric == PHOTOMETRIC_YCBCR &&
142 planar == PLANARCONFIG_CONTIG) // This is interleaved RGB
143 {
144 pixelFormat = Orthanc::PixelFormat_RGB24;
145 }
146 else
147 {
148 return false;
149 }
150
151 return true;
152 }
153
154
155 bool HierarchicalTiff::Initialize()
156 {
157 bool first = true;
158 tdir_t pos = 0;
159
160 do
161 {
162 uint32_t w, h, tw, th;
163 ImageCompression compression;
164 Orthanc::PixelFormat pixelFormat;
165
166 if (TIFFSetDirectory(tiff_, pos) &&
167 TIFFGetField(tiff_, TIFFTAG_IMAGEWIDTH, &w) &&
168 TIFFGetField(tiff_, TIFFTAG_IMAGELENGTH, &h) &&
169 TIFFGetField(tiff_, TIFFTAG_TILEWIDTH, &tw) &&
170 TIFFGetField(tiff_, TIFFTAG_TILELENGTH, &th) &&
171 w > 0 &&
172 h > 0 &&
173 tw > 0 &&
174 th > 0 &&
175 GetCurrentCompression(compression) &&
176 GetCurrentPixelFormat(pixelFormat, compression))
177 {
178 if (first)
179 {
180 tileWidth_ = tw;
181 tileHeight_ = th;
182 compression_ = compression;
183 pixelFormat_ = pixelFormat;
184 first = false;
185 }
186 else if (tw != tileWidth_ ||
187 th != tileHeight_ ||
188 compression_ != compression ||
189 pixelFormat_ != pixelFormat)
190 {
191 LOG(ERROR) << "The tile size or compression of the TIFF file varies along levels, this is not supported";
192 return false;
193 }
194
195 levels_.push_back(Level(tiff_, pos, w, h));
196 }
197
198 pos++;
199 }
200 while (TIFFReadDirectory(tiff_));
201
202 if (levels_.size() == 0)
203 {
204 LOG(ERROR) << "This is not a tiled TIFF image";
205 return false;
206 }
207
208 std::sort(levels_.begin(), levels_.end(), Comparator());
209 return true;
210 }
211
212
213 HierarchicalTiff::HierarchicalTiff(const std::string& path) :
214 tileWidth_(0),
215 tileHeight_(0)
216 {
217 tiff_ = TIFFOpen(path.c_str(), "r");
218 if (tiff_ == NULL)
219 {
220 LOG(ERROR) << "libtiff cannot open: " << path;
221 throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentFile);
222 }
223
224 if (!Initialize())
225 {
226 Finalize();
227 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
228 }
229 }
230
231
232 unsigned int HierarchicalTiff::GetLevelWidth(unsigned int level) const
233 {
234 CheckLevel(level);
235 return levels_[level].width_;
236 }
237
238
239 unsigned int HierarchicalTiff::GetLevelHeight(unsigned int level) const
240 {
241 CheckLevel(level);
242 return levels_[level].height_;
243 }
244
245
246 bool HierarchicalTiff::ReadRawTile(std::string& tile,
247 unsigned int level,
248 unsigned int tileX,
249 unsigned int tileY)
250 {
251 boost::mutex::scoped_lock lock(mutex_);
252
253 CheckLevel(level);
254
255 // Make the TIFF context point to the level of interest
256 if (!TIFFSetDirectory(tiff_, levels_[level].directory_))
257 {
258 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
259 }
260
261 // Get the index of the tile
262 ttile_t index = TIFFComputeTile(tiff_, tileX * tileWidth_, tileY * tileHeight_, 0 /*z*/, 0 /*sample*/);
263
264 // Read the raw tile
265 toff_t *sizes;
266 if (!TIFFGetField(tiff_, TIFFTAG_TILEBYTECOUNTS, &sizes))
267 {
268 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
269 }
270
271 std::string raw;
272 raw.resize(sizes[index]);
273
274 tsize_t read = TIFFReadRawTile(tiff_, index, &raw[0], raw.size());
275 if (read != static_cast<tsize_t>(sizes[index]))
276 {
277 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
278 }
279
280 const std::string& headers = levels_[level].headers_;
281
282 // Possibly prepend the raw tile with the shared JPEG headers
283 if (headers.empty() ||
284 compression_ != ImageCompression_Jpeg)
285 {
286 tile.swap(raw); // Same as "tile.assign(raw)", but optimizes memory
287 }
288 else
289 {
290 assert(compression_ == ImageCompression_Jpeg);
291
292 // Check that the raw JPEG tile starts with the SOI (start-of-image) tag == FF D8
293 if (raw.size() < 2 ||
294 static_cast<uint8_t>(raw[0]) != 0xff ||
295 static_cast<uint8_t>(raw[1]) != 0xd8)
296 {
297 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
298 }
299
300 tile.resize(headers.size() + raw.size() - 2);
301 memcpy(&tile[0], &headers[0], headers.size());
302 memcpy(&tile[0] + headers.size(), &raw[2], raw.size() - 2);
303 }
304
305 return true;
306 }
307 }