Mercurial > hg > orthanc-wsi
comparison Applications/DicomToTiff.cpp @ 57:91fc9583b2de
big refactoring to support sparse tiling
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 24 Nov 2016 17:48:24 +0100 |
parents | 8e1dfd531335 |
children | 7a3853d51c45 |
comparison
equal
deleted
inserted
replaced
56:83cd735c885d | 57:91fc9583b2de |
---|---|
19 | 19 |
20 | 20 |
21 #include "../Framework/DicomToolbox.h" | 21 #include "../Framework/DicomToolbox.h" |
22 #include "../Framework/ImageToolbox.h" | 22 #include "../Framework/ImageToolbox.h" |
23 #include "../Framework/Inputs/DicomPyramid.h" | 23 #include "../Framework/Inputs/DicomPyramid.h" |
24 #include "../Framework/Inputs/TiledPyramidStatistics.h" | |
24 #include "../Framework/Messaging/CurlOrthancConnection.h" | 25 #include "../Framework/Messaging/CurlOrthancConnection.h" |
25 #include "../Framework/Orthanc/Core/Logging.h" | 26 #include "../Framework/Orthanc/Core/Logging.h" |
26 #include "../Framework/Orthanc/Core/OrthancException.h" | 27 #include "../Framework/Orthanc/Core/OrthancException.h" |
27 #include "../Framework/Outputs/HierarchicalTiffWriter.h" | 28 #include "../Framework/Outputs/HierarchicalTiffWriter.h" |
28 | 29 |
112 std::cout << std::endl | 113 std::cout << std::endl |
113 << "Usage: " << argv[0] << " [OPTION]... [INPUT] [OUTPUT]" | 114 << "Usage: " << argv[0] << " [OPTION]... [INPUT] [OUTPUT]" |
114 << std::endl | 115 << std::endl |
115 << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research." | 116 << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research." |
116 << std::endl << std::endl | 117 << std::endl << std::endl |
117 << "Convert a DICOM for digital pathology stored in some Orthanc server as a standard hierarchical TIFF." | 118 << "Convert a DICOM image for digital pathology stored in some Orthanc server as a" << std::endl |
119 << "standard hierarchical TIFF (whose tiles are all encoded using JPEG)." | |
118 << std::endl; | 120 << std::endl; |
119 | 121 |
120 std::cout << allWithoutHidden << "\n"; | 122 std::cout << allWithoutHidden << "\n"; |
121 | 123 |
122 if (error) | 124 if (error) |
165 return tile.release(); | 167 return tile.release(); |
166 } | 168 } |
167 | 169 |
168 | 170 |
169 | 171 |
170 static void RunTranscode(OrthancWSI::ITiledPyramid& source, | 172 static void Run(OrthancWSI::ITiledPyramid& source, |
171 const boost::program_options::variables_map& options) | 173 const boost::program_options::variables_map& options) |
172 { | |
173 OrthancWSI::HierarchicalTiffWriter target(options["output"].as<std::string>(), | |
174 source.GetPixelFormat(), | |
175 source.GetImageCompression(), | |
176 source.GetTileWidth(), | |
177 source.GetTileHeight()); | |
178 | |
179 std::auto_ptr<Orthanc::ImageAccessor> empty(CreateEmptyTile(target, options)); | |
180 | |
181 for (unsigned int level = 0; level < source.GetLevelCount(); level++) | |
182 { | |
183 LOG(WARNING) << "Creating level " << level << " of size " | |
184 << source.GetLevelWidth(level) << "x" << source.GetLevelHeight(level); | |
185 target.AddLevel(source.GetLevelWidth(level), source.GetLevelHeight(level)); | |
186 } | |
187 | |
188 for (unsigned int level = 0; level < source.GetLevelCount(); level++) | |
189 { | |
190 LOG(WARNING) << "Transcoding level " << level; | |
191 | |
192 unsigned int countX = OrthancWSI::CeilingDivision(source.GetLevelWidth(level), source.GetTileWidth()); | |
193 unsigned int countY = OrthancWSI::CeilingDivision(source.GetLevelHeight(level), source.GetTileHeight()); | |
194 | |
195 for (unsigned int tileY = 0; tileY < countY; tileY++) | |
196 { | |
197 for (unsigned int tileX = 0; tileX < countX; tileX++) | |
198 { | |
199 LOG(INFO) << "Dealing with tile (" << tileX << "," << tileY << ") at level " << level; | |
200 std::string tile; | |
201 | |
202 if (source.ReadRawTile(tile, level, tileX, tileY)) | |
203 { | |
204 target.WriteRawTile(tile, source.GetImageCompression(), level, tileX, tileY); | |
205 } | |
206 else | |
207 { | |
208 target.EncodeTile(*empty, level, tileX, tileY); | |
209 } | |
210 } | |
211 } | |
212 | |
213 target.Flush(); | |
214 } | |
215 } | |
216 | |
217 | |
218 static void RunReencode(OrthancWSI::ITiledPyramid& source, | |
219 const boost::program_options::variables_map& options) | |
220 { | 174 { |
221 OrthancWSI::HierarchicalTiffWriter target(options["output"].as<std::string>(), | 175 OrthancWSI::HierarchicalTiffWriter target(options["output"].as<std::string>(), |
222 source.GetPixelFormat(), | 176 source.GetPixelFormat(), |
223 OrthancWSI::ImageCompression_Jpeg, | 177 OrthancWSI::ImageCompression_Jpeg, |
224 source.GetTileWidth(), | 178 source.GetTileWidth(), |
225 source.GetTileHeight()); | 179 source.GetTileHeight()); |
226 | 180 |
181 bool reencode = (options.count("reencode") && | |
182 options["reencode"].as<bool>()); | |
183 | |
227 if (options.count("jpeg-quality")) | 184 if (options.count("jpeg-quality")) |
228 { | 185 { |
229 target.SetJpegQuality(options["jpeg-quality"].as<int>()); | 186 target.SetJpegQuality(options["jpeg-quality"].as<int>()); |
230 } | 187 } |
231 | 188 |
238 target.AddLevel(source.GetLevelWidth(level), source.GetLevelHeight(level)); | 195 target.AddLevel(source.GetLevelWidth(level), source.GetLevelHeight(level)); |
239 } | 196 } |
240 | 197 |
241 for (unsigned int level = 0; level < source.GetLevelCount(); level++) | 198 for (unsigned int level = 0; level < source.GetLevelCount(); level++) |
242 { | 199 { |
243 LOG(WARNING) << "Reencoding level " << level; | 200 LOG(WARNING) << std::string(reencode ? "Reencoding" : "Transcoding") << " level " << level; |
244 | 201 |
245 unsigned int countX = OrthancWSI::CeilingDivision(source.GetLevelWidth(level), source.GetTileWidth()); | 202 unsigned int countX = OrthancWSI::CeilingDivision(source.GetLevelWidth(level), source.GetTileWidth()); |
246 unsigned int countY = OrthancWSI::CeilingDivision(source.GetLevelHeight(level), source.GetTileHeight()); | 203 unsigned int countY = OrthancWSI::CeilingDivision(source.GetLevelHeight(level), source.GetTileHeight()); |
247 | 204 |
248 for (unsigned int tileY = 0; tileY < countY; tileY++) | 205 for (unsigned int tileY = 0; tileY < countY; tileY++) |
249 { | 206 { |
250 for (unsigned int tileX = 0; tileX < countX; tileX++) | 207 for (unsigned int tileX = 0; tileX < countX; tileX++) |
251 { | 208 { |
252 LOG(INFO) << "Dealing with tile (" << tileX << "," << tileY << ") at level " << level; | 209 LOG(INFO) << "Dealing with tile (" << tileX << "," << tileY << ") at level " << level; |
253 | 210 |
254 std::auto_ptr<Orthanc::ImageAccessor> tile(source.DecodeTile(level, tileX, tileY)); | 211 bool missing = false; |
255 if (tile.get() == NULL) | 212 bool success = true; |
213 | |
214 // Give a first try to get the raw tile | |
215 std::string tile; | |
216 OrthancWSI::ImageCompression compression; | |
217 if (source.ReadRawTile(tile, compression, level, tileX, tileY)) | |
256 { | 218 { |
257 target.EncodeTile(*empty, level, tileX, tileY); | 219 if (reencode || |
220 compression == OrthancWSI::ImageCompression_Jpeg) | |
221 { | |
222 target.WriteRawTile(tile, compression, level, tileX, tileY); | |
223 } | |
224 else | |
225 { | |
226 success = false; // Re-encoding is mandatory | |
227 } | |
258 } | 228 } |
259 else | 229 else |
260 { | 230 { |
261 target.EncodeTile(*tile, level, tileX, tileY); | 231 // Give a second try to get the decoded tile |
232 compression = OrthancWSI::ImageCompression_Unknown; | |
233 | |
234 std::auto_ptr<Orthanc::ImageAccessor> tile(source.DecodeTile(level, tileX, tileY)); | |
235 if (tile.get() == NULL) | |
236 { | |
237 // Unable to read the raw tile or to decode it: The tile is missing (sparse tiling) | |
238 missing = true; | |
239 } | |
240 else if (reencode) | |
241 { | |
242 target.EncodeTile(*empty, level, tileX, tileY); | |
243 } | |
244 else | |
245 { | |
246 success = false; // Re-encoding is mandatory | |
247 } | |
248 } | |
249 | |
250 if (!success) | |
251 { | |
252 LOG(WARNING) << "Cannot transcode a DICOM image that is not encoded using JPEG (it is " | |
253 << OrthancWSI::EnumerationToString(compression) | |
254 << "), please use the --reencode=1 option"; | |
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
256 } | |
257 | |
258 if (missing) | |
259 { | |
260 LOG(WARNING) << "Sparse tiling: Using an empty image for missing tile (" | |
261 << tileX << "," << tileY << ") at level " << level; | |
262 target.EncodeTile(*empty, level, tileX, tileY); | |
262 } | 263 } |
263 } | 264 } |
264 } | 265 } |
265 | 266 |
266 target.Flush(); | 267 target.Flush(); |
298 } | 299 } |
299 | 300 |
300 OrthancWSI::CurlOrthancConnection orthanc(params); | 301 OrthancWSI::CurlOrthancConnection orthanc(params); |
301 OrthancWSI::DicomPyramid source(orthanc, options["input"].as<std::string>()); | 302 OrthancWSI::DicomPyramid source(orthanc, options["input"].as<std::string>()); |
302 | 303 |
303 if (options.count("reencode") && | 304 OrthancWSI::TiledPyramidStatistics stats(source); |
304 options["reencode"].as<bool>()) | 305 Run(stats, options); |
305 { | |
306 RunReencode(source, options); | |
307 } | |
308 else | |
309 { | |
310 RunTranscode(source, options); | |
311 } | |
312 } | 306 } |
313 } | 307 } |
314 catch (Orthanc::OrthancException& e) | 308 catch (Orthanc::OrthancException& e) |
315 { | 309 { |
316 LOG(ERROR) << "Terminating on exception: " << e.What(); | 310 LOG(ERROR) << "Terminating on exception: " << e.What(); |