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