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();