Mercurial > hg > orthanc-stone
comparison OrthancStone/Sources/Toolbox/ImageToolbox.cpp @ 1512:244ad1e4e76a
reorganization of folders
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 07 Jul 2020 16:21:02 +0200 |
parents | Framework/Toolbox/ImageToolbox.cpp@121d01aa328e |
children | 4fb8fdf03314 |
comparison
equal
deleted
inserted
replaced
1511:9dfeee74c1e6 | 1512:244ad1e4e76a |
---|---|
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-2020 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 #include "../OrthancStone.h" | |
22 #include "ImageToolbox.h" | |
23 | |
24 #include "../StoneException.h" | |
25 | |
26 #include <Images/ImageProcessing.h> | |
27 #include <Images/PixelTraits.h> | |
28 | |
29 #include <Logging.h> | |
30 #include <OrthancException.h> | |
31 | |
32 #include <boost/static_assert.hpp> | |
33 #include <boost/type_traits.hpp> | |
34 | |
35 #include <vector> | |
36 | |
37 #if !defined(ORTHANC_ENABLE_DCMTK) | |
38 # error ORTHANC_ENABLE_DCMTK is not defined | |
39 #endif | |
40 | |
41 #if !defined(ORTHANC_ENABLE_DCMTK_JPEG) | |
42 # error ORTHANC_ENABLE_DCMTK_JPEG is not defined | |
43 #endif | |
44 | |
45 #if !defined(ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS) | |
46 # error ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS is not defined | |
47 #endif | |
48 | |
49 | |
50 namespace OrthancStone | |
51 { | |
52 namespace | |
53 { | |
54 using Orthanc::PixelTraits; | |
55 using Orthanc::PixelFormat; | |
56 using Orthanc::ImageAccessor; | |
57 using Orthanc::PixelFormat; | |
58 | |
59 template<typename Orthanc::PixelFormat Format> | |
60 class PixelBinner | |
61 { | |
62 // "PixelBinner requires an arithmetic (integer or floating-point) pixel format" | |
63 typedef typename Orthanc::PixelTraits<Format>::PixelType PixelType; | |
64 BOOST_STATIC_ASSERT(boost::is_arithmetic<PixelType>::value); | |
65 | |
66 public: | |
67 PixelBinner(HistogramData& hd, double minValue, double maxValue) | |
68 : hd_(hd) | |
69 , minValue_(minValue) | |
70 , maxValue_(maxValue) | |
71 , division_(1.0 / hd_.binSize) | |
72 { | |
73 ORTHANC_ASSERT(hd_.bins.size() > 0); | |
74 ORTHANC_ASSERT(maxValue > minValue); | |
75 } | |
76 | |
77 ORTHANC_FORCE_INLINE void AddPixel(PixelType p) | |
78 { | |
79 if (p <= minValue_) | |
80 { | |
81 hd_.bins[0] += 1; | |
82 } | |
83 else if (p >= maxValue_) | |
84 { | |
85 hd_.bins.back() += 1; | |
86 } | |
87 else | |
88 { | |
89 double distanceFromMin = p - minValue_; | |
90 size_t binIndex = static_cast<size_t>( | |
91 std::floor(distanceFromMin * division_)); | |
92 if (binIndex >= hd_.bins.size()) | |
93 binIndex = hd_.bins.size() - 1; | |
94 hd_.bins[binIndex] += 1; | |
95 } | |
96 } | |
97 private: | |
98 HistogramData& hd_; | |
99 double minValue_; | |
100 double maxValue_; | |
101 double division_; | |
102 }; | |
103 | |
104 template<PixelFormat Format> | |
105 struct Histogram | |
106 { | |
107 typedef typename PixelTraits<Format>::PixelType PixelType; | |
108 | |
109 static void Apply(const Orthanc::ImageAccessor& img, HistogramData& hd, | |
110 double minValue = 0, | |
111 double maxValue = 0) | |
112 { | |
113 ORTHANC_ASSERT(Format == img.GetFormat(), | |
114 "Internal error. Wrong template histogram type"); | |
115 | |
116 const size_t height = img.GetHeight(); | |
117 const size_t width = img.GetHeight(); | |
118 | |
119 if ((minValue == 0) && (maxValue == 0)) | |
120 { | |
121 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
122 //ORTHANC_ASSERT(boost::is_integral<PixelType>::value, | |
123 // "Min and max values must be supplied for float-based histogram"); | |
124 // | |
125 //PixelTraits<Format>::SetMinValue(minValue); | |
126 //PixelTraits<Format>::SetMaxValue(maxValue); | |
127 } | |
128 | |
129 hd.minValue = minValue; | |
130 | |
131 // the following code is not really pretty but ensures | |
132 size_t numBins = static_cast<size_t>( | |
133 std::ceil((maxValue - minValue) / hd.binSize)); | |
134 | |
135 hd.bins.resize(numBins); | |
136 std::fill(hd.bins.begin(), hd.bins.end(), 0); | |
137 | |
138 PixelBinner<Format> binner(hd, minValue, maxValue); | |
139 for (uint32_t y = 0; y < height; ++y) | |
140 { | |
141 const PixelType* curPix = reinterpret_cast<const PixelType*>( | |
142 img.GetConstRow(y)); | |
143 | |
144 for (uint32_t x = 0; x < width; x++, curPix++) | |
145 { | |
146 binner.AddPixel(*curPix); | |
147 } | |
148 } | |
149 } | |
150 }; | |
151 | |
152 | |
153 template<PixelFormat Format> | |
154 struct ComputeMinMax__ | |
155 { | |
156 typedef typename PixelTraits<Format>::PixelType PixelType; | |
157 | |
158 static void Apply(const Orthanc::ImageAccessor& img, | |
159 PixelType& minValue, PixelType& maxValue) | |
160 { | |
161 ORTHANC_ASSERT(Format == img.GetFormat(), | |
162 "Internal error. Wrong template histogram type"); | |
163 | |
164 const size_t height = img.GetHeight(); | |
165 const size_t width = img.GetHeight(); | |
166 | |
167 if (height * width == 0) | |
168 { | |
169 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
170 } | |
171 | |
172 // min and max are crossed below. Think about it. This is OK :) | |
173 PixelTraits<Format>::SetMaxValue(minValue); | |
174 PixelTraits<Format>::SetMinValue(maxValue); | |
175 | |
176 for (uint32_t y = 0; y < height; ++y) | |
177 { | |
178 const PixelType* curPix = reinterpret_cast<const PixelType*>( | |
179 img.GetConstRow(y)); | |
180 | |
181 for (uint32_t x = 0; x < width; x++, curPix++) | |
182 { | |
183 if (*curPix <= minValue) | |
184 minValue = *curPix; | |
185 if (*curPix >= maxValue) | |
186 maxValue = *curPix; | |
187 } | |
188 } | |
189 } | |
190 }; | |
191 | |
192 template<PixelFormat Format> | |
193 void ComputeMinMax_(const Orthanc::ImageAccessor& img, | |
194 double& minValue, double& maxValue) | |
195 { | |
196 typedef typename PixelTraits<Format>::PixelType PixelType; | |
197 PixelType minValuePix = PixelType(); | |
198 PixelType maxValuePix = PixelType(); | |
199 ComputeMinMax__<Format>::Apply(img, minValuePix, maxValuePix); | |
200 minValue = static_cast<double>(minValuePix); | |
201 maxValue = static_cast<double>(maxValuePix); | |
202 } | |
203 | |
204 template<PixelFormat Format> | |
205 void ComputeHistogram_(const Orthanc::ImageAccessor& img, HistogramData& hd) | |
206 { | |
207 typedef typename PixelTraits<Format>::PixelType PixelType; | |
208 PixelType minValue = PixelType(); | |
209 PixelType maxValue = PixelType(); | |
210 ComputeMinMax__<Format>::Apply(img, minValue, maxValue); | |
211 | |
212 // make bins a little bigger to center integer pixel values | |
213 Histogram<Format>::Apply(img, hd, | |
214 static_cast<double>(minValue) - 0.5, | |
215 static_cast<double>(maxValue) + 0.5); | |
216 } | |
217 } | |
218 | |
219 void ComputeHistogram(const Orthanc::ImageAccessor& img, | |
220 HistogramData& hd, double binSize) | |
221 { | |
222 using namespace Orthanc; | |
223 | |
224 hd.binSize = binSize; | |
225 | |
226 // dynamic/static bridge | |
227 switch (img.GetFormat()) | |
228 { | |
229 case PixelFormat_Grayscale8: | |
230 ComputeHistogram_<PixelFormat_Grayscale8> (img, hd); | |
231 break; | |
232 case PixelFormat_Grayscale16: | |
233 ComputeHistogram_<PixelFormat_Grayscale16> (img, hd); | |
234 break; | |
235 case PixelFormat_SignedGrayscale16: | |
236 ComputeHistogram_<PixelFormat_SignedGrayscale16>(img, hd); | |
237 break; | |
238 case PixelFormat_Float32: | |
239 ComputeHistogram_<PixelFormat_Float32> (img, hd); | |
240 break; | |
241 case PixelFormat_Grayscale32: | |
242 ComputeHistogram_<PixelFormat_Grayscale32> (img, hd); | |
243 break; | |
244 case PixelFormat_Grayscale64: | |
245 ComputeHistogram_<PixelFormat_Grayscale64> (img, hd); | |
246 break; | |
247 default: | |
248 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
249 } | |
250 } | |
251 | |
252 void ComputeMinMax(const Orthanc::ImageAccessor& img, | |
253 double& minValue, double& maxValue) | |
254 { | |
255 using namespace Orthanc; | |
256 | |
257 // dynamic/static bridge | |
258 switch (img.GetFormat()) | |
259 { | |
260 case PixelFormat_Grayscale8: | |
261 ComputeMinMax_<PixelFormat_Grayscale8> (img, minValue, maxValue); | |
262 break; | |
263 case PixelFormat_Grayscale16: | |
264 ComputeMinMax_<PixelFormat_Grayscale16> (img, minValue, maxValue); | |
265 break; | |
266 case PixelFormat_SignedGrayscale16: | |
267 ComputeMinMax_<PixelFormat_SignedGrayscale16>(img, minValue, maxValue); | |
268 break; | |
269 case PixelFormat_Float32: | |
270 ComputeMinMax_<PixelFormat_Float32> (img, minValue, maxValue); | |
271 break; | |
272 case PixelFormat_Grayscale32: | |
273 ComputeMinMax_<PixelFormat_Grayscale32> (img, minValue, maxValue); | |
274 break; | |
275 case PixelFormat_Grayscale64: | |
276 ComputeMinMax_<PixelFormat_Grayscale64> (img, minValue, maxValue); | |
277 break; | |
278 default: | |
279 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
280 } | |
281 | |
282 } | |
283 | |
284 void DumpHistogramResult(std::string& s, const HistogramData& hd) | |
285 { | |
286 std::stringstream ss; | |
287 ss << "Histogram:\n"; | |
288 ss << "==========\n"; | |
289 ss << "\n"; | |
290 ss << "minValue : " << hd.minValue << "\n"; | |
291 ss << "binSize : " << hd.binSize << "\n"; | |
292 ss << "bins.size() : " << hd.bins.size() << "\n"; | |
293 ss << "bins :\n"; | |
294 double curBinStart = hd.minValue; | |
295 size_t pixCount = 0; | |
296 for (size_t i = 0; i < hd.bins.size(); ++i) | |
297 { | |
298 ss << "index: " << i << " (from " << curBinStart << " to " | |
299 << curBinStart + hd.binSize << ") : " << hd.bins[i] << " pixels\n"; | |
300 curBinStart += hd.binSize; | |
301 pixCount += hd.bins[i]; | |
302 } | |
303 ss << "total pix. count: " << pixCount << "\n"; | |
304 s = ss.str(); | |
305 } | |
306 | |
307 | |
308 bool ImageToolbox::IsDecodingSupported(Orthanc::DicomTransferSyntax& transferSyntax) | |
309 { | |
310 switch (transferSyntax) | |
311 { | |
312 case Orthanc::DicomTransferSyntax_LittleEndianImplicit: | |
313 case Orthanc::DicomTransferSyntax_LittleEndianExplicit: | |
314 case Orthanc::DicomTransferSyntax_DeflatedLittleEndianExplicit: | |
315 case Orthanc::DicomTransferSyntax_BigEndianExplicit: | |
316 case Orthanc::DicomTransferSyntax_RLELossless: | |
317 return true; | |
318 | |
319 #if (ORTHANC_ENABLE_DCMTK == 1) && (ORTHANC_ENABLE_DCMTK_JPEG == 1) | |
320 case Orthanc::DicomTransferSyntax_JPEGProcess1: | |
321 case Orthanc::DicomTransferSyntax_JPEGProcess2_4: | |
322 case Orthanc::DicomTransferSyntax_JPEGProcess14: | |
323 case Orthanc::DicomTransferSyntax_JPEGProcess14SV1: | |
324 return true; | |
325 #endif | |
326 | |
327 #if (ORTHANC_ENABLE_DCMTK == 1) && (ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1) | |
328 case Orthanc::DicomTransferSyntax_JPEGLSLossless: | |
329 case Orthanc::DicomTransferSyntax_JPEGLSLossy: | |
330 return true; | |
331 #endif | |
332 | |
333 default: | |
334 return false; | |
335 } | |
336 } | |
337 } |