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 }