Mercurial > hg > orthanc-wsi
annotate 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 |
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 | |
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 "../Framework/DicomToolbox.h" | |
22 #include "../Framework/ImageToolbox.h" | |
23 #include "../Framework/Inputs/DicomPyramid.h" | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
24 #include "../Framework/Inputs/TiledPyramidStatistics.h" |
0 | 25 #include "../Framework/Messaging/CurlOrthancConnection.h" |
26 #include "../Framework/Orthanc/Core/Logging.h" | |
27 #include "../Framework/Orthanc/Core/OrthancException.h" | |
28 #include "../Framework/Outputs/HierarchicalTiffWriter.h" | |
29 | |
30 #include "ApplicationToolbox.h" | |
31 | |
32 #include <boost/program_options.hpp> | |
33 | |
34 | |
35 static bool ParseParameters(int& exitStatus, | |
36 boost::program_options::variables_map& options, | |
37 int argc, | |
38 char* argv[]) | |
39 { | |
40 // Declare the supported parameters | |
41 boost::program_options::options_description generic("Generic options"); | |
42 generic.add_options() | |
43 ("help", "Display this help and exit") | |
8
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
44 ("version", "Output version information and exit") |
0 | 45 ("verbose", "Be verbose in logs") |
46 ; | |
47 | |
48 boost::program_options::options_description source("Options for the source DICOM image"); | |
49 source.add_options() | |
50 ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"), | |
51 "URL to the REST API of the target Orthanc server") | |
52 ("username", boost::program_options::value<std::string>(), "Username for the target Orthanc server") | |
53 ("password", boost::program_options::value<std::string>(), "Password for the target Orthanc server") | |
54 ; | |
55 | |
56 boost::program_options::options_description target("Options for the target TIFF image"); | |
57 target.add_options() | |
58 ("color", boost::program_options::value<std::string>(), "Color of the background for missing tiles (e.g. \"255,0,0\")") | |
59 ("reencode", boost::program_options::value<bool>(), | |
31 | 60 "Whether to re-encode each tile in JPEG (no transcoding, much slower) (Boolean)") |
0 | 61 ("jpeg-quality", boost::program_options::value<int>(), "Set quality level for JPEG (0..100)") |
62 ; | |
63 | |
64 boost::program_options::options_description hidden; | |
65 hidden.add_options() | |
66 ("input", boost::program_options::value<std::string>(), "Orthanc identifier of the input series of interest") | |
67 ("output", boost::program_options::value<std::string>(), "Output TIFF file"); | |
68 ; | |
69 | |
70 boost::program_options::options_description allWithoutHidden; | |
71 allWithoutHidden.add(generic).add(source).add(target); | |
72 | |
73 boost::program_options::options_description all = allWithoutHidden; | |
74 all.add(hidden); | |
75 | |
76 boost::program_options::positional_options_description positional; | |
77 positional.add("input", 1); | |
78 positional.add("output", 1); | |
79 | |
80 bool error = false; | |
81 | |
82 try | |
83 { | |
84 boost::program_options::store(boost::program_options::command_line_parser(argc, argv). | |
85 options(all).positional(positional).run(), options); | |
86 boost::program_options::notify(options); | |
87 } | |
88 catch (boost::program_options::error& e) | |
89 { | |
90 LOG(ERROR) << "Error while parsing the command-line arguments: " << e.what(); | |
91 error = true; | |
92 } | |
93 | |
94 if (!error && | |
8
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
95 options.count("help") == 0 && |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
96 options.count("version") == 0) |
0 | 97 { |
98 if (options.count("input") != 1) | |
99 { | |
100 LOG(ERROR) << "No input series was specified"; | |
101 error = true; | |
102 } | |
103 | |
104 if (options.count("output") != 1) | |
105 { | |
106 LOG(ERROR) << "No output file was specified"; | |
107 error = true; | |
108 } | |
109 } | |
110 | |
111 if (error || options.count("help")) | |
112 { | |
113 std::cout << std::endl | |
114 << "Usage: " << argv[0] << " [OPTION]... [INPUT] [OUTPUT]" | |
115 << std::endl | |
116 << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research." | |
117 << std::endl << std::endl | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
118 << "Convert a DICOM image for digital pathology stored in some Orthanc server as a" << std::endl |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
119 << "standard hierarchical TIFF (whose tiles are all encoded using JPEG)." |
0 | 120 << std::endl; |
121 | |
122 std::cout << allWithoutHidden << "\n"; | |
123 | |
124 if (error) | |
125 { | |
126 exitStatus = -1; | |
127 } | |
128 | |
129 return false; | |
130 } | |
131 | |
8
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
132 if (options.count("version")) |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
133 { |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
134 OrthancWSI::ApplicationToolbox::PrintVersion(argv[0]); |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
135 return false; |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
136 } |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
137 |
0 | 138 if (options.count("verbose")) |
139 { | |
140 Orthanc::Logging::EnableInfoLevel(true); | |
141 } | |
142 | |
143 return true; | |
144 } | |
145 | |
146 | |
147 | |
148 static Orthanc::ImageAccessor* CreateEmptyTile(const OrthancWSI::IPyramidWriter& writer, | |
149 const boost::program_options::variables_map& options) | |
150 { | |
151 std::auto_ptr<Orthanc::ImageAccessor> tile | |
152 (OrthancWSI::ImageToolbox::Allocate(writer.GetPixelFormat(), | |
153 writer.GetTileWidth(), | |
154 writer.GetTileHeight())); | |
155 | |
156 uint8_t red = 255; | |
157 uint8_t green = 255; | |
158 uint8_t blue = 255; | |
159 | |
160 if (options.count("color")) | |
161 { | |
162 OrthancWSI::ApplicationToolbox::ParseColor(red, green, blue, options["color"].as<std::string>()); | |
163 } | |
164 | |
165 OrthancWSI::ImageToolbox::Set(*tile, red, green, blue); | |
166 | |
167 return tile.release(); | |
168 } | |
169 | |
170 | |
171 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
172 static void Run(OrthancWSI::ITiledPyramid& source, |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
173 const boost::program_options::variables_map& options) |
0 | 174 { |
175 OrthancWSI::HierarchicalTiffWriter target(options["output"].as<std::string>(), | |
176 source.GetPixelFormat(), | |
177 OrthancWSI::ImageCompression_Jpeg, | |
178 source.GetTileWidth(), | |
179 source.GetTileHeight()); | |
180 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
181 bool reencode = (options.count("reencode") && |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
182 options["reencode"].as<bool>()); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
183 |
0 | 184 if (options.count("jpeg-quality")) |
185 { | |
186 target.SetJpegQuality(options["jpeg-quality"].as<int>()); | |
187 } | |
188 | |
189 std::auto_ptr<Orthanc::ImageAccessor> empty(CreateEmptyTile(target, options)); | |
190 | |
191 for (unsigned int level = 0; level < source.GetLevelCount(); level++) | |
192 { | |
193 LOG(WARNING) << "Creating level " << level << " of size " | |
194 << source.GetLevelWidth(level) << "x" << source.GetLevelHeight(level); | |
195 target.AddLevel(source.GetLevelWidth(level), source.GetLevelHeight(level)); | |
196 } | |
197 | |
198 for (unsigned int level = 0; level < source.GetLevelCount(); level++) | |
199 { | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
200 LOG(WARNING) << std::string(reencode ? "Reencoding" : "Transcoding") << " level " << level; |
0 | 201 |
202 unsigned int countX = OrthancWSI::CeilingDivision(source.GetLevelWidth(level), source.GetTileWidth()); | |
203 unsigned int countY = OrthancWSI::CeilingDivision(source.GetLevelHeight(level), source.GetTileHeight()); | |
204 | |
205 for (unsigned int tileY = 0; tileY < countY; tileY++) | |
206 { | |
207 for (unsigned int tileX = 0; tileX < countX; tileX++) | |
208 { | |
209 LOG(INFO) << "Dealing with tile (" << tileX << "," << tileY << ") at level " << level; | |
210 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
211 bool missing = false; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
212 bool success = true; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
213 |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
214 // Give a first try to get the raw tile |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
215 std::string tile; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
216 OrthancWSI::ImageCompression compression; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
217 if (source.ReadRawTile(tile, compression, level, tileX, tileY)) |
0 | 218 { |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
219 if (reencode || |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
220 compression == OrthancWSI::ImageCompression_Jpeg) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
221 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
222 target.WriteRawTile(tile, compression, level, tileX, tileY); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
223 } |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
224 else |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
225 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
226 success = false; // Re-encoding is mandatory |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
227 } |
0 | 228 } |
229 else | |
230 { | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
231 // Give a second try to get the decoded tile |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
232 compression = OrthancWSI::ImageCompression_Unknown; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
233 |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
234 std::auto_ptr<Orthanc::ImageAccessor> tile(source.DecodeTile(level, tileX, tileY)); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
235 if (tile.get() == NULL) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
236 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
237 // Unable to read the raw tile or to decode it: The tile is missing (sparse tiling) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
238 missing = true; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
239 } |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
240 else if (reencode) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
241 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
242 target.EncodeTile(*empty, level, tileX, tileY); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
243 } |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
244 else |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
245 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
246 success = false; // Re-encoding is mandatory |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
247 } |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
248 } |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
249 |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
250 if (!success) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
251 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
252 LOG(WARNING) << "Cannot transcode a DICOM image that is not encoded using JPEG (it is " |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
253 << OrthancWSI::EnumerationToString(compression) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
254 << "), please use the --reencode=1 option"; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
256 } |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
257 |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
258 if (missing) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
259 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
260 LOG(WARNING) << "Sparse tiling: Using an empty image for missing tile (" |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
261 << tileX << "," << tileY << ") at level " << level; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
262 target.EncodeTile(*empty, level, tileX, tileY); |
0 | 263 } |
264 } | |
265 } | |
266 | |
267 target.Flush(); | |
268 } | |
269 } | |
270 | |
271 | |
272 | |
273 int main(int argc, char* argv[]) | |
274 { | |
275 OrthancWSI::ApplicationToolbox::GlobalInitialize(); | |
276 | |
277 int exitStatus = 0; | |
278 boost::program_options::variables_map options; | |
279 | |
280 try | |
281 { | |
282 if (ParseParameters(exitStatus, options, argc, argv)) | |
283 { | |
284 Orthanc::WebServiceParameters params; | |
285 | |
286 if (options.count("orthanc")) | |
287 { | |
288 params.SetUrl(options["orthanc"].as<std::string>()); | |
289 } | |
290 | |
291 if (options.count("username")) | |
292 { | |
293 params.SetUsername(options["username"].as<std::string>()); | |
294 } | |
295 | |
296 if (options.count("password")) | |
297 { | |
298 params.SetPassword(options["password"].as<std::string>()); | |
299 } | |
300 | |
301 OrthancWSI::CurlOrthancConnection orthanc(params); | |
302 OrthancWSI::DicomPyramid source(orthanc, options["input"].as<std::string>()); | |
303 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
304 OrthancWSI::TiledPyramidStatistics stats(source); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
31
diff
changeset
|
305 Run(stats, options); |
0 | 306 } |
307 } | |
308 catch (Orthanc::OrthancException& e) | |
309 { | |
310 LOG(ERROR) << "Terminating on exception: " << e.What(); | |
311 | |
312 if (options.count("reencode") == 0) | |
313 { | |
314 LOG(ERROR) << "Consider using option \"--reencode\""; | |
315 } | |
316 | |
317 exitStatus = -1; | |
318 } | |
319 | |
320 OrthancWSI::ApplicationToolbox::GlobalFinalize(); | |
321 | |
322 return exitStatus; | |
323 } |