1 // This is mul/vil3d/vil3d_convert.h
2 #ifndef vil3d_convert_h_
3 #define vil3d_convert_h_
4 //:
5 // \file
6 // \brief Some standard conversion functions.
7 // \author Ian Scott.
8 //
9 // This file contains a large number of image to image conversion
10 // functions.
11 // They are in two basic function types (plus a few helper functions.)
12 // Some involve only explicit types and convert
13 // a vil3d_image_view<T> to a vil3d_image_view<T>,
14 // the others take an unknown pixel type, using a
15 // vil3d_image_view_base_sptr. The basic conversion
16 // operations (e.g. casting, rounding) are available in both types.
17 // All of the conversions attempt to find shortcuts, so the output
18 // may be a reconfigured, or shallow copy of the input.
19 //
20 // \par vil3d_convert with explicit pixel types
21 // These are useful when you have two vil3d_image_view objects you want
22 // to convert between. You can use them in templates where the pixel
23 // type is one of the template parameters. These functions
24 // may create a shallow copy of the input if the types match too save
25 // unnecessary work.
26 // - vil3d_convert_cast
27 // - vil3d_convert_round
28 // - vil3d_convert_stretch_range
29 //
30 // \par vil3d_convert with unknown pixel types
31 // These functions are useful when taking an image from vil3d_load
32 // of vil3d_image_resource::get_view(), where you may not know the
33 // pixel type in advance, but want to force the image into a
34 // particular pixel type.
35 // - vil3d_convert_cast
36 // - vil3d_convert_round
37 // - vil3d_convert_stretch_range
38 // - vil3d_convert_to_n_planes
39 // In general these functions expect to take scalar pixel images as
40 // inputs. Even though many of these functions could return a known
41 // pixel-typed image, they all return a vil3d_image_view_base_sptr,
42 // so that the functions can be strung along
43 //
44 // Note that these vil3d_convert_..( vil3d_image_view_base_sptr ) functions
45 // are provided as a convenience for users of vil3d_load and
46 // vil3d_image_resource::get_view(). Their existence should not suggest
47 // that it is sensible to use a vil3d_image_view_base_sptr as storage,
48 // nor that it is a good idea to write a functions that
49 // take or return a vil3d_image_view_base_sptr. If you need a
50 // pixel-type-agnostic image container then use a vil3d_image_resource_sptr
51 //
52 // It may be a good idea to provide vil3d_image_resource_sptr based
53 // vil3d_converts as well.
54
55 #include <iostream>
56 #include <limits>
57 #include <cassert>
58 #ifdef _MSC_VER
59 # include <vcl_msvc_warnings.h>
60 #endif
61 #include <vil/vil_convert.h>
62 #include <vil3d/vil3d_transform.h>
63 #include <vil3d/vil3d_math.h>
64 #include <vil3d/vil3d_plane.h>
65 #include <vil3d/vil3d_copy.h>
66
67
68 //: Cast one pixel type to another.
69 // There must be a cast operator from inP to outP
70 //
71 // If the two pixel types are the same, the destination may only be a shallow
72 // copy of the source.
73 // \relatesalso vil3d_image_view
74 template <class inP, class outP>
vil3d_convert_cast(const vil3d_image_view<inP> & src,vil3d_image_view<outP> & dest)75 inline void vil3d_convert_cast(const vil3d_image_view<inP >&src,
76 vil3d_image_view<outP >&dest)
77 {
78 if (vil_pixel_format_of(inP()) == vil_pixel_format_of(outP()))
79 dest = src;
80 else
81 vil3d_transform2(src, dest, vil_convert_cast_pixel<inP, outP>());
82 }
83
84
85 //: Convert one pixel type to another with rounding.
86 // This should only be used to convert scalar pixel types to other scalar
87 // pixel types, or RGBs to RGBs. This function only rounds in terms of the
88 // destination type.
89 //
90 // If the two pixel types are the same, the destination may only be a
91 // shallow copy of the source.
92 // \relatesalso vil3d_image_view
93 template <class inP, class outP>
vil3d_convert_round(const vil3d_image_view<inP> & src,vil3d_image_view<outP> & dest)94 inline void vil3d_convert_round(const vil3d_image_view<inP >&src,
95 vil3d_image_view<outP >&dest)
96 {
97 if (vil_pixel_format_of(inP()) == vil_pixel_format_of(outP()))
98 dest = src;
99 else
100 vil3d_transform2(src, dest, vil_convert_round_pixel<inP, outP>());
101 }
102
103
104 //: Convert src to byte image dest by stretching to range [0,255]
105 // \relatesalso vil3d_image_view
106 template <class T>
vil3d_convert_stretch_range(const vil3d_image_view<T> & src,vil3d_image_view<vxl_byte> & dest)107 inline void vil3d_convert_stretch_range(const vil3d_image_view<T>& src,
108 vil3d_image_view<vxl_byte>& dest)
109 {
110 T min_b,max_b;
111 vil3d_math_value_range(src,min_b,max_b);
112 double a = -1.0*double(min_b);
113 double b = 0.0;
114 if (max_b-min_b >0) b = 255.0/(max_b-min_b);
115 dest.set_size(src.ni(), src.nj(), src.nk(), src.nplanes());
116 for (unsigned p = 0; p < src.nplanes(); ++p)
117 for (unsigned k = 0; k < src.nk(); ++k)
118 for (unsigned j = 0; j < src.nj(); ++j)
119 for (unsigned i = 0; i < src.ni(); ++i)
120 dest(i,j,k,p) = static_cast<vxl_byte>( b*( src(i,j,k,p)+ a ) );
121 }
122
123
124 // It doesn't seem sensible to write a general stretch
125 // conversion function from any type to any type.
126 // The individual pixel transfer function has to perform
127 // multiplications which have to be done in double
128 // to provide both the range and precision. You may as well
129 // leave the image in double, and convert it again later.
130
131 //: Convert src to double image dest by stretching to range [lo,hi]
132 template <class inP>
vil3d_convert_stretch_range(const vil3d_image_view<inP> & src,vil3d_image_view<double> & dest,double lo,double hi)133 inline void vil3d_convert_stretch_range(const vil3d_image_view<inP>& src,
134 vil3d_image_view<double>& dest,
135 double lo, double hi)
136 {
137 inP min_b=0, max_b=0;
138 vil3d_math_value_range(src,min_b,max_b);
139 double b = 0.0;
140 if (max_b-min_b >0)
141 b = (hi-lo)/static_cast<double>(max_b-min_b);
142 double a = -1.0*min_b*b + lo;
143 dest.set_size(src.ni(), src.nj(), src.nk(), src.nplanes());
144 for (unsigned p = 0; p < src.nplanes(); ++p)
145 for (unsigned k = 0; k < src.nk(); ++k)
146 for (unsigned j = 0; j < src.nj(); ++j)
147 for (unsigned i = 0; i < src.ni(); ++i)
148 dest(i,j,k,p) = b*src(i,j,k,p) + a;
149 }
150
151
152 //: Convert src image<inP> to dest image<double> by stretching input range [src_lo, src_hi] to output range [dest_lo, dest_hi].
153 // Inputs < src_lo are mapped to dest_lo, and inputs > src_hi to dest_hi.
154 template <class inP>
vil3d_convert_stretch_range_limited(const vil3d_image_view<inP> & src,vil3d_image_view<double> & dest,const inP src_lo,const inP src_hi,const double dest_lo,const double dest_hi)155 inline void vil3d_convert_stretch_range_limited(const vil3d_image_view<inP>& src,
156 vil3d_image_view<double>& dest,
157 const inP src_lo,
158 const inP src_hi,
159 const double dest_lo,
160 const double dest_hi)
161 {
162 double ddest = dest_hi - dest_lo;
163 double dsrc = static_cast<double>(src_hi - src_lo);
164 double dds = ddest / dsrc;
165
166 dest.set_size(src.ni(), src.nj(), src.nk(), src.nplanes());
167 for (unsigned p = 0; p < src.nplanes(); ++p)
168 for (unsigned k = 0; k < src.nk(); ++k)
169 for (unsigned j = 0; j < src.nj(); ++j)
170 for (unsigned i = 0; i < src.ni(); ++i)
171 {
172 inP s = src(i,j,k,p);
173 dest(i,j,k,p) = s<=src_lo ? dest_lo :
174 s>=src_hi ? dest_hi :
175 dest_lo + dds*static_cast<double>(s-src_lo);
176 }
177 }
178
179
180 //: Cast the unknown pixel type to the known one.
181 //
182 // This function is designed to be used with vil3d_load or
183 // vil3d_image_resource::get_view()
184 // where you do not know the pixel type in advance.
185 // If you need a multi-component view, then call this to get the corresponding
186 // multi-planar view, and do a second (cheap) conversion.
187 // The input image's storage arrangement may not be preserved.
188 template <class outP>
vil3d_convert_cast(outP,const vil3d_image_view_base_sptr & src)189 inline vil3d_image_view_base_sptr vil3d_convert_cast(outP /*dummy*/,
190 const vil3d_image_view_base_sptr& src)
191 {
192 if (!src) return vil3d_image_view_base_sptr();
193
194 vil3d_image_view_base_sptr dest = new vil3d_image_view<outP>;
195 vil3d_image_view<outP> & dest_ref = static_cast<vil3d_image_view<outP> &>(*dest);
196
197 switch ( vil_pixel_format_component_format(src->pixel_format()) )
198 {
199 #define macro(F , T) \
200 case F: \
201 vil3d_convert_cast( vil3d_image_view<T >( src ), dest_ref );\
202 break;
203
204 macro( VIL_PIXEL_FORMAT_UINT_32, vxl_uint_32 )
205 macro( VIL_PIXEL_FORMAT_INT_32, vxl_int_32 )
206 macro( VIL_PIXEL_FORMAT_UINT_16, vxl_uint_16 )
207 macro( VIL_PIXEL_FORMAT_INT_16, vxl_int_16 )
208 macro( VIL_PIXEL_FORMAT_BYTE, vxl_byte )
209 macro( VIL_PIXEL_FORMAT_SBYTE, vxl_sbyte )
210 macro( VIL_PIXEL_FORMAT_FLOAT, float )
211 macro( VIL_PIXEL_FORMAT_DOUBLE, double )
212 macro( VIL_PIXEL_FORMAT_BOOL, bool )
213 #undef macro
214 default: dest=nullptr;
215 }
216 return dest;
217 }
218
219
220 //: Convert an image of any pixel type to another with rounding.
221 // This should only be used to convert to scalar
222 // pixel types. This function only rounds in terms of the
223 // destination type.
224 // This function is designed to be used with vil3d_load or
225 // vil3d_image_resource::get_view()
226 // where you do not know the pixel type in advance.
227 //
228 // If the input image already has outP as its pixel type, the destination
229 // may only be a shallow copy of the source.
230 // outP should be a scalar pixel type.
231 // The input image's storage arrangement may not be preserved.
232 template <class outP>
vil3d_convert_round(outP,const vil3d_image_view_base_sptr & src)233 inline vil3d_image_view_base_sptr vil3d_convert_round(
234 outP /*dummy*/, const vil3d_image_view_base_sptr &src)
235 {
236 assert(vil_pixel_format_num_components(vil_pixel_format_of(outP()))==1);
237
238 if (!src) return vil3d_image_view_base_sptr();
239
240 if (vil_pixel_format_component_format(src->pixel_format()) ==
241 vil_pixel_format_of(outP()))
242 return src;
243
244 vil3d_image_view_base_sptr dest = new vil3d_image_view<outP >;
245 vil3d_image_view<outP > &dest_ref = static_cast<vil3d_image_view<outP >&>(*dest);
246
247 switch (vil_pixel_format_component_format(src->pixel_format()))
248 {
249 #define macro( F , T ) \
250 case F: { \
251 vil3d_image_view<T > src1 = src; \
252 vil3d_transform2(src1, dest_ref, vil_convert_round_pixel<T , outP>()); \
253 break; }
254
255 macro(VIL_PIXEL_FORMAT_BYTE, vxl_byte )
256 macro(VIL_PIXEL_FORMAT_SBYTE , vxl_sbyte )
257 macro(VIL_PIXEL_FORMAT_UINT_32 , vxl_uint_32 )
258 macro(VIL_PIXEL_FORMAT_UINT_16 , vxl_uint_16 )
259 macro(VIL_PIXEL_FORMAT_INT_32 , vxl_int_32 )
260 macro(VIL_PIXEL_FORMAT_INT_16 , vxl_int_16 )
261 macro(VIL_PIXEL_FORMAT_FLOAT , float )
262 macro(VIL_PIXEL_FORMAT_DOUBLE , double )
263 #undef macro
264 default: dest=nullptr;
265 }
266 return dest;
267 }
268
269
270 //: Create a greyscale image of specified pixel type from any image src.
271 // This function is designed to be used with vil3d_load or
272 // vil3d_image_resource::get_view()
273 // where you do not know the pixel type in advance. e.g.
274 // \verbatim
275 // vil3d_image_view<float> input = vil3d_convert_cast(
276 // convert_to_grey_using_average(vil3d_load(filename)), float());
277 // \endverbatim
278 // The output may be a reconfigured view of the input.
279 // The input image's pixel type and storage arrangement may not be preserved.
vil3d_convert_to_grey_using_average(const vil3d_image_view_base_sptr & src)280 inline vil3d_image_view_base_sptr vil3d_convert_to_grey_using_average(
281 const vil3d_image_view_base_sptr &src)
282 {
283 if (!src) return vil3d_image_view_base_sptr();
284
285 // convert via vil3d_image_view<double>
286 switch (vil_pixel_format_component_format(src->pixel_format()))
287 {
288 #define macro( F , T ) \
289 case F: { \
290 /* try to do it quickly */ \
291 if (src->nplanes() == 1 && \
292 vil_pixel_format_component_format(src->pixel_format())==1) \
293 return src; \
294 /* create output view */ \
295 vil3d_image_view<T > dest; \
296 vil3d_image_view<T > src1 = *src; \
297 vil3d_math_mean_over_planes(src1, dest, double()); \
298 return vil3d_image_view_base_sptr(new vil3d_image_view<T >(dest)); }
299
300 macro(VIL_PIXEL_FORMAT_BYTE, vxl_byte )
301 macro(VIL_PIXEL_FORMAT_SBYTE , vxl_sbyte )
302 macro(VIL_PIXEL_FORMAT_UINT_32 , vxl_uint_32 )
303 macro(VIL_PIXEL_FORMAT_UINT_16 , vxl_uint_16 )
304 macro(VIL_PIXEL_FORMAT_INT_32 , vxl_int_32 )
305 macro(VIL_PIXEL_FORMAT_INT_16 , vxl_int_16 )
306 macro(VIL_PIXEL_FORMAT_FLOAT , float )
307 macro(VIL_PIXEL_FORMAT_DOUBLE , double )
308 #undef macro
309 default:
310 return vil3d_image_view_base_sptr();
311 }
312 }
313
314
315 //: Create an n plane image from any image src.
316 // This function is designed to be used with vil3d_load or
317 // vil3d_image_resource::get_view()
318 // where you do not know the pixel type or number of planes in advance.
319 // If the input images have too many planes, the higher planes will be
320 // truncated. If the input image has too few planes, the new planes will be
321 // copies of the first plane.
322 //
323 // The output may be a shallow copy of the input.
324 // The input image's storage arrangement may not be preserved.
325 // \endverbatim
vil3d_convert_to_n_planes(unsigned n_planes,const vil3d_image_view_base_sptr & src)326 inline vil3d_image_view_base_sptr vil3d_convert_to_n_planes(
327 unsigned n_planes, const vil3d_image_view_base_sptr &src)
328 {
329 if (!src || n_planes == 0)
330 return vil3d_image_view_base_sptr();
331
332
333 switch (vil_pixel_format_component_format(src->pixel_format()))
334 {
335 #define macro( F, T ) \
336 case F: { \
337 vil3d_image_view<T > src_ref = src; \
338 if (!src_ref) return vil3d_image_view_base_sptr(); \
339 /* try to do it quickly 1 */ \
340 if (src_ref.nplanes() >= n_planes) /* reduce number of planes */ \
341 return vil3d_image_view_base_sptr( new vil3d_image_view<T >( \
342 vil3d_planes(vil3d_image_view<T > (src),0,1,n_planes) )); \
343 else { /* expand number of planes with copying */ \
344 vil3d_image_view_base_sptr dest = new vil3d_image_view<T >( \
345 src_ref.ni(), src_ref.nj(), src_ref.nk(), n_planes); \
346 vil3d_image_view<T > & dest_ref = \
347 static_cast<vil3d_image_view<T > &>(*dest); \
348 vil3d_image_view<T > dest_slices = \
349 vil3d_planes(dest_ref, 0, 1, src_ref.nplanes()); \
350 vil3d_copy_reformat(src_ref, dest_slices); \
351 vil3d_image_view<T > src_slice(vil3d_plane(src_ref, 0)); \
352 for (unsigned i=src_ref.nplanes(); i<n_planes; ++i) { \
353 dest_slices = vil3d_plane(dest_ref, i); \
354 vil3d_copy_reformat(src_slice, dest_slices); } \
355 return dest; } } \
356
357 macro(VIL_PIXEL_FORMAT_BYTE, vxl_byte )
358 macro(VIL_PIXEL_FORMAT_SBYTE , vxl_sbyte )
359 macro(VIL_PIXEL_FORMAT_UINT_32 , vxl_uint_32 )
360 macro(VIL_PIXEL_FORMAT_UINT_16 , vxl_uint_16 )
361 macro(VIL_PIXEL_FORMAT_INT_32 , vxl_int_32 )
362 macro(VIL_PIXEL_FORMAT_INT_16 , vxl_int_16 )
363 macro(VIL_PIXEL_FORMAT_FLOAT , float )
364 macro(VIL_PIXEL_FORMAT_DOUBLE , double )
365 #undef macro
366
367 default:
368 return vil3d_image_view_base_sptr();
369 }
370 }
371
372
373 //: Create an image of the desired type by stretching the range to fit.
374 // This function is designed to be used with vil3d_load or
375 // vil3d_image_resource::get_view()
376 // where you do not know the pixel type in advance.
377 // In the case of floating point output pixels the range is set to [0,1]
378 // The input image's storage arrangement may not be preserved.
379 //
380 // This function works on scalar pixel types only. You can convert the image
381 // to rgb using a cheap assignment afterwards.
382 template <class outP>
vil3d_convert_stretch_range(outP,const vil3d_image_view_base_sptr & src)383 inline vil3d_image_view_base_sptr vil3d_convert_stretch_range(
384 outP /*dummy*/, const vil3d_image_view_base_sptr &src)
385 {
386 // Check that input isn't trying to produce multi-component pixels
387 assert (vil_pixel_format_num_components(vil_pixel_format_of(outP())) == 1);
388
389 if (!src)
390 return vil3d_image_view_base_sptr();
391
392 double hi,lo;
393
394 if (std::numeric_limits<outP>::is_integer)
395 {
396 hi = std::numeric_limits<outP>::max()+0.999;
397 lo = std::numeric_limits<outP>::min();
398 }
399 else
400 {
401 hi=1.0;
402 lo=0.0;
403 }
404
405 vil3d_image_view_base_sptr dest = new vil3d_image_view<outP>;
406 vil3d_image_view<outP> & dest_ref = static_cast<vil3d_image_view<outP> &>(*dest);
407 vil3d_image_view<double> inter;
408 switch (vil_pixel_format_component_format(src->pixel_format()))
409 {
410 #define macro( F , T ) \
411 case F: { \
412 vil3d_image_view<T> src_ref = src; \
413 if (!src_ref) return vil3d_image_view_base_sptr(); \
414 vil3d_convert_stretch_range(src_ref, inter, lo, hi); \
415 vil3d_convert_cast(inter, dest_ref); \
416 break; }
417
418 macro(VIL_PIXEL_FORMAT_BYTE, vxl_byte )
419 macro(VIL_PIXEL_FORMAT_SBYTE , vxl_sbyte )
420 macro(VIL_PIXEL_FORMAT_UINT_32 , vxl_uint_32 )
421 macro(VIL_PIXEL_FORMAT_UINT_16 , vxl_uint_16 )
422 macro(VIL_PIXEL_FORMAT_INT_32 , vxl_int_32 )
423 macro(VIL_PIXEL_FORMAT_INT_16 , vxl_int_16 )
424 macro(VIL_PIXEL_FORMAT_FLOAT , float )
425 macro(VIL_PIXEL_FORMAT_DOUBLE , double )
426 #undef macro
427 default:
428 dest_ref.clear();
429 }
430 return dest;
431 }
432
433
434 #endif // vil3d_convert_h_
435