PowerPoint用非接触プレゼンター
GR-Boards_NonContactMouse を少しだけ改造して、PowerPointを操作する非接触型プレゼンターを作成してみます。
まずはいきなりコードから。上記サンプルをインポートし、main.cppを下記のように変更してください。変更箇所は「#if(1) Keybord」の部分です。
main.cpp
#include "mbed.h" #include "opencv.hpp" #include "EasyAttach_CameraAndLCD.h" #if(1) // Keybord #include "USBKeyboard.h" #else #include "USBMouse.h" #endif #define PLOT_INTERVAL (30) #define DIST_SCALE_FACTOR_X (6.0) #define DIST_SCALE_FACTOR_Y (6.0) /*! Frame buffer stride: Frame buffer stride should be set to a multiple of 32 or 128 in accordance with the frame buffer burst transfer mode. */ #define VIDEO_PIXEL_HW (160u) /* HQVGA */ #define VIDEO_PIXEL_VW (120u) /* HQVGA */ #define FRAME_BUFFER_STRIDE (((VIDEO_PIXEL_HW * 2) + 31u) & ~31u) #define FRAME_BUFFER_HEIGHT (VIDEO_PIXEL_VW) #if defined(__ICCARM__) #pragma data_alignment=32 static uint8_t user_frame_buffer0[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]@ ".mirrorram"; #else static uint8_t user_frame_buffer0[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); #endif static volatile int Vfield_Int_Cnt = 0; DisplayBase Display; DigitalOut led1(LED1); #if(1) // Keybord USBKeyboard key; #else USBMouse mouse; #endif static Thread mainTask(osPriorityNormal, 1024 * 16); static void IntCallbackFunc_Vfield(DisplayBase::int_type_t int_type) { if (Vfield_Int_Cnt > 0) { Vfield_Int_Cnt--; } } static void wait_new_image(void) { Vfield_Int_Cnt = 1; while (Vfield_Int_Cnt > 0) { ThisThread::sleep_for(1); } } static void Start_Video_Camera(void) { // Field end signal for recording function in scaler 0 Display.Graphics_Irq_Handler_Set(DisplayBase::INT_TYPE_S0_VFIELD, 0, IntCallbackFunc_Vfield); // Video capture setting (progressive form fixed) Display.Video_Write_Setting( DisplayBase::VIDEO_INPUT_CHANNEL_0, DisplayBase::COL_SYS_NTSC_358, (void *)user_frame_buffer0, FRAME_BUFFER_STRIDE, DisplayBase::VIDEO_FORMAT_YCBCR422, DisplayBase::WR_RD_WRSWA_32_16BIT, VIDEO_PIXEL_VW, VIDEO_PIXEL_HW ); EasyAttach_CameraStart(Display, DisplayBase::VIDEO_INPUT_CHANNEL_0); } #if MBED_CONF_APP_LCD static void Start_LCD_Display(void) { DisplayBase::rect_t rect; rect.vs = (LCD_PIXEL_HEIGHT - VIDEO_PIXEL_VW) / 2; rect.vw = VIDEO_PIXEL_VW; rect.hs = (LCD_PIXEL_WIDTH - VIDEO_PIXEL_HW); rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_0, (void *)user_frame_buffer0, FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_YCBCR422, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0); ThisThread::sleep_for(50); EasyAttach_LcdBacklight(true); } #endif static void main_task(void) { cv::Mat prev_image; cv::Mat curr_image; std::vector<cv::Point2f> prev_pts; std::vector<cv::Point2f> curr_pts; cv::Point2f point; int16_t x = 0; int16_t y = 0; #if(1) // Keybord int move_x_sum = 0; Timer key_timer; time_t seconds_last = time(NULL); key_timer.reset(); #endif EasyAttach_Init(Display); Start_Video_Camera(); #if MBED_CONF_APP_LCD Start_LCD_Display(); #endif // Initialization of optical flow point.y = (VIDEO_PIXEL_VW / 2) + (PLOT_INTERVAL * 1); for (int32_t i = 0; i < 3; i++) { point.x = (VIDEO_PIXEL_HW / 2) - (PLOT_INTERVAL * 1); for (int32_t j = 0; j < 3; j++) { prev_pts.push_back(point); point.x += PLOT_INTERVAL; } point.y -= PLOT_INTERVAL; } while (1) { // Wait for image input wait_new_image(); // Convert from YUV422 to grayscale cv::Mat img_yuv(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, user_frame_buffer0); cv::cvtColor(img_yuv, curr_image, cv::COLOR_YUV2GRAY_YUY2); point = cv::Point2f(0, 0); if ((!curr_image.empty()) && (!prev_image.empty())) { // Optical flow std::vector<uchar> status; std::vector<float> err; cv::calcOpticalFlowPyrLK(prev_image, curr_image, prev_pts, curr_pts, status, err, cv::Size(21, 21), 0); // Setting movement distance of feature point std::vector<cv::Scalar> samples; for (size_t i = 0; i < (size_t)status.size(); i++) { if (status[i]) { cv::Point2f vec = curr_pts[i] - prev_pts[i]; cv::Scalar sample = cv::Scalar(vec.x, vec.y); samples.push_back(sample); } } // Mean and standard deviation if (samples.size() >= 6) { cv::Scalar mean; cv::Scalar stddev; cv::meanStdDev((cv::InputArray)samples, mean, stddev); //printf("%d, stddev=%lf, %lf\r\n", samples.size(), stddev[0], stddev[1]); // for debug if ((stddev[0] < 10.0) && (stddev[1] < 10.0)) { point.x = mean[0]; point.y = mean[1]; } } } cv::swap(prev_image, curr_image); x = (int16_t)(point.x * DIST_SCALE_FACTOR_X) * -1; y = (int16_t)(point.y * DIST_SCALE_FACTOR_Y); if ((x != 0) || (y != 0)) { led1 = 1; //printf("x=%d, y=%d\r\n", x, y); // for debug #if(1) // Keybord if (key_timer.read_ms() > 500) { key_timer.stop(); key_timer.reset(); } if (key_timer.read_ms() == 0) { time_t seconds = time(NULL); if (seconds >= (seconds_last + 2)) { move_x_sum = 0; } seconds_last = seconds; move_x_sum += x; if (move_x_sum > 50) { key.key_code(RIGHT_ARROW); move_x_sum = 0; key_timer.reset(); key_timer.start(); } if (move_x_sum < (-50)) { key.key_code(LEFT_ARROW); move_x_sum = 0; key_timer.reset(); key_timer.start(); } } #else mouse.move(x, y); #endif } else { led1 = 0; } } } int main(void) { mainTask.start(callback(main_task)); mainTask.join(); }
GR-Boards_NonContactMouseはUSBマウスのように振る舞い、カメラに写った物体の動きをマウスポインタの動きとしてPCに通知していました。今回はUSBキーボードとして振る舞うようにします。
X軸方向に一定以上の動きがあった場合、キーボードの「左矢印」、または、「右矢印」が押されたと通知します。Y軸方向の動きは見ません。
Please log in to post comments.