Mercurial > hg > orthanc-wsi
annotate Applications/Dicomizer.cpp @ 98:ff0ef01c332c
shared copyright with osimis
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 04 Jan 2017 16:43:25 +0100 |
parents | 14146ecd1688 |
children | 18e9a5429cc4 |
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 | |
98
ff0ef01c332c
shared copyright with osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
93
diff
changeset
|
5 * Copyright (C) 2017 Osimis, Belgium |
0 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #include "../Framework/Algorithms/ReconstructPyramidCommand.h" | |
23 #include "../Framework/Algorithms/TranscodeTileCommand.h" | |
24 #include "../Framework/DicomToolbox.h" | |
25 #include "../Framework/DicomizerParameters.h" | |
26 #include "../Framework/ImagedVolumeParameters.h" | |
27 #include "../Framework/Inputs/HierarchicalTiff.h" | |
28 #include "../Framework/Inputs/OpenSlidePyramid.h" | |
29 #include "../Framework/Inputs/TiledJpegImage.h" | |
30 #include "../Framework/Inputs/TiledPngImage.h" | |
31 #include "../Framework/Inputs/TiledPyramidStatistics.h" | |
32 #include "../Framework/Outputs/DicomPyramidWriter.h" | |
33 #include "../Framework/Outputs/TruncatedPyramidWriter.h" | |
34 | |
59
7a3853d51c45
Move "Framework/Orthanc/" as "Resources/Orthanc/"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
57
diff
changeset
|
35 #include "../Resources/Orthanc/Core/HttpClient.h" |
7a3853d51c45
Move "Framework/Orthanc/" as "Resources/Orthanc/"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
57
diff
changeset
|
36 #include "../Resources/Orthanc/Core/Logging.h" |
7a3853d51c45
Move "Framework/Orthanc/" as "Resources/Orthanc/"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
57
diff
changeset
|
37 #include "../Resources/Orthanc/Core/MultiThreading/BagOfTasksProcessor.h" |
7a3853d51c45
Move "Framework/Orthanc/" as "Resources/Orthanc/"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
57
diff
changeset
|
38 #include "../Resources/Orthanc/Core/SystemToolbox.h" |
7a3853d51c45
Move "Framework/Orthanc/" as "Resources/Orthanc/"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
57
diff
changeset
|
39 #include "../Resources/Orthanc/OrthancServer/FromDcmtkBridge.h" |
7a3853d51c45
Move "Framework/Orthanc/" as "Resources/Orthanc/"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
57
diff
changeset
|
40 |
0 | 41 #include "ApplicationToolbox.h" |
42 | |
43 #include <EmbeddedResources.h> | |
44 | |
45 #include <boost/program_options.hpp> | |
46 | |
47 #include <dcmtk/dcmdata/dcdeftag.h> | |
48 #include <dcmtk/dcmdata/dcuid.h> | |
49 #include <dcmtk/dcmdata/dcvrobow.h> | |
50 #include <dcmtk/dcmdata/dcvrat.h> | |
51 | |
52 | |
53 static void TranscodePyramid(OrthancWSI::PyramidWriterBase& target, | |
54 OrthancWSI::ITiledPyramid& source, | |
55 const OrthancWSI::DicomizerParameters& parameters) | |
56 { | |
57 Orthanc::BagOfTasks tasks; | |
58 | |
59 for (unsigned int i = 0; i < source.GetLevelCount(); i++) | |
60 { | |
61 LOG(WARNING) << "Creating level " << i << " of size " | |
62 << source.GetLevelWidth(i) << "x" << source.GetLevelHeight(i); | |
63 target.AddLevel(source.GetLevelWidth(i), source.GetLevelHeight(i)); | |
64 } | |
65 | |
66 LOG(WARNING) << "Transcoding the source pyramid"; | |
67 | |
68 OrthancWSI::TranscodeTileCommand::PrepareBagOfTasks(tasks, target, source, parameters); | |
69 OrthancWSI::ApplicationToolbox::Execute(tasks, parameters.GetThreadsCount()); | |
70 } | |
71 | |
72 | |
73 static void ReconstructPyramid(OrthancWSI::PyramidWriterBase& target, | |
74 OrthancWSI::ITiledPyramid& source, | |
75 const OrthancWSI::DicomizerParameters& parameters) | |
76 { | |
77 Orthanc::BagOfTasks tasks; | |
78 | |
79 unsigned int levelsCount = parameters.GetPyramidLevelsCount(target, source); | |
80 LOG(WARNING) << "The target pyramid will have " << levelsCount << " levels"; | |
81 assert(levelsCount >= 1); | |
82 | |
83 for (unsigned int i = 0; i < levelsCount; i++) | |
84 { | |
85 unsigned int width = OrthancWSI::CeilingDivision(source.GetLevelWidth(0), 1 << i); | |
86 unsigned int height = OrthancWSI::CeilingDivision(source.GetLevelHeight(0), 1 << i); | |
87 | |
88 LOG(WARNING) << "Creating level " << i << " of size " << width << "x" << height; | |
89 target.AddLevel(width, height); | |
90 } | |
91 | |
92 unsigned int lowerLevelsCount = parameters.GetPyramidLowerLevelsCount(target, source); | |
93 if (lowerLevelsCount > levelsCount) | |
94 { | |
95 LOG(WARNING) << "The number of lower levels (" << lowerLevelsCount | |
96 << ") exceeds the number of levels (" << levelsCount | |
97 << "), cropping it"; | |
98 lowerLevelsCount = levelsCount; | |
99 } | |
100 | |
101 assert(lowerLevelsCount <= levelsCount); | |
102 if (lowerLevelsCount != levelsCount) | |
103 { | |
104 LOG(WARNING) << "Constructing the " << lowerLevelsCount << " lower levels of the pyramid"; | |
105 OrthancWSI::TruncatedPyramidWriter truncated(target, lowerLevelsCount); | |
106 OrthancWSI::ReconstructPyramidCommand::PrepareBagOfTasks | |
107 (tasks, truncated, source, lowerLevelsCount + 1, 0, parameters); | |
108 OrthancWSI::ApplicationToolbox::Execute(tasks, parameters.GetThreadsCount()); | |
109 | |
110 assert(tasks.GetSize() == 0); | |
111 | |
112 const unsigned int upperLevelsCount = levelsCount - lowerLevelsCount; | |
113 LOG(WARNING) << "Constructing the " << upperLevelsCount << " upper levels of the pyramid"; | |
114 OrthancWSI::ReconstructPyramidCommand::PrepareBagOfTasks | |
115 (tasks, target, truncated.GetUpperLevel(), | |
116 upperLevelsCount, lowerLevelsCount, parameters); | |
117 OrthancWSI::ApplicationToolbox::Execute(tasks, parameters.GetThreadsCount()); | |
118 } | |
119 else | |
120 { | |
121 LOG(WARNING) << "Constructing the pyramid"; | |
122 OrthancWSI::ReconstructPyramidCommand::PrepareBagOfTasks | |
123 (tasks, target, source, levelsCount, 0, parameters); | |
124 OrthancWSI::ApplicationToolbox::Execute(tasks, parameters.GetThreadsCount()); | |
125 } | |
126 } | |
127 | |
128 | |
129 static void Recompress(OrthancWSI::IFileTarget& output, | |
130 OrthancWSI::ITiledPyramid& source, | |
131 const DcmDataset& dataset, | |
132 const OrthancWSI::DicomizerParameters& parameters, | |
133 const OrthancWSI::ImagedVolumeParameters& volume) | |
134 { | |
135 OrthancWSI::TiledPyramidStatistics stats(source); | |
136 | |
137 LOG(WARNING) << "Size of source tiles: " << stats.GetTileWidth() << "x" << stats.GetTileHeight(); | |
138 LOG(WARNING) << "Pixel format: " << Orthanc::EnumerationToString(stats.GetPixelFormat()); | |
139 LOG(WARNING) << "Smoothing is " << (parameters.IsSmoothEnabled() ? "enabled" : "disabled"); | |
140 | |
141 if (parameters.IsRepaintBackground()) | |
142 { | |
143 LOG(WARNING) << "Repainting the background with color: (" | |
144 << static_cast<int>(parameters.GetBackgroundColorRed()) << "," | |
145 << static_cast<int>(parameters.GetBackgroundColorGreen()) << "," | |
146 << static_cast<int>(parameters.GetBackgroundColorBlue()) << ")"; | |
147 } | |
148 else | |
149 { | |
150 LOG(WARNING) << "No repainting of the background"; | |
151 } | |
152 | |
153 OrthancWSI::DicomPyramidWriter target(output, dataset, | |
154 source.GetPixelFormat(), | |
155 parameters.GetTargetCompression(), | |
156 parameters.GetTargetTileWidth(source), | |
157 parameters.GetTargetTileHeight(source), | |
158 parameters.GetDicomMaxFileSize(), | |
159 volume); | |
29 | 160 target.SetJpegQuality(parameters.GetJpegQuality()); |
0 | 161 |
162 LOG(WARNING) << "Size of target tiles: " << target.GetTileWidth() << "x" << target.GetTileHeight(); | |
163 | |
164 if (target.GetImageCompression() == OrthancWSI::ImageCompression_Jpeg) | |
165 { | |
166 LOG(WARNING) << "Target image compression: Jpeg with quality " << static_cast<int>(target.GetJpegQuality()); | |
167 target.SetJpegQuality(target.GetJpegQuality()); | |
168 } | |
169 else | |
170 { | |
171 LOG(WARNING) << "Target image compression: " << OrthancWSI::EnumerationToString(target.GetImageCompression()); | |
172 } | |
173 | |
174 if (stats.GetTileWidth() % target.GetTileWidth() != 0 || | |
175 stats.GetTileHeight() % target.GetTileHeight() != 0) | |
176 { | |
177 LOG(ERROR) << "When resampling the tile size, it must be a integer divisor of the original tile size"; | |
178 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); | |
179 } | |
180 | |
181 if (target.GetTileWidth() <= 16 || | |
182 target.GetTileHeight() <= 16) | |
183 { | |
184 LOG(ERROR) << "Tiles are too small (16 pixels minimum): " | |
185 << target.GetTileWidth() << "x" << target.GetTileHeight(); | |
186 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); | |
187 } | |
188 | |
189 if (parameters.IsReconstructPyramid()) | |
190 { | |
191 ReconstructPyramid(target, stats, parameters); | |
192 } | |
193 else | |
194 { | |
195 TranscodePyramid(target, stats, parameters); | |
196 } | |
197 | |
198 target.Flush(); | |
199 } | |
200 | |
201 | |
202 | |
203 static DcmDataset* ParseDataset(const std::string& path) | |
204 { | |
205 Json::Value json; | |
206 | |
207 if (path.empty()) | |
208 { | |
209 json = Json::objectValue; // Empty dataset => TODO EMBED | |
210 } | |
211 else | |
212 { | |
213 std::string content; | |
43 | 214 Orthanc::SystemToolbox::ReadFile(content, path); |
0 | 215 |
216 Json::Reader reader; | |
217 if (!reader.parse(content, json, false)) | |
218 { | |
219 LOG(ERROR) << "Cannot parse the JSON file in: " << path; | |
220 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
221 } | |
222 } | |
223 | |
224 std::auto_ptr<DcmDataset> dataset(Orthanc::FromDcmtkBridge::FromJson(json, true, true, Orthanc::Encoding_Latin1)); | |
225 if (dataset.get() == NULL) | |
226 { | |
227 LOG(ERROR) << "Cannot convert to JSON file to a DICOM dataset: " << path; | |
228 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
229 } | |
230 | |
231 // VL Whole Slide Microscopy Image IOD | |
232 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_SOPClassUID, "1.2.840.10008.5.1.4.1.1.77.1.6"); | |
233 | |
234 // Slide Microscopy | |
235 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_Modality, "SM"); | |
236 | |
237 // Patient orientation makes no sense in whole-slide images | |
238 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_PatientOrientation, ""); | |
239 | |
240 // Some basic coordinate information | |
241 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_VolumetricProperties, "VOLUME"); | |
242 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_ImageOrientationSlide, "0\\-1\\0\\-1\\0\\0"); | |
243 | |
244 std::string date, time; | |
43 | 245 Orthanc::SystemToolbox::GetNowDicom(date, time); |
0 | 246 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_StudyDate, date); |
247 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_StudyTime, time); | |
248 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_SeriesDate, date); | |
249 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_SeriesTime, time); | |
250 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_ContentDate, date); | |
251 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_ContentTime, time); | |
252 OrthancWSI::DicomToolbox::SetStringTag(*dataset, DCM_AcquisitionDateTime, date + time); | |
253 | |
254 return dataset.release(); | |
255 } | |
256 | |
257 | |
258 | |
259 static void SetupDimension(DcmDataset& dataset, | |
260 const std::string& opticalPathId, | |
261 const OrthancWSI::ITiledPyramid& source, | |
262 const OrthancWSI::ImagedVolumeParameters& volume) | |
263 { | |
83 | 264 // Extract the identifier of the Dimension Organization, if provided |
265 std::string organization; | |
0 | 266 DcmItem* previous = OrthancWSI::DicomToolbox::ExtractSingleSequenceItem(dataset, DCM_DimensionOrganizationSequence); |
267 | |
83 | 268 if (previous != NULL && |
269 previous->tagExists(DCM_DimensionOrganizationUID)) | |
270 { | |
271 organization = OrthancWSI::DicomToolbox::GetStringTag(*previous, DCM_DimensionOrganizationUID); | |
272 } | |
273 else | |
0 | 274 { |
83 | 275 // No Dimension Organization provided: Generate an unique identifier |
276 organization = Orthanc::FromDcmtkBridge::GenerateUniqueIdentifier(Orthanc::ResourceType_Instance); | |
277 } | |
278 | |
279 | |
280 { | |
281 // Construct tag "Dimension Organization Sequence" (0020,9221) | |
282 std::auto_ptr<DcmItem> item(new DcmItem); | |
283 OrthancWSI::DicomToolbox::SetStringTag(*item, DCM_DimensionOrganizationUID, organization); | |
284 | |
285 std::auto_ptr<DcmSequenceOfItems> sequence(new DcmSequenceOfItems(DCM_DimensionOrganizationSequence)); | |
286 | |
287 if (!sequence->insert(item.release(), false, false).good() || | |
288 !dataset.insert(sequence.release(), true /* replace */, false).good()) | |
0 | 289 { |
83 | 290 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
0 | 291 } |
292 } | |
293 | |
83 | 294 |
0 | 295 { |
83 | 296 // Construct tag "Dimension Index Sequence" (0020,9222) |
297 std::auto_ptr<DcmItem> item(new DcmItem); | |
298 OrthancWSI::DicomToolbox::SetStringTag(*item, DCM_DimensionOrganizationUID, organization); | |
84
727670c5c125
fix generation of "Dimension Index Sequence" and "Dimension Index Values"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
83
diff
changeset
|
299 OrthancWSI::DicomToolbox::SetAttributeTag(*item, DCM_FunctionalGroupPointer, DCM_PlanePositionSlideSequence); |
727670c5c125
fix generation of "Dimension Index Sequence" and "Dimension Index Values"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
83
diff
changeset
|
300 OrthancWSI::DicomToolbox::SetAttributeTag(*item, DCM_DimensionIndexPointer, DCM_ColumnPositionInTotalImagePixelMatrix); |
727670c5c125
fix generation of "Dimension Index Sequence" and "Dimension Index Values"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
83
diff
changeset
|
301 |
727670c5c125
fix generation of "Dimension Index Sequence" and "Dimension Index Values"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
83
diff
changeset
|
302 std::auto_ptr<DcmItem> item2(new DcmItem); |
727670c5c125
fix generation of "Dimension Index Sequence" and "Dimension Index Values"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
83
diff
changeset
|
303 OrthancWSI::DicomToolbox::SetStringTag(*item2, DCM_DimensionOrganizationUID, organization); |
727670c5c125
fix generation of "Dimension Index Sequence" and "Dimension Index Values"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
83
diff
changeset
|
304 OrthancWSI::DicomToolbox::SetAttributeTag(*item2, DCM_FunctionalGroupPointer, DCM_PlanePositionSlideSequence); |
727670c5c125
fix generation of "Dimension Index Sequence" and "Dimension Index Values"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
83
diff
changeset
|
305 OrthancWSI::DicomToolbox::SetAttributeTag(*item2, DCM_DimensionIndexPointer, DCM_RowPositionInTotalImagePixelMatrix); |
0 | 306 |
83 | 307 std::auto_ptr<DcmSequenceOfItems> sequence(new DcmSequenceOfItems(DCM_DimensionIndexSequence)); |
0 | 308 |
83 | 309 if (!sequence->insert(item.release(), false, false).good() || |
84
727670c5c125
fix generation of "Dimension Index Sequence" and "Dimension Index Values"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
83
diff
changeset
|
310 !sequence->insert(item2.release(), false, false).good() || |
83 | 311 !dataset.insert(sequence.release(), true /* replace */, false).good()) |
312 { | |
313 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
314 } | |
0 | 315 } |
316 | |
317 | |
83 | 318 { |
319 // Construct tag "Shared Functional Groups Sequence" (5200,9229) | |
0 | 320 |
83 | 321 // In the 2 lines below, remember to switch X/Y when going from physical to pixel coordinates! |
322 float spacingX = volume.GetWidth() / static_cast<float>(source.GetLevelHeight(0)); | |
323 float spacingY = volume.GetHeight() / static_cast<float>(source.GetLevelWidth(0)); | |
324 | |
325 std::string spacing = (boost::lexical_cast<std::string>(spacingX) + '\\' + | |
326 boost::lexical_cast<std::string>(spacingY)); | |
327 | |
328 std::auto_ptr<DcmItem> item(new DcmItem); | |
0 | 329 |
83 | 330 std::auto_ptr<DcmItem> item2(new DcmItem); |
331 OrthancWSI::DicomToolbox::SetStringTag(*item2, DCM_SliceThickness, | |
332 boost::lexical_cast<std::string>(volume.GetDepth())); | |
333 OrthancWSI::DicomToolbox::SetStringTag(*item2, DCM_PixelSpacing, spacing); | |
0 | 334 |
83 | 335 std::auto_ptr<DcmItem> item3(new DcmItem); |
336 OrthancWSI::DicomToolbox::SetStringTag(*item3, DCM_OpticalPathIdentifier, opticalPathId); | |
0 | 337 |
83 | 338 std::auto_ptr<DcmSequenceOfItems> sequence(new DcmSequenceOfItems(DCM_SharedFunctionalGroupsSequence)); |
339 std::auto_ptr<DcmSequenceOfItems> sequence2(new DcmSequenceOfItems(DCM_PixelMeasuresSequence)); | |
340 std::auto_ptr<DcmSequenceOfItems> sequence3(new DcmSequenceOfItems(DCM_OpticalPathIdentificationSequence)); | |
0 | 341 |
83 | 342 if (!sequence2->insert(item2.release(), false, false).good() || |
343 !sequence3->insert(item3.release(), false, false).good() || | |
344 !item->insert(sequence2.release(), false, false).good() || | |
345 !item->insert(sequence3.release(), false, false).good() || | |
346 !sequence->insert(item.release(), false, false).good() || | |
347 !dataset.insert(sequence.release(), true /* replace */, false).good()) | |
348 { | |
349 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
350 } | |
0 | 351 } |
352 } | |
353 | |
354 | |
355 static void EnrichDataset(DcmDataset& dataset, | |
356 const OrthancWSI::ITiledPyramid& source, | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
357 OrthancWSI::ImageCompression sourceCompression, |
0 | 358 const OrthancWSI::DicomizerParameters& parameters, |
359 const OrthancWSI::ImagedVolumeParameters& volume) | |
360 { | |
361 Orthanc::Encoding encoding = Orthanc::FromDcmtkBridge::DetectEncoding(dataset, Orthanc::Encoding_Latin1); | |
362 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
363 if (sourceCompression == OrthancWSI::ImageCompression_Jpeg || |
0 | 364 parameters.GetTargetCompression() == OrthancWSI::ImageCompression_Jpeg) |
365 { | |
366 // Takes as estimation a 1:10 compression ratio | |
367 OrthancWSI::DicomToolbox::SetStringTag(dataset, DCM_LossyImageCompression, "01"); | |
368 OrthancWSI::DicomToolbox::SetStringTag(dataset, DCM_LossyImageCompressionRatio, "10"); | |
369 OrthancWSI::DicomToolbox::SetStringTag(dataset, DCM_LossyImageCompressionMethod, "ISO_10918_1"); // JPEG Lossy Compression | |
370 } | |
371 else | |
372 { | |
373 OrthancWSI::DicomToolbox::SetStringTag(dataset, DCM_LossyImageCompression, "00"); | |
374 } | |
375 | |
376 OrthancWSI::DicomToolbox::SetStringTag(dataset, DCM_ImagedVolumeWidth, boost::lexical_cast<std::string>(volume.GetWidth())); | |
377 OrthancWSI::DicomToolbox::SetStringTag(dataset, DCM_ImagedVolumeHeight, boost::lexical_cast<std::string>(volume.GetHeight())); | |
378 OrthancWSI::DicomToolbox::SetStringTag(dataset, DCM_ImagedVolumeDepth, boost::lexical_cast<std::string>(volume.GetDepth())); | |
379 | |
380 std::auto_ptr<DcmItem> origin(new DcmItem); | |
381 OrthancWSI::DicomToolbox::SetStringTag(*origin, DCM_XOffsetInSlideCoordinateSystem, | |
382 boost::lexical_cast<std::string>(volume.GetOffsetX())); | |
383 OrthancWSI::DicomToolbox::SetStringTag(*origin, DCM_YOffsetInSlideCoordinateSystem, | |
384 boost::lexical_cast<std::string>(volume.GetOffsetY())); | |
385 | |
386 std::auto_ptr<DcmSequenceOfItems> sequenceOrigin(new DcmSequenceOfItems(DCM_TotalPixelMatrixOriginSequence)); | |
387 if (!sequenceOrigin->insert(origin.release(), false, false).good() || | |
388 !dataset.insert(sequenceOrigin.release(), false, false).good()) | |
389 { | |
390 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
391 } | |
392 | |
393 | |
394 if (parameters.GetOpticalPath() == OrthancWSI::OpticalPath_Brightfield) | |
395 { | |
396 if (dataset.tagExists(DCM_OpticalPathSequence)) | |
397 { | |
398 LOG(ERROR) << "The user DICOM dataset already contains an optical path sequence, giving up"; | |
399 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
400 } | |
401 | |
402 std::string brightfield; | |
403 Orthanc::EmbeddedResources::GetFileResource(brightfield, Orthanc::EmbeddedResources::BRIGHTFIELD_OPTICAL_PATH); | |
404 | |
405 Json::Value json; | |
406 Json::Reader reader; | |
407 if (!reader.parse(brightfield, json, false)) | |
408 { | |
409 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
410 } | |
411 | |
412 std::auto_ptr<DcmElement> element(Orthanc::FromDcmtkBridge::FromJson( | |
413 Orthanc::DicomTag(DCM_OpticalPathSequence.getGroup(), | |
414 DCM_OpticalPathSequence.getElement()), | |
415 json, false, encoding)); | |
416 if (!dataset.insert(element.release()).good()) | |
417 { | |
418 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
419 } | |
420 } | |
421 | |
422 | |
423 std::string profile; | |
424 if (parameters.GetIccProfilePath().empty()) | |
425 { | |
426 Orthanc::EmbeddedResources::GetFileResource(profile, Orthanc::EmbeddedResources::SRGB_ICC_PROFILE); | |
427 } | |
428 else | |
429 { | |
43 | 430 Orthanc::SystemToolbox::ReadFile(profile, parameters.GetIccProfilePath()); |
0 | 431 } |
432 | |
433 | |
434 DcmItem* opticalPath = OrthancWSI::DicomToolbox::ExtractSingleSequenceItem(dataset, DCM_OpticalPathSequence); | |
435 if (opticalPath == NULL) | |
436 { | |
437 LOG(ERROR) << "No optical path specified"; | |
438 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
439 } | |
440 | |
441 if (!opticalPath->tagExists(DCM_ICCProfile)) | |
442 { | |
443 std::auto_ptr<DcmOtherByteOtherWord> icc(new DcmOtherByteOtherWord(DCM_ICCProfile)); | |
444 | |
445 if (!icc->putUint8Array(reinterpret_cast<const Uint8*>(profile.c_str()), profile.size()).good() || | |
446 !opticalPath->insert(icc.release()).good()) | |
447 { | |
448 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
449 } | |
450 } | |
451 | |
452 const char* opticalPathId = NULL; | |
453 if (!opticalPath->findAndGetString(DCM_OpticalPathIdentifier, opticalPathId).good() || | |
454 opticalPathId == NULL) | |
455 { | |
456 LOG(ERROR) << "No identifier in the optical path"; | |
457 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
458 } | |
459 | |
460 SetupDimension(dataset, opticalPathId, source, volume); | |
461 } | |
462 | |
463 | |
464 static bool ParseParameters(int& exitStatus, | |
465 OrthancWSI::DicomizerParameters& parameters, | |
466 OrthancWSI::ImagedVolumeParameters& volume, | |
467 int argc, | |
468 char* argv[]) | |
469 { | |
470 // Declare the supported parameters | |
471 boost::program_options::options_description generic("Generic options"); | |
472 generic.add_options() | |
473 ("help", "Display this help and exit") | |
8
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
474 ("version", "Output version information and exit") |
0 | 475 ("verbose", "Be verbose in logs") |
476 ("threads", boost::program_options::value<int>()->default_value(parameters.GetThreadsCount()), | |
477 "Number of processing threads to be used") | |
478 ("openslide", boost::program_options::value<std::string>(), | |
479 "Path to the shared library of OpenSlide (not necessary if converting from standard hierarchical TIFF)") | |
480 ; | |
481 | |
482 boost::program_options::options_description source("Options for the source image"); | |
483 source.add_options() | |
484 ("dataset", boost::program_options::value<std::string>(), "Path to a JSON file containing the DICOM dataset") | |
485 ("sample-dataset", "Display a minimalistic sample DICOM dataset in JSON format, then exit") | |
31 | 486 ("reencode", boost::program_options::value<bool>(), "Whether to re-encode each tile (no transcoding, much slower) (Boolean)") |
0 | 487 ("repaint", boost::program_options::value<bool>(), "Whether to repaint the background of the image (Boolean)") |
488 ("color", boost::program_options::value<std::string>(), "Color of the background (e.g. \"255,0,0\")") | |
489 ; | |
490 | |
491 boost::program_options::options_description pyramid("Options to construct the pyramid"); | |
492 pyramid.add_options() | |
493 ("pyramid", boost::program_options::value<bool>()->default_value(false), | |
494 "Reconstruct the full pyramid (slow) (Boolean)") | |
495 ("smooth", boost::program_options::value<bool>()->default_value(false), | |
496 "Apply smoothing when reconstructing the pyramid " | |
497 "(slower, but higher quality) (Boolean)") | |
498 ("levels", boost::program_options::value<int>(), "Number of levels in the target pyramid") | |
499 ; | |
500 | |
501 boost::program_options::options_description target("Options for the target image"); | |
502 target.add_options() | |
503 ("tile-width", boost::program_options::value<int>(), "Width of the tiles in the target image") | |
504 ("tile-height", boost::program_options::value<int>(), "Height of the tiles in the target image") | |
505 ("compression", boost::program_options::value<std::string>(), | |
506 "Compression of the target image (\"none\", \"jpeg\" or \"jpeg2000\")") | |
507 ("jpeg-quality", boost::program_options::value<int>(), "Set quality level for JPEG (0..100)") | |
508 ("max-size", boost::program_options::value<int>()->default_value(10), "Maximum size per DICOM instance (in MB)") | |
509 ("folder", boost::program_options::value<std::string>(), | |
510 "Folder where to store the output DICOM instances") | |
511 ("folder-pattern", boost::program_options::value<std::string>()->default_value("wsi-%06d.dcm"), | |
512 "Pattern for the files in the output folder") | |
513 ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"), | |
514 "URL to the REST API of the target Orthanc server") | |
515 ("username", boost::program_options::value<std::string>(), "Username for the target Orthanc server") | |
516 ("password", boost::program_options::value<std::string>(), "Password for the target Orthanc server") | |
517 ; | |
518 | |
519 boost::program_options::options_description volumeOptions("Description of the imaged volume"); | |
520 volumeOptions.add_options() | |
521 ("imaged-width", boost::program_options::value<float>()->default_value(15), "With of the specimen (in mm)") | |
522 ("imaged-height", boost::program_options::value<float>()->default_value(15), "Height of the specimen (in mm)") | |
523 ("imaged-depth", boost::program_options::value<float>()->default_value(1), "Depth of the specimen (in mm)") | |
524 ("offset-x", boost::program_options::value<float>()->default_value(20), | |
525 "X offset the specimen, wrt. slide coordinates origin (in mm)") | |
526 ("offset-y", boost::program_options::value<float>()->default_value(40), | |
527 "Y offset the specimen, wrt. slide coordinates origin (in mm)") | |
528 ; | |
529 | |
530 boost::program_options::options_description advancedOptions("Advanced options"); | |
531 advancedOptions.add_options() | |
532 ("optical-path", boost::program_options::value<std::string>()->default_value("brightfield"), | |
533 "Optical path to be automatically added to the DICOM dataset (\"none\" or \"brightfield\")") | |
534 ("icc-profile", boost::program_options::value<std::string>(), | |
535 "Path to the ICC profile to be included. If empty, a default sRGB profile will be added.") | |
536 ("safety", boost::program_options::value<bool>()->default_value(true), | |
537 "Whether to do additional checks to verify the source image is supported (might slow down) (Boolean)") | |
538 ("lower-levels", boost::program_options::value<int>(), "Number of pyramid levels up to which multithreading " | |
539 "should be applied (only for performance/memory tuning)") | |
540 ; | |
541 | |
542 boost::program_options::options_description hidden; | |
543 hidden.add_options() | |
544 ("input", boost::program_options::value<std::string>(), "Input file"); | |
545 ; | |
546 | |
547 boost::program_options::options_description allWithoutHidden; | |
548 allWithoutHidden.add(generic).add(source).add(pyramid).add(target).add(volumeOptions).add(advancedOptions); | |
549 | |
550 boost::program_options::options_description all = allWithoutHidden; | |
551 all.add(hidden); | |
552 | |
553 boost::program_options::positional_options_description positional; | |
554 positional.add("input", 1); | |
555 | |
556 boost::program_options::variables_map options; | |
557 bool error = false; | |
558 | |
559 try | |
560 { | |
561 boost::program_options::store(boost::program_options::command_line_parser(argc, argv). | |
562 options(all).positional(positional).run(), options); | |
563 boost::program_options::notify(options); | |
564 } | |
565 catch (boost::program_options::error& e) | |
566 { | |
567 LOG(ERROR) << "Error while parsing the command-line arguments: " << e.what(); | |
568 error = true; | |
569 } | |
570 | |
571 if (!error && | |
572 options.count("sample-dataset")) | |
573 { | |
574 std::string sample; | |
575 Orthanc::EmbeddedResources::GetFileResource(sample, Orthanc::EmbeddedResources::SAMPLE_DATASET); | |
576 | |
577 std::cout << std::endl << sample << std::endl; | |
578 | |
579 return false; | |
580 } | |
581 | |
582 if (!error && | |
583 options.count("help") == 0 && | |
8
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
584 options.count("version") == 0 && |
0 | 585 options.count("input") != 1) |
586 { | |
587 LOG(ERROR) << "No input file was specified"; | |
588 error = true; | |
589 } | |
590 | |
591 if (error || options.count("help")) | |
592 { | |
593 std::cout << std::endl | |
594 << "Usage: " << argv[0] << " [OPTION]... [INPUT]" | |
595 << std::endl | |
596 << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research." | |
597 << std::endl << std::endl | |
598 << "Create a DICOM file from a digital pathology image." | |
599 << std::endl; | |
600 | |
601 std::cout << allWithoutHidden << "\n"; | |
602 | |
603 if (error) | |
604 { | |
605 exitStatus = -1; | |
606 } | |
607 | |
608 return false; | |
609 } | |
610 | |
8
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
611 if (options.count("version")) |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
612 { |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
613 OrthancWSI::ApplicationToolbox::PrintVersion(argv[0]); |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
614 return false; |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
615 } |
62adabb8c122
Provide "--version" in command-line tools
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
616 |
0 | 617 if (options.count("verbose")) |
618 { | |
619 Orthanc::Logging::EnableInfoLevel(true); | |
620 } | |
621 | |
622 if (options.count("openslide")) | |
623 { | |
624 OrthancWSI::OpenSlideLibrary::Initialize(options["openslide"].as<std::string>()); | |
625 } | |
626 | |
627 if (options.count("pyramid") && | |
628 options["pyramid"].as<bool>()) | |
629 { | |
630 parameters.SetReconstructPyramid(true); | |
631 } | |
632 | |
633 if (options.count("smooth") && | |
634 options["smooth"].as<bool>()) | |
635 { | |
636 parameters.SetSmoothEnabled(true); | |
637 } | |
638 | |
639 if (options.count("safety") && | |
640 options["safety"].as<bool>()) | |
641 { | |
642 parameters.SetSafetyCheck(true); | |
643 } | |
644 | |
645 if (options.count("reencode") && | |
646 options["reencode"].as<bool>()) | |
647 { | |
648 parameters.SetForceReencode(true); | |
649 } | |
650 | |
651 if (options.count("repaint") && | |
652 options["repaint"].as<bool>()) | |
653 { | |
654 parameters.SetRepaintBackground(true); | |
655 } | |
656 | |
657 if (options.count("tile-width") || | |
658 options.count("tile-height")) | |
659 { | |
660 int w = 0; | |
661 if (options.count("tile-width")) | |
662 { | |
663 w = options["tile-width"].as<int>(); | |
664 } | |
665 | |
666 unsigned int h = 0; | |
667 if (options.count("tile-height")) | |
668 { | |
669 h = options["tile-height"].as<int>(); | |
670 } | |
671 | |
672 if (w < 0 || h < 0) | |
673 { | |
674 LOG(ERROR) << "Negative target tile size specified: " << w << "x" << h; | |
675 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
676 } | |
677 | |
678 parameters.SetTargetTileSize(w, h); | |
679 } | |
680 | |
681 parameters.SetInputFile(options["input"].as<std::string>()); | |
682 | |
683 if (options.count("color")) | |
684 { | |
685 uint8_t r, g, b; | |
686 OrthancWSI::ApplicationToolbox::ParseColor(r, g, b, options["color"].as<std::string>()); | |
687 parameters.SetBackgroundColor(r, g, b); | |
688 } | |
689 | |
690 if (options.count("compression")) | |
691 { | |
692 std::string s = options["compression"].as<std::string>(); | |
693 if (s == "none") | |
694 { | |
695 parameters.SetTargetCompression(OrthancWSI::ImageCompression_None); | |
696 } | |
697 else if (s == "jpeg") | |
698 { | |
699 parameters.SetTargetCompression(OrthancWSI::ImageCompression_Jpeg); | |
700 } | |
701 else if (s == "jpeg2000") | |
702 { | |
703 parameters.SetTargetCompression(OrthancWSI::ImageCompression_Jpeg2000); | |
704 } | |
705 else | |
706 { | |
707 LOG(ERROR) << "Unknown image compression for the target image: " << s; | |
708 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
709 } | |
710 } | |
711 | |
712 if (options.count("jpeg-quality")) | |
713 { | |
714 parameters.SetJpegQuality(options["jpeg-quality"].as<int>()); | |
715 } | |
716 | |
717 if (options.count("levels")) | |
718 { | |
719 parameters.SetPyramidLevelsCount(options["levels"].as<int>()); | |
720 } | |
721 | |
722 if (options.count("lower-levels")) | |
723 { | |
724 parameters.SetPyramidLowerLevelsCount(options["lower-levels"].as<int>()); | |
725 } | |
726 | |
727 if (options.count("threads")) | |
728 { | |
729 parameters.SetThreadsCount(options["threads"].as<int>()); | |
730 } | |
731 | |
732 if (options.count("max-size")) | |
733 { | |
734 parameters.SetDicomMaxFileSize(options["max-size"].as<int>() * 1024 * 1024); | |
735 } | |
736 | |
737 if (options.count("folder")) | |
738 { | |
739 parameters.SetTargetFolder(options["folder"].as<std::string>()); | |
740 } | |
741 | |
742 if (options.count("folder-pattern")) | |
743 { | |
744 parameters.SetTargetFolderPattern(options["folder-pattern"].as<std::string>()); | |
745 } | |
746 | |
747 if (options.count("orthanc")) | |
748 { | |
749 parameters.GetOrthancParameters().SetUrl(options["orthanc"].as<std::string>()); | |
750 | |
751 if (options.count("username") && | |
752 options.count("password")) | |
753 { | |
754 parameters.GetOrthancParameters().SetUsername(options["username"].as<std::string>()); | |
755 parameters.GetOrthancParameters().SetPassword(options["password"].as<std::string>()); | |
756 } | |
757 } | |
758 | |
759 if (options.count("dataset")) | |
760 { | |
761 parameters.SetDatasetPath(options["dataset"].as<std::string>()); | |
762 } | |
763 | |
764 if (options.count("imaged-width")) | |
765 { | |
766 volume.SetWidth(options["imaged-width"].as<float>()); | |
767 } | |
768 | |
769 if (options.count("imaged-height")) | |
770 { | |
771 volume.SetHeight(options["imaged-height"].as<float>()); | |
772 } | |
773 | |
774 if (options.count("imaged-depth")) | |
775 { | |
776 volume.SetDepth(options["imaged-depth"].as<float>()); | |
777 } | |
778 | |
779 if (options.count("offset-x")) | |
780 { | |
781 volume.SetOffsetX(options["offset-x"].as<float>()); | |
782 } | |
783 | |
784 if (options.count("offset-y")) | |
785 { | |
786 volume.SetOffsetY(options["offset-y"].as<float>()); | |
787 } | |
788 | |
789 if (options.count("optical-path")) | |
790 { | |
791 std::string s = options["optical-path"].as<std::string>(); | |
792 if (s == "none") | |
793 { | |
794 parameters.SetOpticalPath(OrthancWSI::OpticalPath_None); | |
795 } | |
796 else if (s == "brightfield") | |
797 { | |
798 parameters.SetOpticalPath(OrthancWSI::OpticalPath_Brightfield); | |
799 } | |
800 else | |
801 { | |
802 LOG(ERROR) << "Unknown optical path definition: " << s; | |
803 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
804 } | |
805 } | |
806 | |
807 if (options.count("icc-profile")) | |
808 { | |
809 parameters.SetIccProfilePath(options["icc-profile"].as<std::string>()); | |
810 } | |
811 | |
812 return true; | |
813 } | |
814 | |
815 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
816 OrthancWSI::ITiledPyramid* OpenInputPyramid(OrthancWSI::ImageCompression& sourceCompression, |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
817 const std::string& path, |
0 | 818 const OrthancWSI::DicomizerParameters& parameters) |
819 { | |
820 LOG(WARNING) << "The input image is: " << path; | |
821 | |
822 OrthancWSI::ImageCompression format = OrthancWSI::DetectFormatFromFile(path); | |
823 LOG(WARNING) << "File format of the input image: " << EnumerationToString(format); | |
824 | |
825 switch (format) | |
826 { | |
827 case OrthancWSI::ImageCompression_Png: | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
828 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
829 sourceCompression = OrthancWSI::ImageCompression_Unknown; |
0 | 830 return new OrthancWSI::TiledPngImage(path, |
831 parameters.GetTargetTileWidth(512), | |
832 parameters.GetTargetTileHeight(512)); | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
833 } |
0 | 834 |
835 case OrthancWSI::ImageCompression_Jpeg: | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
836 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
837 sourceCompression = OrthancWSI::ImageCompression_Unknown; |
0 | 838 return new OrthancWSI::TiledJpegImage(path, |
839 parameters.GetTargetTileWidth(512), | |
840 parameters.GetTargetTileHeight(512)); | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
841 } |
0 | 842 |
843 case OrthancWSI::ImageCompression_Tiff: | |
844 { | |
845 try | |
846 { | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
847 std::auto_ptr<OrthancWSI::HierarchicalTiff> tiff(new OrthancWSI::HierarchicalTiff(path)); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
848 sourceCompression = tiff->GetImageCompression(); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
849 return tiff.release(); |
0 | 850 } |
851 catch (Orthanc::OrthancException&) | |
852 { | |
853 LOG(WARNING) << "This is not a standard hierarchical TIFF file"; | |
854 } | |
855 } | |
856 | |
857 default: | |
858 break; | |
859 } | |
860 | |
861 try | |
862 { | |
863 LOG(WARNING) << "Trying to open the input pyramid with OpenSlide"; | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
864 sourceCompression = OrthancWSI::ImageCompression_Unknown; |
0 | 865 return new OrthancWSI::OpenSlidePyramid(path, |
866 parameters.GetTargetTileWidth(512), | |
867 parameters.GetTargetTileHeight(512)); | |
868 } | |
869 catch (Orthanc::OrthancException&) | |
870 { | |
871 LOG(ERROR) << "This file is not supported by OpenSlide"; | |
872 return NULL; | |
873 } | |
874 } | |
875 | |
876 | |
877 int main(int argc, char* argv[]) | |
878 { | |
879 OrthancWSI::ApplicationToolbox::GlobalInitialize(); | |
93
14146ecd1688
Display version of the framework in the logs
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
84
diff
changeset
|
880 OrthancWSI::ApplicationToolbox::ShowVersionInLog(argv[0]); |
0 | 881 |
882 int exitStatus = 0; | |
883 | |
884 try | |
885 { | |
886 OrthancWSI::DicomizerParameters parameters; | |
887 OrthancWSI::ImagedVolumeParameters volume; | |
888 | |
889 if (ParseParameters(exitStatus, parameters, volume, argc, argv)) | |
890 { | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
891 OrthancWSI::ImageCompression sourceCompression; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
892 std::auto_ptr<OrthancWSI::ITiledPyramid> source; |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
893 |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
894 source.reset(OpenInputPyramid(sourceCompression, parameters.GetInputFile(), parameters)); |
0 | 895 if (source.get() == NULL) |
896 { | |
897 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
898 } | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
899 |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
900 LOG(WARNING) << "Compression of the individual source tiles: " << OrthancWSI::EnumerationToString(sourceCompression); |
0 | 901 |
902 // Create the shared DICOM tags | |
903 std::auto_ptr<DcmDataset> dataset(ParseDataset(parameters.GetDatasetPath())); | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
43
diff
changeset
|
904 EnrichDataset(*dataset, *source, sourceCompression, parameters, volume); |
0 | 905 |
906 std::auto_ptr<OrthancWSI::IFileTarget> output(parameters.CreateTarget()); | |
907 Recompress(*output, *source, *dataset, parameters, volume); | |
908 } | |
909 } | |
910 catch (Orthanc::OrthancException& e) | |
911 { | |
912 LOG(ERROR) << "Terminating on exception: " << e.What(); | |
913 exitStatus = -1; | |
914 } | |
915 | |
916 OrthancWSI::ApplicationToolbox::GlobalFinalize(); | |
917 | |
918 return exitStatus; | |
919 } |