oln 10.58: Histogram of non-scalar type using images

Index: olena/ChangeLog from Niels Van Vliet <niels@lrde.epita.fr> * oln/utils/histogram.hh: Store histograms in images. Support histograms of non-scalar types (ie. rgb_8). Fix bugs. * oln/convert/value_to_point: Add file. Add conversions from values to points. * tests/utils/tests/histogram: Add tests. * oln/core/image.hh: Add dim_traits. * oln/core/image1.hh: Likewise. * oln/core/image2.hh: Likewise. * oln/core/image3.hh: Likewise. * Makefile.am: Include convert/value_to_point.hh Index: olena/oln/core/image1d.hh --- olena/oln/core/image1d.hh Thu, 07 Aug 2003 02:08:21 +0200 david (oln/c/47_image1d.hh 1.28 640) +++ olena/oln/core/image1d.hh Mon, 02 Feb 2004 09:38:51 +0100 niels (oln/c/47_image1d.hh 1.28 640) @@ -1,4 +1,4 @@ -// Copyright (C) 2001, 2002, 2003 EPITA Research and Development Laboratory +// Copyright (C) 2001, 2002, 2003, 2004 EPITA Research and Development Laboratory // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -160,7 +160,11 @@ }; - + template <class T, class Exact> + struct dim_traits<1, T, Exact> + { + typedef image1d<T, Exact> img_type; + }; } // end of oln #endif // ! OLENA_CORE_IMAGE1D_HH Index: olena/oln/core/image2d.hh --- olena/oln/core/image2d.hh Thu, 07 Aug 2003 02:08:21 +0200 david (oln/c/45_image2d.hh 1.30 640) +++ olena/oln/core/image2d.hh Mon, 02 Feb 2004 09:39:16 +0100 niels (oln/c/45_image2d.hh 1.30 640) @@ -1,4 +1,4 @@ -// Copyright (C) 2001, 2002, 2003 EPITA Research and Development Laboratory +// Copyright (C) 2001, 2002, 2003, 2004 EPITA Research and Development Laboratory // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -160,6 +160,11 @@ }; + template <class T, class Exact> + struct dim_traits<2, T, Exact> + { + typedef image2d<T, Exact> img_type; + }; } // end of oln #endif // ! OLENA_CORE_IMAGE2D_HH Index: olena/oln/core/image3d.hh --- olena/oln/core/image3d.hh Thu, 07 Aug 2003 02:08:21 +0200 david (oln/c/43_image3d.hh 1.27 640) +++ olena/oln/core/image3d.hh Mon, 02 Feb 2004 09:39:51 +0100 niels (oln/c/43_image3d.hh 1.27 640) @@ -1,4 +1,4 @@ -// Copyright (C) 2001, 2002, 2003 EPITA Research and Development Laboratory +// Copyright (C) 2001, 2002, 2003, 2004 EPITA Research and Development Laboratory // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -159,6 +159,11 @@ }; + template <class T, class Exact> + struct dim_traits<3, T, Exact> + { + typedef image3d<T, Exact> img_type; + }; } // end of oln #endif // ! OLENA_CORE_IMAGE3D_HH Index: olena/oln/core/image.hh --- olena/oln/core/image.hh Thu, 07 Aug 2003 02:08:21 +0200 david (oln/d/18_image.hh 1.20 640) +++ olena/oln/core/image.hh Mon, 02 Feb 2004 09:37:19 +0100 niels (oln/d/18_image.hh 1.20 640) @@ -1,4 +1,4 @@ -// Copyright (C) 2001, 2002, 2003 EPITA Research and Development Laboratory +// Copyright (C) 2001, 2002, 2003, 2004 EPITA Research and Development Laboratory // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -121,6 +121,11 @@ typedef typename mlc::exact<I>::ret::template mute<T>::ret ret; }; + //define img_type equals to the image of dim Dim + template <unsigned Dim, class T, class Exact = mlc::final> + struct dim_traits + { + }; } // end of oln #endif // ! OLENA_CORE_IMAGE_HH Index: olena/oln/utils/histogram.hh --- olena/oln/utils/histogram.hh Thu, 09 Oct 2003 15:31:32 +0200 burrus_n (oln/10_histogram. 1.6.1.14 640) +++ olena/oln/utils/histogram.hh Sun, 01 Feb 2004 23:07:40 +0100 niels (oln/10_histogram. 1.6.1.14 640) @@ -1,4 +1,4 @@ -// Copyright (C) 2001, 2002, 2003 EPITA Research and Development Laboratory +// Copyright (C) 2001, 2002, 2003, 2004 EPITA Research and Development Laboratory // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -30,98 +30,123 @@ # include <ntg/basics.hh> # include <oln/basics.hh> - +# include <oln/convert/value_to_point.hh> # include <vector> namespace oln { namespace utils { - template< typename T, typename U> + template< typename T, typename CPT, class V2P> class histogram; - template< typename T, typename U > + template< typename T, typename CPT, class V2P > T - min(const histogram<T, U>& hist); + min(const histogram<T, CPT, V2P>& hist); - template< typename T, typename U > + template< typename T, typename CPT, class V2P > T - max(const histogram<T, U>& hist); + max(const histogram<T, CPT, V2P>& hist); - template< typename T, typename U = unsigned> + template<typename T, + typename CPT = unsigned, + class V2P = value_to_point<T> > class histogram { - private: - typedef typename ntg_is_a(T, ntg::non_vectorial)::ensure_type ensure_type; public: + typedef T input_type; + typedef V2P value_to_point_type; + typedef CPT cpt_type; + typedef histogram<T, CPT, V2P> self_type; + typedef typename value_to_point_type::result_type point_type; + enum {dim = value_to_point_type::result_type::dim}; + typedef typename dim_traits<dim, CPT>::img_type img_type; + + friend T min<T, CPT, V2P>(const histogram<T, CPT, V2P>& hist); + friend T max<T, CPT, V2P>(const histogram<T, CPT, V2P>& hist); - histogram() : values_(0) + histogram(const value_to_point_type & c2p = value_to_point_type()): + v2p_(c2p), img_(v2p_.size()) { - unsigned size = unsigned(ntg_max_val(T) - - ntg_min_val(T) - + ntg_unit_val(T)); - // FIXME: what if T is an unsigned int? - // This should be checked more strictly somewhere. - // size = max + 1 = 0 - precondition(size > 0); - values_ = new U[size]; - for (unsigned i = 0; i < size; ++i) - values_[i] = 0; + clear(); } - ~histogram() - { - if (values_) + template <class I> + histogram(const abstract::image<I> & input, + const value_to_point_type & v2p = value_to_point_type()): + v2p_(v2p), img_(v2p_.size()) { - delete[] values_; - values_ = 0; + clear(); + init(input); } + + void + clear() + { + typename img_type::iter_type it(img_ ); + for_all(it) + img_[it] = ntg_zero_val(CPT); } - U& - operator[](const T& i) + const CPT& + operator[](const T &v)const { - return values_[unsigned(i - ntg_min_val(T))]; + return img_[v2p_(v)]; } - friend T min<T, U>(const histogram<T, U>& hist); - friend T max<T, U>(const histogram<T, U>& hist); + CPT& + operator[](const T &v) + { + return img_[v2p_(v)]; + } template <class I> void init(const abstract::image<I> &img) { - oln_iter_type(I) p(img); + oln_iter_type(I) it_img(img); - for_all (p) - values_[unsigned(img[p])]++; + for_all(it_img) + ++img_[v2p_(img[it_img])]; + } + + const img_type & + image() const + { + return img_; } protected: - U *values_; + const value_to_point_type v2p_; + img_type img_; }; - template< typename T, typename U > + //Note: If there is no min an assertion will fail at the end of the loop. + template< typename T, typename CPT, class V2P > inline T - min(const histogram<T, U>& hist) + min(const histogram<T, CPT, V2P>& hist) { - unsigned i; - for (i = 0; i < unsigned(ntg_max_val(T) - ntg_min_val(T)); ++i) - if (hist.values_[i] > 0) + typedef typename ntg_is_a(T, ntg::non_vectorial)::ensure_type ensure_type; + + T i; + for (i = ntg_min_val(T); i <= ntg_max_val(T); ++i) + if (hist[i] > ntg_zero_val(CPT)) break; - return T(ntg_min_val(T) + i); + return i; } - - template< typename T, typename U > + //Note: If there is no max an assertion will fail at the end of the loop. + template< typename T, typename CPT, class V2P > inline T - max(const histogram<T, U>& hist) + max(const histogram<T, CPT, V2P>& hist) { - unsigned i; - for (i = unsigned(ntg_max_val(T) - ntg_min_val(T)); i > 0; --i) - if (hist.values_[i] > 0) + typedef typename ntg_is_a(T, ntg::non_vectorial)::ensure_type ensure_type; + + T i; + for (i = ntg_max_val(T); i >= ntg_min_val(T); --i) + if (hist[i] > ntg_zero_val(CPT)) break; - return T(ntg_min_val(T) + i); + return i; } /* The two functions above can be slow when the histogram is sparse @@ -144,12 +169,14 @@ value are accessed, and delay the _real_ min and max compuation until min() or max() is called. */ - template< typename T, typename U = unsigned> - class histogram_minmax : public histogram<T, U> + template< typename T, typename CPT = unsigned, class V2P = value_to_point<T> > + class histogram_minmax : public histogram<T, CPT, V2P> { + private: + typedef typename ntg_is_a(T, ntg::non_vectorial)::ensure_type ensure_type; protected: void - adjust(unsigned idx) + adjust(const T &idx) { if (idx > max_) max_ = idx; @@ -158,78 +185,101 @@ } public: - histogram_minmax() : - histogram<T,U>(), - min_(0), max_(unsigned(ntg_max_val(T) - ntg_min_val(T))) {} - U& + typedef V2P value_to_point_type; + + histogram_minmax(const value_to_point_type & v2p = value_to_point_type()) : + histogram<T, CPT, V2P>(v2p), + min_(ntg_min_val(T)), max_(ntg_max_val(T)) {} + + template <class I> + histogram_minmax(const abstract::image<I> & input, + const value_to_point_type & v2p = value_to_point_type()) : + histogram<T, CPT, V2P>(input, v2p), + min_(ntg_min_val(T)), max_(ntg_max_val(T)) {} + + + const CPT& + operator[](const T& i) const + { + adjust(i); + return img_[v2p_(i)]; + } + + CPT& operator[](const T& i) { - unsigned idx = unsigned(i - ntg_min_val(T)); - adjust(idx); - return this->values_[idx]; + adjust(i); + return img_[v2p_(i)]; } - U + T min() { - unsigned i; - for (i = min_; i < unsigned(ntg_max_val(T) - ntg_min_val(T)); ++i) - if (this->values_[i] > 0) + for (; min_ <= ntg_max_val(T); ++min_) + if (img_[v2p_(min_)] > ntg_zero_val(CPT)) break; - min_ = i; - return T(ntg_min_val(T) + i); + return min_; } T max() { - unsigned i; - for (i = max_; i > 0; --i) - if (this->values_[i] > 0) + for (; max_ > 0; --max_) + if (img_[v2p_(max_)] > ntg_zero_val(CPT)) break; - max_ = i; - return T(ntg_min_val(T) + i); + return max_; } protected: - unsigned min_, max_; // indices of min and max elements + T min_, max_; // indices of min and max elements }; - template< typename T, typename U = unsigned> - class histogram_min : public histogram<T, U> + template< typename T, typename CPT = unsigned, class V2P = value_to_point<T> > + class histogram_min : public histogram<T, CPT, V2P> { + private: + typedef typename ntg_is_a(T, ntg::non_vectorial)::ensure_type ensure_type; protected: void - adjust(unsigned idx) + adjust(const T& idx) { if (idx < min_) min_ = idx; } - public: - histogram_min() : histogram<T,U>(), min_(0) {} + typedef V2P value_to_point_type; + + histogram_min(const value_to_point_type & v2p = value_to_point_type()) : + histogram<T, CPT, V2P>(v2p), min_(ntg_min_val(T)) {} + + template <class I> + histogram_min(const abstract::image<I> & input, + const value_to_point_type & v2p = value_to_point_type()) : + histogram<T, CPT, V2P>(input, v2p), min_(ntg_min_val(T)) {} + - U& + const CPT& + operator[](const T& i) const + { + adjust(i); + return img_[v2p_(i)]; + } + + CPT& operator[](const T& i) { - // FIXME: subtractions is not necessarily defined for T - // Casts should be done sooner and type conformance checked. - // ntg_storage_type(T) is really weird. - unsigned idx = unsigned(ntg_storage_type(T)(i) - ntg_min_val(T)); - adjust(idx); - return this->values_[idx]; + adjust(i); + return img_[v2p_(i)]; } - U + T min() { - unsigned i; - for (i = min_; i < unsigned(ntg_max_val(T) - ntg_min_val(T)); ++i) - if (this->values_[i] > 0) + for (; min_ <= ntg_max_val(T); ++min_) + if (img_[v2p_(min_)] > ntg_zero_val(CPT)) break; - min_ = i; - return T(ntg_min_val(T) + i); + return min_; } T @@ -239,41 +289,52 @@ } protected: - unsigned min_; // index of min element + T min_; // index of min element }; - template< typename T, typename U = unsigned> - class histogram_max : public histogram<T, U> + template< typename T, typename CPT = unsigned, class V2P = value_to_point<T> > + class histogram_max : public histogram<T, CPT, V2P> { protected: void - adjust(unsigned idx) + adjust(const T& idx) { if (idx > max_) max_ = idx; } public: - histogram_max() : - histogram<T,U>(),max_(unsigned(ntg_max_val(T) - ntg_min_val(T))) {} + typedef V2P value_to_point_type; + + histogram_max(const value_to_point_type & v2p = value_to_point_type()) : + histogram<T, CPT, V2P>(v2p),max_(ntg_max_val(T)) {} + + template <class I> + histogram_max(const abstract::image<I> & input, + const value_to_point_type & v2p = value_to_point_type()) : + histogram<T, CPT, V2P>(input, v2p),max_(ntg_max_val(T)) {} + + const CPT& + operator[](const T& i) const + { + adjust(i); + return img_[v2p_(i)]; + } - U& + CPT& operator[](const T& i) { - unsigned idx = unsigned(unsigned(i) - unsigned(ntg_min_val(T))); - adjust(idx); - return this->values_[idx]; + adjust(i); + return img_[v2p_(i)]; } T max() { - unsigned i; - for (i = max_; i > 0; --i) - if (this->values_[i] > 0) + for (; max_ > 0; --max_) + if (img_[v2p_(max_)] > ntg_zero_val(CPT)) break; - max_ = i; - return T(ntg_min_val(T) + i); + return max_; } T @@ -283,31 +344,30 @@ } protected: - unsigned max_; // index of max element + T max_; // index of max element }; - - template< typename T, typename U > + template< typename T, typename CPT, class V2P > inline T - min(histogram_minmax<T, U>& hist) + min(histogram_minmax<T, CPT, V2P>& hist) { return hist.min(); } - template< typename T, typename U > + template< typename T, typename CPT, class V2P > inline T - min(histogram_min<T, U>& hist) + min(histogram_min<T, CPT, V2P>& hist) { return hist.min(); } - template< typename T, typename U > + template< typename T, typename CPT, class V2P > inline T - max(histogram_minmax<T, U>& hist) + max(histogram_minmax<T, CPT, V2P>& hist) { return hist.max(); } - template< typename T, typename U > + template< typename T, typename CPT, class V2P > inline T - max(histogram_max<T, U>& hist) + max(histogram_max<T, CPT, V2P>& hist) { return hist.max(); } @@ -319,28 +379,29 @@ { typedef oln_value_type(I) val; + typedef typename ntg_is_a(val, ntg::non_vectorial)::ensure_type ensure_type; + // check the size - precondition(v.size() == unsigned(im.npoints())); - // unsigned(im.nrows() * im.ncols())); + precondition(v.size() == im.npoints()); // calculate the histogram of the image - utils::histogram<oln_value_type(I) > histo; - histo.init(im); + utils::histogram<val> histo(im); // initialize the array of pointer to the point in the result // with the histogram we can know the number of each color and // then calculate an array of pointer for quick access to each // value of the image - std::vector<oln_point_type(I)* > ptr(ntg_max_val(val) + 1); + const ntg_cumul_type(val) card = ntg_max_val(val) - ntg_min_val(val) + 1; + std::vector<oln_point_type(I)* > ptr(card); ptr[0] = &(v[0]); - for (int i = ntg_min_val(val) + 1; i <= ntg_max_val(val); i++) - ptr[unsigned(i)] = ptr[unsigned(i - 1)] + histo[i - 1]; + for (ntg_cumul_type(val) i = 1; i < card; i++) + ptr[i] = ptr[i - 1] + histo[i - 1 + ntg_min_val(val)]; // Now iterate on the image to sort point in the order of their // level oln_iter_type(I) p(im); for_all(p) - *(ptr[unsigned(im[p])]++) = p; + *(ptr[unsigned(im[p] - ntg_min_val(val))]++) = p; } } // end of namespace utils Index: olena/oln/Makefile.am --- olena/oln/Makefile.am Thu, 22 Jan 2004 21:46:17 +0100 palma_g (oln/q/47_Makefile.a 1.3.1.1.1.6.1.7 640) +++ olena/oln/Makefile.am Sun, 01 Feb 2004 18:24:02 +0100 niels (oln/q/47_Makefile.a 1.3.1.1.1.6.1.7 640) @@ -33,6 +33,7 @@ convert/rgbnrgb.hh \ convert/rgbxyz.hh \ convert/stretch.hh \ + convert/value_to_point.hh \ convol/convolution.hh \ convol/fast_gaussian.hh \ convol/fast_gaussian.hxx \ Index: olena/tests/utils/tests/histogram --- olena/tests/utils/tests/histogram Thu, 07 Aug 2003 02:37:23 +0200 burrus_n (oln/u/27_histogram. 1.2 600) +++ olena/tests/utils/tests/histogram Sun, 01 Feb 2004 21:07:51 +0100 niels (oln/u/27_histogram. 1.2 600) @@ -2,6 +2,7 @@ #include <ntg/int.hh> #include <oln/basics2d.hh> +#include <oln/basics1d.hh> #include <oln/utils/histogram.hh> #include <iostream> @@ -21,6 +22,112 @@ fail = true; \ } + +bool +check_int_u8() +{ + bool res(true); + image1d<int_u8> iu8(4); + iu8(0) = 8; + iu8(1) = 8; + iu8(2) = 7; + iu8(3) = 1; + histogram<int_u8> hu8(iu8); + res = res && + hu8[8] == 2 && + hu8[7] == 1 && + hu8[1] == 1 && + hu8[4] == 0 && + hu8[ntg_max_val(int_u8)] == 0 && + hu8[ntg_min_val(int_u8)] == 0; + return res; +}; + +bool +check_int_s4_and_distr() +{ + bool res(true); + image1d<int_s<4> > is4(4); + is4(0) = -8; + is4(1) = -8; + is4(2) = +7; + is4(3) = 0; + histogram<int_s<4> > his4(is4); + res = res && + his4[-8] == 2 && + his4[+7] == 1 && + his4[0] == 1 && + his4[-7] == 0; + std::vector<point1d> v1(is4.npoints()); + distrib_sort(is4, v1); + res = res && + (v1[0] == point1d(0) || v1[0] == point1d(1)) && + (v1[1] == point1d(0) || v1[1] == point1d(1)) && + v1[2] == point1d(3) & + v1[3] == point1d(2); + return res; +} + + + +bool +check_min_max() +{ + bool res(true); + image1d<int_s<4> > is4(4); + is4(0) = -4; + is4(1) = -2; + is4(2) = +3; + is4(3) = 1; + histogram<int_s<4> > his4(is4); + + res = res && + min(his4) == -4 + && + max(his4) == 3; + + histogram_min<int_s<4> > his4_min(is4); + histogram_max<int_s<4> > his4_max(is4); + histogram_minmax<int_s<4> > his4_minmax(is4); + + his4_minmax[-5] = his4_min[-5] = his4_max[-5] = his4[-5] = 20; + his4_minmax[3] = his4_min[3] = his4_max[3] = his4[3] = 0; + + his4_minmax[2] = his4_min[2] = his4_max[2] = his4[2] = 42; + + res = res && + min(his4) == -5 && + max(his4) == 2 && + min(his4) == min(his4_min) && + min(his4) == min(his4_max) && + min(his4) == min(his4_minmax) && + max(his4) == max(his4_min) && + max(his4) == max(his4_max) && + max(his4) == max(his4_minmax); + return res; +}; + + +bool +check_rgb_8() +{ + bool res(true); + image1d<rgb_8> ir8(4); + ir8(0) = rgb_8(1, 1, 1); + ir8(1) = rgb_8(1, 2, 1); + ir8(2) = rgb_8(255, 255, 255); + ir8(3) = rgb_8(1, 2, 1); + + histogram<rgb_8> hr8(ir8); + res = res && + hr8[rgb_8(1, 1, 1)] == 1 && + hr8[rgb_8(1, 2, 1)] == 2 && + hr8[rgb_8(255, 255, 255)] == 1 && + hr8[rgb_8(0, 0, 0)] == 0 && + hr8[rgb_8(44, 44, 44)] == 0; + return res; +}; + bool check() { @@ -34,5 +141,11 @@ distrib_sort<input>(src, v); + fail = fail + || check_int_u8() == false + || check_rgb_8() == false + || check_int_s4_and_distr() == false + || check_min_max() == false; + return fail; } Index: olena/oln/convert/value_to_point.hh --- olena/oln/convert/value_to_point.hh Mon, 02 Feb 2004 10:54:55 +0100 niels () +++ olena/oln/convert/value_to_point.hh Mon, 02 Feb 2004 10:08:35 +0100 niels (oln/j/42_value_to_p 644) @@ -0,0 +1,92 @@ +// Copyright (C) 2004 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 59 Temple Place - Suite 330, Boston, +// MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef OLENA_VALUE_TO_POINT +# define OLENA_VALUE_TO_POINT +# include <oln/core/image1d.hh> +# include <oln/core/image2d.hh> +# include <oln/core/image3d.hh> +# include <ntg/int.hh> +# include <ntg/color/color.hh> +namespace oln { + + template <typename T> + struct value_to_point + { + typedef point1d result_type; + typedef T input_type; + typedef image1d_size size_type; + + result_type + operator()(const input_type &input) const + { + result_type r(input - ntg_min_val(input_type)); + return r; + } + + size_type + size() const + { + return size_type(ntg_max_val(input_type) - ntg_min_val(input_type) + 1, + 1); + } + protected: + typedef typename ntg_is_a(input_type, ntg::non_vectorial)::ensure_type ensure_type; + }; + + //FIXME: the number of components is not a parameter (due to size_type) + template <unsigned Qbits,template <unsigned> class S> + struct value_to_point<typename ntg::color<3, Qbits, S> > + { + enum {nbcomps = 3, qbits = Qbits}; + + typedef ntg::color<nbcomps, qbits, S> input_type; + typedef typename dim_traits<nbcomps, unsigned>::img_type::size_type + size_type; + typedef typename input_type::comp_type comp_type; + typedef point3d result_type; + + result_type + operator()(const input_type &input) const + { + result_type r; + for (unsigned i = 0; i < nbcomps; ++i) + r.nth(i) = input[i]; + return r; + } + + size_type + size() const + { + return size_type(ntg_max_val(comp_type) - ntg_min_val(comp_type) + 1, + ntg_max_val(comp_type) - ntg_min_val(comp_type) + 1, + ntg_max_val(comp_type) - ntg_min_val(comp_type) + 1, + 1); + } + }; +} +#endif
participants (1)
-
Niels Van Vliet