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