ACT-CV - Machine Vision for Cognitive Modeling
FrameObserver.h
Go to the documentation of this file.
1 // -*- mode: c++; indent-tabs-mode: nil; c-basic-offset: 4; coding: iso-8859-1; -*-
2 
3 /*
4 ACT-CV - Machine Vision for Cognitive Modeling
5 Copyright (c) 2008 Marc Halbruegge
6 
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21 
22 
27 #pragma once
28 
29 #include <vector>
30 #include <map>
31 #include <iostream>
32 #include <sstream>
33 #include <algorithm>
34 
35 //#include <tcpclient/TcpClient.h>
36 
37 #include <cv.h>
38 #include <highgui.h>
39 //#include <opencv2/core/core.hpp>
40 
41 #if HAVE_CONFIG_H
42 #include <config.h>
43 #endif
44 
45 #include <mhexcptn.h>
46 #include <DynamicsHelpers.h>
47 #include <GCString.h>
48 #include <StoppUhr.h>
49 
50 #include <Threads/Mutex.h>
51 
52 #include <oflow/OpticalFlow.h>
53 #include "FrameSource.h"
54 #include "ActCvSource.h"
55 #include "RegionInfo.h"
56 
57 #include <Matcher.h>
58 
59 extern "C" {
60 #include "server/ACT-CV.i"
61 }
62 
63 #include "server/ResultHolder.h"
64 
68 class FrameObs : virtual public ReferenceCount {
69 public:
71  virtual void FrameNotify(IplImage *img, int frameNr) =0;
72 
73  virtual void RegionInfoNotify(const std::vector<RegionInfo> &regions) {}
74 
82  virtual const char* GetName() const =0;
83 };
84 
87  std::map<std::string, GCPointer<FrameObs> > observers;
88  typedef std::map<std::string, GCPointer<FrameObs> >::const_iterator obs_iter;
89 protected:
90  void FireFrameNotification(IplImage *img, int frameNr) const {
91  for (obs_iter i=observers.begin();i!=observers.end();i++) {
92  i->second->FrameNotify(img,frameNr);
93  }
94  }
95  void FireRegionNotification(const std::vector<RegionInfo> &regions) const {
96  for (obs_iter i=observers.begin();i!=observers.end();i++) {
97  i->second->RegionInfoNotify(regions);
98  }
99  }
100 public:
102  void AddObserver(const GCPointer<FrameObs> &obs) {
103  if (observers.find(obs->GetName())==observers.end()) {
104  observers[obs->GetName()]=obs;
105  }
106  }
107 
113  std::map<std::string, GCPointer<FrameObs> >::iterator
114  i = observers.find(obs->GetName());
115  if (i!=observers.end()) {
116  observers.erase(i);
117  }
118  }
119 
121  GCPointer<FrameObs> FindObserver(const std::string &s) const {
122  obs_iter i=observers.find(s);
123  if (i==observers.end()) {
124  return nullptr;
125  } else {
126  return i->second;
127  }
128  }
129 };
130 
131 
132 
134 class FpsFrameObs : public FrameObs {
137 public:
138  FpsFrameObs() : fps(-1.0f, 0.05f) {}
139 
140  void FrameNotify(IplImage *img, int frameNr) {
141  zeiten.Messung();
142  if (zeiten.GetSeconds()>0.0) {
143  if (fps.get()<0) {
144  fps.set(static_cast<float>(1.0/zeiten.GetSeconds()));
145  } else {
146  fps.add(static_cast<float>(1.0/zeiten.GetSeconds()));
147  }
148  std::cout << fps.get() << " fps" << std::endl;
149  }
150  }
151 
152  const char* GetName() const {
153  return "frames per seconds";
154  }
155 };
156 
157 
159 class WriterObs : public FrameObs {
160  CvVideoWriter *pWriter;
161  const char *videoFName;
162 public:
163  WriterObs(const char* fname = "actcv-out.mpg")
164  : pWriter(nullptr),
165  videoFName(fname) {
166  }
168  if(pWriter) {
169  cvReleaseVideoWriter(&pWriter);
170  }
171  }
172 
173  void FrameNotify(IplImage *img, int frameNr) {
174  if(!pWriter) {
175  pWriter = cvCreateVideoWriter(videoFName, CV_FOURCC('m','p','4','v'), 25, cvGetSize(img));
176  }
177  if(pWriter) {
178  cvWriteFrame(pWriter, img);
179  }
180  }
181  const char* GetName() const {
182  return "writer";
183  }
184 };
185 
186 
188 class FrameObsWindow : public FrameObs {
189  std::string name;
190 public:
191  FrameObsWindow(const char *s)
192  : name(s) {
193  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
194  cvNamedWindow(name.c_str());
195  cvWaitKey(100);
196  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
197  }
199  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
200  cvDestroyWindow(name.c_str());
201  cvWaitKey(100); // doesn't help :(
202  }
203  void FrameNotify(IplImage *img, int frameNr) {
204  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
205  MHDBGMSG_P(__FILE__,__LINE__,"cvShowImage", img);
206  cvShowImage(name.c_str(), img);
207  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
208  }
209  const char* GetName() const {
210  return "window";
211  }
212 };
213 
215 class MatchObs : public FrameObs,
216  public ResultSource<ObjectPosition> {
217  std::vector<GCPointer<MatchImage> > matchers;
218 
222  std::vector<ObjectPosition> lastResult;
223 
224 public:
225  MatchObs(const char *defFileName);
226  void FrameNotify(IplImage *img, int frameNr) {
227  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
228  if (img==nullptr) return;
229 
230  std::vector<ObjectPosition> result;
231 
232  for (unsigned int i=0;i<matchers.size();i++) {
233  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
234  matchers[i]->Match(img);
235  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
236 
237  for (unsigned int j=0;j<matchers[i]->GetPoints().size();j++) {
238  result.push_back(
239  ObjectPosition(matchers[i]->GetPoints().at(j).x,
240  matchers[i]->GetPoints().at(j).y,
241  matchers[i]->GetWidth(),
242  matchers[i]->GetHeight(),
243  matchers[i]->GetVisiconName()));
244  }
245 
246 #ifdef DEBUG
247  if (matchers[i]->TargetFound()) {
248  CvPoint target = matchers[i]->GetRandomTarget();
249  //robot.MouseMove(target.x,target.y);
250  std::cout << matchers[i]->GetVisiconName()
251  << " found at position " << target << std::endl;
252  }
253 #endif
254  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
255  }
256 
257  mutex.Enter();
258  lastResult=result;
259  mutex.Leave();
260  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
261  }
262  const char* GetName() const {
263  return "match";
264  }
265 
267  mutex.Enter();
268  }
269  std::vector<ObjectPosition>& GetData() {
270  return lastResult;
271  }
273  mutex.Leave();
274  }
275 };
276 
277 
279 class ImageCopy {
280  IplImage* copy;
282 
283  const int divisor;
284 public:
285  ImageCopy(int divi=1) : copy(nullptr), cpFrameNr(0), divisor(divi) {}
287  cvReleaseImage(&copy);
288  }
289  IplImage* GetCurCopy() {
290  return copy;
291  }
292  IplImage* GetCopy(IplImage*img, int frameNr) {
293  if (cpFrameNr!=frameNr) {
294  if (copy==nullptr) {
295  if(divisor == 1) {
296  copy = cvCloneImage(img);
297  } else {
298  copy = cvCreateImage(cvSize(img->width/divisor,img->height/divisor),img->depth,img->nChannels);
299  cvResize(img, copy, CV_INTER_LINEAR);
300  }
301  } else {
302  // ACHTUNG: erst src, dann dst!
303  if(divisor == 1) {
304  cvCopy(img, copy);
305  } else {
306  cvResize(img, copy, CV_INTER_LINEAR);
307  }
308  }
309  cpFrameNr=frameNr;
310  return copy;
311  } else {
312  return copy;
313  }
314  }
315 };
316 
317 
319 class ColorObs : public FrameObs,
320  public ResultSource<ObjectPosition>, private ImageCopy {
321 
325  std::vector<ObjectPosition> lastResult;
326 
329 
331  std::vector<float> means;
332  std::vector<float> sd;
333 
334 public:
335  ColorObs(const char *defFileName);
336 
337  void FrameNotify(IplImage *img, int frameNr) {
338  IplImage* pCopy = GetCopy(img,frameNr);
339 
340 #if 0
341  cv::Mat mat(pCopy);
342  cv::MatConstIterator_<cv::Vec3b> it = mat.begin<cv::Vec3b>(), it_end = mat.end<cv::Vec3b>();
343  for(; it != it_end; ++it) {
344  float maxDiff = .0f;
345  for(int c=0;c<pCopy->nChannels;++c) {
346  float curVal = (*it)[c];
347  float curDiff = std::abs(curVal-means[c])/sd[c];
348 #ifdef max
349  maxDiff = max(maxDiff, curDiff);
350 #else
351  maxDiff = std::max(maxDiff, curDiff);
352 #endif
353  }
354 
355  // overlay
356  (*it) /= 2;
357  const unsigned char overlay = (maxDiff < 1.5f) ? 255 : 0;
358  for(int c=0;c<pCopy->nChannels;++c) {
359  (*it)[c] += overlay/2;
360  }
361  }
362 #else
363  unsigned char* p = reinterpret_cast<unsigned char*>(pCopy->imageData);
364  for(int y=0; y<pCopy->height; ++y) {
365  unsigned char* row = p + y*pCopy->widthStep;
366  for(int x=0; x<pCopy->width; ++x, row += pCopy->nChannels) {
367  float maxDiff = .0f;
368  for(int c=0;c<pCopy->nChannels;++c) {
369  float curVal = row[c];
370  float curDiff = std::abs(curVal-means[c])/sd[c];
371 #ifdef max
372  maxDiff = max(maxDiff, curDiff);
373 #else
374  maxDiff = std::max(maxDiff, curDiff);
375 #endif
376  }
377 
378  const unsigned char overlay = (maxDiff < 1.5f) ? 255 : 0;
379  for(int c=0;c<pCopy->nChannels;++c) {
380  row[c] = row[c] / 2 + overlay/2;
381  }
382  }
383  }
384 #endif
385 
386  win->FrameNotify(pCopy, frameNr);
387  }
388  const char* GetName() const {
389  return "color";
390  }
391 
393  mutex.Enter();
394  }
395  std::vector<ObjectPosition>& GetData() {
396  return lastResult;
397  }
399  mutex.Leave();
400  }
401 };
402 
403 
405 class DiffObs : public FrameObs, private ImageCopy {
406 
410 
411  IplImage* pPrevImg;
412  IplImage* pWorkImg;
413 
414 public:
416  : pPrevImg(nullptr),
417  pWorkImg(nullptr),
418  win(new FrameObsWindow("diff")),
419  ImageCopy(1) {
420  writer = new WriterObs("diff-out.avi");
421  }
423  if(pPrevImg != nullptr) cvReleaseImage(&pPrevImg);
424  if(pWorkImg != nullptr) cvReleaseImage(&pWorkImg);
425  }
426 
427  void FrameNotify(IplImage *img, int frameNr) {
428  IplImage* pCopy = GetCopy(img,frameNr);
429  if(pPrevImg == nullptr) {
430  pPrevImg = cvCloneImage(pCopy);
431  pWorkImg = cvCloneImage(pCopy);
432  return;
433  } else {
434  cvCopy(pPrevImg, pWorkImg);
435  cvCopy(pCopy, pPrevImg);
436  }
437 
438  unsigned char* p = reinterpret_cast<unsigned char*>(pCopy->imageData);
439  const unsigned char* pPrev = reinterpret_cast<unsigned char*>(pWorkImg->imageData);
440  for(int y=0; y<pCopy->height; ++y) {
441  unsigned char* row = p + y*pCopy->widthStep;
442  const unsigned char* rowPrev = pPrev + y*pWorkImg->widthStep;
443  for(int x=0;
444  x<pCopy->width;
445  ++x, row += pCopy->nChannels, rowPrev += pWorkImg->nChannels) {
446 
447  for(int c=0;c<pCopy->nChannels;++c) {
448  int cur = row[c];
449  cur -= rowPrev[c];
450 #ifdef min
451  row[c] = min(abs(cur),255);
452 #else
453  row[c] = std::min(abs(cur),255);
454 #endif
455  }
456  }
457  }
458 
459  win->FrameNotify(pCopy, frameNr);
460  if(writer) {
461  writer->FrameNotify(pCopy, frameNr);
462  }
463  }
464  const char* GetName() const {
465  return "diff";
466  }
467 };
468 
469 
471 class TextObs : public FrameObs, private ImageCopy,
472  public ResultSource<ObjectPosition> {
473 
477  std::vector<ObjectPosition> lastResult;
478 
480 
482 
483 public:
484  TextObs(bool showWin = false)
485  : win(showWin ? new FrameObsWindow("text") : nullptr),
486  lastFrameNr(0) {
487  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
488  }
489  void FrameNotify(IplImage *img, int frameNr) {
490  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
491  if(img && win) {
492  win->FrameNotify(GetCopy(img,frameNr), frameNr);
493  }
494  lastFrameNr = frameNr;
495  }
496 
497  void RegionInfoNotify(const std::vector<RegionInfo> &regions) {
498  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
499 
500  std::vector<ObjectPosition> result;
501  for (const RegionInfo &region : regions) {
502  result.push_back(
503  ObjectPosition((region.right+region.left) / 2,
504  (region.bottom+region.top) / 2,
505  region.right-region.left,
506  region.bottom-region.top,
507  region.info.c_str()));
508 
509 #if 0
510  std::cout << "["
511  << region.left << ";"
512  << region.top // << ";"
513  << "] "
514  << region.info << std::endl;
515 #endif
516  }
517 
518  mutex.Enter();
519  lastResult=result;
520  mutex.Leave();
521  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
522  }
523  const char* GetName() const {
524  return "text";
525  }
526 
528  mutex.Enter();
529  }
530  std::vector<ObjectPosition>& GetData() {
531  return lastResult;
532  }
534  mutex.Leave();
535  }
536 };
537 
538 
539 
541 class FrameObsAutoGray : public FrameObs {
542  IplImage* gray;
543 protected:
545  virtual void FrameNotifyGray(IplImage *img, IplImage *gray, int frameNr) = 0;
546 public:
549  cvReleaseImage(&gray);
550  };
551  void FrameNotify(IplImage *img, int frameNr) {
552  if (img==nullptr) return;
553 
554  if (img->nChannels==3) {
555  // assume rgb
556  if (gray==0) {
557  gray = cvCreateImage(cvSize(img->width,img->height), IPL_DEPTH_8U, 1);
558  }
559  cvCvtColor(img, gray, CV_BGR2GRAY);
560  FrameNotifyGray(img,gray,frameNr);
561  } else {
562  // assume gray
563  FrameNotifyGray(img,img,frameNr);
564  }
565  }
566 };
567 
575 class AttendedLocObs : public FrameObs, private ImageCopy {
579  CvPoint focus;
581  CvPoint mouse;
584 
586 #define ATTLOC_CROSS_LWIDTH 2
587 
588  void PrintCross(IplImage *img) {
589  if(focus.x > 0 && focus.y > 0) {
590  cvLine(img,
591  focus+cvPoint(-ATTLOC_CROSS_LWIDTH*3,0),
592  focus+cvPoint(ATTLOC_CROSS_LWIDTH*3,0),
593  CV_RGB(0,255,0),
595  CV_AA, 0 );
596  cvLine(img,
597  focus+cvPoint(0,-ATTLOC_CROSS_LWIDTH*3),
598  focus+cvPoint(0,ATTLOC_CROSS_LWIDTH*3),
599  CV_RGB(0,255,0),
601  CV_AA, 0 );
602  }
603 
604  if(mouse.x > 0 && mouse.y > 0) {
605  cvLine(img,
606  mouse+cvPoint(-ATTLOC_CROSS_LWIDTH*3,0),
607  mouse+cvPoint(ATTLOC_CROSS_LWIDTH*3,0),
608  CV_RGB(0,0,255),
610  CV_AA, 0 );
611  cvLine(img,
612  mouse+cvPoint(0,-ATTLOC_CROSS_LWIDTH*3),
613  mouse+cvPoint(0,ATTLOC_CROSS_LWIDTH*3),
614  CV_RGB(0,0,255),
616  CV_AA, 0 );
617  }
618  }
619 public:
621  : focus(cvPoint(-10,-10)),
622  mouse(cvPoint(-10,-10)) {
623  win = new FrameObsWindow(GetName());
624  }
626  win = nullptr;
627  }
629  void FrameNotify(IplImage *img, int frameNr) {
630  if (img==nullptr) return;
631  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
632  mutex.Enter();
633  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
634  PrintCross(GetCopy(img,frameNr));
635  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
636  win->FrameNotify(GetCopy(img,frameNr),frameNr);
637  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
638  mutex.Leave();
639  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
640  }
645  void UpdateAttendedLoc(int x, int y) {
646  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
647  mutex.Enter();
648  focus.x=x;
649  focus.y=y;
650  mutex.Leave();
651  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
652  }
653  void UpdateMousePos(int x, int y) {
654  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
655  mutex.Enter();
656  mouse.x=x;
657  mouse.y=y;
658  mutex.Leave();
659  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
660  }
661  const char* GetName() const {
662  return "attended location";
663  }
664 };
665 
667 class OFlowObs : public FrameObsAutoGray, public FocusObserver,
668  private ImageCopy, public ActCvSource, public ActCvObsSupport {
671 
673 
674  std::string curResult;
675 public:
676  OFlowObs() : flowWinkel(0.0f, 0.4f) {
677  win = new FrameObsWindow(GetName());
678  }
679  void FrameNotifyGray(IplImage *img, IplImage *gray, int frameNr) {
680  if (img==nullptr) return;
681 
682  if (frameNr==1) {
683  oFlow.SetSize(cvSize(img->width,img->height));
684 #if 0
685  oFlow.SetFocus(cvPoint(img->width/2,
686  static_cast<int>(img->height - img->height*.5)));
687 #else
688  oFlow.SetFocus(cvPoint(img->width/2,
689  static_cast<int>(img->height - img->height*.75)));
690 #endif
691  }
692 
693  oFlow.AddImage(gray);
694 
695  if (frameNr>1) {
696  oFlow.Calc();
697  oFlow.PrintFlowField(GetCopy(img,frameNr));
698  // still some work to be done
699  //oFlow.PrintFocus(GetCopy(img,frameNr));
701  //std::cout << flowWinkel << std::endl;
702 
703  std::ostringstream ostr;
704  ostr << "(" << flowWinkel << ")";
705  curResult = ostr.str();
706  FireVisionNotification(*this);
707  }
708 
709  win->FrameNotify(GetCopy(img,frameNr),frameNr);
710  }
711  void FocusNotify(const CvPoint &p) {
712  oFlow.FocusNotify(p);
713  }
714  const char* GetResult() const {
715  return curResult.c_str();
716  }
717  const char* GetName() const {
718  return "optical flow";
719  }
720  const char* GetTypeName() const {
721  return "flowangle";
722  }
723 };
724 
725 
728 
730 public:
731  MatchContourObs() : matcher ("FG", "imgs/finger.png") {
732  }
733  void FrameNotifyGray(IplImage *img, IplImage *gray, int frameNr) {
734  if (img==nullptr) return;
735  matcher.Match(gray);
736  }
737  const char* GetName() const {
738  return "match-contour";
739  }
740 };
741 
747 class LineObs : public FrameObsAutoGray,
748  private ImageCopy, public ResultSource<Line> {
749  IplImage* canny;
750  CvMemStorage* storage;
751 
753 
754 public:
755  LineObs() : canny(0), storage(0) {
756  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
757  win = new FrameObsWindow("lines");
758  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
759  }
760  LineObs(const LineObs &lo) : canny(0), storage(0), win(0) {
761  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
762  win = new FrameObsWindow("lines");
763  }
765  // release structures to allow resizing
766  cvReleaseImage(&canny);
767  cvReleaseMemStorage(&storage);
768  }
770  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
771  win = 0;
772  cvReleaseImage(&canny);
773  cvReleaseMemStorage(&storage);
774  cvWaitKey(100);
775  }
776  void FrameNotifyGray(IplImage *img, IplImage *gray, int frameNr) {
777  MHDBGMSG(__FILE__,__LINE__,__PRETTY_FUNCTION__);
778  if (img==nullptr) return;
779 
780  if (canny==0) canny = cvCreateImage(cvGetSize(gray), 8, 1);
781  if (storage==0) storage = cvCreateMemStorage();
782 
783  cvCanny(gray, canny, 50, 200, 3);
784  CvSeq* lines = cvHoughLines2(canny, storage,
785  CV_HOUGH_PROBABILISTIC, 1, CV_PI/180,
786  40, 40, 10);
787 
788  mutex.Enter();
789  lastResult.resize(lines->total);
790  for(int i = 0; i < lines->total; i++) {
791  CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
792  cvLine(GetCopy(img,frameNr), line[0], line[1], CV_RGB(255,0,0), 2, 8 );
793  lastResult[i].end1x=line[0].x;
794  lastResult[i].end1y=line[0].y;
795  lastResult[i].end2x=line[1].x;
796  lastResult[i].end2y=line[1].y;
797  }
798  mutex.Leave();
799 
800  cvClearMemStorage(storage);
801 
802  win->FrameNotify(GetCopy(img,frameNr),frameNr);
803  }
804 
805  const char* GetName() const {
806  return "lines";
807  }
808 
809 protected:
813  std::vector<Line> lastResult;
814 
815 public:
817  mutex.Enter();
818  }
819  std::vector<Line>& GetData() {
820  return lastResult;
821  }
823  mutex.Leave();
824  }
825 
826 };
827 
828 


ACT-CV - Machine Vision for Cognitive Modeling
© 2015 Marc Halbruegge (actcvlibrary@googlemail.com)