Mercurial > hg > orthanc-stone
comparison Framework/Deprecated/Toolbox/DicomFrameConverter.cpp @ 732:c35e98d22764
move Deprecated classes to a separate folder
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 21 May 2019 14:27:35 +0200 |
parents | Framework/Toolbox/DicomFrameConverter.cpp@d2c0e347ddc2 |
children | 1f74bc3459ba |
comparison
equal
deleted
inserted
replaced
729:529189f399ec | 732:c35e98d22764 |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium | |
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 "DicomFrameConverter.h" | |
23 | |
24 #include "../../Toolbox/LinearAlgebra.h" | |
25 | |
26 #include <Core/Images/Image.h> | |
27 #include <Core/Images/ImageProcessing.h> | |
28 #include <Core/OrthancException.h> | |
29 #include <Core/Toolbox.h> | |
30 | |
31 namespace Deprecated | |
32 { | |
33 static const Orthanc::DicomTag IMAGE_TAGS[] = | |
34 { | |
35 Orthanc::DICOM_TAG_BITS_STORED, | |
36 Orthanc::DICOM_TAG_DOSE_GRID_SCALING, | |
37 Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION, | |
38 Orthanc::DICOM_TAG_PIXEL_REPRESENTATION, | |
39 Orthanc::DICOM_TAG_RESCALE_INTERCEPT, | |
40 Orthanc::DICOM_TAG_RESCALE_SLOPE, | |
41 Orthanc::DICOM_TAG_WINDOW_CENTER, | |
42 Orthanc::DICOM_TAG_WINDOW_WIDTH | |
43 }; | |
44 | |
45 | |
46 void DicomFrameConverter::SetDefaultParameters() | |
47 { | |
48 isSigned_ = true; | |
49 isColor_ = false; | |
50 hasRescale_ = false; | |
51 rescaleIntercept_ = 0; | |
52 rescaleSlope_ = 1; | |
53 hasDefaultWindow_ = false; | |
54 defaultWindowCenter_ = 128; | |
55 defaultWindowWidth_ = 256; | |
56 expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; | |
57 } | |
58 | |
59 | |
60 void DicomFrameConverter::ReadParameters(const Orthanc::DicomMap& dicom) | |
61 { | |
62 SetDefaultParameters(); | |
63 | |
64 OrthancStone::Vector c, w; | |
65 if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && | |
66 OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && | |
67 c.size() > 0 && | |
68 w.size() > 0) | |
69 { | |
70 hasDefaultWindow_ = true; | |
71 defaultWindowCenter_ = static_cast<float>(c[0]); | |
72 defaultWindowWidth_ = static_cast<float>(w[0]); | |
73 } | |
74 | |
75 int32_t tmp; | |
76 if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_PIXEL_REPRESENTATION)) | |
77 { | |
78 // Type 1 tag, must be present | |
79 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
80 } | |
81 | |
82 isSigned_ = (tmp == 1); | |
83 | |
84 double doseGridScaling; | |
85 bool isRTDose = false; | |
86 | |
87 if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && | |
88 dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) | |
89 { | |
90 hasRescale_ = true; | |
91 } | |
92 else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) | |
93 { | |
94 // This is for RT-DOSE | |
95 hasRescale_ = true; | |
96 isRTDose = true; | |
97 rescaleIntercept_ = 0; | |
98 rescaleSlope_ = doseGridScaling; | |
99 | |
100 if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_BITS_STORED)) | |
101 { | |
102 // Type 1 tag, must be present | |
103 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
104 } | |
105 | |
106 switch (tmp) | |
107 { | |
108 case 16: | |
109 expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; | |
110 break; | |
111 | |
112 case 32: | |
113 expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32; | |
114 break; | |
115 | |
116 default: | |
117 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
118 } | |
119 } | |
120 | |
121 std::string photometric; | |
122 if (dicom.CopyToString(photometric, Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION, false)) | |
123 { | |
124 photometric = Orthanc::Toolbox::StripSpaces(photometric); | |
125 } | |
126 else | |
127 { | |
128 // Type 1 tag, must be present | |
129 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
130 } | |
131 | |
132 photometric_ = Orthanc::StringToPhotometricInterpretation(photometric.c_str()); | |
133 | |
134 isColor_ = (photometric != "MONOCHROME1" && | |
135 photometric != "MONOCHROME2"); | |
136 | |
137 // TODO Add more checks, e.g. on the number of bytes per value | |
138 // (cf. DicomImageInformation.h in Orthanc) | |
139 | |
140 if (!isRTDose) | |
141 { | |
142 if (isColor_) | |
143 { | |
144 expectedPixelFormat_ = Orthanc::PixelFormat_RGB24; | |
145 } | |
146 else if (isSigned_) | |
147 { | |
148 expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16; | |
149 } | |
150 else | |
151 { | |
152 expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; | |
153 } | |
154 } | |
155 } | |
156 | |
157 | |
158 void DicomFrameConverter::ReadParameters(const OrthancPlugins::IDicomDataset& dicom) | |
159 { | |
160 Orthanc::DicomMap converted; | |
161 | |
162 for (size_t i = 0; i < sizeof(IMAGE_TAGS) / sizeof(Orthanc::DicomTag); i++) | |
163 { | |
164 OrthancPlugins::DicomTag tag(IMAGE_TAGS[i].GetGroup(), IMAGE_TAGS[i].GetElement()); | |
165 | |
166 std::string value; | |
167 if (dicom.GetStringValue(value, tag)) | |
168 { | |
169 converted.SetValue(IMAGE_TAGS[i], value, false); | |
170 } | |
171 } | |
172 | |
173 ReadParameters(converted); | |
174 } | |
175 | |
176 | |
177 void DicomFrameConverter::ConvertFrameInplace(std::auto_ptr<Orthanc::ImageAccessor>& source) const | |
178 { | |
179 assert(sizeof(float) == 4); | |
180 | |
181 if (source.get() == NULL) | |
182 { | |
183 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
184 } | |
185 | |
186 if (source->GetFormat() == GetExpectedPixelFormat() && | |
187 source->GetFormat() == Orthanc::PixelFormat_RGB24) | |
188 { | |
189 // No conversion has to be done, check out (*) | |
190 return; | |
191 } | |
192 else | |
193 { | |
194 source.reset(ConvertFrame(*source)); | |
195 } | |
196 } | |
197 | |
198 | |
199 Orthanc::ImageAccessor* DicomFrameConverter::ConvertFrame(const Orthanc::ImageAccessor& source) const | |
200 { | |
201 assert(sizeof(float) == 4); | |
202 | |
203 Orthanc::PixelFormat sourceFormat = source.GetFormat(); | |
204 | |
205 if (sourceFormat != GetExpectedPixelFormat()) | |
206 { | |
207 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
208 } | |
209 | |
210 if (sourceFormat == Orthanc::PixelFormat_RGB24) | |
211 { | |
212 // This is the case of a color image. No conversion has to be done (*) | |
213 std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_RGB24, | |
214 source.GetWidth(), | |
215 source.GetHeight(), | |
216 false)); | |
217 Orthanc::ImageProcessing::Copy(*converted, source); | |
218 return converted.release(); | |
219 } | |
220 else | |
221 { | |
222 assert(sourceFormat == Orthanc::PixelFormat_Grayscale16 || | |
223 sourceFormat == Orthanc::PixelFormat_Grayscale32 || | |
224 sourceFormat == Orthanc::PixelFormat_SignedGrayscale16); | |
225 | |
226 // This is the case of a grayscale frame. Convert it to Float32. | |
227 std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, | |
228 source.GetWidth(), | |
229 source.GetHeight(), | |
230 false)); | |
231 Orthanc::ImageProcessing::Convert(*converted, source); | |
232 | |
233 // Correct rescale slope/intercept if need be | |
234 ApplyRescale(*converted, sourceFormat != Orthanc::PixelFormat_Grayscale32); | |
235 | |
236 return converted.release(); | |
237 } | |
238 } | |
239 | |
240 | |
241 void DicomFrameConverter::ApplyRescale(Orthanc::ImageAccessor& image, | |
242 bool useDouble) const | |
243 { | |
244 if (image.GetFormat() != Orthanc::PixelFormat_Float32) | |
245 { | |
246 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
247 } | |
248 | |
249 if (hasRescale_) | |
250 { | |
251 for (unsigned int y = 0; y < image.GetHeight(); y++) | |
252 { | |
253 float* p = reinterpret_cast<float*>(image.GetRow(y)); | |
254 | |
255 if (useDouble) | |
256 { | |
257 // Slower, accurate implementation using double | |
258 for (unsigned int x = 0; x < image.GetWidth(); x++, p++) | |
259 { | |
260 double value = static_cast<double>(*p); | |
261 *p = static_cast<float>(value * rescaleSlope_ + rescaleIntercept_); | |
262 } | |
263 } | |
264 else | |
265 { | |
266 // Fast, approximate implementation using float | |
267 for (unsigned int x = 0; x < image.GetWidth(); x++, p++) | |
268 { | |
269 *p = (*p) * static_cast<float>(rescaleSlope_) + static_cast<float>(rescaleIntercept_); | |
270 } | |
271 } | |
272 } | |
273 } | |
274 } | |
275 | |
276 | |
277 double DicomFrameConverter::Apply(double x) const | |
278 { | |
279 return x * rescaleSlope_ + rescaleIntercept_; | |
280 } | |
281 | |
282 } |