codac 2.0.0
Loading...
Searching...
No Matches
codac2_BinaryImage.h
Go to the documentation of this file.
1
9
10#pragma once
11
12#include <vector>
13#include <fstream>
15#include "codac2_Grid.h"
16
17namespace codac2
18{
19 using ImageDataType = uint32_t;
20 // The items of the binary image are not only booleans, they can be long
21 // integers resulting from the associated integral image. So they are coded
22 // in uint32_t.
23
24 template<size_t N>
25 class BinaryImage : public Grid
26 {
27 public:
28
29 explicit BinaryImage(const Eigen::Matrix<size_t,-1,1>& size, const Vector& dx, const Vector& origin)
30 : BinaryImage(size, dx, origin, Eigen::Matrix<int,-1,1>::Zero(N))
31 { }
32
33 explicit BinaryImage(const Eigen::Matrix<size_t,-1,1>& size, const Vector& dx, const Vector& origin, const Eigen::Matrix<int,-1,1>& local_shift)
34 : Grid(dx, origin, local_shift), _size(size)
35 {
36 assert_release(size.size() == N);
37 assert_release(all_same_size(size, dx, origin, local_shift));
38
39 _array_offsets[0] = 1;
40 for(Index i = 1 ; i < (Index)N ; i++)
41 {
42 _array_offsets[i] = _array_offsets[i-1]*_size[i-1];
43 if(i == N-1)
44 _data.resize(_array_offsets[i]*_size[i], 0); // all elements are set to 0
45 }
46 }
47
48 BinaryImage(const std::string& file, bool load_data = true)
49 : Grid(), _file_name(file)
50 {
51 std::ifstream f;
52 f.open(file, std::ios::binary);
53 if(!f.is_open())
54 throw std::runtime_error("Unable to load BinaryImage: " + file);
55
56 Grid::deserialize(f);
57 codac2::deserialize(f, _size);
58 codac2::deserialize(f, _array_offsets);
59 codac2::deserialize(f, _computed_as_integrale);
60
61 assert(_size.size() == N);
62 assert(all_same_size(_size, _dx, _origin, _local_shift));
63
64 BinaryImage<N>::_data_start_pos = f.tellg();
65
66 if(load_data)
67 this->load_data(f);
68
69 f.close();
70 }
71
72 void load_data()
73 {
74 assert(BinaryImage<N>::_data_start_pos > 0);
75
76 std::ifstream f;
77 f.open(_file_name, std::ios::binary);
78 if(!f.is_open())
79 throw std::runtime_error("Unable to load BinaryImage: " + _file_name);
80
81 load_data(f);
82 }
83
84 size_t nb_voxels() const
85 {
86 size_t data_size = _size[0];
87 for(Index i = 1 ; i < _size.size() ; i++) data_size *= _size[i];
88 return data_size;
89 }
90
91 void load_data(std::ifstream& f)
92 {
93 f.seekg(BinaryImage<N>::_data_start_pos, f.beg);
94 size_t data_size = nb_voxels();
95
96 if(data_size != 0)
97 {
98 _data.resize(data_size);
99 for(size_t i = 0 ; i < data_size ; i++)
100 f.read((char*)&_data[i], sizeof(ImageDataType));
101 assert(_data.size() == data_size);
102 }
103 }
104
105 void clear_data()
106 {
107 _data.clear();
108 }
109
110 bool data_has_been_loaded() const
111 {
112 return !_data.empty() || nb_voxels() == 0;
113 // nb_voxels() == 0 <=> _data.empty()
114 }
115
116 const Eigen::Matrix<size_t,N,1>& size() const
117 {
118 return _size;
119 }
120
121 void serialize() const
122 {
123 serialize(_file_name);
124 }
125
126 void serialize(const std::string& file) const
127 {
128 assert(file != "");
129 std::ofstream f;
130 f.open(file, std::ios::binary);
131 if(!f.is_open())
132 throw std::runtime_error("Unable to serialize: " + file);
133
134 Grid::serialize(f);
135 codac2::serialize(f, _size);
136 codac2::serialize(f, _array_offsets);
137 codac2::serialize(f, _computed_as_integrale);
138 f.write(reinterpret_cast<const char*>(_data.data()), sizeof(ImageDataType)*_data.size());
139 // todo: use a smaller type than ImageDataType for serialization
140
141 f.close();
142 }
143
144 GridBox to_grid(const IntervalVector& x) const
145 {
146 auto g = Grid::to_grid(x);
147
148 for(size_t i = 0 ; i < dim() ; i++)
149 if(x[i].ub() == real_box()[i].ub()) // upper border of the image
150 {
151 double last = local_indices_box()[i].ub();
152 g[i] = Interval(std::min(g[i].lb(),last),last);
153 }
154
155 return g;
156 }
157
158 ImageDataType& operator()(const std::array<int,N>& x)
159 {
160 return const_cast<ImageDataType&>(static_cast<const BinaryImage<N>&>(*this).operator()(x));
161 }
162
163 const ImageDataType& operator()(const std::array<int,N>& x) const
164 {
165 if(*std::min_element(std::begin(x),std::end(x)) < 0)
166 return _zero; // used for integral computation
167
168 assert(local_indices_box().contains(to_vector<N>(x)));
169 size_t array_index = _array_offsets[0]*x[0];
170 for(size_t i = 1 ; i < N ; i++)
171 array_index += _array_offsets[i]*x[i];
172
173 assert(array_index < _data.size());
174 return _data[array_index];
175 }
176
177 GridBox local_indices_box() const
178 {
179 GridBox b(_size.size());
180 for(Index i = 0 ; i < _size.size() ; i++)
181 if(_size[i] != 0)
182 b[i] = { 0, _size[i]-1. };
183
184 assert(b.has_integer_bounds());
185 return b;
186 }
187
188 GridBox absolute_indices_box() const
189 {
190 GridBox i = GridBox(Grid::local_shift().template cast<double>()) + local_indices_box();
191 assert(i.has_integer_bounds());
192 return i;
193 }
194
195 IntervalVector real_box() const
196 {
197 return to_reals(local_indices_box());
198 }
199
200 void convert_as_integrale_img()
201 {
202 static_assert((N == 2 || N == 3) && "dim > 3 cases are not handled");
203 // Note: for higher dimensions, one can refer to the publication
204 // "A note on the computation of high-dimensional integral images",
205 // Ernesto Tapia, Pattern Recognition Letters
206 // https://doi.org/10.1016/j.patrec.2010.10.007
207
208 if(_computed_as_integrale)
209 return;
210 _computed_as_integrale = true;
211
212 if constexpr(N == 2)
213 {
214 for(int i = 0 ; i < (int)_size[0] ; i++)
215 for(int j = 0 ; j < (int)_size[1] ; j++)
216 {
217 uint64_t s = (*this)({i-1,j}) + (*this)({i,j-1}) + (*this)({i,j}) - (*this)({i-1,j-1});
218 assert(s < std::numeric_limits<ImageDataType>::max());
219 (*this)({i,j}) = (ImageDataType)s;
220 }
221 }
222
223 else if constexpr(N == 3)
224 {
225 for(int i = 0 ; i < (int)_size[0] ; i++)
226 for(int j = 0 ; j < (int)_size[1] ; j++)
227 for(int k = 0 ; k < (int)_size[2] ; k++)
228 {
229 uint64_t s = (*this)({i-1,j,k}) + (*this)({i,j-1,k}) + (*this)({i,j,k-1})
230 + (*this)({i-1,j-1,k-1}) + (*this)({i,j,k}) - (*this)({i-1,j,k-1})
231 - (*this)({i,j-1,k-1}) - (*this)({i-1,j-1,k});
232 assert(s < std::numeric_limits<ImageDataType>::max()
233 && "integrale value exceeds the maximum value of ImageDataType");
234 (*this)({i,j,k}) = (ImageDataType)s;
235 }
236 }
237 }
238
239 void contract_indices(GridBox& x) const
240 {
241 assert(x.has_integer_bounds());
242 if(_data.size() == 0 || !x.is_subset(local_indices_box()) || x.is_empty())
243 return; // no contraction
244
245 assert(data_has_been_loaded());
246
247 if(nb_pixels_in(x) == 0)
248 {
249 x.set_empty();
250 return;
251 }
252
253 #if 1 // dichotomic algorithm
254
255 for(size_t k = 0 ; k < N ; k++)
256 {
257 int a, b;
258 auto x_ = x;
259
260 a = x[k].lb(); b = x[k].ub();
261 do {
262 int i = std::floor((a+b)/2.);
263 x_[k] = Interval(x[k].lb(),i);
264 if(nb_pixels_in(x_) == 0)
265 a = i+1;
266 else
267 b = i;
268 } while(b!=a);
269 x[k] = Interval(a,x[k].ub());
270
271 a = x[k].lb(); b = x[k].ub();
272 do {
273 int i = std::ceil((a+b)/2.);
274 x_[k] = Interval(i,x[k].ub());
275 if(nb_pixels_in(x_) == 0)
276 b = i-1;
277 else
278 a = i;
279 } while(b!=a);
280 x[k] = Interval(x[k].lb(),a);
281 }
282
283 #else // iterative algorithm
284
285 for(size_t k = 0 ; k < N ; k++)
286 {
287 int imin = x[k].lb(), imax = x[k].ub();
288 for(auto i = imin ; i <= imax ; i++)
289 {
290 auto x_ = x;
291 x_[k] = Interval(imin,i);
292 if(nb_pixels_in(x_) > 0)
293 {
294 imin = i;
295 break;
296 }
297 }
298 x[k] = Interval(imin,imax);
299
300 for(auto i = imax ; i >= imin ; i--)
301 {
302 auto x_ = x;
303 x_[k] = Interval(i,imax);
304 if(nb_pixels_in(x_) > 0)
305 {
306 imax = i;
307 break;
308 }
309 }
310 x[k] = Interval(imin,imax);
311 }
312
313 #endif
314 }
315
316 unsigned int nb_pixels_in(const GridBox& x) const
317 {
318 assert(x.has_integer_bounds());
319 assert(_computed_as_integrale && "conversion must be done beforehand");
320 assert(!local_indices_box().is_empty());
321 assert(!x.is_unbounded());
322
323 if constexpr(N == 2)
324 {
325 int xmin = x[0].lb(), xmax = x[0].ub();
326 int ymin = x[1].lb(), ymax = x[1].ub();
327
328 unsigned int n1 = (*this)({xmax, ymax });
329 unsigned int n2 = (*this)({xmax, ymin-1});
330 unsigned int n3 = (*this)({xmin-1, ymax });
331 unsigned int n4 = (*this)({xmin-1, ymin-1});
332
333 return n1-n2-n3+n4;
334 }
335
336 else if constexpr(N == 3)
337 {
338 int xmin = x[0].lb(), xmax = x[0].ub();
339 int ymin = x[1].lb(), ymax = x[1].ub();
340 int zmin = x[2].lb(), zmax = x[2].ub();
341
342 unsigned int n8 = (*this)({xmax, ymax, zmax });
343 unsigned int n5 = (*this)({xmin-1, ymin-1, zmax });
344 unsigned int n6 = (*this)({xmin-1, ymax, zmax });
345 unsigned int n7 = (*this)({xmax, ymin-1, zmax });
346 unsigned int n4 = (*this)({xmax, ymax, zmin-1});
347 unsigned int n1 = (*this)({xmin-1, ymin-1, zmin-1});
348 unsigned int n2 = (*this)({xmin-1, ymax, zmin-1});
349 unsigned int n3 = (*this)({xmax, ymin-1, zmin-1});
350
351 return (n8+n5-n6-n7)-(n4+n1-n2-n3);
352 }
353
354 else
355 static_assert(N >= 2 && N <= 3 && "dim > 3 cases are not handled");
356 }
357
358 protected:
359
360 std::string _file_name;
361 bool _computed_as_integrale = false;
362
363 Eigen::Matrix<size_t,N,1> _size, _array_offsets;
364 std::vector<ImageDataType> _data;
365 ImageDataType _zero = 0;
366
367 inline static std::streampos _data_start_pos = -1;
368 };
369}
bool is_empty() const
Checks whether the interval matrix is empty.
Definition codac2_MatrixBase_addons_IntervalMatrixBase.h:56
bool contains(const Matrix< double, RowsAtCompileTime, ColsAtCompileTime > &x) const
Checks if this interval matrix contains the specified matrix x.
Definition codac2_MatrixBase_addons_IntervalMatrixBase.h:408
auto ub() const
Returns a matrix containing the upper bounds of each interval element.
Definition codac2_MatrixBase_addons_IntervalMatrixBase.h:103
auto lb() const
Returns a matrix containing the lower bounds of each interval element.
Definition codac2_MatrixBase_addons_IntervalMatrixBase.h:91
Definition codac2_OctaSym.h:21
Eigen::Matrix< double,-1, 1 > Vector
Alias for a dynamically-sized column vector of doubles.
Definition codac2_Vector.h:24
void serialize(std::ostream &f, const T &x)
Writes the binary representation of a trivially copyable object to the given output stream.
Definition codac2_serialization.h:35
Eigen::Matrix< Interval,-1, 1 > IntervalVector
Alias for a dynamic-size column vector of intervals.
Definition codac2_IntervalVector.h:25
Eigen::Matrix< double,-1,-1 > Matrix
Alias for a dynamic-size matrix of doubles.
Definition codac2_Matrix.h:26
void deserialize(std::istream &f, T &x)
Reads the binary representation of a trivially copyable object from the given input stream.
Definition codac2_serialization.h:55