ldpk
tde4_ldp_radial_deg_8.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <ldpk/ldpk_ldp_builtin.h>
5 
8 
12 template <class VEC2,class MAT2>
14  {
15 private:
16  typedef VEC2 vec2_type;
17  typedef MAT2 mat2_type;
19 
21 
22  static const char* _para[4];
23  double _r_clip_factor;
24 
25  bool decypher(const char* name,int& i)
26  {
27  typedef base_type bt;
28  int n;
29  getNumParameters(n);
30  for(i = 0;i < n;++i)
31  {
32  if(0 == strcmp(name,_para[i]))
33  {
34  return true;
35  }
36  }
37  return false;
38  }
39  bool initializeParameters()
40  {
41  typedef base_type bt;
42  bt::check_builtin_parameters();
43  return true;
44  }
45  bool getNumParameters(int& n)
46  {
47  n = 4;
48  return true;
49  }
50  bool getParameterName(int i,char* identifier)
51  {
52  strcpy(identifier,_para[i]);
53  return true;
54  }
55  bool setParameterValue(const char *identifier,double v)
56  {
57  typedef base_type bt;
58  int i;
59 // Does the base class know the parameter?
60  if(bt::set_builtin_parameter_value(identifier,v))
61  {
62  return true;
63  }
64  if(!decypher(identifier,i))
65  {
66  return false;
67  }
68  if(_distortion.get_coeff(i) != v)
69  {
70  bt::no_longer_uptodate_lut();
71  }
72  _distortion.set_coeff(i,v);
73  return true;
74  }
78  bool remap_esa2plain(const vec2_type& p_esa_dn,vec2_type& p_plain_dn)
79  {
80  bool rc = true;
81  double f_dn = this->fl_cm() / this->r_fb_cm();
82 // Remove fisheye projection
83  double r_esa_dn = norm2(p_esa_dn);
84  if(r_esa_dn == 0.0)
85  {
86  p_plain_dn = p_esa_dn;
87  return rc;
88  }
89  double arg = r_esa_dn / (2.0 * f_dn),arg_clip = arg;
90 // Black areas, undefined.
91  if(arg_clip >= 1.0)
92  {
93  arg_clip = 0.9999;
94  }
95  double phi = 2.0 * asin(arg_clip);
96  if(phi >= M_PI / 2.0)
97  {
98 // This can and will happen in re-distorting footage...
99  phi = M_PI / 2.0 - 1e-5;
100  rc = false;
101  }
102  double r_plain_dn = f_dn * tan(phi);
103  if(r_plain_dn > _r_clip_factor)
104  {
105 // ... as well as this.
106  r_plain_dn = _r_clip_factor;
107  }
108  p_plain_dn = r_plain_dn * unit(p_esa_dn);
109  return rc;
110  }
111  bool remap_plain2esa(const vec2_type& p_plain_dn,vec2_type& p_esa_dn)
112  {
113  typedef base_type bt;
114  double f_dn = bt::fl_cm() / bt::r_fb_cm();
115 // Apply fisheye projection
116  double r_plain_dn = norm2(p_plain_dn);
117  if(r_plain_dn == 0.0)
118  {
119  p_esa_dn = p_plain_dn;
120  return true;
121  }
122  double phi = atan2(r_plain_dn,f_dn);
123  double r_esa_dn = 2.0 * f_dn * std::sin(phi / 2.0);
124  p_esa_dn = r_esa_dn * unit(p_plain_dn);
125  return true;
126  }
127 // Overwriting tde4_ldp_common
128  bool undistort(double x0,double y0,double &x1,double &y1)
129  {
130  typedef base_type bt;
131  vec2_type p_esa_dn = bt::map_unit_to_dn(vec2_type(x0,y0));
132  vec2_type p_plain_dn;
133  if(!remap_esa2plain(p_esa_dn,p_plain_dn)) return false;
134 
135 // Clipping to a reasonable area, still n times as large as the image.
136  vec2_type q_dn = _distortion.eval(p_plain_dn);
137  if(norm2(q_dn) > 100.0)
138  { q_dn = 100.0 * unit(q_dn); }
139 
140  vec2_type q = bt::map_dn_to_unit(q_dn);
141  x1 = q[0];
142  y1 = q[1];
143  return true;
144  }
145  bool distort(double x0,double y0,double &x1,double &y1)
146  {
147  typedef base_type bt;
148 // The distort-method without initial values is not constant by semantics,
149 // since it may cause an update of the lookup-tables. Implementing a Nuke node
150 // it turned out that we need to prevent threads from trying so simultaneously.
151 // By the following double check of is_uptodate_lut() we keep the mutex lock
152 // out of our frequently called distort stuff (for performance reasons) and
153 // prevent threads from updating without need.
154  if(!bt::is_uptodate_lut())
155  {
156  bt::lock();
157  if(!bt::is_uptodate_lut())
158  {
159  bt::update_lut();
160  }
161  bt::unlock();
162  }
163 // Get initial value from lookup-table. Note that we have defined our LUT
164 // as a table of distortion vales for the model function (i.e. without projection).
165 // We get the initial value and feed it into the distortion class (no projection),
166 // so everything should be fine.
167  vec2_type qs = bt::get_lut().get_initial_value(vec2_type(x0,y0));
168 // Call version of distort with initial value.
169  vec2_type p_plain_dn = _distortion.map_inverse(bt::map_unit_to_dn(vec2_type(x0,y0)),bt::map_unit_to_dn(qs));
170  vec2_type p_esa_dn;
171 
172  if(!remap_plain2esa(p_plain_dn,p_esa_dn)) return false;
173 
174  vec2_type q = bt::map_dn_to_unit(p_esa_dn);
175  x1 = q[0];
176  y1 = q[1];
177  return true;
178  }
179  bool distort(double x0,double y0,double x1_start,double y1_start,double &x1,double &y1)
180  {
181  typedef base_type bt;
182  vec2_type p_plain_dn = _distortion.map_inverse(bt::map_unit_to_dn(vec2_type(x0,y0)),bt::map_unit_to_dn(vec2_type(x1_start,y1_start)));
183  vec2_type p_esa_dn;
184  if(!remap_plain2esa(p_plain_dn,p_esa_dn)) return false;
185 
186  vec2_type q = bt::map_dn_to_unit(p_esa_dn);
187  x1 = q[0];
188  y1 = q[1];
189  return true;
190  }
191  bool undistort_gnomonic(double x0,double y0,double &x1,double &y1)
192  {
193  typedef base_type bt;
194  vec2_type q = bt::map_dn_to_unit(
195  _distortion.eval(
196  bt::map_unit_to_dn(vec2_type(x0,y0))));
197  x1 = q[0];
198  y1 = q[1];
199  return true;
200  }
201  bool distort_gnomonic(double x0,double y0,double &x1,double &y1)
202  {
203  typedef base_type bt;
204 // see distort().
205  if(!bt::is_uptodate_lut())
206  {
207  bt::lock();
208  if(!bt::is_uptodate_lut())
209  {
210  bt::update_lut();
211  }
212  bt::unlock();
213  }
214 // Get initial value from lookup-table. See distort().
215  vec2_type qs = bt::get_lut().get_initial_value(vec2_type(x0,y0));
216 // Call version of distort with initial value.
217  vec2_type p_plain_dn = _distortion.map_inverse(bt::map_unit_to_dn(vec2_type(x0,y0)),bt::map_unit_to_dn(qs));
218  x1 = p_plain_dn[0];
219  y1 = p_plain_dn[1];
220  return true;
221  }
222  bool distort_gnomonic(double x0,double y0,double x1_start,double y1_start,double &x1,double &y1)
223  {
224  typedef base_type bt;
225  vec2_type p_plain_dn = _distortion.map_inverse(bt::map_unit_to_dn(vec2_type(x0,y0)),bt::map_unit_to_dn(vec2_type(x1_start,y1_start)));
226  vec2_type q = bt::map_dn_to_unit(p_plain_dn);
227  x1 = q[0];
228  y1 = q[1];
229  return true;
230  }
231 public:
232 // Mutex initialized and destroyed in baseclass.
233  tde4_ldp_radial_deg_8():_r_clip_factor(5.0)
234  { }
236  { }
237  double r_clip_factor() const
238  { return _r_clip_factor; }
239  void r_clip_factor(double f)
240  { _r_clip_factor = f; }
241  bool getModelName(char *name)
242  {
243 #ifdef LDPK_COMPILE_AS_PLUGIN_SDV
244  strcpy(name,"3DE4 Radial - Fisheye, Degree 8 [Plugin]");
245 #else
246  strcpy(name,"3DE4 Radial - Fisheye, Degree 8");
247 #endif
248  return true;
249  }
250  bool getParameterType(const char* identifier,tde4_ldp_ptype& ptype)
251  {
252  typedef base_type bt;
253  int i;
254  if(bt::get_builtin_parameter_type(identifier,ptype)) return true;
255  if(!decypher(identifier,i)) return false;
256  ptype = TDE4_LDP_ADJUSTABLE_DOUBLE;
257  return true;
258  }
259  bool getParameterDefaultValue(const char* identifier,double& v)
260  {
261  typedef base_type bt;
262  int i;
263  if(!decypher(identifier,i)) return false;
264  v = 0.0;
265  return true;
266  }
267  bool getParameterRange(const char* identifier,double& a,double& b)
268  {
269  typedef base_type bt;
270  int i;
271  if(!decypher(identifier,i)) return false;
272  a = -0.5;
273  b = 0.5;
274  return true;
275  }
276  bool getJacobianMatrix(double x0,double y0,double& m00,double& m01,double& m10,double& m11)
277  {
278  typedef base_type bt;
279 // The function we need to derive is a concatenation of a simple radially symmetric
280 // undistort funtion and the equisolid-angle transform. We compute this by Leipnitz's rule.
281 // Exterior derivative:
282  vec2_type p_esa_dn = bt::map_unit_to_dn(vec2_type(x0,y0));
283  vec2_type p_plain_dn;
284  if(!remap_esa2plain(p_esa_dn,p_plain_dn))
285  {
286  m00 = 1;m01 = 0;m10 = 0;m11 = 1;
287  return false;
288  }
289  mat2_type m = _distortion.jacobi(p_plain_dn);
290 // Interior derivative
291  mat2_type e;
292  double f_dn = this->fl_cm() / this->r_fb_cm();
293  double r_esa_dn = norm2(p_esa_dn);
294  double r_esa_div_2f = r_esa_dn / (2.0 * f_dn);
295 // Test (a): make sure argument of arcsine is valid.
296  if(fabs(r_esa_div_2f) > 0.99)
297  {
298  m00 = 1;m01 = 0;m10 = 0;m11 = 1;
299  return false;
300  }
301  double asn = asin(r_esa_div_2f);
302 // Test (b): make sure cos(..) is greater than 0.
303  if(fabs(2.0 * asn) > 0.99 * M_PI / 2.0)
304  {
305  m00 = 1;m01 = 0;m10 = 0;m11 = 1;
306  return false;
307  }
308  double csn = cos(2.0 * asn);
309 // Argument of square root is positive because of test (a).
310  double sqr = sqrt(1.0 - r_esa_div_2f * r_esa_div_2f);
311  if(r_esa_dn > 1e-12)
312  {
313 // The analytic expression is undefined at 0...
314  e = (mat2_type(1) - tensq(p_esa_dn / r_esa_dn)) * (f_dn / r_esa_dn) * tan(2.0 * asn)
315  + (tensq(p_esa_dn / r_esa_dn)) / (csn * csn * sqr);
316  }
317  else
318  {
319 // ...but has well-defined limit
320  e = mat2_type(1);
321  }
322 
323 // Maps between unit and diagnorm coordinates.
324  mat2_type u2d((bt::w_fb_cm() / 2) / bt::r_fb_cm(),0.0,0.0,(bt::h_fb_cm() / 2) / bt::r_fb_cm());
325  mat2_type d2u(bt::r_fb_cm() / (bt::w_fb_cm() / 2),0.0,0.0,bt::r_fb_cm() / (bt::h_fb_cm() / 2));
326  m = d2u * m * e * u2d;
327 // We have to make sure, that the matrix doesn't run berserk in the outer regions
328  if(tr(trans(m) * m) > 100.0)
329  {
330 // Chill.
331  m = mat2_type(0);
332  }
333  m00 = m[0][0];m01 = m[0][1];m10 = m[1][0];m11 = m[1][1];
334  return true;
335  }
336  };
337 
338 template <class VEC2,class MAT2>
340  "Distortion - Degree 2",
341  "Quartic Distortion - Degree 4",
342  "Degree 6",
343  "Degree 8"
344  };
A polynomial radially symmetric model of degree N (even)
mat2d tensq(const vec2d &a)
Tensor (dyadic) product square.
Definition: ldpk_vec2d.h:186
Plugin class for radial distortion. Does not compensate for decentering. Parameters can be calculated...
Definition: tde4_ldp_radial_deg_8.h:13
vec2_type eval(const vec2_type &p) const
Same as method instead of operator.
Definition: ldpk_generic_distortion_base.h:102
bool getJacobianMatrix(double x0, double y0, double &m00, double &m01, double &m10, double &m11)
calculate the Jacobian matrix of the undistort()-Method. Overwrite this, if you know the Jacobian for...
Definition: tde4_ldp_radial_deg_8.h:276
bool getParameterRange(const char *identifier, double &a, double &b)
returns range for adjustable double parameters...
Definition: tde4_ldp_radial_deg_8.h:267
bool getParameterDefaultValue(const char *identifier, double &v)
returns default value for given parameter (maximum length of "char *v": 1000 bytes)......
Definition: tde4_ldp_radial_deg_8.h:259
mat2_type jacobi(const vec2_type &p_dn) const
Analytic version of the Jacobi-matrix, about 2.5 times faster than the base class version which uses ...
Definition: ldpk_generic_radial_distortion.h:59
This class handles the built-in parameter and the lookup table. You may find it useful for your own d...
Definition: ldpk_ldp_builtin.h:31
bool getParameterType(const char *identifier, tde4_ldp_ptype &ptype)
returns type of given parameter... The method should return false, if the parameter addressed by iden...
Definition: tde4_ldp_radial_deg_8.h:250
virtual vec2_type map_inverse(const vec2_type &q) const
Inverse mapping by solving the fixed point equation without providing initial values. Virtual, because the derived class might use some smart data structure for calculating an initial value.
Definition: ldpk_generic_distortion_base.h:120
void set_coeff(int i, double q)
Set coefficient c[i], 0 <= i < N.
Definition: ldpk_generic_radial_distortion.h:38
double get_coeff(int i) const
Get coefficient c[i], 0 <= i < N (i.e. coefficient power r^(2i))
Definition: ldpk_generic_radial_distortion.h:32
bool getModelName(char *name)
returns a name for the model as to show up in the GUI (maximum length of "name": 100 bytes)...
Definition: tde4_ldp_radial_deg_8.h:241