Mercurial > hg > orthanc-stone
comparison Framework/Toolbox/ImageToolbox.cpp @ 1286:ddb6676bbcbf bugs/2020-02-invisible-slice
Added Histogram functions (new files: ImageToolbox...)
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Fri, 14 Feb 2020 14:59:32 +0100 |
parents | |
children | d8c42e5e5843 |
comparison
equal
deleted
inserted
replaced
1284:1bb54005e2bd | 1286:ddb6676bbcbf |
---|---|
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 PixelFormat Format> | |
46 class PixelBinner | |
47 { | |
48 // "PixelBinner requires an arithmetic (integer or floating-point) pixel format" | |
49 typedef typename 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 uint8_t* buffer = reinterpret_cast<const uint8_t*>( | |
103 img.GetConstBuffer()); | |
104 | |
105 const size_t pitch = img.GetPitch(); | |
106 const size_t bytesPerPix = img.GetBytesPerPixel(); | |
107 | |
108 const size_t height = img.GetHeight(); | |
109 const size_t width = img.GetHeight(); | |
110 | |
111 if ((minValue == 0) && (maxValue == 0)) | |
112 { | |
113 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
114 //ORTHANC_ASSERT(boost::is_integral<PixelType>::value, | |
115 // "Min and max values must be supplied for float-based histogram"); | |
116 // | |
117 //PixelTraits<Format>::SetMinValue(minValue); | |
118 //PixelTraits<Format>::SetMaxValue(maxValue); | |
119 } | |
120 | |
121 hd.minValue = minValue; | |
122 | |
123 // the following code is not really pretty but ensures | |
124 size_t numBins = static_cast<size_t>( | |
125 std::ceil((maxValue - minValue) / hd.binSize)); | |
126 | |
127 hd.bins.resize(numBins); | |
128 std::fill(hd.bins.begin(), hd.bins.end(), 0); | |
129 | |
130 PixelBinner<Format> binner(hd, minValue, maxValue); | |
131 for (uint32_t y = 0; y < height; ++y) | |
132 { | |
133 const PixelType* curPix = reinterpret_cast<const PixelType*>( | |
134 img.GetConstRow(y)); | |
135 | |
136 for (uint32_t x = 0; x < width; x++, curPix++) | |
137 { | |
138 binner.AddPixel(*curPix); | |
139 } | |
140 } | |
141 } | |
142 }; | |
143 | |
144 | |
145 template<PixelFormat Format> | |
146 struct ComputeMinMax__ | |
147 { | |
148 typedef typename PixelTraits<Format>::PixelType PixelType; | |
149 | |
150 static void Apply(const Orthanc::ImageAccessor& img, | |
151 PixelType& minValue, PixelType& maxValue) | |
152 { | |
153 ORTHANC_ASSERT(Format == img.GetFormat(), | |
154 "Internal error. Wrong template histogram type"); | |
155 | |
156 const uint8_t* buffer = reinterpret_cast<const uint8_t*>( | |
157 img.GetConstBuffer()); | |
158 | |
159 const size_t pitch = img.GetPitch(); | |
160 const size_t bytesPerPix = img.GetBytesPerPixel(); | |
161 | |
162 const size_t height = img.GetHeight(); | |
163 const size_t width = img.GetHeight(); | |
164 | |
165 if (height * width == 0) | |
166 { | |
167 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
168 } | |
169 | |
170 // min and max are crossed below. Think about it. This is OK :) | |
171 PixelTraits<Format>::SetMaxValue(minValue); | |
172 PixelTraits<Format>::SetMinValue(maxValue); | |
173 | |
174 for (uint32_t y = 0; y < height; ++y) | |
175 { | |
176 const PixelType* curPix = reinterpret_cast<const PixelType*>( | |
177 img.GetConstRow(y)); | |
178 | |
179 for (uint32_t x = 0; x < width; x++, curPix++) | |
180 { | |
181 if (*curPix <= minValue) | |
182 minValue = *curPix; | |
183 if (*curPix >= maxValue) | |
184 maxValue = *curPix; | |
185 } | |
186 } | |
187 } | |
188 }; | |
189 | |
190 template<PixelFormat Format> | |
191 void ComputeMinMax_(const Orthanc::ImageAccessor& img, | |
192 double& minValue, double& maxValue) | |
193 { | |
194 typedef typename PixelTraits<Format>::PixelType PixelType; | |
195 PixelType minValuePix = PixelType(); | |
196 PixelType maxValuePix = PixelType(); | |
197 ComputeMinMax__<Format>::Apply(img, minValuePix, maxValuePix); | |
198 minValue = static_cast<double>(minValuePix); | |
199 maxValue = static_cast<double>(maxValuePix); | |
200 } | |
201 | |
202 template<PixelFormat Format> | |
203 void ComputeHistogram_(const Orthanc::ImageAccessor& img, HistogramData& hd) | |
204 { | |
205 typedef typename PixelTraits<Format>::PixelType PixelType; | |
206 PixelType minValue = PixelType(); | |
207 PixelType maxValue = PixelType(); | |
208 ComputeMinMax__<Format>::Apply(img, minValue, maxValue); | |
209 | |
210 // make bins a little bigger to center integer pixel values | |
211 Histogram<Format>::Apply(img, hd, | |
212 static_cast<double>(minValue) - 0.5, | |
213 static_cast<double>(maxValue) + 0.5); | |
214 } | |
215 } | |
216 | |
217 void ComputeHistogram(const Orthanc::ImageAccessor& img, | |
218 HistogramData& hd, double binSize) | |
219 { | |
220 using namespace Orthanc; | |
221 | |
222 hd.binSize = binSize; | |
223 | |
224 // dynamic/static bridge | |
225 switch (img.GetFormat()) | |
226 { | |
227 case PixelFormat_Grayscale8: | |
228 ComputeHistogram_<PixelFormat_Grayscale8> (img, hd); | |
229 break; | |
230 case PixelFormat_Grayscale16: | |
231 ComputeHistogram_<PixelFormat_Grayscale16> (img, hd); | |
232 break; | |
233 case PixelFormat_SignedGrayscale16: | |
234 ComputeHistogram_<PixelFormat_SignedGrayscale16>(img, hd); | |
235 break; | |
236 case PixelFormat_Float32: | |
237 ComputeHistogram_<PixelFormat_Float32> (img, hd); | |
238 break; | |
239 case PixelFormat_Grayscale32: | |
240 ComputeHistogram_<PixelFormat_Grayscale32> (img, hd); | |
241 break; | |
242 case PixelFormat_Grayscale64: | |
243 ComputeHistogram_<PixelFormat_Grayscale64> (img, hd); | |
244 break; | |
245 default: | |
246 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
247 } | |
248 } | |
249 | |
250 void ComputeMinMax(const Orthanc::ImageAccessor& img, | |
251 double& minValue, double& maxValue) | |
252 { | |
253 using namespace Orthanc; | |
254 | |
255 // dynamic/static bridge | |
256 switch (img.GetFormat()) | |
257 { | |
258 case PixelFormat_Grayscale8: | |
259 ComputeMinMax_<PixelFormat_Grayscale8> (img, minValue, maxValue); | |
260 break; | |
261 case PixelFormat_Grayscale16: | |
262 ComputeMinMax_<PixelFormat_Grayscale16> (img, minValue, maxValue); | |
263 break; | |
264 case PixelFormat_SignedGrayscale16: | |
265 ComputeMinMax_<PixelFormat_SignedGrayscale16>(img, minValue, maxValue); | |
266 break; | |
267 case PixelFormat_Float32: | |
268 ComputeMinMax_<PixelFormat_Float32> (img, minValue, maxValue); | |
269 break; | |
270 case PixelFormat_Grayscale32: | |
271 ComputeMinMax_<PixelFormat_Grayscale32> (img, minValue, maxValue); | |
272 break; | |
273 case PixelFormat_Grayscale64: | |
274 ComputeMinMax_<PixelFormat_Grayscale64> (img, minValue, maxValue); | |
275 break; | |
276 default: | |
277 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
278 } | |
279 | |
280 } | |
281 | |
282 void DumpHistogramResult(std::string& s, const HistogramData& hd) | |
283 { | |
284 std::stringstream ss; | |
285 ss << "Histogram:\n"; | |
286 ss << "==========\n"; | |
287 ss << "\n"; | |
288 ss << "minValue : " << hd.minValue << "\n"; | |
289 ss << "binSize : " << hd.binSize << "\n"; | |
290 ss << "bins.size() : " << hd.bins.size() << "\n"; | |
291 ss << "bins :\n"; | |
292 double curBinStart = hd.minValue; | |
293 size_t pixCount = 0; | |
294 for (size_t i = 0; i < hd.bins.size(); ++i) | |
295 { | |
296 ss << "index: " << i << " (from " << curBinStart << " to " | |
297 << curBinStart + hd.binSize << ") : " << hd.bins[i] << " pixels\n"; | |
298 curBinStart += hd.binSize; | |
299 pixCount += hd.bins[i]; | |
300 } | |
301 ss << "total pix. count: " << pixCount << "\n"; | |
302 s = ss.str(); | |
303 } | |
304 } |