Visualization Library 2.0.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
VolumeUtils.cpp
Go to the documentation of this file.
1 /**************************************************************************************/
2 /* */
3 /* Visualization Library */
4 /* http://visualizationlibrary.org */
5 /* */
6 /* Copyright (c) 2005-2020, Michele Bosi */
7 /* All rights reserved. */
8 /* */
9 /* Redistribution and use in source and binary forms, with or without modification, */
10 /* are permitted provided that the following conditions are met: */
11 /* */
12 /* - Redistributions of source code must retain the above copyright notice, this */
13 /* list of conditions and the following disclaimer. */
14 /* */
15 /* - Redistributions in binary form must reproduce the above copyright notice, this */
16 /* list of conditions and the following disclaimer in the documentation and/or */
17 /* other materials provided with the distribution. */
18 /* */
19 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
23 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
24 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
25 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
26 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
27 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
28 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* */
30 /**************************************************************************************/
31 
32 #include <vlVolume/VolumeUtils.hpp>
33 #include <vlCore/Log.hpp>
34 #include <vlCore/glsl_math.hpp>
35 
36 using namespace vl;
37 
38 //-----------------------------------------------------------------------------
39 ref<Image> vl::genRGBAVolume(const Image* data, const Image* trfunc, const fvec3& light_dir, bool alpha_from_data)
40 {
41  ref<Image> img;
42 
43  if(data->type() == IT_UNSIGNED_BYTE)
44  img = genRGBAVolumeT<unsigned char,IT_UNSIGNED_BYTE>(data,trfunc,light_dir,alpha_from_data);
45  else
46  if(data->type() == IT_UNSIGNED_SHORT)
47  img = genRGBAVolumeT<unsigned short,IT_UNSIGNED_SHORT>(data,trfunc,light_dir,alpha_from_data);
48  else
49  if(data->type() == IT_FLOAT)
50  img = genRGBAVolumeT<float,IT_FLOAT>(data,trfunc,light_dir,alpha_from_data);
51  else
52  Log::error("genRGBAVolume() called with non supported data type().\n");
53 
54  return img;
55 }
56 //-----------------------------------------------------------------------------
57 ref<Image> vl::genRGBAVolume(const Image* data, const Image* trfunc, bool alpha_from_data)
58 {
59  ref<Image> img;
60 
61  if(data->type() == IT_UNSIGNED_BYTE)
62  img = genRGBAVolumeT<unsigned char,IT_UNSIGNED_BYTE>(data,trfunc,alpha_from_data);
63  else
64  if(data->type() == IT_UNSIGNED_SHORT)
65  img = genRGBAVolumeT<unsigned short,IT_UNSIGNED_SHORT>(data,trfunc,alpha_from_data);
66  else
67  if(data->type() == IT_FLOAT)
68  img = genRGBAVolumeT<float,IT_FLOAT>(data,trfunc,alpha_from_data);
69  else
70  Log::error("genRGBAVolume() called with non supported data type().\n");
71 
72  return img;
73 }
74 //-----------------------------------------------------------------------------
75 template<typename data_type, EImageType img_type>
76 ref<Image> vl::genRGBAVolumeT(const Image* data, const Image* trfunc, const fvec3& light_dir, bool alpha_from_data)
77 {
78  if (!trfunc || !data)
79  return NULL;
80  if (data->format() != IF_LUMINANCE)
81  {
82  Log::error("genRGBAVolume() called with non IF_LUMINANCE data format().\n");
83  return NULL;
84  }
85  if (data->type() != img_type)
86  {
87  Log::error("genRGBAVolume() called with invalid data type().\n");
88  return NULL;
89  }
90  if (data->dimension() != ID_3D)
91  {
92  Log::error("genRGBAVolume() called with non 3D data.\n");
93  return NULL;
94  }
95  if (trfunc->dimension() != ID_1D)
96  {
97  Log::error("genRGBAVolume() transfer function image must be an 1D image.\n");
98  return NULL;
99  }
100  if (trfunc->format() != IF_RGBA)
101  {
102  Log::error("genRGBAVolume() transfer function format() must be IF_RGBA.\n");
103  return NULL;
104  }
105  if (trfunc->type() != IT_UNSIGNED_BYTE)
106  {
107  Log::error("genRGBAVolume() transfer function format() must be IT_UNSIGNED_BYTE.\n");
108  return NULL;
109  }
110 
111  float normalizer_num = 0;
112  switch(data->type())
113  {
114  case IT_UNSIGNED_BYTE: normalizer_num = 1.0f/255.0f; break;
115  case IT_UNSIGNED_SHORT: normalizer_num = 1.0f/65535.0f; break;
116  case IT_FLOAT: normalizer_num = 1.0f; break;
117  default:
118  break;
119  }
120 
121  // light normalization
122  fvec3 L = light_dir;
123  L.normalize();
124  int w = data->width();
125  int h = data->height();
126  int d = data->depth();
127  int pitch = data->pitch();
128  const unsigned char* lum_px = data->pixels();
129  // generated volume
130  ref<Image> volume = new Image( w, h, d, 1, IF_RGBA, IT_UNSIGNED_BYTE );
131  ubvec4* rgba_px = (ubvec4*)volume->pixels();
132  for(int z=0; z<d; ++z)
133  {
134  int z1 = z-1;
135  int z2 = z+1;
136  z1 = clamp(z1, 0, d-1);
137  z2 = clamp(z2, 0, d-1);
138  for(int y=0; y<h; ++y)
139  {
140  int y1 = y-1;
141  int y2 = y+1;
142  y1 = clamp(y1, 0, h-1);
143  y2 = clamp(y2, 0, h-1);
144  for(int x=0; x<w; ++x, ++rgba_px)
145  {
146  // value
147  float lum = (*(data_type*)(lum_px + x*sizeof(data_type) + y*pitch + z*pitch*h)) * normalizer_num;
148  // value -> transfer function
149  float xval = lum*trfunc->width();
150  VL_CHECK(xval>=0)
151  if (xval > trfunc->width()-1.001f)
152  xval = trfunc->width()-1.001f;
153  int ix1 = (int)xval;
154  int ix2 = ix1+1;
155  VL_CHECK(ix2<trfunc->width())
156  float w21 = (float)fract(xval);
157  float w11 = 1.0f - w21;
158  fvec4 c11 = (fvec4)((ubvec4*)trfunc->pixels())[ix1];
159  fvec4 c21 = (fvec4)((ubvec4*)trfunc->pixels())[ix2];
160  fvec4 rgba = (c11*w11 + c21*w21)*(1.0f/255.0f);
161 
162  // bake the lighting
163  int x1 = x-1;
164  int x2 = x+1;
165  x1 = clamp(x1, 0, w-1);
166  x2 = clamp(x2, 0, w-1);
167  data_type vx1 = (*(data_type*)(lum_px + x1*sizeof(data_type) + y *pitch + z *pitch*h));
168  data_type vx2 = (*(data_type*)(lum_px + x2*sizeof(data_type) + y *pitch + z *pitch*h));
169  data_type vy1 = (*(data_type*)(lum_px + x *sizeof(data_type) + y1*pitch + z *pitch*h));
170  data_type vy2 = (*(data_type*)(lum_px + x *sizeof(data_type) + y2*pitch + z *pitch*h));
171  data_type vz1 = (*(data_type*)(lum_px + x *sizeof(data_type) + y *pitch + z1*pitch*h));
172  data_type vz2 = (*(data_type*)(lum_px + x *sizeof(data_type) + y *pitch + z2*pitch*h));
173  fvec3 N1(float(vx1-vx2), float(vy1-vy2), float(vz1-vz2));
174  N1.normalize();
175  fvec3 N2 = -N1 * 0.15f;
176  float l1 = max(dot(N1,L),0.0f);
177  float l2 = max(dot(N2,L),0.0f); // opposite dim light to enhance 3D perception
178  rgba.r() = rgba.r()*l1 + rgba.r()*l2+0.2f; // +0.2f = ambient light
179  rgba.g() = rgba.g()*l1 + rgba.g()*l2+0.2f;
180  rgba.b() = rgba.b()*l1 + rgba.b()*l2+0.2f;
181  rgba.r() = clamp(rgba.r(), 0.0f, 1.0f);
182  rgba.g() = clamp(rgba.g(), 0.0f, 1.0f);
183  rgba.b() = clamp(rgba.b(), 0.0f, 1.0f);
184 
185  // map pixel
186  rgba_px->r() = (unsigned char)(rgba.r()*255.0f);
187  rgba_px->g() = (unsigned char)(rgba.g()*255.0f);
188  rgba_px->b() = (unsigned char)(rgba.b()*255.0f);
189  if (alpha_from_data)
190  rgba_px->a() = (unsigned char)(lum*255.0f);
191  else
192  rgba_px->a() = (unsigned char)(rgba.a()*255.0f);
193  }
194  }
195  }
196 
197  return volume;
198 }
199 //-----------------------------------------------------------------------------
200 template<typename data_type, EImageType img_type>
201 ref<Image> vl::genRGBAVolumeT(const Image* data, const Image* trfunc, bool alpha_from_data)
202 {
203  if (!trfunc || !data)
204  return NULL;
205  if (data->format() != IF_LUMINANCE)
206  {
207  Log::error("genRGBAVolume() called with non IF_LUMINANCE data format().\n");
208  return NULL;
209  }
210  if (data->type() != img_type)
211  {
212  Log::error("genRGBAVolume() called with invalid data type().\n");
213  return NULL;
214  }
215  if (data->dimension() != ID_3D)
216  {
217  Log::error("genRGBAVolume() called with non 3D data.\n");
218  return NULL;
219  }
220  if (trfunc->dimension() != ID_1D)
221  {
222  Log::error("genRGBAVolume() transfer function image must be an 1D image.\n");
223  return NULL;
224  }
225  if (trfunc->format() != IF_RGBA)
226  {
227  Log::error("genRGBAVolume() transfer function format() must be IF_RGBA.\n");
228  return NULL;
229  }
230  if (trfunc->type() != IT_UNSIGNED_BYTE)
231  {
232  Log::error("genRGBAVolume() transfer function format() must be IT_UNSIGNED_BYTE.\n");
233  return NULL;
234  }
235 
236  float normalizer_num = 0;
237  switch(data->type())
238  {
239  case IT_UNSIGNED_BYTE: normalizer_num = 1.0f/255.0f; break;
240  case IT_UNSIGNED_SHORT: normalizer_num = 1.0f/65535.0f; break;
241  case IT_FLOAT: normalizer_num = 1.0f; break;
242  default:
243  break;
244  }
245 
246  // light normalization
247  int w = data->width();
248  int h = data->height();
249  int d = data->depth();
250  int pitch = data->pitch();
251  const unsigned char* lum_px = data->pixels();
252  // generated volume
253  ref<Image> volume = new Image( w, h, d, 1, IF_RGBA, IT_UNSIGNED_BYTE );
254  ubvec4* rgba_px = (ubvec4*)volume->pixels();
255  for(int z=0; z<d; ++z)
256  {
257  int z1 = z-1;
258  int z2 = z+1;
259  z1 = clamp(z1, 0, d-1);
260  z2 = clamp(z2, 0, d-1);
261  for(int y=0; y<h; ++y)
262  {
263  int y1 = y-1;
264  int y2 = y+1;
265  y1 = clamp(y1, 0, h-1);
266  y2 = clamp(y2, 0, h-1);
267  for(int x=0; x<w; ++x, ++rgba_px)
268  {
269  // value
270  float lum = (*(data_type*)(lum_px + x*sizeof(data_type) + y*pitch + z*pitch*h)) * normalizer_num;
271  // value -> transfer function
272  float xval = lum*trfunc->width();
273  VL_CHECK(xval>=0)
274  if (xval > trfunc->width()-1.001f)
275  xval = trfunc->width()-1.001f;
276  int ix1 = (int)xval;
277  int ix2 = ix1+1;
278  VL_CHECK(ix2<trfunc->width())
279  float w21 = (float)fract(xval);
280  float w11 = 1.0f - w21;
281  fvec4 c11 = (fvec4)((ubvec4*)trfunc->pixels())[ix1];
282  fvec4 c21 = (fvec4)((ubvec4*)trfunc->pixels())[ix2];
283  fvec4 rgba = (c11*w11 + c21*w21)*(1.0f/255.0f);
284 
285  // map pixel
286  rgba_px->r() = (unsigned char)(rgba.r()*255.0f);
287  rgba_px->g() = (unsigned char)(rgba.g()*255.0f);
288  rgba_px->b() = (unsigned char)(rgba.b()*255.0f);
289  if (alpha_from_data)
290  rgba_px->a() = (unsigned char)(lum*255.0f);
291  else
292  rgba_px->a() = (unsigned char)(rgba.a()*255.0f);
293  }
294  }
295  }
296 
297  return volume;
298 }
299 //-----------------------------------------------------------------------------
300 #if 1
302 {
303  ref<Image> img = in_img->convertFormat( IF_LUMINANCE );
304  img = img->convertType( IT_FLOAT );
305  ref<Image> gradient = new Image;
306  gradient->allocate3D(img->width(), img->height(), img->depth(), 1, IF_RGB, IT_FLOAT);
307  float* src_px = (float*)img->pixels();
308  fvec3* dst_px = (fvec3*)gradient->pixels();
309  int w = img->width();
310  int h = img->height();
311 
312  fvec3 A, B;
313  for(int z=0; z<gradient->depth(); ++z)
314  {
315  for(int y=0; y<gradient->height(); ++y)
316  {
317  for(int x=0; x<gradient->width(); ++x)
318  {
319  // clamped coordinates
320  int xp = x+1, xn = x-1;
321  int yp = y+1, yn = y-1;
322  int zp = z+1, zn = z-1;
323  if (xn<0) xn = 0;
324  if (yn<0) yn = 0;
325  if (zn<0) zn = 0;
326  if (xp>img->width() -1) xp = img->width() -1;
327  if (yp>img->height()-1) yp = img->height()-1;
328  if (zp>img->depth() -1) zp = img->depth() -1;
329 
330  A.x() = src_px[xn + w*y + w*h*z];
331  B.x() = src_px[xp + w*y + w*h*z];
332  A.y() = src_px[x + w*yn + w*h*z];
333  B.y() = src_px[x + w*yp + w*h*z];
334  A.z() = src_px[x + w*y + w*h*zn];
335  B.z() = src_px[x + w*y + w*h*zp];
336 
337  // write normal packed into 0..1 format
338  dst_px[x + w*y + w*h*z] = (normalize(A - B) * 0.5f + 0.5f);
339  }
340  }
341  }
342  return gradient;
343 }
344 //-----------------------------------------------------------------------------
345 #else
347 {
348  ref<Image> gradient = new Image;
349  gradient->allocate3D(img->width(), img->height(), img->depth(), 1, IF_RGB, IT_FLOAT);
350  fvec3* px = (fvec3*)gradient->pixels();
351  fvec3 A, B;
352  for(int z=0; z<gradient->depth(); ++z)
353  {
354  for(int y=0; y<gradient->height(); ++y)
355  {
356  for(int x=0; x<gradient->width(); ++x)
357  {
358  // clamped coordinates
359  int xp = x+1, xn = x-1;
360  int yp = y+1, yn = y-1;
361  int zp = z+1, zn = z-1;
362  if (xn<0) xn = 0;
363  if (yn<0) yn = 0;
364  if (zn<0) zn = 0;
365  if (xp>img->width() -1) xp = img->width() -1;
366  if (yp>img->height()-1) yp = img->height()-1;
367  if (zp>img->depth() -1) zp = img->depth() -1;
368 
369  A.x() = img->sample(xn,y,z).r();
370  B.x() = img->sample(xp,y,z).r();
371  A.y() = img->sample(x,yn,z).r();
372  B.y() = img->sample(x,yp,z).r();
373  A.z() = img->sample(x,y,zn).r();
374  B.z() = img->sample(x,y,zp).r();
375 
376  // write normal packed into 0..1 format
377  px[x + img->width()*y + img->width()*img->height()*z] = normalize(A - B) * 0.5f + 0.5f;
378  }
379  }
380  }
381  return gradient;
382 }
383 #endif
VLVOLUME_EXPORT ref< Image > genGradientNormals(const Image *data)
Generates an image whose RGB components represent the normals computed from the input image gradient ...
float clamp(float x, float minval, float maxval)
Definition: Vector2.hpp:315
const Vector3 & normalize(T_Scalar *len=NULL)
Definition: Vector3.hpp:227
int depth() const
Definition: Image.hpp:211
Vector4< float > fvec4
A 4 components vector with float precision.
Definition: Vector4.hpp:279
const unsigned char * pixels() const
Raw pointer to pixels.
Definition: Image.hpp:170
const T_Scalar & z() const
Definition: Vector3.hpp:91
ref< Image > convertType(EImageType new_type) const
Converts the type() of an image.
Definition: Image.cpp:1303
const T_Scalar & r() const
Definition: Vector4.hpp:111
T fract(T a)
Definition: glsl_math.hpp:724
static void error(const String &message)
Use this function to provide information about run-time errors: file not found, out of memory...
Definition: Log.cpp:165
The Vector4 class is a template class that implements a generic 4 components vector, see also vl::fvec4, vl::dvec4, vl::uvec4, vl::ivec4, vl::svec4, vl::usvec4, vl::bvec4, vl::ubvec4.
Definition: Vector4.hpp:44
VLVOLUME_EXPORT ref< Image > genRGBAVolumeT(const Image *data, const Image *trfunc, const fvec3 &light_dir, bool alpha_from_data)
Internally used.
void allocate3D(int x, int y, int z, int bytealign, EImageFormat format, EImageType type)
Definition: Image.cpp:631
Visualization Library main namespace.
float dot(float a, float b)
Definition: glsl_math.hpp:1111
VLVOLUME_EXPORT ref< Image > genRGBAVolume(const Image *data, const Image *trfunc, const fvec3 &light_dir, bool alpha_from_data=true)
Generates an RGBA image based on the given data source and transfer function.
Definition: VolumeUtils.cpp:39
const T_Scalar & g() const
Definition: Vector4.hpp:112
int height() const
Definition: Image.hpp:209
float max(float a, float b)
Definition: Vector2.hpp:311
EImageDimension dimension() const
Definition: Image.cpp:372
Implements the OpenGL Shading Language convenience functions for scalar and vector operations...
int width() const
Definition: Image.hpp:207
const T_Scalar & y() const
Definition: Vector3.hpp:90
int pitch() const
Definition: Image.hpp:213
const T_Scalar & b() const
Definition: Vector4.hpp:113
#define NULL
Definition: OpenGLDefs.hpp:81
ref< Image > convertFormat(EImageFormat new_format) const
Converts the format() of an image.
Definition: Image.cpp:1719
EImageType type() const
Definition: Image.hpp:217
const T_Scalar & x() const
Definition: Vector3.hpp:89
Implements a generic 1d, 2d, 3d and cubemap image that can have mipmaps.
Definition: Image.hpp:54
The ref<> class is used to reference-count an Object.
Definition: Object.hpp:55
fvec4 sample(int x, int y=0, int z=0) const
Returns the color associated to the specified pixel.
Definition: Image.cpp:1998
#define VL_CHECK(expr)
Definition: checks.hpp:73
EImageFormat format() const
Definition: Image.hpp:215
T normalize(T)
Definition: glsl_math.hpp:1128
const T_Scalar & a() const
Definition: Vector4.hpp:114