Search

顯示具有 OpenCV 標籤的文章。 顯示所有文章
顯示具有 OpenCV 標籤的文章。 顯示所有文章

2019年11月25日 星期一

ESP32-CAM MJPEG Stream Decoder and Control Library


  半年前在做自己的Computer Vision Board採買材料時,無意間知道ESP32-CAM這東西,買來放一段時間後,上週末有空拿出來測試這東西挺讓我驚艷,相見恨晚,主要是150NTD的價格加上已經有現成的Arduino相容範例,直接搞定SCCB設定跟DCMI Data還有一個4M PSRAM作為Frame Buffer,收到的MJPEG Stream在 UXGA 1600x1200解析度可以約10FPS每個JPEG 100Kb左右。
  就整體來說這東西在Wi-Fi環境感覺上非常具有淺力,一些多Camera Computer Vision的應用可以配合Raspberry Pi運算,或者簡單的Robot需要Computer Vision然後輔以別的控制器以UART通訊控制(GPIO扣掉SCCB+DCMI和SDIO只剩UART...)都是不錯的選擇。
  我把wxRovio的軟體改一改後主要是抽出C/C++ MJPEG Stream decode,還有示範POST參數給ESP32-CAM修改解析度,基本上相依OpenCV和libcurl,在Github有用OpenCV GUI和wxWidgets GUI的範例,後者順便放YOLO V3和OpenPose的DNN應用,Library在MAC和Win10+VS2017編譯x64版本都可以通過。




2016年1月7日 星期四

Digital Holography - Distance Meter


Grating製作與原理

sensitive to energy(exposure)= intensity * time = |Uo(x,y)|^2 * t = |a(x,y)|^2

z0 = film
object wave Uo(x,y) = |a(x,y)|*e^j fi(x,y)

reference wave Ur(x,y) = |A(x,y)|*e^j fi(x,y)

Exposure E(x,y) = I(x,y)*t = |Uo(x,y) + Ur(x,y)|^2 * t

tA(x) = BetaE^(x)
= Beta * t * I(x) 
= Beta * t * |A|^2 + Beta*t{|a(x)|^2 + A_star(x)a(x) + A(x)a_star(x)}
=t_beta + Beta’*{|a(x)|^2 + a|A||a(x) [fi_r-fi_o]|}

當重建時的調變元素

Reconstruct of object wave Illumination wave Up(x)照Hologram film重建,穿過去的光
Transmitted wave Ut(x)
= Up(x)*tA(x)
= Up(x)*t{|a(x)|^2 + A_star(x)a(x) + A(x)a_star(x)}
= A(x)*{t_beta + beta’*[|a(x)|^2 + A_star(x)a(x) + A(x)a_star(x)]}
= U1(x)+U2(x)+U3(x)+U4(x)
{
U1(x) = A(x)*t_beta = directing transmitted A(x)
U2(x) = beta’*|a(x)|^2*A(x) = Modulated A(x)
U3(x) = beta’*|A|^2*a(x) = original object wave(primary image,virtual image)
U4(x) = beta’*|A|^2*e^j fi_r(x,y)*a(x)^x = conjugate image,real image
}

由不同角度的共軛光去重建
U4(x) = beta’*|A|^2*a(x)^x,conjugate image a(x)^2 real image會出現在virtual image方向,real image看起來會跟virtual image相反,例如凹凸前後相反看起來不合日常邏輯。

Fourier Transform 待補

Fresnel Equation 待補


由三角原理求距離



其中在此範例 theta i = 0,grating light pair = 1/125*1/2mm,波長 = 532nm,CCD Pixel size = 1.75um,theta A = 4.5mm = 雷射通過光圈,O0~O1|O-1 = 中間強度干涉分佈最強與旁邊兩點的距離任取一。





2015年10月6日 星期二

Raspberry Pi 2 + Raspicam + wxWidgets + OpenCV

    最近在設計一些Prototype所以先找RPi2實驗後再轉到FPGA,因爲我是裝Raspbian,所以wxWidgets跟OpenCV編法跟Debian一樣,主要就多個Raspicam c++ API直接給OpenCV 2 API的格式,找來編完後依照要的Mode給參數就可以讀取Raspicam的Video Stream了。






2013年4月1日 星期一

wxKinect - Hand Detect

  前幾天看到一個日本人寫的這篇檢測手的方法,流程:Frame轉成HSV(因為要檢測顏色) =>  濾波(去除雜訊) => 將膚色作為閾值做二值化 => 接著就是標準的找輪廓(Find Contours)、找尋凸包(Convex Hull)、找尋凸缺陷(Convexity Defects),就可以找到手的幾個辨識關鍵,實作這個方法後感覺還可以但是就沒有發揮到Kinect的功能,不過昨天在GitHub看到這篇,有個我覺得很高明的地方也利用到Kinect的特點,就是直接把手的深度範圍當作閾值獨立出手的部分算是蠻準的,其餘的處理就都是一樣了,玩Kinect一個禮拜感覺上可以發揮的點很多。






wxKinect.h


#ifndef __WX_KINECT__
#define  __WX_KINECT__

#if _MSC_VER < 1600
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t;
#else
#include <stdint.h>
#endif

#include <wx/wx.h>

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

#include <NiTE.h>

const int SCREEN_WIDTH = 640;
const int SCREEN_HIGHT = 480;
/*
const int ROI_MAX_SIZE_X = 540;
const int ROI_MAX_SIZE_Y = 380;
*/
const float DEPTH_SACLE = 255.0f / 4096.0f;
const int HAND_ROI_SIZE = 100;

const int LIKELY_THE_HAND_AREA = 2000;
const float HAND_IS_GRASPING = 0.8f;

const int DEPTH_RANGE = 7;

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

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

void InitKinect();
void CreateUI();
void Display();

void OnExit(wxCommandEvent&);
private:
friend class Thread;
Thread *thread;

nite::HandTracker hand_tracker;
nite::HandTrackerFrameRef hand_tracker_frame;

wxPanel *depth_screen;
wxPanel *hand_screen;

DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(Frame,wxFrame)
EVT_MENU(wxID_EXIT,Frame::OnExit)
END_EVENT_TABLE()

class Thread:public wxThread
{
public:
Thread(Frame*);

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

#endif



wxKinect.cpp


#include "wxKinect.h"

DECLARE_APP(App)
IMPLEMENT_APP(App)

bool App::OnInit()
{
Frame *frame = new Frame(wxT("wxKinect - Hand Detect"));

frame->Show(true);

return true;
}

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

thread = new Thread(this);
thread->Create();
thread->Run();
}

void Frame::CreateUI()
{
wxMenu *file = new wxMenu;
file->Append(wxID_EXIT,wxT("E&xit\tAlt-q"),wxT("exit"));

wxMenuBar *bar = new wxMenuBar;
bar->Append(file,wxT("file"));
SetMenuBar(bar);

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

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

depth_screen = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(SCREEN_WIDTH,SCREEN_HIGHT));
screen_box->Add(depth_screen,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);

hand_screen = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(HAND_ROI_SIZE * 2,HAND_ROI_SIZE * 2));
screen_box->Add(hand_screen,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);

CreateStatusBar(2);
SetStatusText(wxDateTime::Now().Format());
}

void Frame::InitKinect()
{
nite::NiTE::initialize();

hand_tracker.create();
hand_tracker.startGestureDetection(nite::GESTURE_CLICK);
hand_tracker.startGestureDetection(nite::GESTURE_WAVE);
hand_tracker.startGestureDetection(nite::GESTURE_HAND_RAISE);
}

Frame::~Frame()
{
thread->Delete();

//hand_tracker.destroy();
nite::NiTE::shutdown();
}

void Frame::Display()
{
hand_tracker.readFrame(&hand_tracker_frame);

cv::Mat depth_mat(hand_tracker_frame.getDepthFrame().getHeight(),hand_tracker_frame.getDepthFrame().getWidth(),CV_16UC1,(void*)hand_tracker_frame.getDepthFrame().getData());
depth_mat.convertTo(depth_mat,CV_8UC1,DEPTH_SACLE);

const nite::Array<nite::GestureData> &gestures = hand_tracker_frame.getGestures();
CvPoint2D32f position;

for(int i = 0;i < gestures.getSize();++i){

if(gestures[i].isComplete()){

const nite::Point3f &pos = gestures[i].getCurrentPosition();
nite::HandId hand_id;
hand_tracker.startHandTracking(pos,&hand_id);
}
}

const nite::Array<nite::HandData> &hands = hand_tracker_frame.getHands();

for(int i = 0;i < hands.getSize();++i){

const nite::HandData hand = hands[i];

if(hand.isTracking()){ //如果跟蹤到手的運動

const nite::Point3f &pos = hand.getPosition();
hand_tracker.convertHandCoordinatesToDepth(pos.x,pos.y,pos.z,&position.x,&position.y);
float hand_depth = pos.z * DEPTH_SACLE;

/*
* 將ROI設定在手的大小左右,
* 而且ROI範圍不可以超出擷取的影像否則會丟出Exception。
*/
cv::Rect hand_roi;
hand_roi.width = HAND_ROI_SIZE * 2;
hand_roi.height = HAND_ROI_SIZE * 2;
hand_roi.x = position.x - HAND_ROI_SIZE;
hand_roi.y = position.y - HAND_ROI_SIZE;
int ROI_MAX_SIZE_X = SCREEN_WIDTH - (HAND_ROI_SIZE * 2);
int ROI_MAX_SIZE_Y = SCREEN_HIGHT - (HAND_ROI_SIZE * 2);
if(hand_roi.x < 0){hand_roi.x = 0;}
if(hand_roi.x > ROI_MAX_SIZE_X){hand_roi.x = ROI_MAX_SIZE_X;}
if(hand_roi.y < 0){hand_roi.y = 0;}
if(hand_roi.y > ROI_MAX_SIZE_Y){hand_roi.y = ROI_MAX_SIZE_Y;}

cv::Mat hand_roi_mat(cv::Mat(depth_mat,hand_roi).clone());
hand_roi_mat = (hand_roi_mat > (hand_depth - DEPTH_RANGE)) & (hand_roi_mat < (hand_depth + DEPTH_RANGE)); //這裡是關鍵,二值化的閾值取決於手的深度範圍。

cv::medianBlur(hand_roi_mat,hand_roi_mat,5); //做中值濾波使邊緣明顯
cv::Mat hand_roi_debug;
hand_roi_debug = hand_roi_mat.clone();
cvtColor(hand_roi_debug,hand_roi_debug,CV_GRAY2RGB);

std::vector<std::vector<cv::Point> > contours;
cv::findContours(hand_roi_mat,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE); //找尋所有可能的多邊形邊緣

if(contours.size()){ //如果有找到

for (int i = 0;i < contours.size();i++){ //則迭代每個多邊形邊緣

std::vector<cv::Point> contour = contours[i];
cv::Mat contour_mat = cv::Mat(contour);
double contour_area = cv::contourArea(contour_mat); //計算該多邊形面積

if(contour_area > LIKELY_THE_HAND_AREA){ //如果大於這個值則可能是手的面積

cv::Scalar center = mean(contour_mat);
cv::Point center_point = cv::Point(center.val[0],center.val[1]);

std::vector<cv::Point> approx_curve;
cv::approxPolyDP(contour_mat,approx_curve,10,true); //逼近該多邊形的邊緣

std::vector<std::vector<cv::Point> > contour_vector;
contour_vector.push_back(approx_curve);
cv::drawContours(hand_roi_debug,contour_vector,0,CV_RGB(255,0,0),3); //畫出該多邊形的邊緣

/*
* 找尋凸包(Convex Hull)並畫出點。
*/
std::vector<int> hull;
cv::convexHull(cv::Mat(approx_curve),hull,false,false);
for(int j = 0;j < hull.size();j++){
int index = hull[j];
cv::circle(hand_roi_debug,approx_curve[index],3,CV_RGB(0,255,0),2);
}

/*
* 找尋凸缺陷(Convexity Defects)並畫出點。
*/
std::vector<CvConvexityDefect> convex_defects;
CvSeq* contour_points;
CvSeq* defects;
CvMemStorage* storage;
CvMemStorage* str_defects;
CvMemStorage* contour_str;
CvConvexityDefect *defect_array = 0;
str_defects = cvCreateMemStorage();
defects = cvCreateSeq(CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq),sizeof(CvPoint),str_defects);
contour_str = cvCreateMemStorage();
contour_points = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2,sizeof(CvSeq),sizeof(CvPoint),contour_str);
for(int j = 0; j < (int)approx_curve.size(); j++) {
CvPoint cp = {approx_curve[j].x,approx_curve[j].y};
cvSeqPush(contour_points, &cp);
}
int count = (int)hull.size();
int *convert_hull = (int*)malloc(count * sizeof(int));
for(int j = 0;j < count;j++){
convert_hull[j] = hull.at(j);
}
CvMat hull_mat = cvMat(1,count,CV_32SC1,convert_hull);
storage = cvCreateMemStorage(0);
defects = cvConvexityDefects(contour_points, &hull_mat,storage);
defect_array = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*defects->total);
cvCvtSeqToArray(defects,defect_array,CV_WHOLE_SEQ);
for(int j = 0;j < defects->total;j++){
CvConvexityDefect def;
def.start       = defect_array[j].start;
def.end         = defect_array[j].end;
def.depth_point = defect_array[j].depth_point;
def.depth       = defect_array[j].depth;
convex_defects.push_back(def);
}
for(int j = 0;j < convex_defects.size();j++){
cv::circle(hand_roi_debug,cv::Point(convex_defects[j].depth_point->x,convex_defects[j].depth_point->y),3,CV_RGB(0,0,255),2);
}
cvReleaseMemStorage(&contour_str);
cvReleaseMemStorage(&str_defects);
cvReleaseMemStorage(&storage);
free(defect_array);

/*
* 這裡也算關鍵,直接把逼近的面積除以凸包的面積得到的值來決定手是張開還是合閉。
*/
std::vector<cv::Point> hull_points;
for(int j = 0;j < hull.size();j++){
int curve_index = hull[j];
cv::Point p = approx_curve[curve_index];
hull_points.push_back(p);
}
double hull_area  = cv::contourArea(cv::Mat(hull_points));
double curve_area = cv::contourArea(cv::Mat(approx_curve));
double hand_ratio = curve_area / hull_area;
if(hand_ratio > HAND_IS_GRASPING){
cv::circle(hand_roi_debug,center_point,5,CV_RGB(255,0,255),5); //張手就在手中心畫出淺綠的點
}
else{
cv::circle(hand_roi_debug,center_point,5,CV_RGB(100,220,80),5); //閉手就在手中心畫出粉紅的點
}

IplImage hand_image(hand_roi_debug);
wxClientDC hand_dc(hand_screen);
cvConvertImage(&hand_image,&hand_image,CV_CVTIMG_SWAP_RB);
unsigned char *data;
cvGetRawData(&hand_image,&data);
wxImage *image = new wxImage(hand_image.width,hand_image.height,data,true);
wxBitmap *bitmap = new wxBitmap(*image);
int x,y,width,height;
hand_dc.GetClippingBox(&x,&y,&width,&height);
hand_dc.DrawBitmap(*bitmap,x,y);
delete image;
delete bitmap;
}
}
}
//imwrite("hand.jpg",hand_roi_image);
}
}

IplImage depth_image(depth_mat);
IplImage *convert_image = cvCreateImage(cvGetSize(&depth_image),IPL_DEPTH_8U,3);
cvCvtColor(&depth_image,convert_image,CV_GRAY2BGR);

wxClientDC depth_dc(depth_screen);
cvConvertImage(convert_image,convert_image,CV_CVTIMG_SWAP_RB);
unsigned char *data;
cvGetRawData(convert_image,&data);
wxImage *image = new wxImage(convert_image->width,convert_image->height,data,true);
wxBitmap *bitmap = new wxBitmap(*image);
int x,y,width,height;
depth_dc.GetClippingBox(&x,&y,&width,&height);
depth_dc.DrawBitmap(*bitmap,x,y);

delete image;
delete bitmap;
cvReleaseImage(&convert_image);
}

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

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

void* Thread::Entry()
{
while(!TestDestroy()){
frame->Display();
}

return NULL;
}









參考:

2013年3月25日 星期一

wxKinect

  話說老師的Lab東西很多昨天借了Kinect來玩一下,寫一個Demo試試SDK可以很簡單讀取骨架也可以選擇顯示Color & Depth & Canny...等等的影像處理,感覺OpenNI很好用,OpenNI 2在Windows下已經不使用這個Driver了而改用MS Kinect SDK的Driver,整體來說影像格式轉換比較浪費時間 OpenNI & NiTE => OpenCV => wxWidgets 不過應付FPS只有30的Kinect已經足夠,使用OpenNI或其他Middleware如果在VS2008含以下必須判斷一下然後做一些Define,收到的深度影像是CV_16UC1且數值是0 ~ 4096(10000),要轉換成CV_8UC1要把數值從0 ~ 4096(10000)映射到0 ~ 255畫出來顏色才不會太黑,如果要轉換到wxImage直接灰階轉彩色就好(CV_GRAY2BGR),然後如果要Release Binarry就要把OpenNI含Middleware的SDK安裝目錄下的Redist目錄內的動態連結檔案與Redist底下還有一個資料夾放一起就可以了。





wxKinect.h


#ifndef __WX_KINECT__
#define __WX_KINECT__

#if _MSC_VER < 1600
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t;
#else
#include <stdint.h>
#endif


#include <wx/wx.h>

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

#include <OpenNI.h>
#include <NiTE.h>

const int SCREEN_WIDTH = 640;
const int SCREEN_HIGHT = 480;

const int COLOR_MODE = 1;
const int DEPTH_MODE = 2;
const int CANNY_MODE = 3;

enum{
ID_COLOR_FRAME = 100,
ID_DEPTH_FRAME,
ID_CANNY_FRAME
};

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

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

void CreateUI();
void InitKinect();

void Display();

void OnExit(wxCommandEvent&);
void OnColor(wxCommandEvent&);
void OnDepth(wxCommandEvent&);
void OnCanny(wxCommandEvent&);
private:
friend class Thread;
Thread *thread;

openni::Device device;
nite::UserTracker user_tracker;
openni::VideoMode color_mode;
openni::VideoStream color_stream;
openni::VideoMode depth_mode;
openni::VideoStream depth_stream;
openni::VideoFrameRef color_frame;
openni::VideoFrameRef depth_frame;
nite::UserTrackerFrameRef user_tracker_frame;
int max_depth;

int select_mode;

wxPanel *screen;

DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(Frame,wxFrame)
EVT_MENU(wxID_EXIT,Frame::OnExit)
EVT_MENU(ID_COLOR_FRAME,Frame::OnColor)
EVT_MENU(ID_DEPTH_FRAME,Frame::OnDepth)
EVT_MENU(ID_CANNY_FRAME,Frame::OnCanny)
END_EVENT_TABLE()

class Thread:public wxThread
{
public:
Thread(Frame*);

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

#endif


wxKinect.cpp


#include "wxKinect.h"

DECLARE_APP(App)
IMPLEMENT_APP(App)

bool App::OnInit()
{
Frame *frame = new Frame(wxT("wxKinect"));

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)
{
InitKinect();
CreateUI();

select_mode = COLOR_MODE;

thread = new Thread(this);
thread->Create();
thread->Run();
}

Frame::~Frame()
{
thread->Delete();

color_stream.destroy();
depth_stream.destroy();
device.close();
openni::OpenNI::shutdown();
nite::NiTE::shutdown();
}

void Frame::CreateUI()
{
wxMenu *file = new wxMenu;
file->Append(wxID_EXIT,wxT("E&xit\tAlt-q"),wxT("exit"));

wxMenu *select = new wxMenu;
select->AppendRadioItem(ID_COLOR_FRAME,wxT("c&olor\tCtrl-c"),wxT("color"));
select->AppendRadioItem(ID_DEPTH_FRAME,wxT("d&epth\tCtrl-d"),wxT("depth"));
select->AppendRadioItem(ID_CANNY_FRAME,wxT("c&anny\tCtrl-x"),wxT("canny"));

wxMenuBar *bar = new wxMenuBar;
bar->Append(file,wxT("file"));
bar->Append(select,wxT("select"));
SetMenuBar(bar);

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,wxID_ANY,wxDefaultPosition,wxSize(SCREEN_WIDTH,SCREEN_HIGHT));
screen_box->Add(screen,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);

CreateStatusBar(2);
SetStatusText(wxDateTime::Now().Format());
}

void Frame::InitKinect()
{
openni::OpenNI::initialize();
nite::NiTE::initialize();

device.open(openni::ANY_DEVICE);
device.setImageRegistrationMode(openni::IMAGE_REGISTRATION_DEPTH_TO_COLOR);

user_tracker.create();
user_tracker.setSkeletonSmoothingFactor(0.1f);

color_mode.setFps(30);
color_mode.setResolution(SCREEN_WIDTH,SCREEN_HIGHT);
color_mode.setPixelFormat(openni::PIXEL_FORMAT_RGB888);

color_stream.setVideoMode(color_mode);
color_stream.create(device,openni::SENSOR_COLOR);
color_stream.start();

depth_mode.setFps(30);
depth_mode.setResolution(SCREEN_WIDTH,SCREEN_HIGHT);
depth_mode.setPixelFormat(openni::PIXEL_FORMAT_DEPTH_100_UM);

depth_stream.setVideoMode(depth_mode);
depth_stream.create(device,openni::SENSOR_DEPTH);
depth_stream.start();
max_depth = depth_stream.getMaxPixelValue();
}

void Frame::Display()
{
color_stream.readFrame(&color_frame);
depth_stream.readFrame(&depth_frame);
user_tracker.readFrame(&user_tracker_frame);

cv::Mat RGBMat(color_frame.getHeight(),color_frame.getWidth(),CV_8UC3,(void*)color_frame.getData());
IplImage color_image(RGBMat);
cvCvtColor(&color_image,&color_image,CV_RGB2BGR);

cv::Mat DepthMat(depth_frame.getHeight(),depth_frame.getWidth(),CV_16UC1,(void*)depth_frame.getData());
DepthMat.convertTo(DepthMat,CV_8UC1,255.0f / max_depth);
IplImage depth_image(DepthMat);

IplImage *select_image = cvCreateImage(cvGetSize(&color_image),IPL_DEPTH_8U,3);

if(select_mode == COLOR_MODE){
cvCopyImage(&color_image,select_image);
}
else if(select_mode == DEPTH_MODE){
cvCvtColor(&depth_image,select_image,CV_GRAY2BGR);
}
else if(select_mode == CANNY_MODE){
cvCanny(&depth_image,&depth_image,50,200);
cvCvtColor(&depth_image,select_image,CV_GRAY2BGR);
}

const nite::Array<nite::UserData> &users = user_tracker_frame.getUsers();
for(int i = 0;i < users.getSize();++i){
const nite::UserData &user = users[i];

if(user.isNew()){
user_tracker.startSkeletonTracking(user.getId());
}
else if(user.isLost()){
}

if(user.isVisible()){
const nite::Skeleton &skeleton = user.getSkeleton();
nite::SkeletonJoint joints[15];

if(skeleton.getState() == nite::SKELETON_TRACKED){
joints[0] = skeleton.getJoint(nite::JOINT_HEAD);
joints[1] = skeleton.getJoint(nite::JOINT_NECK);
joints[2] = skeleton.getJoint(nite::JOINT_LEFT_SHOULDER);
joints[3] = skeleton.getJoint(nite::JOINT_RIGHT_SHOULDER);
joints[4] = skeleton.getJoint(nite::JOINT_LEFT_ELBOW);
joints[5] = skeleton.getJoint(nite::JOINT_RIGHT_ELBOW);
joints[6] = skeleton.getJoint(nite::JOINT_LEFT_HAND);
joints[7] = skeleton.getJoint(nite::JOINT_RIGHT_HAND);
joints[8] = skeleton.getJoint(nite::JOINT_TORSO);
joints[9] = skeleton.getJoint(nite::JOINT_LEFT_HIP);
joints[10] = skeleton.getJoint(nite::JOINT_RIGHT_HIP);
joints[11] = skeleton.getJoint(nite::JOINT_LEFT_KNEE);
joints[12] = skeleton.getJoint(nite::JOINT_RIGHT_KNEE);
joints[13] = skeleton.getJoint(nite::JOINT_LEFT_FOOT);
joints[14] = skeleton.getJoint(nite::JOINT_RIGHT_FOOT);
}

CvPoint2D32f point[15];
for(int i = 0;i < 15;++i){
const nite::Point3f &pos = joints[i].getPosition();
user_tracker.convertJointCoordinatesToDepth(pos.x,pos.y,pos.z,&(point[i].x),&(point[i].y));
}

cvLine(select_image,cvPoint(point[0].x,point[0].y),cvPoint(point[1].x,point[1].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[1].x,point[1].y),cvPoint(point[2].x,point[2].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[1].x,point[1].y),cvPoint(point[3].x,point[3].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[2].x,point[2].y),cvPoint(point[4].x,point[4].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[3].x,point[3].y),cvPoint(point[5].x,point[5].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[4].x,point[4].y),cvPoint(point[6].x,point[6].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[5].x,point[5].y),cvPoint(point[7].x,point[7].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[1].x,point[1].y),cvPoint(point[8].x,point[8].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[8].x,point[8].y),cvPoint(point[9].x,point[9].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[8].x,point[8].y),cvPoint(point[10].x,point[10].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[9].x,point[9].y),cvPoint(point[11].x,point[11].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[10].x,point[10].y),cvPoint(point[12].x,point[12].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[11].x,point[11].y),cvPoint(point[13].x,point[13].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[12].x,point[12].y),cvPoint(point[14].x,point[14].y),CV_RGB(255,0,0),3);

for(int i = 0;i < 15;++i){
if(joints[i].getPositionConfidence() > 0.5f){
cvCircle(select_image,cvPoint(point[i].x,point[i].y),3,CV_RGB(0,255,0),2);
}
else{
cvCircle(select_image,cvPoint(point[i].x,point[i].y),3,CV_RGB(0,0,255),2);
}
}
}
}

wxClientDC dc(screen);
cvConvertImage(select_image,select_image,CV_CVTIMG_SWAP_RB);
unsigned char *data;
cvGetRawData(select_image,&data);
wxImage *image = new wxImage(select_image->width,select_image->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;

cvReleaseImage(&select_image);
}

void Frame::OnColor(wxCommandEvent &event)
{
select_mode = COLOR_MODE;
}

void Frame::OnDepth(wxCommandEvent &event)
{
select_mode = DEPTH_MODE;
}

void Frame::OnCanny(wxCommandEvent &event)
{
select_mode = CANNY_MODE;
}

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

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

void* Thread::Entry()
{
while(!TestDestroy()){
frame->Display();
}

return NULL;
}



2013年3月12日 星期二

wxRovio ver.0.1.0 - WowWee Rovio

  過年後跟人拿了一隻WowWee Rovio玩玩,玩了兩個禮拜發現好像沒有錄影的軟體,所以只好自己寫一個用,在GitHub看到roviolib於是參考了之後跟他一樣用Libcurl送參數給Rovio的CGI,格式為:http://<username>:<password>@<ip>/<command>(括弧要去掉),如果使用的Command會使CGI回傳數據,則必須要寫Callback去處理他,靜態影像處理很簡單判斷JPEG格式而已,不過Video Stream搞不太懂他傳回來的是MJPEG但是處理起來很怪大小也不一,所以參考roviolib作者的Callback之後感覺是在10k~50k之間,然後啟用Video Stream時就會一直接收所以要開個Thread才不會讓Main Thread掛住,另外有個Debug功能可以開啟I2C的功能可以看我下面的參考聯結,最後我改寫的wxRovio目前沒有全功能只有我常用的,然後用wxWidget刻一個Demo用的GUI,有時間再增加功能或改改GUI。(PS:如果按鈕hold住就直接Reboot或者按Stop試試)

使用功能前記得先登入否則會Exception XD,錄影(Video Start)錄完記得停止(Video Stop),頭部的控制按鈕如果一直按著會用分段的方式升降,如果只按一下就會直接升降到定點。



開啟夜間模式會根據Framerate除以的數字決定亮度,換句話說越亮的話Framerate越少,而Auto Gain Control以為跟控制有關,不過其實功能類似於夜間模式,但不同的地方在於這個功能不會減少Framerate但是Noise會增加不少,而這兩個功能都是Debug.cgi裡面的。



wxRovio錄影(錄一小時約1.2GB)



=============2013/03/14===============

一樣的Code在Linux上編過沒問題,當然得要有wxWidgets & OpenCV & Libcurl。




如果還有改版就寫Makefile XD


g++ -o2 -o wxRovio './AddUserDialog.cpp' './App.cpp' './DeleteUserDialog.cpp' './LoggingDialog.cpp' './SetCameraDialog.cpp' './SetOtherDialog.cpp' './wxRovio.cpp' `wx-config --cxxflags --libs --unicode` `pkg-config opencv --cflags --libs` -lcurl





參考:

2012年11月16日 星期五

Visual CAPTCHA Detection

    幾個禮拜前上課寫個去除背景的demo給人看後沒有在碰了,所以就放上來等哪天想改就有,對於圖形驗證碼感覺上如果背景夠複雜,去除背景的演算法複雜度就很高,至於雜訊一些去除線條的演算法或者濾波可以容易去除常見的雜訊,文字本身扭曲的話可以讓Tesseract之類的OCR Library辨識率降低(即使背景、雜訊完整去除),而一些方法的閾值也是要看情況調整,總結來說個人感覺是背景複雜求平均值不容易,而且可能得將背景分區快去除,但針對設計應該是還是可以去除大部分只是複雜度的問題,但如果文字扭曲變形到一定程度似乎常見OCR Library辨識也不易,所以感覺上文字扭曲是必須的,當然最好還是不要用同系列工具生產的驗證圖片,最好混和多種系列使用,不過沒有試過用ML訓練萬筆樣本測試是否能有效提昇辨識度就是了,這裡這裡有對驗證碼類型的分析。

extractor.h


#ifndef __EXTRACTOR__
#define __EXTRACTOR__

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

#include <iostream>
#include <map>

class Extractor
{
   public:
      Extractor(char*);
      long Average(); //It depends
      void BackgroundErase(); //It depends
      void ColorFull();
      void NoiseClear(); //It depends
      void Cut();
   private:
      long avg;
      IplImage *image;
};

#endif


extractor.cpp


#include "extractor.h"

Extractor::Extractor(char *name)
{
   image = cvLoadImage(name,1);
}

long Extractor::Average()
{
   cvSmooth(image,image,CV_BLUR);

   avg = 0;

   for(int y = 0;y < 3;++y){
      for(int x = 0;x < 3;++x){
long temp = 0;
temp += image->imageData[y * image->widthStep + (x * 3)] + image->imageData[y * image->widthStep + (x * 3) + 1] + image->imageData[y * image->widthStep + (x * 3) + 2];
avg += temp / 3;
      }
   }


   for(int y = image->height - 1;y > image->height - 4;--y){
      for(int x = 0;x < 3;++x){
long temp = 0;
temp += image->imageData[y * image->widthStep + (x * 3)] + image->imageData[y * image->widthStep + (x * 3) + 1] + image->imageData[y * image->widthStep + (x * 3) + 2];
avg += temp / 3;
      }
   }

   for(int y = 0;y < 3;++y){
      for(int x = image->width - 1;x > image->width - 4;--x){
long temp = 0;
temp += image->imageData[y * image->widthStep + (x * 3)] + image->imageData[y * image->widthStep + (x * 3) + 1] + image->imageData[y * image->widthStep + (x * 3) + 2];
avg += temp / 3;
      }
   }

   for(int y = image->height - 1;y > image->height - 4;--y){
      for(int x = image->width - 1;x > image->width - 4;--x){
long temp = 0;
temp += image->imageData[y * image->widthStep + (x * 3)] + image->imageData[y * image->widthStep + (x * 3) + 1] + image->imageData[y * image->widthStep + (x * 3) + 2];
avg += temp / 3;
      }
   }

   avg /= 36;


   return avg ;
}

void Extractor::BackgroundErase()
{
   long range = 20;

   for(int y = 0;y < image->height;++y){
      for(int x = 0;x < image->widthStep;++x){
long temp = (image->imageData[y * image->widthStep + x] +
      image->imageData[y * image->widthStep + x + 1] +
      image->imageData[y * image->widthStep + x + 2]) / 3;
if(temp  < (avg + range) && temp > (avg - range)){
   image->imageData[y * image->widthStep + x] = image->imageData[y * image->widthStep + x + 1] = image->imageData[y * image->widthStep + x + 2] = 255;
}
      }
   }
}

void Extractor::ColorFull()
{
   for(int y = 0;y < image->height;++y){
      for(int x = 0;x < image->widthStep - 3;x += 3){
long temp = (image->imageData[y * image->widthStep + x] +
      image->imageData[y * image->widthStep + x + 1] +
      image->imageData[y * image->widthStep + x + 2]) / 3;
if(temp > 0){
   image->imageData[y * image->widthStep + x] = image->imageData[y * image->widthStep + x + 1] = image->imageData[y * image->widthStep + x + 2] = 0;
}
      }
   }
}

void Extractor::NoiseClear()
{
   int range = 20;
   IplImage *itemp = cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,3);
   cvCopy(image,itemp);

   for(int y = 0;y < itemp->height;++y){
      for(int x = 0;x < itemp->widthStep - 3;x += 3){
long temp = (itemp->imageData[y * itemp->widthStep + x] +
      itemp->imageData[y * itemp->widthStep + x + 1] +
      itemp->imageData[y * itemp->widthStep + x + 2]) / 3;

long ref =  (itemp->imageData[(y - 1) * itemp->widthStep + x] +
      itemp->imageData[(y - 1) * itemp->widthStep + x + 1] +
      itemp->imageData[(y - 1) * itemp->widthStep + x + 2] / 3)
   +
   (itemp->imageData[(y + 1) * itemp->widthStep + x]  +
    itemp->imageData[(y + 1) * itemp->widthStep + x + 1] +
    itemp->imageData[(y + 1) * itemp->widthStep + x + 1] / 3)
   +
   (itemp->imageData[y * itemp->widthStep + (x + 3)] +
    itemp->imageData[y * itemp->widthStep + (x + 3) + 1] +
    itemp->imageData[y * itemp->widthStep + (x + 3) + 2] / 3)
   +
   (itemp->imageData[y * itemp->widthStep + (x - 3)] +
    itemp->imageData[y * itemp->widthStep + (x - 3) + 1] +
    itemp->imageData[y * itemp->widthStep + (x - 3) + 2] / 3)
   +
   (itemp->imageData[(y + 1) * itemp->widthStep + (x - 3)]  +
    itemp->imageData[(y + 1) * itemp->widthStep + (x - 3) + 1] +
    itemp->imageData[(y + 1) * itemp->widthStep + (x - 3) + 1] / 3)
   +
   (itemp->imageData[(y + 1) * itemp->widthStep + (x + 3)]  +
    itemp->imageData[(y + 1) * itemp->widthStep + (x + 3) + 1] +
    itemp->imageData[(y + 1) * itemp->widthStep + (x + 3) + 1] / 3)
   +
   (itemp->imageData[(y - 1) * itemp->widthStep + (x + 3)] +
    itemp->imageData[(y - 1) * itemp->widthStep + (x + 3) + 1] +
    itemp->imageData[(y - 1) * itemp->widthStep + (x + 3) + 2] / 3)
   +
   (itemp->imageData[(y - 1) * itemp->widthStep + (x - 3)] +
    itemp->imageData[(y - 1) * itemp->widthStep + (x - 3) + 1] +
    itemp->imageData[(y - 1) * itemp->widthStep + (x - 3) + 2] / 3)
   ;

ref /= 8;

if((temp + range) < ref){
   image->imageData[y * image->widthStep + x] = image->imageData[y * image->widthStep + x + 1] = image->imageData[y * image->widthStep + x + 2] = 255;
}
      }
   }
}

void Extractor::Cut()
{
   IplImage *gray = cvCreateImage(cvSize(image->width,image->height),IPL_DEPTH_8U ,1);
   cvCvtColor(image,gray,CV_RGB2GRAY);

   cvCanny(gray,gray,30,90);
   cvSmooth(gray,gray,CV_BLUR);
   cvSmooth(gray,gray,CV_BLUR);
   cvSmooth(gray,gray,CV_BLUR);

   std::multimap<int,CvRect> list;

   CvMemStorage* storage = cvCreateMemStorage( 0 );
   CvSeq* contours = NULL;
   cvFindContours(gray, storage, &contours, sizeof( CvContour ), CV_RETR_LIST,CV_CHAIN_APPROX_NONE);
   for( ; contours != NULL; contours = contours->h_next ){
      CvRect rect = cvBoundingRect( contours, 0 );
      //cvRectangle(image, cvPoint( rect.x, rect.y ),cvPoint( rect.x + rect.width, rect.y + rect.height ), cvScalar(0,0,255), 0 );
      list.insert(std::make_pair(rect.width * rect.height,rect));
   }

   //cvSaveImage("find.jpg",image);

 
   int captcha_numbers = 5;
   std::multimap<int,CvRect> result;
   std::multimap<int,CvRect>::reverse_iterator rit = list.rbegin();
   for(int i = 0;rit != list.rend() && i < captcha_numbers;++rit,++i){
      result.insert(std::make_pair(rit->second.x,rit->second));
   }

   char name[] = "1.jpg";
   std::multimap<int,CvRect>::iterator it = result.begin();
   for(;it != result.end();++it){
      cvSetImageROI(image,it->second);
      cvSaveImage(name,image);
      name[0] += 1;
      //std::cout << it->first << std::endl;
   }
}


main.cpp


#include "extractor.h"

int main(int argc,char**argv)
{
   char *name = argv[1];

   if(name){

      Extractor extractor(name);

      /*************pipeline*************/

      extractor.Average();

      extractor.BackgroundErase();

      extractor.ColorFull();

      extractor.NoiseClear();

      extractor.Cut();
   }
   return 0;
}




針對的樣本:

(這組失敗,7和8被當成同範圍)










2012年9月26日 星期三

wxWidgets+OpenGL+OpenCV

    也可以結合Arduino做有趣又簡單的應用,當然還是寫成Multi Thread會比較順暢。


render_data.h

#ifndef __RENDER_DATA__
#define __RENDER_DATA__

typedef struct
{
   float vertex[3];
   float texcoord[2];
}VertexTexture;

VertexTexture vertex[4] = {
   {{-0.5f,-0.5f,0.0f},{0.0f,1.0f}},
   {{0.5f,-0.5f,0.0f},{1.0f,1.0f}},
   {{-0.5f,0.5f,0.0f},{0.0f,0.0f}},
   {{0.5f,0.5f,0.0f},{1.0f,0.0f}}
};

#endif

wxGLCV.h

#ifndef __WX_GLCV__
#define __WX_GLCV__

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

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

#include "render_data.h"

class wxGL;

class App:public wxApp
{
   public:

   bool OnInit();
};

class Frame:public wxFrame
{
   public:

      Frame(const wxString&);
      ~Frame();

      void OnExit(wxCommandEvent&);

      void CreateUI();
      void Render(wxTimerEvent&);
      void Capture();

   private:

      wxGL *gl;
      wxTimer timer;
      wxSlider *angle[3];
      wxImage *image;
      static const unsigned long fps = 25;
      unsigned long begin,last;

      CvCapture *camera;
      IplImage *frame;
      IplImage *temp;

      DECLARE_EVENT_TABLE();
};

class wxGL:public wxGLCanvas
{
   public:

      wxGL(wxWindow*,wxWindowID,
   const wxPoint&,
   const wxSize&,
   long,const wxString);
      ~wxGL();

      void OnSize(wxSizeEvent&);

      void InitGL();
      void Render(wxImage*);
      void SetAngleXYZ(int x,int y,int z){angleX = x;angleY = y,angleZ = z;};

   private:

      bool init;
      float angleX,angleY,angleZ;
      GLuint texture_id;

      DECLARE_EVENT_TABLE();
};

enum{
   RENDER_TIMER = 1000
};

DECLARE_APP(App)
IMPLEMENT_APP(App)

BEGIN_EVENT_TABLE(wxGL,wxGLCanvas)
   EVT_SIZE(wxGL::OnSize)
END_EVENT_TABLE()

BEGIN_EVENT_TABLE(Frame,wxFrame)
   EVT_MENU(wxID_EXIT,Frame::OnExit)
   EVT_TIMER(RENDER_TIMER,Frame::Render)
END_EVENT_TABLE()

#endif

wxGLCV.cpp

#include "wxGLCV.h"

bool App::OnInit()
{
   Frame *frame = new Frame(wxT("wxGL"));

   frame->Show(true);

   return true;
}

Frame::Frame(const wxString &title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(800,600)),timer(this,RENDER_TIMER)
{
   CreateUI();

   begin = 0;
   last = 0;

   camera = cvCaptureFromCAM(-1);

   timer.Start(10);
}

void Frame::CreateUI()
{
   wxMenu *file = new wxMenu;
   file->Append(wxID_EXIT,wxT("E&xit\tAlt-q"),wxT("Exit"));

   wxMenuBar *bar = new wxMenuBar;
   bar->Append(file,wxT("File"));
   SetMenuBar(bar);

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

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

   wxPanel *panel = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(380,380));
   screen_box->Add(panel,0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);
 
   for(int i = 0;i < 3;++i){
      angle[i] = new wxSlider(this,wxID_ANY,0,0,360,wxDefaultPosition,wxSize(300,-1),wxSL_HORIZONTAL | wxSL_AUTOTICKS | wxSL_LABELS);
      screen_box->Add(angle[i],0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);
   }

   gl = new wxGL(panel,wxID_ANY,wxDefaultPosition,wxSize(380,380),0,wxT("wxGL"));

   CreateStatusBar(2);
   SetStatusText(wxDateTime::Now().Format());

}

void Frame::Render(wxTimerEvent &event){

   begin = wxGetLocalTimeMillis().ToLong();

   gl->SetAngleXYZ(angle[0]->GetValue(),angle[1]->GetValue(),angle[2]->GetValue());

   if(begin - last > (1000 / fps)){
      last = begin;
      Capture();
      gl->Render(image);
   }

}

void Frame::Capture()
{
   frame = cvQueryFrame(camera);
   temp = cvCreateImage(cvSize(frame->width,frame->height),8,3);
   cvZero(temp);

   cvConvertImage(frame,temp,CV_CVTIMG_SWAP_RB);

   unsigned char *data;
   cvGetRawData(temp,&data);

   if(image){
      delete image;
      image = new wxImage(temp->width,temp->height,data,true);
   }
   else{
      image = new wxImage(temp->width,temp->height,data,true);
   }
}

Frame::~Frame()
{
   if(frame){
      cvReleaseImage(&frame);
   }
   if(temp){
      cvReleaseImage(&temp);
   }
   if(camera){
      cvReleaseCapture(&camera);
   }
   timer.Stop();
}

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

wxGL::wxGL(wxWindow *paraent,wxWindowID id = wxID_ANY,
      const wxPoint &pos = wxDefaultPosition,
      const wxSize &size = wxDefaultSize,
      long style = 0,const wxString name = wxT("wxGL")):
   wxGLCanvas(paraent,(wxGLCanvas*)NULL,id,pos,size,style | wxFULL_REPAINT_ON_RESIZE,name)
{
   wxInitAllImageHandlers();
   texture_id = 0;
   init = false;
}

void wxGL::InitGL()
{
   SetCurrent();
 
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(60.0f,1.0f,0.1f,100.0f);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(0.0f,0.0f,2.0f,
0.0f,0.0f,0.0f,
0.0f,-1.0f,0.0f);

   for(int i = 0;i < 4;++i){
      vertex[i].texcoord[1] = 1.0f - vertex[i].texcoord[1];
   }

   glEnable(GL_DEPTH_TEST);
   //glEnable(GL_CULL_FACE);
   glEnable(GL_TEXTURE_2D);
}

wxGL::~wxGL()
{
}

void wxGL::OnSize(wxSizeEvent &event)
{
   wxGLCanvas::OnSize(event);

   int w,h;
   GetClientSize(&w,&h);

   if(GetContext()){
      SetCurrent();
      glViewport(0,0,(GLint)w,(GLint)h);
   }
}

void wxGL::Render(wxImage *image)
{
   wxPaintDC dc(this);

   if(!init){
      InitGL();
      init = true;
   }

   SetCurrent();

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glGenTextures(1,&texture_id);
   glBindTexture(GL_TEXTURE_2D,texture_id);
   glTexImage2D(GL_TEXTURE_2D,0,GL_RGB8,
(GLint)image->GetWidth(),(GLint)image->GetHeight(),0,
GL_RGB,GL_UNSIGNED_BYTE,image->GetData());

   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(0.0f,0.0f,2.0f,
0.0f,0.0f,0.0f,
0.0f,-1.0f,0.0f);
   glRotatef(angleX,1.0f,0.0f,0.0f);
   glRotatef(angleY,0.0f,1.0f,0.0f);
   glRotatef(angleZ,0.0f,0.0f,1.0f);

   glEnableClientState(GL_VERTEX_ARRAY);
   glVertexPointer(3,GL_FLOAT,sizeof(VertexTexture),&vertex[0].vertex);

   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   glTexCoordPointer(2,GL_FLOAT,sizeof(VertexTexture),&vertex[0].texcoord);

   glDrawArrays(GL_TRIANGLE_STRIP,0,4);

   SwapBuffers();
}




g++ -o2 -o wxGLCV wxGLCV.cpp `wx-config --cxxflags --libs --unicode --gl-libs` `pkg-config opencv --cflags --libs`