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.
