SDCard version

Fork of gr-peach-opencv-project-sd-card by the do

main.cpp

Committer:
thedo
Date:
2017-07-21
Revision:
167:2ee3e82cb6f5
Parent:
166:240bc5a0f42a

File content as of revision 167:2ee3e82cb6f5:

#include "mbed.h"
#include "DisplayBace.h"
#include "rtos.h"
#include "AsciiFont.h"
#include "define.h"
#include "mStorage.hpp"

// Include for graphic
#include "graphicFramework/myImage.h"
#include "graphicFramework/mGraphic.hpp"

// Include for opencv
#include "cProcess/cProcess.hpp"
#include "opencv_3_1/opencv2/core.hpp"
#include "opencv_3_1/opencv2/imgproc.hpp"
#include "opencv_3_1/opencv2/objdetect.hpp"
#include "opencv_3_1/opencv2/face.hpp"
using namespace cv;
using namespace face;

typedef struct {
    int x;
    int y;
    int radius;
    uint8_t color[2];
}mCircle;

typedef struct {
    int x1;
    int y1;
    int x2;
    int y2;
    uint8_t color[2];
}mLine;

typedef struct {
    vector<Mat> database_image;
    vector<int> database_label;
}face_database_t;

typedef struct {
    Rect obj;
    int label;
}obj_detect_result;

typedef struct {
    vector<mCircle> circles;
    vector<mLine> lines;
    vector<Point> contour;//only have max contour
}gesture_result;

typedef struct {
    vector<Point> Pointstart;
    vector<Point> Pointdepth;
    vector<int> Pointindex;
    int detecthand;
}ConvexPoint;

typedef struct
{
    double hand_max;
    double hand_min;
}Sampling;

/* Hardware */
static DisplayBase Display;
static DigitalOut  lcd_pwon(P7_15);
static DigitalOut  lcd_blon(P8_1);
static PwmOut      lcd_cntrst(P8_15);
static Serial      pc(USBTX, USBRX);
static TouckKey_LCD_shield touch(P4_0, P2_13, I2C_SDA, I2C_SCL);

/* Semaphore and mutex */
rtos::Mutex mMutexProcess;
rtos::Mutex mMutexObjects;
rtos::Mutex mMutexClicked;

static Semaphore semProcessThread(0);
static Semaphore semTouch(0);
static Semaphore semDrawAction(0);

/* Threads */
static Thread *    p_VideoLcdTask = NULL;
static Thread *    p_DrawObjects = NULL;
static Thread *    p_Touch = NULL;

static cStorage *myStorage = NULL;
static vector<obj_detect_result> gObjectDetects;
static gesture_result gGestureResult;
static bool gIsProcessing = false;
static bool gIsDrawing = false;
static int write_buff_num = 0;
static int read_buff_num = 0;
static bool graphics_init_end = false;
static APP_MODE appMode = GUESTURE_RECOGNITION;
static CLICKED_CODE clickedCode = CLICKED_UNKNOWN;

/****** cache control ******/
static void dcache_clean(void * p_buf, uint32_t size) {
    uint32_t start_addr = (uint32_t)p_buf & 0xFFFFFFE0;
    uint32_t end_addr   = (uint32_t)p_buf + size;
    uint32_t addr;

    /* Data cache clean */
    for (addr = start_addr; addr < end_addr; addr += 0x20) {
        __v7_clean_dcache_mva((void *)addr);
    }
}

/****** LCD ******/
#if(0) /* When needing LCD Vsync interrupt, please make it effective. */
static void IntCallbackFunc_LoVsync(DisplayBase::int_type_t int_type) {
    /* Interrupt callback function for Vsync interruption */
}
#endif

static void Init_LCD_Display(void) {
    DisplayBase::graphics_error_t error;
    DisplayBase::lcd_config_t lcd_config;
    PinName lvds_pin[8] = {
        /* data pin */
        P5_7, P5_6, P5_5, P5_4, P5_3, P5_2, P5_1, P5_0
    };

    lcd_pwon = 0;
    lcd_blon = 0;
    Thread::wait(100);
    lcd_pwon = 1;
    lcd_blon = 1;

    Display.Graphics_Lvds_Port_Init(lvds_pin, 8);

    /* Graphics initialization process */
    lcd_config = LcdCfgTbl_LCD_shield;
    error = Display.Graphics_init(&lcd_config);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
    graphics_init_end = true;

#if(0) /* When needing LCD Vsync interrupt, please make it effective. */
    /* Interrupt callback function setting (Vsync signal output from scaler 0)*/
    error = Display.Graphics_Irq_Handler_Set(DisplayBase::INT_TYPE_S0_LO_VSYNC,
                                                    0, IntCallbackFunc_LoVsync);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
#endif
}

static void Start_LCD_Display(uint8_t * p_buf) {
    DisplayBase::rect_t rect;

    rect.vs = 0;
    rect.vw = LCD_PIXEL_HEIGHT;
    rect.hs = 0;
    rect.hw = LCD_PIXEL_WIDTH;
    Display.Graphics_Read_Setting(
        DisplayBase::GRAPHICS_LAYER_0,
        (void *)p_buf,
        FRAME_BUFFER_STRIDE,
        GRAPHICS_FORMAT,
        WR_RD_WRSWA,
        &rect
    );
    Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0);
}

/****** Video ******/
#if(0) /* When needing video Vsync interrupt, please make it effective. */
static void IntCallbackFunc_ViVsync(DisplayBase::int_type_t int_type) {
    /* Interrupt callback function for Vsync interruption */
}
#endif

static void IntCallbackFunc_Vfield(DisplayBase::int_type_t int_type) {
    /* Interrupt callback function */
#if VIDEO_INPUT_METHOD == VIDEO_CVBS
    if (vfield_count == 0) {
        vfield_count = 1;
    } else {
        vfield_count = 0;
#else
    {
#endif
        if (p_VideoLcdTask != NULL) {
            p_VideoLcdTask->signal_set(1);
        }
    }
}

static void Init_Video(void) {
    DisplayBase::graphics_error_t error;

    /* Graphics initialization process */
    if (graphics_init_end == false) {
        /* When not initializing LCD, this processing is needed. */
        error = Display.Graphics_init(NULL);
        if (error != DisplayBase::GRAPHICS_OK) {
            printf("Line %d, error %d\n", __LINE__, error);
            mbed_die();
        }
        graphics_init_end = true;
    }

#if VIDEO_INPUT_METHOD == VIDEO_CVBS
    error = Display.Graphics_Video_init( DisplayBase::INPUT_SEL_VDEC, NULL);
    if( error != DisplayBase::GRAPHICS_OK ) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
#elif VIDEO_INPUT_METHOD == VIDEO_CMOS_CAMERA
    DisplayBase::video_ext_in_config_t ext_in_config;
    PinName cmos_camera_pin[11] = {
        /* data pin */
        P2_7, P2_6, P2_5, P2_4, P2_3, P2_2, P2_1, P2_0,
        /* control pin */
        P10_0,      /* DV0_CLK   */
        P1_0,       /* DV0_Vsync */
        P1_1        /* DV0_Hsync */
    };

    /* MT9V111 camera input config */
    ext_in_config.inp_format     = DisplayBase::VIDEO_EXTIN_FORMAT_BT601; /* BT601 8bit YCbCr format */
    ext_in_config.inp_pxd_edge   = DisplayBase::EDGE_RISING;              /* Clock edge select for capturing data          */
    ext_in_config.inp_vs_edge    = DisplayBase::EDGE_RISING;              /* Clock edge select for capturing Vsync signals */
    ext_in_config.inp_hs_edge    = DisplayBase::EDGE_RISING;              /* Clock edge select for capturing Hsync signals */
    ext_in_config.inp_endian_on  = DisplayBase::OFF;                      /* External input bit endian change on/off       */
    ext_in_config.inp_swap_on    = DisplayBase::OFF;                      /* External input B/R signal swap on/off         */
    ext_in_config.inp_vs_inv     = DisplayBase::SIG_POL_NOT_INVERTED;     /* External input DV_VSYNC inversion control     */
    ext_in_config.inp_hs_inv     = DisplayBase::SIG_POL_INVERTED;         /* External input DV_HSYNC inversion control     */
    ext_in_config.inp_f525_625   = DisplayBase::EXTIN_LINE_525;           /* Number of lines for BT.656 external input */
    ext_in_config.inp_h_pos      = DisplayBase::EXTIN_H_POS_CRYCBY;       /* Y/Cb/Y/Cr data string start timing to Hsync reference */
    ext_in_config.cap_vs_pos     = 6;                                     /* Capture start position from Vsync */
    ext_in_config.cap_hs_pos     = 150;                                   /* Capture start position form Hsync */
#if (LCD_TYPE == 0)
    /* The same screen ratio as the screen ratio of the LCD. */
    ext_in_config.cap_width      = 640;                                   /* Capture width */
    ext_in_config.cap_height     = 363;                                   /* Capture height Max 468[line]
                                                                             Due to CMOS(MT9V111) output signal timing and VDC5 specification */
#else
    ext_in_config.cap_width      = 640;                                   /* Capture width  */
    ext_in_config.cap_height     = 468;                                   /* Capture height Max 468[line]
                                                                             Due to CMOS(MT9V111) output signal timing and VDC5 specification */
#endif
    error = Display.Graphics_Video_init( DisplayBase::INPUT_SEL_EXT, &ext_in_config);
    if( error != DisplayBase::GRAPHICS_OK ) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Camera input port setting */
    error = Display.Graphics_Dvinput_Port_Init(cmos_camera_pin, 11);
    if( error != DisplayBase::GRAPHICS_OK ) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
#endif

#if(0) /* When needing video Vsync interrupt, please make it effective. */
    /* Interrupt callback function setting (Vsync signal input to scaler 0) */
    error = Display.Graphics_Irq_Handler_Set(DisplayBase::INT_TYPE_S0_VI_VSYNC,
                                                 0, IntCallbackFunc_ViVsync);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
#endif

    /* Interrupt callback function setting (Field end signal for recording function in scaler 0) */
    error = Display.Graphics_Irq_Handler_Set(VIDEO_INT_TYPE, 0, 
                                                        IntCallbackFunc_Vfield);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
}

static void initDrawResultLayer(void)
{
    DisplayBase::rect_t rect;
    /* The layer by which the touch panel location is drawn */
    memset(user_frame_buffer_draw, 0, sizeof(user_frame_buffer_draw));
    dcache_clean(user_frame_buffer_draw, sizeof(user_frame_buffer_draw));
    rect.vs = 0;
    rect.vw = LCD_PIXEL_HEIGHT;
    rect.hs = 0;
    rect.hw = LCD_PIXEL_WIDTH;
    Display.Graphics_Read_Setting(
        DisplayBase::GRAPHICS_LAYER_1,
        (void *)user_frame_buffer_draw,
        DRAW_BUFFER_STRIDE,
        DisplayBase::GRAPHICS_FORMAT_ARGB4444,
        DisplayBase::WR_RD_WRSWA_32_16BIT,
        &rect
    );
    Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_1);
}

static void initDrawAction(void)
{
    // Init draw control
    DisplayBase::rect_t rect;
    rect.vs = 0;
    rect.vw = LCD_PIXEL_HEIGHT;
    rect.hs = 0;
    rect.hw = LCD_PIXEL_WIDTH;
    
    memset(user_frame_buffer_draw_action, 0, 
                                        sizeof(user_frame_buffer_draw_action));     
    /* Clean cache */
    dcache_clean(user_frame_buffer_draw_action, 
                                        sizeof(user_frame_buffer_draw_action));
    
    Display.Graphics_Read_Setting(
        DisplayBase::GRAPHICS_LAYER_3,
        (void *)user_frame_buffer_draw_action,
        DRAW_BUFFER_STRIDE,
        DisplayBase::GRAPHICS_FORMAT_ARGB4444,
        DisplayBase::WR_RD_WRSWA_32_16BIT,
        &rect
    );
    Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_3);
}
static void initDrawButtonLayer(void)
{
    DisplayBase::rect_t rect;
    /* The layer by which the touch panel location is drawn */
    memset(user_frame_buffer_draw_button, 0, 
                                        sizeof(user_frame_buffer_draw_button));
    dcache_clean(user_frame_buffer_draw_button, 
                                        sizeof(user_frame_buffer_draw_button));
    rect.vs = 0;
    rect.vw = LCD_PIXEL_HEIGHT;
    rect.hs = 0;
    rect.hw = LCD_PIXEL_WIDTH;
    Display.Graphics_Read_Setting(
        DisplayBase::GRAPHICS_LAYER_2,
        (void *)user_frame_buffer_draw_button,
        DRAW_BUFFER_STRIDE,
        DisplayBase::GRAPHICS_FORMAT_ARGB4444,
        DisplayBase::WR_RD_WRSWA_32_16BIT,
        &rect
    );
    Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_2);
}

static void Start_Video(uint8_t * p_buf) {
    DisplayBase::graphics_error_t error;

    /* Video capture setting (progressive form fixed) */
    error = Display.Video_Write_Setting(
                VIDEO_INPUT_CH,
                COL_SYS,
                p_buf,
                FRAME_BUFFER_STRIDE,
                VIDEO_FORMAT,
                WR_RD_WRSWA,
                VIDEO_PIXEL_VW,
                VIDEO_PIXEL_HW
            );
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Video write process start */
    error = Display.Video_Start(VIDEO_INPUT_CH);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Video write process stop */
    error = Display.Video_Stop(VIDEO_INPUT_CH);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Video write process start */
    error = Display.Video_Start(VIDEO_INPUT_CH);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }
}

/* Select function from button */
APP_MODE getFunctionSelected(int x, int y)
{
    int w = 100; //width of image button
    if ( MODE_BTN_Y > y )
    {
        return MODE_UNKNOWN;
    }
    
    if ( MODE_BTN_X <= x && x <= (MODE_BTN_X + w))
    {
        return FACE_DETECTION;
    }
    else if ( (2*MODE_BTN_X + w) <= x && x <= (2*MODE_BTN_X + 2*w))
    {
        return FACE_RECOGNITION;
    }
    else if ( (3*MODE_BTN_X + 2*w) <= x && x <= (3*MODE_BTN_X + 3*w) )
    {
        return MOTION_DETECTION;
    }
    else if ((4*MODE_BTN_X + 3*w) <= x && x <= (4*MODE_BTN_X + 4*w))
    {
        return GUESTURE_RECOGNITION;
    }
    else
        return MODE_UNKNOWN;
}

/* Click action in face recognition */
void clickActionFaceReg(int x, int y)
{
    mMutexClicked.lock();
    if(REGIS_FACE_BTN_X <= x && x <= (REGIS_FACE_BTN_X + 30) &&
       REGIS_FACE_BTN_Y <= y && y <= (REGIS_FACE_BTN_Y + 30))
    {
        clickedCode = CLICKED_REGIS_FACE;
    }
    else if (FACE_REG_ACT_MENU_X <= x && x <= (FACE_REG_ACT_MENU_X + 60) &&
        FACE_REG_ACT_MENU_Y <= y && y <= (FACE_REG_ACT_MENU_Y + 20))
    {
        clickedCode = CLICKED_CHANGE_ID;
    }
    else if (FACE_REG_ACT_MENU_X <= x && x <= (FACE_REG_ACT_MENU_X + 60) &&
        (FACE_REG_ACT_MENU_Y + 20 + 12)<= y && 
        y <= (FACE_REG_ACT_MENU_Y + 2*20 + 12))//space:12
    {
        clickedCode = CLICKED_ADD;
    }
    else if (FACE_REG_ACT_MENU_X <= x && x <= (FACE_REG_ACT_MENU_X + 60) &&
        (FACE_REG_ACT_MENU_Y + 2*20 + 2*12)<= y && 
        y <= (FACE_REG_ACT_MENU_Y + 3*20 + 2*12))
    {
        clickedCode = CLICKED_IGNORE;
    }
    else
    {
        clickedCode = CLICKED_UNKNOWN;
    }
    mMutexClicked.unlock();
}

/* Click on screen gesture recognition */
void clickActionGestureReg(int x, int y)
{
    mMutexClicked.lock();
    if(GESTURE_SAMPLING_BTN_X <= x && x <= (GESTURE_SAMPLING_BTN_X + 60) &&
       GESTURE_SAMPLING_BTN_Y <= y && y <= (GESTURE_SAMPLING_BTN_Y + 20))
    {
        clickedCode = CLICKED_HAND_SAMPLING;
    }
    else
    {
        clickedCode = CLICKED_UNKNOWN;
    }
    mMutexClicked.unlock();
}

/**** Draw button controls ****/
void drawButtonControls(graphicFrameworkCanvas myCanvasButton)
{
    int space = 16;
    int x = 0;
    int y = MODE_BTN_Y;
    
    x+=space;
    myCanvasButton.drawImage(my_img_button_face_detect,x,y); // 100x40
    x+=100;
    
    x+=space;
    myCanvasButton.drawImage(my_img_button_face_recognition,x,y);
    x+=100;

    x+=space;
    myCanvasButton.drawImage(my_img_button_obj_motion,x,y);
    x+=100;
    
    x+=space;
    myCanvasButton.drawImage(my_img_button_gesture_reg,x,y);
    x+=100;
}

void drawTextScreen(graphicFrameworkCanvas myCanvasButton)
{
    int x = 160;
    int y = 5;
    
    if( appMode == MOTION_DETECTION )
    {
        myCanvasButton.drawImage(my_img_text_obj_motion,x, y);
    }
    else if ( appMode == FACE_DETECTION )
    {
        myCanvasButton.drawImage(my_img_text_face_detection,x, y);
    }
    else if ( appMode == FACE_RECOGNITION )
    {
        myCanvasButton.drawImage(my_img_text_face_recognition,x, y);
    }
    else if ( appMode == GUESTURE_RECOGNITION )
    {
        myCanvasButton.drawImage(my_img_text_gesture_reg,x, y);
    }
}

void clearGestureResult(gesture_result res)
{
    res.circles.clear();
    res.lines.clear();
    res.contour.clear();
}
/****** Thread image process ******/
static void img_draw_objects(void)
{
    graphicFrameworkCanvas myCanvas(user_frame_buffer_draw, 0x01E0,
                    0x0110, (uint8_t)DRAW_BUFFER_BYTE_PER_PIXEL, 
                    (uint8_t)DRAW_POINT,0x06);

    initDrawResultLayer();
    
    uint8_t color[2] = {0x0B,0xFF};// Color Pink
    APP_MODE oldMode = appMode;
    while(1)
    {
        Thread::signal_wait(1);        
        memset(user_frame_buffer_draw, 0, sizeof(user_frame_buffer_draw));

        if ( oldMode != appMode)
        {
            oldMode = appMode;
            
            mMutexObjects.lock();
            gIsDrawing = false;
            mMutexObjects.unlock();     
                   
            // Data cache clean 
            dcache_clean(user_frame_buffer_draw, sizeof(user_frame_buffer_draw));
            continue;
        }

        // Draw faces on screen
        switch(appMode)
        {
            case FACE_DETECTION:
            case MOTION_DETECTION:
            {
                if(gObjectDetects.size() > 0)
                {
                    for(int i = 0;i < gObjectDetects.size();i++)
                    {
                        int _x = gObjectDetects[i].obj.x*IMG_DOWN_SAMPLE;
                        int _y = gObjectDetects[i].obj.y*IMG_DOWN_SAMPLE;
                        int _w = gObjectDetects[i].obj.width*IMG_DOWN_SAMPLE;
                        int _h = gObjectDetects[i].obj.height*IMG_DOWN_SAMPLE;
                        myCanvas.drawRect(_x,_y,_w,_h,color);
                    }
                }
                gObjectDetects.clear();
                break;
            }
            case FACE_RECOGNITION:
            {
                if(gObjectDetects.size() > 0)
                {
                    for(int i = 0;i < gObjectDetects.size();i++)
                    {
                        int _x = gObjectDetects[i].obj.x*IMG_DOWN_SAMPLE;
                        int _y = gObjectDetects[i].obj.y*IMG_DOWN_SAMPLE;
                        int _w = gObjectDetects[i].obj.width*IMG_DOWN_SAMPLE;
                        int _h = gObjectDetects[i].obj.height*IMG_DOWN_SAMPLE;
                        if(gObjectDetects[i].label == 1)
                        {
                            // Yellow
                            color[0] = 0xF0;
                            color[1] = 0xFF;
                        }
                        else if (gObjectDetects[i].label == 2)
                        {
                            // Green
                            color[0] = 0xF0;
                            color[1] = 0xF0;
                        }
                        else
                        {
                            // Pink
                            color[0] = 0x0B;
                            color[1] = 0xFF;
                        }
                        myCanvas.drawRect(_x,_y,_w,_h,color);
                    }
                }
                gObjectDetects.clear();
                break;
            }
            case GUESTURE_RECOGNITION:
            {
                uint8_t contoursColor[2] = {0xF0,0xFF};
                    
                //Draw max contour
                for(int i = 0; i < gGestureResult.contour.size();i++)//i=0
                {
                    myCanvas.draw_pixel(gGestureResult.contour[i].x,
                            gGestureResult.contour[i].y,contoursColor);
                }
                // Draw lines, circles
                for(int i = 0;i < gGestureResult.lines.size();i++)
                {
                    myCanvas.drawLine(gGestureResult.lines[i].x1,
                                      gGestureResult.lines[i].y1,
                                      gGestureResult.lines[i].x2,
                                      gGestureResult.lines[i].y2,
                                      gGestureResult.lines[i].color);
                }
                
                for(int i = 0;i < gGestureResult.circles.size();i++)
                {
                    myCanvas.drawCircle(gGestureResult.circles[i].x,
                                        gGestureResult.circles[i].y,
                                        gGestureResult.circles[i].radius,
                                        gGestureResult.circles[i].color);
                }
                break;
            }
            default:
            break;
        } // switch

        mMutexObjects.lock();
        gIsDrawing = false;
        mMutexObjects.unlock();
        
        // Data cache clean 
        dcache_clean(user_frame_buffer_draw, sizeof(user_frame_buffer_draw));
    }//while
}

/* Touch task*/
static void touch_int_callback(void) 
{
    semTouch.release();
}
static void touch_task(void) 
{
    graphicFrameworkCanvas myCanvasButton(user_frame_buffer_draw_button, 0x01E0,
                    0x0110, (uint8_t)DRAW_BUFFER_BYTE_PER_PIXEL, 
                    (uint8_t)DRAW_POINT,0x06);
    TouchKey::touch_pos_t touch_pos[TOUCH_NUM];
    
    /* Callback setting */
    touch.SetCallback(&touch_int_callback);

    /* Reset touch IC */
    touch.Reset();
    
    initDrawButtonLayer();
    drawTextScreen(myCanvasButton);
    drawButtonControls(myCanvasButton);
    
    int x_touch = -1, y_touch = -1;

    set_time(0);//set_time(1498450609);//11:15,Mon-26-June
    double seconds = (double)time(NULL);

    while(1)
    {
        /* Wait touch event */
        semTouch.wait();

        touch.GetCoordinates(TOUCH_NUM, touch_pos);

        for (int i = 0; i < TOUCH_NUM; i ++) {
            if (touch_pos[i].valid) {
                int new_touch_x = touch_pos[i].x;
                int new_touch_y = touch_pos[i].y;
                APP_MODE mode = getFunctionSelected(new_touch_x, new_touch_y);

                if(mode != appMode && mode != MODE_UNKNOWN)
                {
                    appMode = mode;
                    memset(user_frame_buffer_draw_button, 0, sizeof(uint8_t)*LCD_PIXEL_WIDTH*(MODE_BTN_Y)*FRAME_BUFFER_BYTE_PER_PIXEL);
                    drawTextScreen(myCanvasButton);
                    dcache_clean(user_frame_buffer_draw_button, sizeof(user_frame_buffer_draw_button));
                }
                else if (appMode == FACE_RECOGNITION)
                {
                    double time_1 = (double)time(NULL) - seconds;
                    int posAbs = abs(new_touch_x - x_touch) + 
                                      abs(new_touch_y - y_touch); 
                    
                    if(posAbs > 5 || time_1 > 0.5) //1s
                    {
                        x_touch = new_touch_x;
                        y_touch = new_touch_y;

                        clickActionFaceReg(new_touch_x,new_touch_y);

                    }
                    seconds = (double)time(NULL);
                }
                else if (appMode == GUESTURE_RECOGNITION)
                {
                    double time_1 = (double)time(NULL) - seconds;
                    int posAbs = abs(new_touch_x - x_touch) + 
                                      abs(new_touch_y - y_touch); 

                    if(posAbs > 5 || time_1 > 0.5) //1s
                    {
                        x_touch = new_touch_x;
                        y_touch = new_touch_y;
                        clickActionGestureReg(new_touch_x,new_touch_y);
                    }

                    seconds = (double)time(NULL);
                }
            }
        }
    }
}

/****** Video input is output to LCD ******/
static void video_lcd_task(void) {
    DisplayBase::graphics_error_t error;
    int wk_num;

    /* Initialization memory */
    for (int i = 0; i < FRAME_BUFFER_NUM; i++) {
        memset(FrameBufferTbl[i], 0, (FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT));
        dcache_clean(FrameBufferTbl[i],(FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT));
    }

    /* Start of Video */
    Start_Video(FrameBufferTbl[write_buff_num]);

    /* Wait for first video drawing */
    Thread::signal_wait(1);
    write_buff_num++;
    if (write_buff_num >= FRAME_BUFFER_NUM) {
        write_buff_num = 0;
    }
    error = Display.Video_Write_Change(VIDEO_INPUT_CH, 
                        FrameBufferTbl[write_buff_num], FRAME_BUFFER_STRIDE);
    if (error != DisplayBase::GRAPHICS_OK) {
        printf("Line %d, error %d\n", __LINE__, error);
        mbed_die();
    }

    /* Start of LCD */
    Start_LCD_Display(FrameBufferTbl[read_buff_num]);

    /* Backlight on */
    Thread::wait(200);
    lcd_cntrst.write(1.0);

    while (1) {
        Thread::signal_wait(1);
        wk_num = write_buff_num + 1;
        if (wk_num >= FRAME_BUFFER_NUM) {
            wk_num = 0;
        }
        /* If the next buffer is empty, it's changed. */
        if (wk_num != read_buff_num) {
            read_buff_num  = write_buff_num;
            write_buff_num = wk_num;
            
            /* Change video buffer */
            error = Display.Video_Write_Change(VIDEO_INPUT_CH, 
                    FrameBufferTbl[0/*write_buff_num*/], FRAME_BUFFER_STRIDE);
            if (error != DisplayBase::GRAPHICS_OK) {
                printf("Line %d, error %d\n", __LINE__, error);
                mbed_die();
            }
            
            mMutexProcess.lock();
            if ( gIsProcessing == false)
            {
                gIsProcessing = true;    
                mMutexProcess.unlock();

                memcpy((void *)my_frame,(void*)FrameBufferTbl[0],
                                        FRAME_BUFFER_STRIDE * LCD_PIXEL_HEIGHT);
                semProcessThread.release();
            }
            else
            {
                mMutexProcess.unlock();
            }
            
            /* Change LCD buffer */
            Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_0, 
                                    (void *)FrameBufferTbl[0/*read_buff_num*/]);
        }
    }
}

bool initSdCard()
{
    if(myStorage == NULL)
    {
        return false;
    }
    if (myStorage->isConnectSdCard() == STORG_PASS) 
    {
        if(myStorage->mountSdCard() == STORG_PASS)
        {
            return true;
        }
        else
        {
            // mount sdcard failed!
            return false;
        }
    }
    else
    {
        // no sdcard!
        togle_led(LED_RED);
        return false;
    }
}
void faceDectectionApp(void)
{
    Size face_size(LCD_PIXEL_WIDTH/IMG_DOWN_SAMPLE,
                                            LCD_PIXEL_HEIGHT/IMG_DOWN_SAMPLE);
    Mat smallImage = Mat::zeros(face_size, CV_8UC1);
    CascadeClassifier haar_cascade;
    vector<Rect> faces;
    if (!haar_cascade.load("/SD/lbpcascade_frontalface.xml"))
    {
        // load failed
        togle_led(LED_RED);
        while(1)
        {
            wait(0.5);
        }
    }
    else{
        togle_led(LED_GREEN);
    }
    togle_reset(LED_RED,LED_BLUE);
    faces.clear();
    
    gObjectDetects.clear();
    while (1) {
        semProcessThread.wait();
        
        if ( appMode != FACE_DETECTION )
        {
            mMutexProcess.lock();
            gIsProcessing = false;
            mMutexProcess.unlock();
            break;
        }
        
        Mat gray;
        {
            Mat res = _m_cvtRgb5652Rgb(my_frame,(int)LCD_PIXEL_WIDTH,
                                                    (int)LCD_PIXEL_HEIGHT,2);
            cvtColor(res,gray,COLOR_RGB2GRAY);
            res.release();
        }

        equalizeHist(gray,gray);
        resize(gray, smallImage, face_size);

        haar_cascade.detectMultiScale(smallImage,faces,1.1,
                                        2,0|CV_HAAR_SCALE_IMAGE,Size(10,10));        
        mMutexObjects.lock();
        if(gIsDrawing == false)
        {
            mMutexObjects.unlock();
            for(int i = 0;i < faces.size();i++)
            {
                obj_detect_result tmp;
                tmp.obj = faces[i];
                tmp.label = -1;
                gObjectDetects.push_back(tmp);
            }
            //gObjectDetects = faces;
            gIsDrawing = true;
            p_DrawObjects->signal_set(1);
        }
        else
        {
            mMutexObjects.unlock();
        }

        /* Reset flag */
        faces.clear();
        mMutexProcess.lock();
        gIsProcessing = false;
        mMutexProcess.unlock();
    };
}

void motionDetectionApp(void)
{
    bool isNewStart = true;
    int threshValue = 30;
    int blurSize = 5;
    float mArea = 0.0;
    float mMaxArea = 0.0;
    int mMaxIndex = 0;
    bool _isDrawing;
    int mCounter = 0;
    Mat curFrameGray;
    Mat prevFrameGray;
    Mat diffImage;
    Mat binImage;
    vector<vector<Point> > contours;
    vector<Rect> _objects;

    gObjectDetects.clear();
    while(1)
    {
        semProcessThread.wait();
        
        if ( appMode != MOTION_DETECTION )
        {
            mMutexProcess.lock();
            gIsProcessing = false;
            mMutexProcess.unlock();
            
            break;
        }

        _objects.clear();
        mCounter++;
        if( isNewStart == false)
        {
            {
                Mat res = _m_cvtRgb5652Rgb(my_frame,(int)LCD_PIXEL_WIDTH,
                                                    (int)LCD_PIXEL_HEIGHT,2);
                cvtColor(res,curFrameGray,COLOR_RGB2GRAY);
                res.release();
            }
            /* differential between foreground and background */
            absdiff(curFrameGray, prevFrameGray, diffImage);
            blur(diffImage, diffImage, Size(blurSize, blurSize));
            threshold(diffImage, binImage, threshValue, 255, CV_THRESH_BINARY);
            findContours(binImage, contours, CV_RETR_EXTERNAL, 
                                                        CV_CHAIN_APPROX_NONE);

            if(contours.size() > 0)
            {
                for (int i = 0; i < contours.size(); i++)
                {
                    mArea = contourArea(contours[i]);
                    if (mArea > mMaxArea)
                    {
                        mMaxArea = mArea;
                        mMaxIndex = i;
                    }
                }
                
                if (mMaxArea > MAX_COUNTOURS)
                {
                    Rect objectBoundingRectangle = boundingRect(
                                                        contours.at(mMaxIndex));
                    _objects.push_back(objectBoundingRectangle);
                }
            }
            
            /* Set display motion objects */
            mMutexObjects.lock();
            _isDrawing = gIsDrawing;
            mMutexObjects.unlock();
            
            if(_isDrawing == false)
            {
                for(int i = 0;i < _objects.size();i++)
                {
                    obj_detect_result tmp;
                    tmp.obj = _objects[i];
                    tmp.label = -1;
                    gObjectDetects.push_back(tmp);
                }
                //gObjectDetects = _objects;
                gIsDrawing = true;
                p_DrawObjects->signal_set(1);
            }
            
            /* Reset values */
            mMaxArea = 0;
            mArea = 0;
            mMaxIndex = 0;
            
            /* Update background */
            if(mCounter == 50)
            {
                mCounter = 0;
                isNewStart = true;
            }
        }
        else
        {
            isNewStart = false;
            prevFrameGray = _m_cvtRgb5652Rgb(my_frame,(int)LCD_PIXEL_WIDTH,
                                                    (int)LCD_PIXEL_HEIGHT,2);
            cvtColor(prevFrameGray,prevFrameGray,COLOR_RGB2GRAY);
        }
        
        mMutexProcess.lock();
        gIsProcessing = false;
        mMutexProcess.unlock();
    };
}

void faceRecognitionApp(void)
{
    // Init draw
    graphicFrameworkCanvas myCanvas(user_frame_buffer_draw_action, 0x01E0,
                    0x0110, (uint8_t)DRAW_BUFFER_BYTE_PER_PIXEL, 
                    (uint8_t)DRAW_POINT,0x06);

    uint8_t label_id = 1;
    bool oldStateFace = false;// no faces
    // End init draw
    
    Size face_size(LCD_PIXEL_WIDTH/IMG_DOWN_SAMPLE,
                                            LCD_PIXEL_HEIGHT/IMG_DOWN_SAMPLE);
    Mat smallImage = Mat::zeros(face_size, CV_8UC1);
    CascadeClassifier haar_cascade;
    vector<Rect> faces;
    
    //Init recognizer
    int radius = 2, neighbors = 4, gridx = 8, gridy = 8, thresmax = 50;
    Ptr<LBPHFaceRecognizer> modelReg = createLBPHFaceRecognizer(radius, neighbors,
                                    gridx, gridy, thresmax);
    face_database_t face_database;
    Size regSize(70,70);
    bool flag_class = false;
    bool flag_train = false;
    bool isFirstTrain = true;
    bool changeApp = false;
    
    if (!haar_cascade.load("/SD/lbpcascade_frontalface.xml"))
    {
        // load failed
        togle_led(LED_RED);
        while(1)
        {
            wait(0.5);
        }
    }
    else{
        togle_led(LED_GREEN);
    }
    togle_reset(LED_RED,LED_BLUE);
    faces.clear();
    
    gObjectDetects.clear();
    while(1)
    {
        semProcessThread.wait();

        if ( appMode != FACE_RECOGNITION )
        {
            mMutexProcess.lock();
            gIsProcessing = false;
            mMutexProcess.unlock();
            memset(user_frame_buffer_draw_action, 0, 
                                    sizeof(user_frame_buffer_draw_action));     
            break;
        }
        
        Mat gray;
        Mat imgRgb = _m_cvtRgb5652Rgb(my_frame,(int)LCD_PIXEL_WIDTH,
                                                    (int)LCD_PIXEL_HEIGHT,2);
        cvtColor(imgRgb,gray,COLOR_RGB2GRAY);

        equalizeHist(gray,gray);
        resize(gray, smallImage, face_size);

        haar_cascade.detectMultiScale(smallImage,faces,1.1,
                                        2,0|CV_HAAR_SCALE_IMAGE,Size(10,10));
        
        mMutexObjects.lock();
        if(gIsDrawing == false)
        {
            mMutexObjects.unlock();

            if( faces.size() > 0)
            {
                // Show label
                if (oldStateFace == false)
                {
                    oldStateFace = true;
                    myCanvas.drawImage(my_img_register_face,    
                                        REGIS_FACE_BTN_X ,REGIS_FACE_BTN_Y);
                }
                
                // Click update database
                mMutexClicked.lock();
                if(clickedCode == CLICKED_REGIS_FACE)
                {
                    mMutexClicked.unlock();
                    
                    // Clean draw faces
                    mMutexObjects.lock();
                    if(gIsDrawing == false)
                    {
                        mMutexObjects.unlock();
                        gObjectDetects.clear();
                        gIsDrawing = true;
                        p_DrawObjects->signal_set(1);
                    }
                    else
                    {
                        mMutexObjects.unlock();
                    }

                    // Update database/ train data
                    face_database.database_image.clear();
                    face_database.database_label.clear();
                    flag_train = false;
                    
                    for (int i = 0; i < faces.size(); i++)
                    {
                        // Check exit app
                        if(changeApp == true)
                        {
                            break;
                        }
                        mMutexClicked.lock();
                        clickedCode = CLICKED_REGIS_FACE;
                        mMutexClicked.unlock();
                        
                        int _x = faces[i].x*IMG_DOWN_SAMPLE;
                        int _y = faces[i].y*IMG_DOWN_SAMPLE;
                        int _w = faces[i].width*IMG_DOWN_SAMPLE;
                        int _h = faces[i].height*IMG_DOWN_SAMPLE;
                        
                        Rect roi(_x,_y,_w,_h);
                        
                        Mat imgShow = imgRgb(roi);
                        if(_w > 100)
                        {
                            resize(imgShow,imgShow,Size(100,100));
                            _w = 100;
                            _h = 100;
                        }

                        uint8_t* src = cvtMat2RGBA444(_w,_h,imgShow);
                        
                        label_id = 1;
                        
                        memset(user_frame_buffer_draw_action, 0, 
                                    sizeof(user_frame_buffer_draw_action));
                        myCanvas.drawImage(src,100, 90);
                        myCanvas.drawImage(my_img_id_01,    
                                        FACE_REG_ID_MENU_X ,FACE_REG_ID_MENU_Y);
                        myCanvas.drawImage(my_img_change_id,    
                                FACE_REG_ACT_MENU_X ,FACE_REG_ACT_MENU_Y);
                        myCanvas.drawImage(my_img_add_this,    
                            FACE_REG_ACT_MENU_X ,FACE_REG_ACT_MENU_Y + 12 + 20);
                        myCanvas.drawImage(my_img_ignore,    
                            FACE_REG_ACT_MENU_X ,FACE_REG_ACT_MENU_Y+ 24 + 40);
                        
                        /* Clean cache */
                        dcache_clean(user_frame_buffer_draw_action, 
                                    sizeof(user_frame_buffer_draw_action));
                        while(1)
                        {             
                            bool isBreak = false;               
                            mMutexClicked.lock();
                            CLICKED_CODE codeTmp = clickedCode;
                            mMutexClicked.unlock();
                            
                            // Check exit app
                            if ( appMode != FACE_RECOGNITION )
                            {
                                changeApp = true;     
                                isBreak = true;
                                break;
                            }
                            
                            switch(codeTmp)
                            {
                                case CLICKED_CHANGE_ID:
                                {
                                    memset(user_frame_buffer_draw_action + FACE_REG_ID_MENU_Y*2*LCD_PIXEL_WIDTH, 0, 
                                                    21*2*LCD_PIXEL_WIDTH);// clear 21 lines
                                    // Change ID label
                                    if(label_id == 1)
                                    {
                                        label_id = 2;
                                        myCanvas.drawImage(my_img_id_02,    
                                            FACE_REG_ID_MENU_X ,FACE_REG_ID_MENU_Y);
                                    }
                                    else
                                    {
                                        label_id = 1;
                                        myCanvas.drawImage(my_img_id_01,
                                            FACE_REG_ID_MENU_X ,FACE_REG_ID_MENU_Y);
                                    }
                                    
                                    /* Clean cache */
                                    dcache_clean(user_frame_buffer_draw_action, 
                                            sizeof(user_frame_buffer_draw_action));
                                            
                                    clickedCode = CLICKED_UNKNOWN;
                                    break;
                                }
                                case CLICKED_ADD:
                                {
                                    
                                    Mat imgReg = gray(roi);
                                    resize(imgReg,imgReg,regSize);
                                    //train face
                                    face_database.database_image.push_back(imgReg);
                                    face_database.database_label.push_back(label_id);
                                    flag_train = true;
                                    isBreak = true;
                                    break;
                                }
                                case CLICKED_IGNORE:
                                {
                                    //ignore
                                    isBreak = true;
                                    break;
                                }
                                default:
                                break;
                            }
                            
                            if(isBreak)
                                break;
                            wait(0.1);
                        }
                    }// for faces.size()
                    
                    if( flag_train == true)
                    {
                        if(isFirstTrain == true)
                        {
                            isFirstTrain = false;
                            modelReg->train(face_database.database_image, face_database.database_label);
                        }
                        else
                        {
                            modelReg->update(face_database.database_image, face_database.database_label);
                        }
                    }
                    
                    // Clean screen
                    memset(user_frame_buffer_draw_action, 0, 
                                    sizeof(user_frame_buffer_draw_action));
                    oldStateFace = false;

                    mMutexClicked.lock();
                    clickedCode = CLICKED_UNKNOWN;
                    mMutexClicked.unlock();
                } //clickedCode == CLICKED_REGIS_FACE
                else
                {   
                    mMutexClicked.unlock();
                    if (isFirstTrain == false)
                    {
                        for(int i = 0; i < faces.size();i++)
                        {
                            int _x = faces[i].x*IMG_DOWN_SAMPLE;
                            int _y = faces[i].y*IMG_DOWN_SAMPLE;
                            int _w = faces[i].width*IMG_DOWN_SAMPLE;
                            int _h = faces[i].height*IMG_DOWN_SAMPLE;
                            
                            Rect roi(_x,_y,_w,_h);
                            Mat target_face = gray(roi);
                            resize(target_face,target_face,regSize);
                            int predicted = -1;
                            double confidence_level = 0;
                            modelReg->predict(target_face, predicted, confidence_level);
                            
                            obj_detect_result tmp;
                            tmp.obj = faces[i];
                 
                            if(predicted != -1)
                            {
                                if (confidence_level < thresmax)
                                {
                                    tmp.label = predicted;
                                }
                                else
                                {
                                    tmp.label = -1;
                                }
                            }
                            else
                            {
                                tmp.label = -1;
                            }
                            gObjectDetects.push_back(tmp);
                        }
                    }
                    else{
                        for(int i = 0;i < faces.size();i++)
                        {
                            obj_detect_result tmp;
                            tmp.obj = faces[i];
                            tmp.label = -1;
                            gObjectDetects.push_back(tmp);
                        }
                    }
                    
                    gIsDrawing = true;
                    p_DrawObjects->signal_set(1);
                }
            } //faces.size() > 0
            else
            {
                oldStateFace = false;
                memset(user_frame_buffer_draw_action, 0, 
                                    sizeof(user_frame_buffer_draw_action));
                mMutexClicked.lock();
                clickedCode = CLICKED_UNKNOWN;
                mMutexClicked.unlock();
                
                // Clean draw faces
                mMutexObjects.lock();
                if(gIsDrawing == false)
                {
                    mMutexObjects.unlock();
                    gObjectDetects.clear();
                    gIsDrawing = true;
                    p_DrawObjects->signal_set(1);
                }
                else
                {
                    mMutexObjects.unlock();
                }
            }
        }
        else
        {
            mMutexObjects.unlock();
        }
        
        /* Reset flag */
        faces.clear();
        mMutexProcess.lock();
        gIsProcessing = false;
        mMutexProcess.unlock();
        
        /* Clean cache */
        dcache_clean(user_frame_buffer_draw_action, 
                                    sizeof(user_frame_buffer_draw_action));
    }
}

/* 2 functions for gesture recognition */
int distanceP2P(Point2f a, Point2f b)
{
    int d = (int)sqrt(fabs(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)));
    return d;
}

float getAngle(Point s, Point f, Point e){
    float l1 = distanceP2P(f, s);
    float l2 = distanceP2P(f, e);
    float dot = (s.x - f.x)*(e.x - f.x) + (s.y - f.y)*(e.y - f.y);
    float angle = acos(dot / (l1*l2));
    angle = angle * 180 / M_PI;
    return angle;
}

void drawButtonSampling(graphicFrameworkCanvas canvas,bool sampling)
{
    // Clear 21 lines
    memset(user_frame_buffer_draw_action + (GESTURE_SAMPLING_BTN_Y - 1)*
        LCD_PIXEL_WIDTH*FRAME_BUFFER_BYTE_PER_PIXEL, 0, 
        21*LCD_PIXEL_WIDTH*FRAME_BUFFER_BYTE_PER_PIXEL);
    if(sampling == true) // Sampling, Clicking stop, draw sampling
    { 
        canvas.drawImage(my_img_sampling, GESTURE_SAMPLING_BTN_X, 
                            GESTURE_SAMPLING_BTN_Y);
    }
    else
    {
        canvas.drawImage(my_img_stop, GESTURE_SAMPLING_BTN_X, 
                            GESTURE_SAMPLING_BTN_Y);
    }
}

void gestureRegconition(void)
{
    // Init draw
    graphicFrameworkCanvas myCanvas(user_frame_buffer_draw_action, 0x01E0,
                    0x0110, (uint8_t)DRAW_BUFFER_BYTE_PER_PIXEL, 
                    (uint8_t)DRAW_POINT,0x06);

    bool sampling = true;
    int kernelSize = 7;
    int thresValue = 100;
    int maxIndex = 0;
    double area = 0.0;
    unsigned long lArea = 0, maxArea = 0;
    int hullSize = 0;
    bool handSamp = true;
    bool isFinger = false;
    int oneFinger = 0;
    int countFinger = 0;
    Sampling handdist;
    Point2f mPa;
    ConvexPoint checkPoint;
    
    Mat imgGray;
    Mat blurImg;
    Mat thresholdImg;
    vector<vector<Point> > contours;
    vector<mLine> lLines;
    vector<mCircle> lCircles;
    vector<Point> lMaxContour;
    
    myCanvas.drawImage(my_img_stop, GESTURE_SAMPLING_BTN_X, 
                                    GESTURE_SAMPLING_BTN_Y);
    mMutexClicked.lock();
    clickedCode = CLICKED_UNKNOWN;
    mMutexClicked.unlock();
    clearGestureResult(gGestureResult);
    while(1)
    {
        semProcessThread.wait();
        
        //Test
        /*char f_name[40];
        sprintf(f_name,"/SD/img_test%d.txt",count++);
        Mat res_tmp = _m_cvtRgb5652Rgb(my_frame,(int)LCD_PIXEL_WIDTH,
                                                (int)LCD_PIXEL_HEIGHT,2);
        writeMatToTxt(res_tmp,f_name);
        mMutexProcess.lock();
        gIsProcessing = false;
        mMutexProcess.unlock();
        continue;     */                                        
        
        //end test
        
        if ( appMode != GUESTURE_RECOGNITION )
        {
            mMutexProcess.lock();
            gIsProcessing = false;
            mMutexProcess.unlock();
            memset(user_frame_buffer_draw_action, 0, 
                                    sizeof(user_frame_buffer_draw_action));   
            clearGestureResult(gGestureResult);  
            break;
        }
        
        //Draw button sampling
        mMutexClicked.lock();
        if (clickedCode == CLICKED_HAND_SAMPLING)
        {
            clickedCode = CLICKED_UNKNOWN;
            mMutexClicked.unlock();
            drawButtonSampling(myCanvas,sampling);
            sampling = !sampling;
        }
        else
        {
            mMutexClicked.unlock();
        }

        // Get frame camera
        {
            Mat res = _m_cvtRgb5652Rgb(my_frame,(int)LCD_PIXEL_WIDTH,
                                                (int)LCD_PIXEL_HEIGHT,2);
            cvtColor(res,imgGray,COLOR_RGB2GRAY);
            res.release();
        }
        
        //Clear old data
        lLines.clear();
        lCircles.clear();
        lMaxContour.clear();
        
        // Start processing
        blur(imgGray,blurImg,Size(kernelSize,kernelSize));
        threshold(blurImg,thresholdImg,thresValue,255,CV_THRESH_BINARY);

        checkPoint.Pointstart.clear();
        checkPoint.Pointdepth.clear();
        checkPoint.Pointindex.clear();
        checkPoint.detecthand = 0;
        
        findContours(thresholdImg,contours,CV_RETR_EXTERNAL,
                                            CV_CHAIN_APPROX_SIMPLE,Point(0,0));

        if(contours.size() > 0)
        {
            //togle_led(LED_GREEN);
            maxIndex = 0;
            vector<vector<int> > hull(contours.size());
            vector<vector<Vec4i> > convDef(contours.size());
            vector<vector<Point> > hull_point(contours.size());
            vector<vector<Point> > detectPoint(contours.size());
            
            for(int i = 0;i < contours.size();i++)
            {
                area = contourArea(contours[i]);
                lArea = long(area);
                if(lArea > maxArea)
                {
                    hullSize++;
                    maxArea = lArea;
                    maxIndex = i;
                }
            }
            
            if(maxArea > 50000)
            {
                //togle_reset(LED_RED,LED_BLUE);
                checkPoint.detecthand = 1;
                convexHull(Mat(contours[maxIndex]),hull[maxIndex],false);
                convexityDefects(contours[maxIndex],hull[maxIndex],convDef[maxIndex]);
            }
            
            for (int i = 0;i < convDef[maxIndex].size();i++)
            {
                if(convDef[maxIndex][i][3] > 20*256)
                {
                    int ind_0 = convDef[maxIndex][i][0];
                    int ind_1 = convDef[maxIndex][i][2];
                    checkPoint.Pointstart.push_back(contours[maxIndex][ind_0]);
                    checkPoint.Pointdepth.push_back(contours[maxIndex][ind_1]);
                    checkPoint.Pointindex.push_back(ind_1);
                }
            }

            vector<Moments> mu(contours.size());
            vector<Point2d> mc(contours.size());    
            mu[maxIndex] = moments(contours[maxIndex],false);
            mc[maxIndex] = Point2f(mu[maxIndex].m10 / mu[maxIndex].m00,
                                   mu[maxIndex].m01 / mu[maxIndex].m00);
            mPa = mc[maxIndex];

            // Reset values
            lArea = 0;
            maxArea = 0;
            hullSize = 0;
            area = 0;
            
            
            // Draw contours
            Mat imgContour(LCD_PIXEL_HEIGHT,LCD_PIXEL_WIDTH,CV_8UC1,Scalar(0));
            drawContours(imgContour,contours,maxIndex,Scalar(255));
            for(int i=0;i<imgContour.rows;i++)
            {
                for(int j=0;j<imgContour.cols;j++)
                {
                    if(imgContour.at<uchar>(i,j) == 255)
                    {
                        lMaxContour.push_back(Point(j,i));
                    }
                }
            }
            
            
        }// contours.size() > 0
        else
        {
            checkPoint.detecthand = 0;
        }
        
        if(sampling == true)
        {
            if (checkPoint.Pointstart.size() > 1)
            {
                for(int i = 0;i < checkPoint.Pointstart.size() -1;i++)
                {
                    float pAngle = getAngle(checkPoint.Pointstart[i],mPa,
                                                    checkPoint.Pointstart[i+1]);
                    float fAngle = getAngle(checkPoint.Pointstart[1],mPa,
                                                    checkPoint.Pointstart[i+1]);
    
                    if(pAngle < 90.00 && pAngle > 5.00 && fAngle < 180)
                    {
                        double pFdist = distanceP2P(mPa,
                                                    checkPoint.Pointstart[i]);
                        double pLdist = distanceP2P(mPa,
                                                    checkPoint.Pointdepth[i]);
                        double _Pmax = handdist.hand_max;
                        double _Pmin = handdist.hand_min;
                        if (pFdist > pLdist)
                        {
                            if (pFdist > _Pmax)
                            {
                                handdist.hand_max = pFdist;
                            }
                            if (pLdist < _Pmin)
                            {
                                handdist.hand_min = pLdist;
                            }
                        }
                        else
                        {
                            if (pLdist > _Pmin)
                            {
                                handdist.hand_max = pLdist;
                            }
                            if (pFdist < _Pmin)
                            {
                                handdist.hand_min = pFdist;
                            }
                        }
                    }
                }
            }
        }//sampling == true
        else
        {
            if(checkPoint.detecthand == 1)
            {
                if (checkPoint.Pointstart.size() > 1)
                {
                    for(int i = 0;i < checkPoint.Pointstart.size() -1;i++)
                    {
                        float pAngle = getAngle(checkPoint.Pointstart[i],mPa,
                                                    checkPoint.Pointstart[i+1]);
                        float fAngle = getAngle(checkPoint.Pointstart[1],mPa,
                                                    checkPoint.Pointstart[i+1]);

                        // Draw line
                        mLine aLine;
                        aLine.x1 = (int)checkPoint.Pointstart[i].x;
                        aLine.y1 = (int)checkPoint.Pointstart[i].y;
                        aLine.x2 = (int)mPa.x;
                        aLine.y2 = (int)mPa.y;
                        aLine.color[0] = 0x00;//Color red
                        aLine.color[1] = 0xFF;
                        lLines.push_back(aLine);
                        //line(showRes,checkPoint.Pointstart[i],mPa,
//                                                            Scalar(255,255,0));
                        //Draw circle
                        mCircle cir;
                        cir.x = (int)checkPoint.Pointstart[i].x;
                        cir.y = (int)checkPoint.Pointstart[i].y;
                        cir.radius = 10;
                        cir.color[0] = 0xF0;
                        cir.color[1] = 0xF0;//Color green
                        lCircles.push_back(cir);
                        
                        
                        //circle(showRes,checkPoint.Pointstart[i],10,
//                                                            Scalar(0,255,0));
    
                        if(pAngle < 90.00 && pAngle > 5.00 && fAngle < 180)
                        {
                            double pFdist = distanceP2P(mPa,
                                                    checkPoint.Pointstart[i]);
                            if(pFdist > (handdist.hand_min + 110) && 
                                pFdist < (handdist.hand_max + 40))
                            {
                                isFinger = true;
                                //line(showRes,checkPoint.Pointstart[i],mPa,Scalar(255,255,255));
                                aLine.x1 = (int)checkPoint.Pointstart[i].x;
                                aLine.y1 = (int)checkPoint.Pointstart[i].y;
                                aLine.x2 = (int)mPa.x;
                                aLine.y2 = (int)mPa.y;// Keep color red
                                lLines.push_back(aLine);
                                
                                oneFinger++;
                            }
                            else
                            {
                                isFinger = false;
                            }
    
                            if(isFinger == true)
                            {
                                //circle(showRes,mPa,handdist.hand_min,Scalar(255,0,0));
                                cir.x = (int)mPa.x;
                                cir.y = (int)mPa.y;
                                cir.radius = (int)handdist.hand_min;
                                lCircles.push_back(cir);//color green
                                
                                //circle(showRes,mPa,handdist.hand_min + 110,Scalar(255,0,0));
                                cir.radius = (int)handdist.hand_min + 110;
                                lCircles.push_back(cir);//color green
                                
                                double pLdist = distanceP2P(mPa,checkPoint.Pointdepth[i]);
                                if(pLdist > handdist.hand_min - 5)
                                {
                                    countFinger++;
                                    //circle(showRes,contours[maxIndex][checkPoint.Pointindex[i]],2,Scalar(0,255,255));
                                    cir.x = (int)contours[maxIndex][checkPoint.Pointindex[i]].x;
                                    cir.y = (int)contours[maxIndex][checkPoint.Pointindex[i]].y;
                                    cir.radius = 2;
                                    lCircles.push_back(cir);//color green
                                }
                            }
                        }
                    }// for
                    if(isFinger == false)
                    {
                        for(int i = 0; i < checkPoint.Pointstart.size() -1;i++)
                        {
                            double pLdist = distanceP2P(mPa,
                                                    checkPoint.Pointdepth[i]);
                            if (pLdist > (handdist.hand_min - 5))
                            {
                                countFinger = 0;
                                isFinger = true;
                            }
                        }
                    }
                }//checkPoint.Pointstart.size() > 1
            }//checkPoint.detecthand == 1
        }
        
        // Draw
        mMutexObjects.lock();
        if(gIsDrawing == false)
        {
            mMutexObjects.unlock();
            
            // Assign data
            gGestureResult.circles = lCircles;
            gGestureResult.lines = lLines;
            gGestureResult.contour = lMaxContour;
            
            gIsDrawing = true;
            p_DrawObjects->signal_set(1);
        }
        else
        {
            mMutexObjects.unlock();
        }
        
        mMutexProcess.lock();
        gIsProcessing = false;
        mMutexProcess.unlock();

        /* Clean cache */
        dcache_clean(user_frame_buffer_draw_action, 
                                    sizeof(user_frame_buffer_draw_action));
    }
    
}

/****** main image process here******/
int main(void) {
    /* Init SD card*/
    myStorage = new cStorage(SDCARD_NAME);
    if( initSdCard() == false)
    {
        while(1);
    }
    togle_reset(LED_RED,LED_BLUE);
    
    /* Initialization of LCD */
    Init_LCD_Display();    /* When using LCD, please call before than Init_Video(). */

    /* Initialization of Video */
    Init_Video();
    
    /* Start Video and Lcd processing */
    p_VideoLcdTask = new Thread();
    p_VideoLcdTask->start(video_lcd_task);
    
    p_Touch = new Thread();
    p_Touch->start(touch_task);
    
    /* Start image processor*/
    p_DrawObjects = new Thread();
    p_DrawObjects->signal_set(0);
    p_DrawObjects->start(img_draw_objects);
    
    initDrawAction();
    
    while(1)
    {
        switch(appMode)
        {
            case FACE_DETECTION:
            {
                faceDectectionApp();
                break;
            }
            case MOTION_DETECTION:
            {
                motionDetectionApp();
                break;
            }
            case FACE_RECOGNITION:
            {
                faceRecognitionApp();
                break;
            }
            case GUESTURE_RECOGNITION:
            {
                gestureRegconition();
                break;
            }
            default:
            {
                wait(0.5);
                break;
            }
        }
    }
    return 1;
}