Mercurial > hg > orthanc-wsi
annotate Framework/Inputs/HierarchicalTiff.cpp @ 142:faabdb7f2003
fix
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 17 Apr 2018 20:16:12 +0200 |
parents | a0f9a3df1110 |
children | 6b8ccfc02051 |
rev | line source |
---|---|
0 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
115
a51dee6a1515
upgrade to year 2018
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
98
diff
changeset
|
5 * Copyright (C) 2017-2018 Osimis S.A., Belgium |
0 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
16
7a88c614be04
preparing for precompiled headers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
22 #include "../PrecompiledHeadersWSI.h" |
0 | 23 #include "HierarchicalTiff.h" |
24 | |
140
a0f9a3df1110
resort to Orthanc framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
115
diff
changeset
|
25 #include <Core/Logging.h> |
a0f9a3df1110
resort to Orthanc framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
115
diff
changeset
|
26 #include <Core/OrthancException.h> |
0 | 27 |
28 #include <iostream> | |
29 #include <algorithm> | |
30 #include <cassert> | |
31 #include <string.h> | |
32 | |
33 namespace OrthancWSI | |
34 { | |
35 HierarchicalTiff::Level::Level(TIFF* tiff, | |
36 tdir_t directory, | |
37 unsigned int width, | |
38 unsigned int height) : | |
39 directory_(directory), | |
40 width_(width), | |
41 height_(height) | |
42 { | |
43 // Read the JPEG headers shared at that level, if any | |
44 uint8_t *tables = NULL; | |
45 uint32_t size; | |
46 if (TIFFGetField(tiff, TIFFTAG_JPEGTABLES, &size, &tables) && | |
47 size > 0 && | |
48 tables != NULL) | |
49 { | |
50 // Look for the EOI (end-of-image) tag == FF D9 | |
51 // https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format | |
52 | |
53 bool found = false; | |
54 | |
55 for (size_t i = 0; i + 1 < size; i++) | |
56 { | |
57 if (tables[i] == 0xff && | |
58 tables[i + 1] == 0xd9) | |
59 { | |
60 headers_.assign(reinterpret_cast<const char*>(tables), i); | |
61 found = true; | |
62 } | |
63 } | |
64 | |
65 if (!found) | |
66 { | |
67 headers_.assign(reinterpret_cast<const char*>(tables), size); | |
68 } | |
69 } | |
70 } | |
71 | |
72 struct HierarchicalTiff::Comparator | |
73 { | |
74 bool operator() (const HierarchicalTiff::Level& a, | |
75 const HierarchicalTiff::Level& b) const | |
76 { | |
77 return a.width_ > b.width_; | |
78 } | |
79 }; | |
80 | |
81 | |
82 void HierarchicalTiff::Finalize() | |
83 { | |
84 if (tiff_) | |
85 { | |
86 TIFFClose(tiff_); | |
87 tiff_ = NULL; | |
88 } | |
89 } | |
90 | |
91 | |
92 void HierarchicalTiff::CheckLevel(unsigned int level) const | |
93 { | |
94 if (level >= levels_.size()) | |
95 { | |
96 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
97 } | |
98 } | |
99 | |
100 | |
101 bool HierarchicalTiff::GetCurrentCompression(ImageCompression& compression) | |
102 { | |
103 uint16_t c; | |
104 if (!TIFFGetField(tiff_, TIFFTAG_COMPRESSION, &c)) | |
105 { | |
106 return false; | |
107 } | |
108 | |
109 switch (c) | |
110 { | |
111 case COMPRESSION_NONE: | |
112 compression = ImageCompression_None; | |
113 return true; | |
114 | |
115 case COMPRESSION_JPEG: | |
116 compression = ImageCompression_Jpeg; | |
117 return true; | |
118 | |
119 default: | |
120 return false; | |
121 } | |
122 } | |
123 | |
124 | |
125 bool HierarchicalTiff::GetCurrentPixelFormat(Orthanc::PixelFormat& pixelFormat, | |
126 ImageCompression compression) | |
127 { | |
128 // http://www.awaresystems.be/imaging/tiff/tifftags/baseline.html | |
129 | |
130 uint16_t channels, photometric, bpp, planar; | |
131 if (!TIFFGetField(tiff_, TIFFTAG_SAMPLESPERPIXEL, &channels) || | |
132 channels == 0 || | |
133 !TIFFGetField(tiff_, TIFFTAG_PHOTOMETRIC, &photometric) || | |
134 !TIFFGetField(tiff_, TIFFTAG_BITSPERSAMPLE, &bpp) || | |
135 !TIFFGetField(tiff_, TIFFTAG_PLANARCONFIG, &planar)) | |
136 { | |
137 return false; | |
138 } | |
139 | |
140 if (compression == ImageCompression_Jpeg && | |
141 channels == 3 && // This is a color image | |
142 bpp == 8 && | |
143 photometric == PHOTOMETRIC_YCBCR && | |
144 planar == PLANARCONFIG_CONTIG) // This is interleaved RGB | |
145 { | |
146 pixelFormat = Orthanc::PixelFormat_RGB24; | |
147 } | |
148 else | |
149 { | |
150 return false; | |
151 } | |
152 | |
153 return true; | |
154 } | |
155 | |
156 | |
157 bool HierarchicalTiff::Initialize() | |
158 { | |
159 bool first = true; | |
160 tdir_t pos = 0; | |
161 | |
162 do | |
163 { | |
164 uint32_t w, h, tw, th; | |
165 ImageCompression compression; | |
166 Orthanc::PixelFormat pixelFormat; | |
167 | |
168 if (TIFFSetDirectory(tiff_, pos) && | |
169 TIFFGetField(tiff_, TIFFTAG_IMAGEWIDTH, &w) && | |
170 TIFFGetField(tiff_, TIFFTAG_IMAGELENGTH, &h) && | |
171 TIFFGetField(tiff_, TIFFTAG_TILEWIDTH, &tw) && | |
172 TIFFGetField(tiff_, TIFFTAG_TILELENGTH, &th) && | |
173 w > 0 && | |
174 h > 0 && | |
175 tw > 0 && | |
176 th > 0 && | |
177 GetCurrentCompression(compression) && | |
178 GetCurrentPixelFormat(pixelFormat, compression)) | |
179 { | |
180 if (first) | |
181 { | |
182 tileWidth_ = tw; | |
183 tileHeight_ = th; | |
184 compression_ = compression; | |
185 pixelFormat_ = pixelFormat; | |
186 first = false; | |
187 } | |
188 else if (tw != tileWidth_ || | |
189 th != tileHeight_ || | |
190 compression_ != compression || | |
191 pixelFormat_ != pixelFormat) | |
192 { | |
193 LOG(ERROR) << "The tile size or compression of the TIFF file varies along levels, this is not supported"; | |
194 return false; | |
195 } | |
196 | |
197 levels_.push_back(Level(tiff_, pos, w, h)); | |
198 } | |
199 | |
200 pos++; | |
201 } | |
202 while (TIFFReadDirectory(tiff_)); | |
203 | |
204 if (levels_.size() == 0) | |
205 { | |
206 LOG(ERROR) << "This is not a tiled TIFF image"; | |
207 return false; | |
208 } | |
209 | |
210 std::sort(levels_.begin(), levels_.end(), Comparator()); | |
211 return true; | |
212 } | |
213 | |
214 | |
215 HierarchicalTiff::HierarchicalTiff(const std::string& path) : | |
216 tileWidth_(0), | |
217 tileHeight_(0) | |
218 { | |
219 tiff_ = TIFFOpen(path.c_str(), "r"); | |
220 if (tiff_ == NULL) | |
221 { | |
222 LOG(ERROR) << "libtiff cannot open: " << path; | |
223 throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentFile); | |
224 } | |
225 | |
226 if (!Initialize()) | |
227 { | |
228 Finalize(); | |
229 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
230 } | |
231 } | |
232 | |
233 | |
234 unsigned int HierarchicalTiff::GetLevelWidth(unsigned int level) const | |
235 { | |
236 CheckLevel(level); | |
237 return levels_[level].width_; | |
238 } | |
239 | |
240 | |
241 unsigned int HierarchicalTiff::GetLevelHeight(unsigned int level) const | |
242 { | |
243 CheckLevel(level); | |
244 return levels_[level].height_; | |
245 } | |
246 | |
247 | |
248 bool HierarchicalTiff::ReadRawTile(std::string& tile, | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
16
diff
changeset
|
249 ImageCompression& compression, |
0 | 250 unsigned int level, |
251 unsigned int tileX, | |
252 unsigned int tileY) | |
253 { | |
254 boost::mutex::scoped_lock lock(mutex_); | |
255 | |
256 CheckLevel(level); | |
257 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
16
diff
changeset
|
258 compression = compression_; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
16
diff
changeset
|
259 |
0 | 260 // Make the TIFF context point to the level of interest |
261 if (!TIFFSetDirectory(tiff_, levels_[level].directory_)) | |
262 { | |
263 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); | |
264 } | |
265 | |
266 // Get the index of the tile | |
267 ttile_t index = TIFFComputeTile(tiff_, tileX * tileWidth_, tileY * tileHeight_, 0 /*z*/, 0 /*sample*/); | |
268 | |
269 // Read the raw tile | |
270 toff_t *sizes; | |
271 if (!TIFFGetField(tiff_, TIFFTAG_TILEBYTECOUNTS, &sizes)) | |
272 { | |
273 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); | |
274 } | |
275 | |
276 std::string raw; | |
277 raw.resize(sizes[index]); | |
278 | |
279 tsize_t read = TIFFReadRawTile(tiff_, index, &raw[0], raw.size()); | |
280 if (read != static_cast<tsize_t>(sizes[index])) | |
281 { | |
282 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); | |
283 } | |
284 | |
285 const std::string& headers = levels_[level].headers_; | |
286 | |
287 // Possibly prepend the raw tile with the shared JPEG headers | |
288 if (headers.empty() || | |
289 compression_ != ImageCompression_Jpeg) | |
290 { | |
291 tile.swap(raw); // Same as "tile.assign(raw)", but optimizes memory | |
292 } | |
293 else | |
294 { | |
295 assert(compression_ == ImageCompression_Jpeg); | |
296 | |
297 // Check that the raw JPEG tile starts with the SOI (start-of-image) tag == FF D8 | |
298 if (raw.size() < 2 || | |
299 static_cast<uint8_t>(raw[0]) != 0xff || | |
300 static_cast<uint8_t>(raw[1]) != 0xd8) | |
301 { | |
302 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); | |
303 } | |
304 | |
305 tile.resize(headers.size() + raw.size() - 2); | |
306 memcpy(&tile[0], &headers[0], headers.size()); | |
307 memcpy(&tile[0] + headers.size(), &raw[2], raw.size() - 2); | |
308 } | |
309 | |
310 return true; | |
311 } | |
312 } |