Search

2012年5月26日 星期六

wxWidgets Connect Arduino(Linux)


    其實前陣子都在幫人做PC端控制PLC與接收一些電壓電流參數的軟體,只是想到還沒測試過Arduino的Serial Library,所以前幾天有空就先做Linux上的,其實Windows取得COM Port也不難API Call一下就好了,主要想幫Arduino與PIC寫類似一些賣Hexapod套件附的動作編輯軟體,除了動作編輯與除存我還要用OpenGL寫個簡單的動畫模擬在螢幕中央以及支援Webcam(前述部份很簡單),還要有正逆向運動學的計算以及一些校準(這部份不熟)。

    要讓PC端連接一些設備可以用UART或是USB的方式傳輸,只要搞懂Spec文件就可以了,Linux下通過Serial Port可以跟Arduino、PIC等等的單晶片溝通,Linux下可以用linudev取得可用的Serial Port設備的Node Path,然後就可以直接當作檔案Open與Close,只是跟讀寫一般純文字文件不同的是Open之後必須要設定Baudrate與Parity,然後用Write與Read 就可以接收資料了,而Arduino的Serial Port接收資料必須要Delay 1ms左右的時間,否則速度太快會接收錯誤資料。




2012年5月10日 星期四

Real Time Face Detection - wxThread

    使用wxTHREAD DETACHED的模式,不過比較奇怪的是如果啟動一次後再暫停,然後再啟動一次CPU使用率會減少20%左右(LINUX)....,可以用top指令觀察變化,然後這個版本Windows是可以執行但是Destroy時釋放OpenCV那幾個結構有問題,這兩個原因等手邊案子結束再來研究。

RTFD.h


#include <wx/wx.h>
#include <wx/thread.h>

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>

class Thread;

enum{
   ID_SCREEN = 100,
   ID_START,
   ID_STOP,
   ID_THREAD_CLOSE
};

class App:public wxApp
{
   public:
      bool OnInit();
};

class Frame:public wxFrame
{
   public:
      Frame(const wxString&);
      ~Frame();

      void Capture();
      void FindFace();

   private:
      wxPanel *screen;
      wxButton *start;
      wxButton *stop;

      CvCapture *camera;
      IplImage *frame;
      CvVideoWriter *video_writer;

      CvHaarClassifierCascade *classifier;
      CvMemStorage *face_storage;
      CvSeq *faces;
      int min_face_width;
      int min_face_height;
      wxString cascade_name;

      void CreateUI();
      void OnStart(wxCommandEvent&);
      void OnStop(wxCommandEvent&);
      void OnCapture(wxCommandEvent&);
      void OnExit(wxCommandEvent&);
      void OnClose(wxCloseEvent&);

      Thread *thread;

      DECLARE_EVENT_TABLE()
};

class Thread:public wxThread
{
   public:
      Thread(Frame *parent):wxThread(wxTHREAD_DETACHED){frame = parent;}
      ~Thread();

      void* Entry();
   private:
      Frame *frame;
};




RTFD.cpp



#include "RTFD.h"


IMPLEMENT_APP(App)
DECLARE_APP(App)


BEGIN_EVENT_TABLE(Frame,wxFrame)
   EVT_MENU(wxID_EXIT,Frame::OnExit)
   EVT_CLOSE(Frame::OnClose)
   EVT_BUTTON(ID_START,Frame::OnStart)
   EVT_BUTTON(ID_STOP,Frame::OnStop)
END_EVENT_TABLE()


bool App::OnInit()
{
   wxFrame *frame = new Frame(wxT("Real Time Face Detection"));


   frame->Show(true);


   return true;
}


Frame::Frame(const wxString &title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(800,600),wxMINIMIZE_BOX | wxCLOSE_BOX | wxCAPTION | wxSYSTEM_MENU)
{


   wxInitAllImageHandlers();


   CreateUI();


   thread = NULL;


   camera = NULL;
   frame = NULL;


   video_writer = cvCreateVideoWriter("video.avi",CV_FOURCC('P','I','M','1'),20,cvSize(640,480),1);


   min_face_width = 110;
   min_face_height = 110;


   cascade_name = wxT("./haarcascades/haarcascade_frontalface_alt.xml");
   classifier = (CvHaarClassifierCascade*)cvLoad(cascade_name.mb_str(),0,0,0);


   if(!classifier){
      wxMessageBox(wxT("Classifier Error!"));
      Close();
   }


   face_storage = cvCreateMemStorage(0);
   cvClearMemStorage(face_storage);
}


Frame::~Frame()
{
}


void Frame::CreateUI()
{
   wxMenu *file = new wxMenu;


   file->Append(wxID_EXIT,wxT("E&xit\tAlt-e"),wxT("Exit"));


   wxMenuBar *bar = new wxMenuBar;


   bar->Append(file,wxT("File"));
   SetMenuBar(bar);


   CreateStatusBar(2);
   SetStatusText(wxT("wxCV"));


   wxBoxSizer *top = new wxBoxSizer(wxVERTICAL);
   this->SetSizer(top);


   wxBoxSizer *screen_box = new wxBoxSizer(wxHORIZONTAL);
   top->Add(screen_box,0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);


   screen = new wxPanel(this,ID_SCREEN,wxDefaultPosition,wxSize(600,470));
   screen_box->Add(screen,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);


   wxBoxSizer *button_box = new wxBoxSizer(wxHORIZONTAL);
   top->Add(button_box,0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);


   start = new wxButton(this,ID_START,wxT("&Start"),wxDefaultPosition,wxDefaultSize);
   button_box->Add(start,0,wxALIGN_CENTER_VERTICAL | wxALL,5);


   stop = new wxButton(this,ID_STOP,wxT("&Stop"),wxDefaultPosition,wxDefaultSize);
   button_box->Add(stop,0,wxALIGN_CENTER_VERTICAL | wxALL,5);


   stop->Enable(false);
}


void Frame::OnExit(wxCommandEvent &event)
{
   Close();
}


void Frame::OnClose(wxCloseEvent &event)
{
   if(thread){
      thread->Delete();
      thread = NULL;
   }


   if(frame){
      cvReleaseImage(&frame);
   }
   if(camera){
      cvReleaseCapture(&camera);
   }
   if(video_writer){
      cvReleaseVideoWriter(&video_writer);
   }
   if(classifier){
      cvReleaseHaarClassifierCascade(&classifier);
   }
   if(face_storage){
      cvReleaseMemStorage(&face_storage);
   }  


   Destroy();
}


void Frame::OnStart(wxCommandEvent &event)
{
   start->Enable(false);
   stop->Enable(true);


   if(camera){
      cvReleaseCapture(&camera);
   }


   camera = cvCaptureFromCAM(-1);


   if(!camera){
      wxMessageBox(wxT("Camera Error¡I"),wxT("Error"),wxOK,this);
start->Enable(true);
stop->Enable(false);
   }
   else{
      
      thread = new Thread(this);


      if(thread->Create() != wxTHREAD_NO_ERROR){
wxLogError(wxT("Thread Create Error!"));
thread->Delete();
thread = NULL;
      }
      else{
if(thread){
   if(thread->Run() != wxTHREAD_NO_ERROR){
      wxLogError(wxT("Thread Run Error!"));
      thread->Delete();
      thread = NULL;
   }
}
      }
   }
}


void Frame::OnStop(wxCommandEvent &event)
{
   start->Enable(true);
   stop->Enable(false);


   if(thread){
      thread->Delete();
      thread = NULL;
   }


   screen->Refresh();
   screen->Update();
}


void Frame::Capture()
{
   wxClientDC dc(screen);


   frame = cvQueryFrame(camera);


   FindFace();


   cvWriteFrame(video_writer,frame);


   cvConvertImage(frame,frame,CV_CVTIMG_SWAP_RB);


   unsigned char *data;


   cvGetRawData(frame,&data);


   wxImage *image = new wxImage(frame->width,frame->height,data,true);


   wxBitmap *bitmap = new wxBitmap(*image);


   int x,y,width,height;


   dc.GetClippingBox(&x,&y,&width,&height);
   dc.DrawBitmap(*bitmap,x,y);


   delete image;
   delete bitmap;
}


void Frame::FindFace()
{
   faces = cvHaarDetectObjects(frame,classifier,face_storage,
1.1,3,
CV_HAAR_DO_CANNY_PRUNING,
cvSize(min_face_width,min_face_height));


   if(faces){
      for(int i = 0;i < faces->total;++i){
CvPoint p1,p2;


CvRect *rect = (CvRect*)cvGetSeqElem(faces,i);


p1.x = rect->x;
p1.y = rect->y;
p2.x = rect->x + rect->width;
p2.y = rect->y + rect->height;


cvRectangle(frame,p1,p2,CV_RGB(255,0,0),3,8,0);
      }
   }
}


Thread::~Thread()
{
}


void* Thread::Entry()
{
   while(!TestDestroy()){
      wxMutexGuiEnter();
      frame->Capture();
      wxMutexGuiLeave();
      Sleep(10);
   }


   return NULL;
}


2012年5月4日 星期五

Real Time Face Detection - wxTimer

    人臉檢測的演算法可以參考這篇很多人引用的論文:『Robust Real-Time Face Detection
大致上就是先做Feature Extraction然後做Cascade Detection,也就是先取得特徵然後一步一步檢查直到最後才能判定是否為人臉,Feature在論文裡面有四種形式,然而有可能檢測對象會是歪頭之類的狀態,因此就必須要讓檢測視窗(四種特徵圖內的矩形)在搜尋視窗(包圍四種特徵圖內的矩形的方形)中改變角度搜尋。

    而用OpenCV可以用cvHaarDetectObjects來作人臉檢測,可以參考這邊有詳細的cvHaarDetectObjects參數說明。



RTFD.h

#include <wx/wx.h>

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>

enum{
   ID_SCREEN1 = 100,
   ID_SCREEN2,
   ID_START,
   ID_STOP,
   ID_TIMER
};

class App:public wxApp
{
   public:
      bool OnInit();
};

class Frame:public wxFrame
{
   public:
      Frame(const wxString&);
      ~Frame();

   private:
      wxPanel *screen;
      wxButton *start;
      wxButton *stop;
      wxTimer timer;

      CvCapture *camera;
      IplImage *frame;
      CvVideoWriter *video_writer;

      CvHaarClassifierCascade *classifier;
      CvMemStorage *face_storage;
      CvSeq *faces;
      int min_face_width;
      int min_face_height;
      wxString cascade_name;

      void CreateUI();
      void OnPaint(wxPaintEvent&);
      void OnTimer(wxTimerEvent&);
      void OnStart(wxCommandEvent&);
      void OnStop(wxCommandEvent&);
      void OnCapture(wxCommandEvent&);
      void OnExit(wxCommandEvent&);

      void FindFace();

      DECLARE_EVENT_TABLE()
};


RTFD.cpp


#include "RTFD.h"

IMPLEMENT_APP(App)
DECLARE_APP(App)

BEGIN_EVENT_TABLE(Frame,wxFrame)
   EVT_MENU(wxID_EXIT,Frame::OnExit)
   EVT_PAINT(Frame::OnPaint)
   EVT_TIMER(ID_TIMER,Frame::OnTimer)
   EVT_BUTTON(ID_START,Frame::OnStart)
   EVT_BUTTON(ID_STOP,Frame::OnStop)
END_EVENT_TABLE()

bool App::OnInit()
{
   wxFrame *frame = new Frame(wxT("Real Time Face Detection"));

   frame->Show(true);

   return true;
}

Frame::Frame(const wxString &title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(800,600),wxMINIMIZE_BOX | wxCLOSE_BOX | wxCAPTION | wxSYSTEM_MENU),timer(this,ID_TIMER)
{
   timer.Stop();

   wxInitAllImageHandlers();

   CreateUI();

   camera = NULL;
   frame = NULL;

   video_writer = cvCreateVideoWriter("video.avi",CV_FOURCC('P','I','M','1'),20,cvSize(640,480),1);

   min_face_width = 110;
   min_face_height = 110;

   cascade_name = wxT("./haarcascades/haarcascade_frontalface_alt.xml");
   classifier = (CvHaarClassifierCascade*)cvLoad(cascade_name.mb_str(),0,0,0);

   if(!classifier){
      wxMessageBox(wxT("Classifier Error!"));
      Close();
   }

   face_storage = cvCreateMemStorage(0);
   cvClearMemStorage(face_storage);
}

Frame::~Frame()
{
}

void Frame::CreateUI()
{
   wxMenu *file = new wxMenu;

   file->Append(wxID_EXIT,wxT("E&xit\tAlt-e"),wxT("Exit"));

   wxMenuBar *bar = new wxMenuBar;

   bar->Append(file,wxT("File"));
   SetMenuBar(bar);

   CreateStatusBar(2);
   SetStatusText(wxT("wxCV"));

   wxBoxSizer *top = new wxBoxSizer(wxVERTICAL);
   this->SetSizer(top);

   wxBoxSizer *screen_box = new wxBoxSizer(wxHORIZONTAL);
   top->Add(screen_box,0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);

   screen = new wxPanel(this,ID_SCREEN1,wxDefaultPosition,wxSize(800,500));
   screen_box->Add(screen,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);

   wxBoxSizer *button_box = new wxBoxSizer(wxHORIZONTAL);
   top->Add(button_box,0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);

   start = new wxButton(this,ID_START,wxT("&Start"),wxDefaultPosition,wxDefaultSize);
   button_box->Add(start,0,wxALIGN_CENTER_VERTICAL | wxALL,5);

   stop = new wxButton(this,ID_STOP,wxT("&Stop"),wxDefaultPosition,wxDefaultSize);
   button_box->Add(stop,0,wxALIGN_CENTER_VERTICAL | wxALL,5);
}

void Frame::OnPaint(wxPaintEvent &event)
{
   screen->Refresh();
   screen->Update();
}

void Frame::OnExit(wxCommandEvent &event)
{
   timer.Stop();

   if(frame){
      cvReleaseImage(&frame);
   }
   if(camera){
      cvReleaseCapture(&camera);
   }
   if(video_writer){
      cvReleaseVideoWriter(&video_writer);
   }
   if(classifier){
      cvReleaseHaarClassifierCascade(&classifier);
   }
   if(face_storage){
      cvReleaseMemStorage(&face_storage);
   }

   Close();
}

void Frame::OnStart(wxCommandEvent &event)
{
   camera = cvCaptureFromCAM(-1);

   if(!camera){
      wxMessageBox(wxT("Camera Error!"),wxT("Error"),wxOK,this);
   }
   else{
      timer.Start(40);
   }
}

void Frame::OnStop(wxCommandEvent &event)
{
   timer.Stop();

   if(camera){
      cvReleaseCapture(&camera);
   }
}

void Frame::OnTimer(wxTimerEvent &event)
{
   wxClientDC dc(screen);

   frame = cvQueryFrame(camera);

   FindFace();

   cvWriteFrame(video_writer,frame);

   cvConvertImage(frame,frame,CV_CVTIMG_SWAP_RB);

   unsigned char *data;

   cvGetRawData(frame,&data);

   wxImage *image = new wxImage(frame->width,frame->height,data,true);

   wxBitmap *bitmap = new wxBitmap(*image);

   int x,y,width,height;

   dc.GetClippingBox(&x,&y,&width,&height);
   dc.DrawBitmap(*bitmap,x,y);

   delete image;
   delete bitmap;
}

void Frame::FindFace()
{
   faces = cvHaarDetectObjects(frame,classifier,face_storage,
1.1,3,
CV_HAAR_DO_CANNY_PRUNING,
cvSize(min_face_width,min_face_height));

   if(faces){
      for(int i = 0;i < faces->total;++i){
CvPoint p1,p2;

CvRect *rect = (CvRect*)cvGetSeqElem(faces,i);

p1.x = rect->x;
p1.y = rect->y;
p2.x = rect->x + rect->width;
p2.y = rect->y + rect->height;

cvRectangle(frame,p1,p2,CV_RGB(255,0,0),3,8,0);
      }
   }
}

因為我沒有修過相關的課程,所以如果有名詞或觀念錯誤請指教:)

Download