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 }