FastOpticalFlowOp.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30 */
31 #ifndef SLIDING_WINDOW_OPTICALFLOW_OP_H_
32 #define SLIDING_WINDOW_OPTICALFLOW_OP_H_
33 
34 #include <drain/image/ImageFile.h>
35 #include <sstream>
36 #include <math.h>
37 
38 //#include "drain/image/ImageTray.h"
39 #include "SlidingWindowOp.h"
40 //#include "QuadraticSmootherOp.h"
41 #include "DifferentialOp.h"
42 #include "BlenderOp.h"
43 
44 
45 namespace drain
46 {
47 
48 namespace image
49 {
50 
52 
58 
59 public:
60 
61 
62  OpticalFlowConfig() : invertU(false), invertV(true) {
63 
64  }
65 
67  bool invertU;
68 
70  bool invertV;
71 
72 };
73 
74 
76 
77 public:
78 
79  typedef double data_t;
80  typedef double cumul_t;
81 
84 
85  inline
86  void setSrcFrameWeight(const ImageFrame & srcW){
87  Logger mout(getImgLog(), "WeightedOpticalFlowCore", __FUNCTION__);
88  this->srcWeight.setView(srcW);
89  //mout.debug("srcW: " , srcW );
90  mout.debug("srcWeight (view): " , this->srcWeight );
91  };
92 
93  // Destination images
94 
97 
100 
103 
104 
106 
112  void setDstFrames(ImageTray<Channel> & dstTray);
113 
114  inline
115  void setDstFrameWeight(const ImageFrame & dstW){
116  //Logger mout(getImgLog(), "OpticalFlowCore2", __FUNCTION__);
117  this->dstWeight.setView(dstW);
118  //mout.debug("srcW: " , srcW );
119  //mout.debug("view: " , this->srcWeight );
120  };
121 
122  inline
123  data_t nominator() const {
124  return static_cast<data_t>(Gxx*Gyy - Gxy*Gxy);
125  };
126 
128  inline
129  data_t uDenominator() const {
130  return static_cast<data_t>(Gxy*Gyt - Gyy*Gxt);
131  };
132 
134  inline
135  data_t vDenominator() const {
136  return static_cast<data_t>(Gxy*Gxt - Gxx*Gyt);
137  };
138 
139 
140 protected:
141 
142  // sum w fx*fx
143  cumul_t Gxx;
144  // sum w fx*fy
145  cumul_t Gxy;
146  // sum w fx*fx
147  cumul_t Gyy;
148  // sum w fx*ft
149  cumul_t Gxt;
150  // sum w fy*ft
151  cumul_t Gyt;
152 
153  // sum w ft*ft (needed only for prediction error, and quality index thereof)
154  cumul_t Gtt;
155  data_t W;
156 
160  // Consider: weight = > weightSUm
161  //virtual
162  void clearStats(){
163  Gxx = 0.0;
164  Gxy = 0.0;
165  Gyy = 0.0;
166  Gxt = 0.0;
167  Gyt = 0.0;
168  // Quality only:
169  Gtt = 0.0;
170  W = 0.0;
171 
172  //std::cerr << __FUNCTION__ << ": " << this->nominator() << ", " << this->uDenominator() << ", " << this->vDenominator() << std::endl;
173 
174  }
175 
176 };
177 
179 
180 public:
181 
182  static inline
183  size_t getDiffChannelCount() {return 3;};
184 
191 
193 
200  void setSrcFrames(const ImageTray<const Channel> & srcTray);
201 
202 
203 protected:
204 
205  ~OpticalFlowCore1(){};
206 
207 };
208 
209 
210 
211 template <class R = OpticalFlowCore1 >
212 class SlidingOpticalFlow : public SlidingWindow<OpticalFlowConfig, R> {
213 
214 public:
215 
216  SlidingOpticalFlow(int width = 0, int height = 0) : SlidingWindow<OpticalFlowConfig, R>(width, height) {
217  //setSize(width,height);
218  }
219 
221  //setSize(conf.width, conf.height);
222  }
223 
224  virtual
225  ~SlidingOpticalFlow(){};
226 
227  // "Inherit" data types.
228  typedef OpticalFlowCore1::data_t data_t;
229  typedef OpticalFlowCore1::cumul_t cumul_t;
230 
231  // protected:
232  //double areaD;
233 
234  virtual
235  inline
236  void setImageLimits() const {
237  //this->adjustCoordHandler(this->coordinateHandler);
238  this->coordinateHandler.setPolicy(this->Dx.getCoordinatePolicy());
239  this->coordinateHandler.setLimits(this->Dx.getHeight(), this->Dy.getHeight());
240  }
241 
242 
243  virtual
244  void initialize();
245 
246 
247 
248 
249 
250  virtual inline
252 
253  if (! this->coordinateHandler.validate(p))
254  return;
255 
256  address = this->Dx.address(p.x, p.y);
257 
258  dx = this->Dx.template get<data_t>(this->address);
259  dy = this->Dy.template get<data_t>(this->address);
260  dt = this->Dt.template get<data_t>(this->address);
261  // now "w = 1";
262 
263  // if ((location.x == 100) && (location.y == 100)) std::cout << locationHandled << '\n';
264  /*
265  if (this->debugDiag(4)){
266  std::cout << this->location
267  << dx*dx << '\t'
268  << dx*dy << '\t'
269  << dy*dy << '\t'
270  << dx*dt << '\t'
271  << dy*dt << '\n';
272  }
273  */
274 
275 
276  this->Gxx += dx*dx;
277  this->Gxy += dx*dy;
278  this->Gyy += dy*dy;
279  this->Gxt += dx*dt;
280  this->Gyt += dy*dt;
281 
282  // Quality only
283  this->Gtt -= dt*dt;
284  this->W += 1.0;
285  }
286 
287  virtual inline
289 
290  if (! this->coordinateHandler.validate(p))
291  return;
292 
293  address = this->Dx.address(p.x, p.y);
294 
295  dx = this->Dx.template get<data_t>(address);
296  dy = this->Dy.template get<data_t>(address);
297  dt = this->Dt.template get<data_t>(address);
298  //now "w = 1";
299 
300  this->Gxx -= dx*dx;
301  this->Gxy -= dx*dy;
302  this->Gyy -= dy*dy;
303  this->Gxt -= dx*dt;
304  this->Gyt -= dy*dt;
305 
306  // Quality only
307  this->Gtt -= dt*dt;
308  this->W -= 1.0;
309  }
310 
311 
313  inline
314  data_t predictionError(double u, double v) const {
315  //return u*Dx.get<double>(address) + v*Dy.get<double>(address) - Dt.get<double>(address);
316  if (this->W > 0.0)
317  //return sqrt((Gtt - 2.0*(Gxt*u + Gyt*v) + Gxx*u*u + 2.0*Gxy*u*v + Gyy*v*v)/W);
318  return sqrt((this->Gtt + 2.0*(this->Gxy*u*v - this->Gxt*u - this->Gyt*v) + this->Gxx*u*u + this->Gyy*v*v)/this->W);
319  else
320  return 0;
321  };
322 
323  virtual inline
324  void write(){
325 
326  /*
327  if (this->debugDiag(4)){
328 
329  dx = this->Dx.template get<double>(address);
330  dy = this->Dy.template get<double>(address);
331  dt = this->Dt.template get<double>(address);
332  //w = srcWeight.get<data_t>(address);
333 
334  std::cout << this->location << " \t"
335  << dx*dx << '\t'
336  << dx*dy << '\t'
337  << dy*dy << '\t'
338  << dx*dt << '\t'
339  << dy*dt << '\n';
340  }
341  */
342 
343  nom = this->nominator();
344  /*
345  Logger mout(getImgLog(), "SlidingOpticalFlow", __FUNCTION__);
346  mout.warn("start: " , location );
347  mout.note("uField: " , uField );
348  mout.note("vField: " , vField );
349  mout.note("w: " , dstWeight );
350  File::write(uField, "uField.png");
351  File::write(vField, "vField.png");
352  File::write(dstWeight, "w.png");
353  mout.error("halt" );
354  */
355 
356  if (nom > 0.01){ // todo minQuality
357  //if (w > 0.05){ // todo minQuality
358 
359  u = this->uDenominator()/nom;
360  v = this->vDenominator()/nom;
361  quality = sqrt(nom/this->W);
362  //quality = this->srcWeight.template get<data_t>(this->location); // iGradient stability
363  //quality = predictionError(u, v); // TODO multiply with gradQuality
364  //quality = this->srcWeight.getScaled(this->location.x, this->location.y);
365  //quality = sqrt(nom/W) * this->srcWeight.getScaled(this->location.x, this->location.y); // predictionError(u, v); // TODO multiply with gradQuality
366  //quality = sqrt(nom/(u*u + v*v));
367  //quality = sqrt(nom/(Gxx*Gxx + Gyy+Gyy));
368 
369 
370 
371  /*
372  if (this->location.x == this->location.y)
373  if ((this->location.x & 7) == 0){
374  std::cout << this->location << ' ';
375  std::cout << "\t v=(" << u << ',' << v << ")\t";
376  //std::cout << "Diff:\t" << dx << '\t' << dy << '\t' << dt << "\tuvq:\t";
377  //std::cout << uDenominator()/nom << '\t' << vDenominator()/nom << '\t' << quality << '\n';
378  //std::cout << "\t w=(" << this->srcWeight.template get<data_t>(this->location) << ',' << (W/areaD) << ")\t";
379  //std::cout << "\t w=(" << w << ',' << W << ',' << this->srcWeight.template get<data_t>(this->location) << ")\t";
380  std::cout << "\t w=(" << (W/areaD) << ")\t";
381  std::cout << quality << '\n';
382  }
383  */
384 
385  this->uField.put(this->location, this->conf.invertU ? -u : u);
386  this->vField.put(this->location, this->conf.invertV ? -v : v);
387  //this->dstWeight.put(this->location, ( 1.0 - 1.0/(1.0+quality)) * this->dstWeight.getScaling().getScale());
388  //this->dstWeight.putScaled(this->location.x, this->location.y, quality);
389  this->dstWeight.put(this->location, quality);
390  //this->dstWeight.put(this->location, this->dstWeight.getScaling().inv( 1.0 - 1.0/(1.0+quality)));
391  }
392  else {
393  this->uField.put(this->location, 0);
394  this->vField.put(this->location, 0);
395  this->dstWeight.put(this->location, 0);
396  }
397 
398  }
399 
400 protected:
401 
402  virtual
403  void clear(){
404  std::cerr << "SlidingOpticalFlow::" << __FUNCTION__ << std::endl;
405  this->clearStats();
406  }
407 
408  // Used by addPixel, removePixel
409  mutable size_t address;
410  mutable data_t dx, dy, dt, w; // inner-loop (instantaneous)
411 
412  // Used by write
413  mutable data_t u, v;
414  mutable data_t nom;
415  mutable data_t quality;
416 
417 };
418 
419 template <class R>
421 
422  Logger mout(getImgLog(), "SlidingOpticalFlow", __FUNCTION__);
423 
424  //this->areaD = this->getArea();
425  this->setImageLimits();
426  this->setLoopLimits();
427 
428  mout.debug("window: " , *this );
429  mout.debug3("Dx: " , this->Dx );
430  mout.debug3("Dy: " , this->Dy );
431  mout.debug3("Dt: " , this->Dt );
432 
433  //clear();
434  //fill(0, 0);
435 
436 }
437 
438 
439 /*
440 class SlidingOpticalFlow : public SlidingOpticalFlowBase<OpticalFlowCore1> {
441 
442 public:
443 
444  SlidingOpticalFlow(const config & conf) : SlidingOpticalFlowBase<OpticalFlowCore1>(conf) {
445  //setSize(conf.width, conf.height);
446  }
447 
448 };
449 */
450 
451 class SlidingOpticalFlowWeighted : public SlidingOpticalFlow<OpticalFlowCore1> {
452 
453 public:
454 
456  }
457 
459 
460  virtual
461  inline
463 
464  if (! coordinateHandler.validate(p))
465  return;
466 
467  address = Dx.address(p.x, p.y);
468 
469  dx = Dx.get<data_t>(address);
470  dy = Dy.get<data_t>(address);
471  dt = Dt.get<data_t>(address);
472  w = srcWeight.get<data_t>(address);
473 
474  // if ((location.x == 100) && (location.y == 100)) std::cout << locationHandled << '\n';
475  /*
476  if ((location.x == 70) && (location.y == 120)){
477  Logger mout(getImgLog(), "SlidingOpticalFlow", __FUNCTION__);
478  mout.note(this->location , '\t' , p );
479  }
480  */
481 
482  Gxx += w*dx*dx;
483  Gxy += w*dx*dy;
484  Gyy += w*dy*dy;
485  Gxt += w*dx*dt;
486  Gyt += w*dy*dt;
487  // Quality only:
488  Gtt += w*dt*dt;
489  W += w;
490  }
491 
492  virtual inline
494 
495  if (! coordinateHandler.validate(p))
496  return;
497 
498  address = Dx.address(p.x, p.y);
499 
500  dx = Dx.get<data_t>(address);
501  dy = Dy.get<data_t>(address);
502  dt = Dt.get<data_t>(address);
503  w = srcWeight.get<data_t>(address);
504  // w = 1;
505 
506  Gxx -= w*dx*dx;
507  Gxy -= w*dx*dy;
508  Gyy -= w*dy*dy;
509  Gxt -= w*dx*dt;
510  Gyt -= w*dy*dt;
511  // Quality only:
512  Gtt -= w*dt*dt;
513  W -= w;
514  }
515 
516 };
517 
518 //--------------------------------------------------------------std::cout << locationHandled << '\n';----------------------------
519 
520 
523 
544 class FastOpticalFlowOp : public SlidingWindowOp<SlidingOpticalFlowWeighted> {
545 
546 public:
547 
548  FastOpticalFlowOp(int width=5, int height=5): //, double smoothing=0.01) : //, double gradPow=2.0) : //, double gradWidth = 16) :
549  SlidingWindowOp<SlidingOpticalFlowWeighted>(__FUNCTION__, "A pipeline implementation of optical flow."), blender(width, height, "avg", "blend", 1) {
550  parameters.append(blender.getParameters(), false);
551  }
552 
554  virtual inline
555  void getDstConf(const ImageConf & src, ImageConf & dst) const {
556  dst.setType(typeid(OpticalFlowCore1::data_t));
557  dst.setGeometry(src.getWidth(), src.getHeight(), 2, 1);
558  }
559 
560  /*
561  void make Compatible(const ImageFrame & src, Image & dst) const {
562  dst.initialize(typeid(OpticalFlowCore1::data_t), src.getWidth(), src.getHeight(), 2, 1);
563  };
564  */
565 
567 
570  virtual
572 
573 
574  //virtual void traverseChannels(const ImageTray<const Channel> & srcTray, ImageTray<Channel> & dstTray) const;
575 
576  virtual inline
577  void traverseChannels(const ImageTray<const Channel> & src, ImageTray<Channel> & dst) const {
578  this->traverseMultiChannel(src, dst);
579  }
580 
581  virtual inline
582  void traverseChannel(const Channel & src, Channel & dst) const {
583  Logger mout(getImgLog(), __FILE__, __FUNCTION__);
584  mout.error("Not implemented (1/1)" );
585  }
586 
587  virtual inline
588  size_t getDiffChannelCount() const {
589  return window_t::getDiffChannelCount();
590  }
591 
592 
593  inline
594  bool preSmooth() const {
595  return !blender.getSmootherKey().empty();
596  }
597 
598  mutable
599  BlenderOp blender;
600 
601 };
602 
603 }
604 }
605 
606 #endif
607 
608 // Drain
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition: Log.h:308
Logger & error(const TT &... args)
Echoes.
Definition: Log.h:412
Logger & debug(const TT &... args)
Public, yet typically used "internally", when TIMING=true.
Definition: Log.h:676
void append(ReferenceMap &rMap, bool replace=true)
Adopts the references of r. If replace==false, only new entries are appended.
Definition: ReferenceMap.h:320
Image with static geometry.
Definition: ImageChannel.h:60
void setLimits(int xMin, int yMin, int xUpperLimit, int yUpperLimit)
Sets minimum values and outer upper limits for x and y.
Definition: CoordinateHandler.h:135
bool validate(Point2D< int > &p) const
Handles the coordinate, returning true if the position is reversible.
Definition: CoordinateHandler.h:223
void setPolicy(const CoordinatePolicy &p)
Assigns internal function pointers.
Definition: CoordinateHandler.h:160
void setType(const std::type_info &t)
Set storage type.
Definition: ImageConf.h:121
Definition: FastOpticalFlowOp.h:544
virtual void computeDifferentials(const ImageTray< const Channel > &src, ImageTray< Channel > &dst) const
Computes an image with channels dx, dy, dt and w (quality of gradients). User may redefine this.
Definition: FastOpticalFlowOp.cpp:116
virtual void traverseChannel(const Channel &src, Channel &dst) const
Apply to single channel.
Definition: FastOpticalFlowOp.h:582
virtual void getDstConf(const ImageConf &src, ImageConf &dst) const
Creates a double precision image of 2+1 channels for storing motion (uField,vField) and quality (q).
Definition: FastOpticalFlowOp.h:555
Struct for image (excluding data)
Definition: ImageConf.h:333
Image with static geometry.
Definition: ImageFrame.h:67
void put(size_t i, T x)
Sets the intensity in location i to x. See \address.
Definition: ImageFrame.h:192
T get(size_t i) const
Gets the intensity at location i. See address().
Definition: ImageFrame.h:254
Container applicable for Channels and Images, with alpha support.
Definition: ImageTray.h:267
ImageFrame that also has channels.
Definition: ImageView.h:52
void setView(const ImageFrame &src)
Views the whole image.
Definition: ImageView.h:65
Consider using SlidingStripeOpticalFlow instead.
Definition: FastOpticalFlowOp.h:57
bool invertV
If true, negate Y so that it will increase towards top (North)
Definition: FastOpticalFlowOp.h:70
bool invertU
If true, negate X so that it will increase towards left (West)
Definition: FastOpticalFlowOp.h:67
Definition: FastOpticalFlowOp.h:178
ImageView Dt
Precomputed time diffential.
Definition: FastOpticalFlowOp.h:190
ImageView Dy
Precomputed vertical diffential.
Definition: FastOpticalFlowOp.h:188
ImageView Dx
Precomputed horizontal diffential.
Definition: FastOpticalFlowOp.h:183
void setSrcFrames(const ImageTray< const Channel > &srcTray)
Set inputs as channels 0:Dx, 1:Dy, 2:Dt, 3/alpha: weight.
Definition: FastOpticalFlowOp.cpp:82
Definition: FastOpticalFlowOp.h:75
ImageView uField
Horizontal velocity.
Definition: FastOpticalFlowOp.h:91
data_t uDenominator() const
Returns the horizontal component of motion. Must be scaled by nominator().
Definition: FastOpticalFlowOp.h:129
void setDstFrames(ImageTray< Channel > &dstTray)
Set outputs as channels (uField, vField, w)
Definition: FastOpticalFlowOp.cpp:50
ImageView dstWeight
Quality of the velocity.
Definition: FastOpticalFlowOp.h:102
ImageView vField
Vertical velocity.
Definition: FastOpticalFlowOp.h:99
data_t vDenominator() const
Returns the vertical component of motion. Must be scaled by nominator().
Definition: FastOpticalFlowOp.h:135
ImageView srcWeight
Precomputed weight field (optional)
Definition: FastOpticalFlowOp.h:83
Definition: FastOpticalFlowOp.h:451
virtual void removePixel(Point2D< int > &p)
Removes a pixel from window statistics. Unvalidated location.
Definition: FastOpticalFlowOp.h:493
virtual void addPixel(Point2D< int > &p)
Adds a pixel to window statistics. Unvalidated location.
Definition: FastOpticalFlowOp.h:462
Definition: FastOpticalFlowOp.h:212
virtual void setImageLimits() const
Sets internal limits corresponding to image geometries. Typically using coordHandler.
Definition: FastOpticalFlowOp.h:236
virtual void write()
Write the result in the target image.
Definition: FastOpticalFlowOp.h:324
virtual void initialize()
Sets class-specific initial values. Does not change general window state (e.g. location)....
Definition: FastOpticalFlowOp.h:420
virtual void removePixel(Point2D< int > &p)
Removes a pixel from window statistics. Unvalidated location.
Definition: FastOpticalFlowOp.h:288
data_t predictionError(double u, double v) const
Returns the mean squared error of predicted.
Definition: FastOpticalFlowOp.h:314
virtual void addPixel(Point2D< int > &p)
Adds a pixel to window statistics. Unvalidated location.
Definition: FastOpticalFlowOp.h:251
virtual void clear()
Clears the applied statistics. Redefined in derived classes.
Definition: FastOpticalFlowOp.h:403
Template for operators applying pipeline-like sliding window.
Definition: SlidingWindowOp.h:59
Window implementation that uses incremental update of state.
Definition: SlidingWindow.h:50
Base class for configurations applied in image processing windows, e.g. for operators of type WindowO...
Definition: Window.h:56
Container for source and target images, and their setters.
Definition: Window.h:156
Point2D< int > location
Current location of this window.
Definition: Window.h:520
Definition: DataSelector.cpp:1277