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