STIR 6.4.0
convert_range.inl
Go to the documentation of this file.
1//
2//
3/*
4 Copyright (C) 2000 PARAPET partners
5 Copyright (C) 2000 - 2007, Hammersmith Imanet Ltd
6 This file is part of STIR.
7
8 SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license
9
10 See STIR/LICENSE.txt for details
11*/
23#include "stir/NumericInfo.h"
24#include "stir/round.h"
25#include <algorithm>
26#include "boost/iterator/iterator_traits.hpp"
27#include "boost/limits.hpp"
28START_NAMESPACE_STIR
29
30// anonymous namespace for local functions
31namespace
32{
33
34/* Declaration of auxiliary function is_negative() with the obvious
35 implementation.
36 However, we overload it for unsigned types to return always false.
37 The compiler would do this automatically for us. However, many
38 compilers (including gcc) will warn when you do
39 unsigned x=...;
40 if (x<0)
41 {
42 // never get here
43 }
44 This file relies on templated definitions of convert_array. So,
45 if we use if-statements as above with templated code, we will get
46 warnings when instantiating the code with unsigned types.
47
48 Summary, instead of the above if, write
49 T x;
50 if (is_negative(x))
51 {
52 // never get here if T is an unsigned type
53 }
54 and you won't get a warning message
55*/
56
57template <class T>
58inline bool
59is_negative(const T x)
60{
61 return x < 0;
62}
63
64inline bool
65is_negative(const unsigned char x)
66{
67 return false;
68}
69
70inline bool
71is_negative(const unsigned short x)
72{
73 return false;
74}
75
76inline bool
77is_negative(const unsigned int x)
78{
79 return false;
80}
81
82inline bool
83is_negative(const unsigned long x)
84{
85 return false;
86}
87
88} // namespace
89
90template <class InputIteratorT, class T2, class scaleT>
91inline void
92find_scale_factor(scaleT& scale_factor,
93 const InputIteratorT& begin,
94 const InputIteratorT& end,
95 const NumericInfo<T2> info_for_out_type)
96{
97 typedef typename boost::iterator_value<InputIteratorT>::type T1;
98 NumericInfo<T1> info1;
99
100 if (info1.type_id() == info_for_out_type.type_id())
101 {
102 // TODO could use different scale factor in this case as well, but at the moment we don't)
103 scale_factor = scaleT(1);
104 return;
105 }
106
107 // find the scale factor to use when converting to the maximum range in T2
108 const double data_in_max = *std::max_element(begin, end);
109 double tmp_scale = data_in_max / static_cast<double>(info_for_out_type.max_value());
110 if (info_for_out_type.signed_type() && info1.signed_type())
111 {
112 const double data_in_min = *std::min_element(begin, end);
113 tmp_scale = std::max(tmp_scale, data_in_min / static_cast<double>(info_for_out_type.min_value()));
114 }
115 // use an extra factor of 1.01. Otherwise, rounding errors can
116 // cause data_in.find_max() / scale_factor to be bigger than the
117 // max_value
118 tmp_scale *= 1.01;
119
120 if (scale_factor == 0 || tmp_scale > scale_factor)
121 {
122 // We need to convert to the maximum range in T2
123 scale_factor = scaleT(tmp_scale);
124 }
125}
126
127template <class OutputIteratorT, class InputIteratorT, class scaleT>
128void
129convert_range(const OutputIteratorT& out_begin,
130 scaleT& scale_factor,
131 const InputIteratorT& in_begin,
132 const InputIteratorT& in_end)
133{
134 typedef typename boost::iterator_value<OutputIteratorT>::type OutType;
135
136 find_scale_factor(scale_factor, in_begin, in_end, NumericInfo<OutType>());
137 if (scale_factor == 0)
138 {
139 // data_in contains only 0
140 OutputIteratorT out_iter = out_begin;
141 InputIteratorT in_iter = in_begin;
142 for (; in_iter != in_end; ++in_iter, ++out_iter)
143 {
144 *out_iter = static_cast<OutType>(0);
145 }
146 return;
147 }
148
149 // do actual conversion
150 OutputIteratorT out_iter = out_begin;
151 InputIteratorT in_iter = in_begin;
152 if (!std::numeric_limits<OutType>::is_integer)
153 {
154 for (; in_iter != in_end; ++in_iter, ++out_iter)
155 {
156 *out_iter = static_cast<OutType>(*in_iter / scale_factor);
157 }
158 }
159 else
160 {
161 for (; in_iter != in_end; ++in_iter, ++out_iter)
162 {
163 // KT coded the checks on the data types in the loop.
164 // This is presumably slow, but all these conditionals can be
165 // resolved at compile time, so a good compiler does the work for me.
166 if (!std::numeric_limits<OutType>::is_signed && is_negative(*in_iter))
167 {
168 // truncate negatives
169 *out_iter = 0;
170 }
171 else
172 {
173 // convert using rounding
174 *out_iter = static_cast<OutType>(round(*in_iter / scale_factor));
175 }
176 }
177 }
178}
179
180// specialisation for equal Iterator types
181// In fact, we could do better and test for boost::iterator_value<InIteratorT>::type
182// etc, but that requires some template trickery
183template <class IteratorT, class scaleT>
184void
185convert_range(const IteratorT& out_begin, scaleT& scale_factor, const IteratorT& in_begin, const IteratorT& in_end)
186{
187 scale_factor = scaleT(1);
188 std::copy(in_begin, in_end, out_begin);
189}
190
191END_NAMESPACE_STIR
This file declares the class stir::NumericInfo.
class NumericInfo<NUMBER> defines properties for the type NUMBER.
Definition NumericInfo.h:68
void convert_range(const OutputIteratorT &out_begin, scaleT &scale_factor, const InputIteratorT &in_begin, const InputIteratorT &in_end)
Converts the data in the input range to the output range (with elements of different types) such that...
Definition convert_range.inl:129
void find_scale_factor(scaleT &scale_factor, const Array< num_dimensions, T1 > &data_in, const NumericInfo< T2 > info_for_out_type)
A function that finds a scale factor to use when converting data to a new type.
Definition convert_array.inl:28
int round(const float x)
Implements rounding of floating point numbers.
Definition round.inl:59
Declaration of the stir::round functions.