OpenCVサンプルメモ
OpenCV-CookBookで紹介されている画像処理をGR-PEACH、GR-LYCHEEで動かす際のメモ。
ベースとなるサンプルはGR-Boards_NonContactMouseで、mbed_app.jsonを下記のように設定して使用する。
mbed_app.json
{ "config": { "camera":{ "help": "0:disable 1:enable", "value": "1" }, "lcd":{ "help": "0:disable 1:enable", "value": "1" } } }
サンプルのmain.cppをコピペして使うことでその動作を試せる。
画像を平滑化する(ぼかす)
メディアンフィルタを用いた平滑化を行います。
USER_BUTTON0を押すとフィルタの係数が変化します。
main.cpp
#include "mbed.h" #include "EasyAttach_CameraAndLCD.h" #include "opencv.hpp" #define VIDEO_PIXEL_HW (480u) /* WQVGA */ #define VIDEO_PIXEL_VW (272u) /* WQVGA */ /*! 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 DATA_SIZE_PER_PIC (4u) #define FRAME_BUFFER_STRIDE (((VIDEO_PIXEL_HW * DATA_SIZE_PER_PIC) + 31u) & ~31u) #define FRAME_BUFFER_HEIGHT (VIDEO_PIXEL_VW) static uint8_t FrameBuffer_Video[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Lcd[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static DisplayBase Display; static cv::Mat src_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC4, FrameBuffer_Video); static cv::Mat dst_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC4, FrameBuffer_Lcd); static InterruptIn btn0(USER_BUTTON0); static int btn0_num = 1; static void camera_start(void) { // Camera EasyAttach_Init(Display, VIDEO_PIXEL_HW, VIDEO_PIXEL_VW); // Video capture setting (progressive form fixed) Display.Video_Write_Setting( DisplayBase::VIDEO_INPUT_CHANNEL_0, DisplayBase::COL_SYS_NTSC_358, (void *)FrameBuffer_Video, FRAME_BUFFER_STRIDE, DisplayBase::VIDEO_FORMAT_RGB888, DisplayBase::WR_RD_WRSWA_32BIT, VIDEO_PIXEL_VW, VIDEO_PIXEL_HW ); EasyAttach_CameraStart(Display, DisplayBase::VIDEO_INPUT_CHANNEL_0); } static void lcd_start(void) { DisplayBase::rect_t rect; // GRAPHICS_LAYER_0 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_0, (void *)&dst_img.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_RGB888, DisplayBase::WR_RD_WRSWA_32BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0); Thread::wait(50); EasyAttach_LcdBacklight(true); } static void btn0_fall(void) { if (btn0_num < 30) { btn0_num += 2; } else { btn0_num = 1; } printf("pram:%d\r\n", btn0_num); } int main() { // button setting btn0.fall(&btn0_fall); // カメラの取り込み開始 camera_start(); // LCD表示の開始 lcd_start(); while (1) { cv::medianBlur(src_img, dst_img, btn0_num); } }
楕円フィッティングを行う
画像から輪郭を検出し、その輪郭に対して楕円フィッティングを行う。
USER_BUTTON0を押すとLCD表示が「カメラ画像 -> グレースケール -> 2値化データ」の順に変化します。
main.cpp
#include "mbed.h" #include "EasyAttach_CameraAndLCD.h" #include "opencv.hpp" #define VIDEO_PIXEL_HW (480u) /* WQVGA */ #define VIDEO_PIXEL_VW (272u) /* WQVGA */ /*! 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 DATA_SIZE_PER_PIC (2u) #define FRAME_BUFFER_STRIDE (((VIDEO_PIXEL_HW * DATA_SIZE_PER_PIC) + 31u) & ~31u) #define FRAME_BUFFER_HEIGHT (VIDEO_PIXEL_VW) static uint8_t FrameBuffer_Video[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Lcd[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Result[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static DisplayBase Display; static cv::Mat src_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Video); static cv::Mat dst_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Lcd); static cv::Mat result_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Result); static InterruptIn btn0(USER_BUTTON0); static int btn0_type = 0; static bool btn0_change = false; #define BTN0_TYPE_MAX (2) static void camera_start(void) { // Camera EasyAttach_Init(Display, VIDEO_PIXEL_HW, VIDEO_PIXEL_VW); // Video capture setting (progressive form fixed) Display.Video_Write_Setting( DisplayBase::VIDEO_INPUT_CHANNEL_0, DisplayBase::COL_SYS_NTSC_358, (void *)FrameBuffer_Video, 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); } static void lcd_start(void) { DisplayBase::rect_t rect; // GRAPHICS_LAYER_0 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_0, (void *)&src_img.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_YCBCR422, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0); // GRAPHICS_LAYER_2 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_2, (void *)&result_img.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_ARGB4444, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_2); Thread::wait(50); EasyAttach_LcdBacklight(true); } static void convert_gray2yuv422(cv::Mat& src, cv::Mat& dst) { uint32_t src_size = src.cols * src.rows; uint32_t dst_size = dst.cols * dst.rows; uint32_t size; uint32_t idx = 0; if (src_size < dst_size) { size = src_size; } else { size = dst_size; } for (uint32_t i = 0; i < size; i++) { dst.data[idx++] = src.data[i]; dst.data[idx++] = 0x80; } } static void btn0_fall(void) { if (btn0_type < BTN0_TYPE_MAX) { btn0_type++; } else { btn0_type = 0; } btn0_change = true; } int main() { cv::Mat gray_img; cv::Mat bin_img; std::vector<std::vector<cv::Point> > contours; // button setting btn0.fall(&btn0_fall); // カメラの取り込み開始 camera_start(); // LCD表示の開始 lcd_start(); while (1) { // グレースケール化 cv::cvtColor(src_img, gray_img, cv::COLOR_YUV2GRAY_YUY2); // 画像の二値化 cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY|cv::THRESH_OTSU); // 輪郭の検出 cv::findContours(bin_img, contours, cv::RETR_LIST, cv::CHAIN_APPROX_NONE); // 検出結果バッファのクリア memset(FrameBuffer_Result, 0, sizeof(FrameBuffer_Result)); // 検出結果の描画 for (int i = 0; i < contours.size(); i++) { size_t count = contours[i].size(); if (count < 150 || count > 1000) continue; // (小さすぎる|大きすぎる)輪郭を除外 cv::Mat pointsf; cv::Mat(contours[i]).convertTo(pointsf, CV_32F); // 楕円フィッティング cv::RotatedRect box = cv::fitEllipse(pointsf); // 楕円の描画 (BGRA4444 を8bit2チャンネルと見立てる) cv::ellipse(result_img, box, cv::Scalar(0x00, 0xFF), 2, cv::LINE_AA); } // カメラ画像の描画 switch (btn0_type) { case 0: // 元データ if (btn0_change) { btn0_change = false; Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_0, &src_img.data[0]); } break; case 1: // グレースケール convert_gray2yuv422(gray_img, dst_img); if (btn0_change) { btn0_change = false; Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_0, &dst_img.data[0]); } break; case 2: // 二値化データ convert_gray2yuv422(bin_img, dst_img); break; default: break; } } }
輪郭の検出
USER_BUTTON0を押すとLCD表示が「カメラ画像 -> グレースケール -> 2値化データ」の順に変化します。
main.cpp
#include "mbed.h" #include "EasyAttach_CameraAndLCD.h" #include "opencv.hpp" #define VIDEO_PIXEL_HW (480u) /* WQVGA */ #define VIDEO_PIXEL_VW (272u) /* WQVGA */ /*! 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 DATA_SIZE_PER_PIC (2u) #define FRAME_BUFFER_STRIDE (((VIDEO_PIXEL_HW * DATA_SIZE_PER_PIC) + 31u) & ~31u) #define FRAME_BUFFER_HEIGHT (VIDEO_PIXEL_VW) static uint8_t FrameBuffer_Video[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Lcd[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Result0[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Result1[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static DisplayBase Display; static cv::Mat src_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Video); static cv::Mat dst_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Lcd); static cv::Mat result_img0(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Result0); static cv::Mat result_img1(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Result1); static int result_idx = 0; static cv::Mat * p_result_img[2] = {&result_img0, &result_img1}; static InterruptIn btn0(USER_BUTTON0); static int btn0_type = 0; static bool btn0_change = false; #define BTN0_TYPE_MAX (2) static void camera_start(void) { // Camera EasyAttach_Init(Display, VIDEO_PIXEL_HW, VIDEO_PIXEL_VW); // Video capture setting (progressive form fixed) Display.Video_Write_Setting( DisplayBase::VIDEO_INPUT_CHANNEL_0, DisplayBase::COL_SYS_NTSC_358, (void *)FrameBuffer_Video, 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); } static void lcd_start(void) { DisplayBase::rect_t rect; // GRAPHICS_LAYER_0 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_0, (void *)&src_img.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_YCBCR422, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0); // GRAPHICS_LAYER_2 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_2, (void *)&result_img0.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_ARGB4444, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_2); Thread::wait(50); EasyAttach_LcdBacklight(true); } static void convert_gray2yuv422(cv::Mat& src, cv::Mat& dst) { uint32_t src_size = src.cols * src.rows; uint32_t dst_size = dst.cols * dst.rows; uint32_t size; uint32_t idx = 0; if (src_size < dst_size) { size = src_size; } else { size = dst_size; } for (uint32_t i = 0; i < size; i++) { dst.data[idx++] = src.data[i]; dst.data[idx++] = 0x80; } } static void btn0_fall(void) { if (btn0_type < BTN0_TYPE_MAX) { btn0_type++; } else { btn0_type = 0; } btn0_change = true; } int main() { cv::Mat gray_img; cv::Mat bin_img; std::vector<std::vector<cv::Point> > contours; std::vector<cv::Vec4i> hierarchy; // button setting btn0.fall(&btn0_fall); // カメラの取り込み開始 camera_start(); // LCD表示の開始 lcd_start(); while (1) { // グレースケール化 cv::cvtColor(src_img, gray_img, cv::COLOR_YUV2GRAY_YUY2); // 画像の2値化 cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY|cv::THRESH_OTSU); // 輪郭検出 (2値画像,輪郭(出力),階層構造(出力),輪郭抽出モード,輪郭の近似手法) cv::findContours(bin_img, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE); // 検出結果バッファのクリア *p_result_img[result_idx] = cv::Scalar(0x00, 0x00); /// 輪郭の描画 (画像,輪郭,描画輪郭指定インデックス,色,太さ,種類,階層構造,描画輪郭の最大レベル) cv::drawContours(*p_result_img[result_idx], contours, -1, cv::Scalar(0xBE, 0xA0), 2, 8, hierarchy, 1); // 検出結果の表示 Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_2, (void *)(*p_result_img[result_idx]).data); // 検出結果バッファの切り替え result_idx ^= 1; // カメラ画像の描画 switch (btn0_type) { case 0: // カメラ画像 if (btn0_change) { btn0_change = false; Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_0, &src_img.data[0]); } break; case 1: // グレースケール convert_gray2yuv422(gray_img, dst_img); if (btn0_change) { btn0_change = false; Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_0, &dst_img.data[0]); } break; case 2: // 2値化データ convert_gray2yuv422(bin_img, dst_img); break; default: break; } } }
微分画像・エッジ画像を求める
USER_BUTTON0を押すとLCD表示が「カメラ画像 -> Cannyエッジ -> カメラ画像とCannyエッジの重ね画像」の順に変化します。
main.cpp
#include "mbed.h" #include "EasyAttach_CameraAndLCD.h" #include "opencv.hpp" #include "dcache-control.h" #define VIDEO_PIXEL_HW (480u) /* WQVGA */ #define VIDEO_PIXEL_VW (272u) /* WQVGA */ /*! 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 DATA_SIZE_PER_PIC (2u) #define FRAME_BUFFER_STRIDE (((VIDEO_PIXEL_HW * DATA_SIZE_PER_PIC) + 31u) & ~31u) #define FRAME_BUFFER_HEIGHT (VIDEO_PIXEL_VW) static uint8_t FrameBuffer_Video[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Result[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((aligned(32))); static DisplayBase Display; static cv::Mat src_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Video); static cv::Mat result_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Result); static InterruptIn btn0(USER_BUTTON0); static int btn0_type = 0; static bool btn0_change = false; #define BTN0_TYPE_MAX (2) static void camera_start(void) { // Camera EasyAttach_Init(Display, VIDEO_PIXEL_HW, VIDEO_PIXEL_VW); // Video capture setting (progressive form fixed) Display.Video_Write_Setting( DisplayBase::VIDEO_INPUT_CHANNEL_0, DisplayBase::COL_SYS_NTSC_358, (void *)FrameBuffer_Video, 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); } static void lcd_start(void) { DisplayBase::rect_t rect; // GRAPHICS_LAYER_0 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_0, (void *)&src_img.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_YCBCR422, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0); // GRAPHICS_LAYER_2 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_2, (void *)&result_img.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_ARGB4444, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_2); Thread::wait(50); EasyAttach_LcdBacklight(true); } static void convert_bin2RGB4444(cv::Mat& src, cv::Mat& dst) { uint32_t src_size = src.cols * src.rows; uint32_t dst_size = dst.cols * dst.rows; uint32_t size; uint32_t idx = 0; if (src_size < dst_size) { size = src_size; } else { size = dst_size; } for (uint32_t i = 0; i < size; i++) { if (src.data[i] != 0) { dst.data[idx++] = 0x0F; // G:4 B:4 dst.data[idx++] = 0xF0; // A:4 R:4 } else { dst.data[idx++] = 0x00; // G:4 B:4 dst.data[idx++] = 0x00; // A:4 R:4 } } } static void convert_bin2RGB4444_mono(cv::Mat& src, cv::Mat& dst) { uint32_t src_size = src.cols * src.rows; uint32_t dst_size = dst.cols * dst.rows; uint32_t size; uint32_t idx = 0; if (src_size < dst_size) { size = src_size; } else { size = dst_size; } for (uint32_t i = 0; i < size; i++) { if (src.data[i] != 0) { dst.data[idx++] = 0xFF; // G:4 B:4 dst.data[idx++] = 0xFF; // A:4 R:4 } else { dst.data[idx++] = 0x00; // G:4 B:4 dst.data[idx++] = 0xF0; // A:4 R:4 } } } static void btn0_fall(void) { if (btn0_type < BTN0_TYPE_MAX) { btn0_type++; } else { btn0_type = 0; } btn0_change = true; } int main() { cv::Mat gray_img; cv::Mat canny_img; memset(&result_img.data[0], 0, FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT); dcache_clean(&result_img.data[0], FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT); // button setting btn0.fall(&btn0_fall); // カメラの取り込み開始 camera_start(); // LCD表示の開始 lcd_start(); while (1) { // カメラ画像の描画 switch (btn0_type) { case 0: // カメラ画像のみ if (btn0_change) { btn0_change = false; memset(&result_img.data[0], 0, FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT); dcache_clean(&result_img.data[0], FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT); } break; case 1: // Cannyのみ case 2: // カメラ画像とCannyの重ね // グレースケール化 cv::cvtColor(src_img, gray_img, cv::COLOR_YUV2GRAY_YUY2); // Canny cv::Canny(gray_img, canny_img, 100, 200); if (btn0_type == 1) { convert_bin2RGB4444_mono(canny_img, result_img); } else { convert_bin2RGB4444(canny_img, result_img); } dcache_clean(&result_img.data[0], FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT); break; default: break; } } }
人検出
少し改造しました。
main.cpp
#include "mbed.h" #include "EasyAttach_CameraAndLCD.h" #include "opencv.hpp" #define VIDEO_PIXEL_HW (480u) /* WQVGA */ #define VIDEO_PIXEL_VW (272u) /* WQVGA */ /*! 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 DATA_SIZE_PER_PIC (2u) #define FRAME_BUFFER_STRIDE (((VIDEO_PIXEL_HW * DATA_SIZE_PER_PIC) + 31u) & ~31u) #define FRAME_BUFFER_HEIGHT (VIDEO_PIXEL_VW) static uint8_t FrameBuffer_Video[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Lcd[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Result0[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static uint8_t FrameBuffer_Result1[FRAME_BUFFER_STRIDE * FRAME_BUFFER_HEIGHT]__attribute((section("NC_BSS"),aligned(32))); static DisplayBase Display; static cv::Mat src_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Video); static cv::Mat dst_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Lcd); static cv::Mat result_img0(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Result0); static cv::Mat result_img1(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_Result1); static int result_idx = 0; static cv::Mat * p_result_img[2] = {&result_img0, &result_img1}; static InterruptIn btn0(USER_BUTTON0); static int btn0_type = 0; static bool btn0_change = false; #define BTN0_TYPE_MAX (1) static void camera_start(void) { // Initialize the background to black for (uint32_t i = 0; i < sizeof(FrameBuffer_Video); i += 2) { FrameBuffer_Video[i + 0] = 0x10; FrameBuffer_Video[i + 1] = 0x80; } // Camera EasyAttach_Init(Display, VIDEO_PIXEL_HW, VIDEO_PIXEL_VW); // Video capture setting (progressive form fixed) Display.Video_Write_Setting( DisplayBase::VIDEO_INPUT_CHANNEL_0, DisplayBase::COL_SYS_NTSC_358, (void *)FrameBuffer_Video, 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); } static void lcd_start(void) { DisplayBase::rect_t rect; // GRAPHICS_LAYER_0 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_0, (void *)&src_img.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_YCBCR422, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0); // GRAPHICS_LAYER_2 rect.vs = 0; rect.vw = VIDEO_PIXEL_VW; rect.hs = 0; rect.hw = VIDEO_PIXEL_HW; Display.Graphics_Read_Setting( DisplayBase::GRAPHICS_LAYER_2, (void *)&result_img0.data[0], FRAME_BUFFER_STRIDE, DisplayBase::GRAPHICS_FORMAT_ARGB4444, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_2); Thread::wait(50); EasyAttach_LcdBacklight(true); } static void convert_gray2yuv422(cv::Mat& src, cv::Mat& dst) { uint32_t src_size = src.cols * src.rows; uint32_t dst_size = dst.cols * dst.rows; uint32_t size; uint32_t idx = 0; if (src_size < dst_size) { size = src_size; } else { size = dst_size; } for (uint32_t i = 0; i < size; i++) { dst.data[idx++] = src.data[i]; dst.data[idx++] = 0x80; } } static void btn0_fall(void) { if (btn0_type < BTN0_TYPE_MAX) { btn0_type++; } else { btn0_type = 0; } btn0_change = true; } int main() { cv::Mat gray_img; // button setting btn0.fall(&btn0_fall); // カメラの取り込み開始 camera_start(); // LCD表示の開始 lcd_start(); cv::HOGDescriptor hog; // hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); hog.winSize = cv::Size(48, 96); hog.setSVMDetector(hog.getDaimlerPeopleDetector()); double scale = 2.0; cv::Mat smallImg(cv::saturate_cast<int>(src_img.rows/scale), cv::saturate_cast<int>(src_img.cols/scale), CV_8UC1); while (1) { // グレースケール化 cv::cvtColor(src_img, gray_img, cv::COLOR_YUV2GRAY_YUY2); // 処理時間短縮のために画像を縮小 cv::resize(gray_img, smallImg, smallImg.size(), 0, 0, cv::INTER_LINEAR); cv::equalizeHist(smallImg, smallImg); std::vector<cv::Rect> found; // hog.detectMultiScale(smallImg, found, 0, cv::Size(8,8), cv::Size(0,0), 1.05, 5); hog.detectMultiScale(smallImg, found); // 検出結果バッファのクリア *p_result_img[result_idx] = cv::Scalar(0x00, 0x00); std::vector<cv::Rect>::const_iterator it = found.begin(); for (; it!=found.end(); ++it) { cv::Rect r = *it; // 描画に際して,検出矩形を若干小さくする r.x = cvRound(r.x*scale + r.width*scale*0.15); r.width = cvRound(r.width*scale*0.7); r.y = cvRound(r.y*scale + r.height*scale*0.10); r.height = cvRound(r.height*scale*0.8); cv::rectangle(*p_result_img[result_idx], r.tl(), r.br(), cv::Scalar(0x0F, 0xA0), 2); } // 検出結果の表示 Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_2, (void *)(*p_result_img[result_idx]).data); // 検出結果バッファの切り替え result_idx ^= 1; // カメラ画像の描画 switch (btn0_type) { case 0: // 元データ if (btn0_change) { btn0_change = false; Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_0, &src_img.data[0]); } break; case 1: // グレースケール convert_gray2yuv422(gray_img, dst_img); if (btn0_change) { btn0_change = false; Display.Graphics_Read_Change(DisplayBase::GRAPHICS_LAYER_0, &dst_img.data[0]); } break; default: break; } } }
非接触マウス(指検出版)
手を握る(グー)と「マウス左ボタン押し込み」、手を開く(パー)と「マウス左ボタンを離す」動作を行います。
ソース中のコメントアウト箇所を有効にすると、使えるようになります。
コメントアウト箇所
// mouse.press(MOUSE_LEFT); // mouse.release(MOUSE_LEFT);
誤作動が多いので、自己責任でご利用ください。(PCで試すとファイルがどこかに行ってしまったりする)
main.cpp
#include "mbed.h" #include "opencv.hpp" #include "EasyAttach_CameraAndLCD.h" #include "USBMouse.h" #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 * 4) + 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); USBMouse mouse; static void IntCallbackFunc_Vfield(DisplayBase::int_type_t int_type) { if (Vfield_Int_Cnt > 0) { Vfield_Int_Cnt--; } } static void wait_new_image(void) { while (Vfield_Int_Cnt > 0) { Thread::wait(1); } Vfield_Int_Cnt = 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_RGB888, DisplayBase::WR_RD_WRSWA_32BIT, VIDEO_PIXEL_VW, VIDEO_PIXEL_HW ); EasyAttach_CameraStart(Display, DisplayBase::VIDEO_INPUT_CHANNEL_0); } #if MBED_CONF_APP_LCD static void convert_gray2yuv422(cv::Mat& src, cv::Mat& dst) { uint32_t src_size = src.cols * src.rows; uint32_t dst_size = dst.cols * dst.rows; uint32_t size; uint32_t idx = 0; if (src_size < dst_size) { size = src_size; } else { size = dst_size; } for (int i = 0; i < size; i++) { dst.data[idx++] = src.data[i]; dst.data[idx++] = 0x80; } } static uint8_t FrameBuffer_Lcd[FRAME_BUFFER_STRIDE/2 * FRAME_BUFFER_HEIGHT]__attribute((aligned(32))); static cv::Mat dst_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC2, FrameBuffer_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, &dst_img.data[0], FRAME_BUFFER_STRIDE/2, DisplayBase::GRAPHICS_FORMAT_YCBCR422, DisplayBase::WR_RD_WRSWA_32_16BIT, &rect ); Display.Graphics_Start(DisplayBase::GRAPHICS_LAYER_0); Thread::wait(50); EasyAttach_LcdBacklight(true); } #endif int main() { 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; 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; } cv::Mat hsv, skin; bool click_last = false; int close_cnt = 0; int open_cnt = 0; int init_cnt = 0; int click_wait = 0; while (1) { // Wait for image input wait_new_image(); // Convert from YUV422 to grayscale cv::Mat src_img(VIDEO_PIXEL_VW, VIDEO_PIXEL_HW, CV_8UC4, user_frame_buffer0); cv::cvtColor(src_img, curr_image, cv::COLOR_BGR2GRAY); cv::cvtColor(src_img, hsv, CV_BGR2HSV); cv::inRange(hsv, cv::Scalar(0, 20, 20), cv::Scalar(25, 255, 255), skin); // 抜き出した部分をなめらかにする処理 cv::Mat structElem = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); cv::morphologyEx(skin, skin, cv::MORPH_CLOSE, structElem); // 最も長い輪郭線を選ぶ vector<vector<cv::Point> > contours; vector<cv::Vec4i> hierarchy; cv::findContours(skin, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); double largestArea = 0.0; int largestContourIndex = 0; for (int i = 0; i < contours.size(); ++i) { double a = cv::contourArea(contours[i], false); if (a > largestArea) { largestArea = a; largestContourIndex = i; } } // 欠けた部分を求める vector<vector<int> > hulls (1); cv::convexHull(contours[largestContourIndex], hulls[0], false, false); std::vector<cv::Vec4i> defects; cv::convexityDefects(contours[largestContourIndex], hulls[0], defects); // 小さいものや離れすぎているものを除いて指の数を数える int fingerCount = 0; for (int i = 0; i< defects.size(); i++) { int start_index = defects[i][0]; CvPoint start_point = contours[largestContourIndex][start_index]; int end_index = defects[i][1]; CvPoint end_point = contours[largestContourIndex][end_index]; double d1 = (end_point.x - start_point.x); double d2 = (end_point.y - start_point.y); double distance = sqrt((d1*d1)+(d2*d2)); int depth = defects[i][3]/1000; if (depth > 5 && distance > 2.0 && distance < 100.0) { fingerCount++; } } // グーorパー if (init_cnt < 100) { init_cnt++; // 輝度安定待ち } else if ((click_last == false) && (fingerCount <= 1)) { open_cnt = 0; close_cnt++; if (close_cnt >= 3) { click_last = true; // mouse.press(MOUSE_LEFT); printf("Close\r\n"); close_cnt = 0; click_wait = 10; } } else if ((click_last != false) && (fingerCount >= 2)) { close_cnt = 0; open_cnt++; if (open_cnt >= 3) { click_last = false; // mouse.release(MOUSE_LEFT); printf("Open\r\n"); open_cnt = 0; click_wait = 10; } } else { close_cnt = 0; open_cnt = 0; } convert_gray2yuv422(skin, dst_img); if ((close_cnt != 0) || (open_cnt != 0)) { continue; } if (click_wait > 0) { click_wait--; continue; } 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 mouse.move(x, y); } else { led1 = 0; } } }
Please log in to post comments.