メインコンテンツに移動

GR-LYCHEE 特設:OpenCVで画像処理をする

概要

GR-LYCHEEではOpenCV 3.2のC++でプログラムが作れるようにライブラリが用意されています。カメラから取得した画像から顔を検出したり、画像の加工を行うことができます。

なお、GR-LYCHEEに搭載されているRZ/A1LU MPUはRAMが3MBですので、処理できることは限定されてきます。また、ROMに書き込むメモリも大きくなり、書込み時間が30秒ぐらいに長くなりますので、その点はご留意ください。


準備

ハードウェア

GR-LYCHEE、USBケーブル(マイクロBタイプ)を準備します。カメラの取り付け方は「カメラや付属品、LCDの取り付け方」を参照してください。

SDカードがあれば、後半に顔検出サンプルもお試しできます。

ソフトウェア

DisplayAppをダウンロードして解凍してください。PCでカメラの画像を確認するためのアプリケーションです。

gr-lychee-board
usb-cablesd-card-2gb

OpenCVのプロジェクト作成

Webコンパイラの場合

新規プロジェクト作成画面で、以下のようにOpenCV用のテンプレートが用意されていますので、Arduinoライクなスケッチか、Mbedスタイルでのプログラム作成を行うかどちらかを選択します。

GR-LYCHEE OpenCV Webテンプレート選択

IDE for GRの場合

メニューから「ツール」→「マイコンボード」→「GR-LYCHEE(OpenCV)」を選択します。

GR-LYCHEE OpenCV IDEマイコンボード選択

OpenCVを試してみる

まずは試してみましょう。以下のプログラムを実行してみてください。

プログラム書き込みには30秒ほどかかりますので、書込みが終わってMbedドライブが再認識されるまで、USBケーブルを抜いたりしないでください。


#include <Arduino.h>
#include <Camera.h>
#include <opencv.hpp>
#include <DisplayApp.h>
using namespace cv;
 
#define IMAGE_HW 320
#define IMAGE_VW 240
#define DEMO_NUMBER   4
#define LOOP_WAITTIME 16
 
static Camera camera(IMAGE_HW, IMAGE_VW);
static DisplayApp display_app;
static uint8_t g_mode = 0;
 
void ub0_interrupt() {
  while (digitalRead(PIN_SW0) == LOW)
    ;
  g_mode++;
  if (g_mode >= DEMO_NUMBER)
    g_mode = 0;
}
 
void setup() {
  Serial.begin(9600);
  pinMode(PIN_SW0, INPUT);
  pinMode(PIN_LED_RED, OUTPUT);
  digitalWrite(PIN_LED_RED, LOW);
  camera.begin();
  attachInterrupt(4, ub0_interrupt, FALLING);
}
 
void loop() {
  static unsigned long loop_time = millis();
 
  while ((millis() - loop_time) < LOOP_WAITTIME)
    ; //
  Mat img_raw(IMAGE_VW, IMAGE_HW, CV_8UC2, camera.getImageAdr());
 
  if (g_mode == 0) {
    ///////////////////////////////////
    // MODE0: Original Image
    ///////////////////////////////////
    display_app.SendJpeg(camera.getJpegAdr(), (int) camera.createJpeg());
    loop_time = millis();
  } 
  else if (g_mode == 1) {
    ///////////////////////////////////
    // MODE1: Canny
    ///////////////////////////////////
    Mat src, dst;
    cvtColor(img_raw, src, COLOR_YUV2GRAY_YUYV); //covert from YUV to GRAY
    Canny(src, dst, 50, 150); // Canny
    size_t jpegSize = camera.createJpeg(IMAGE_HW, IMAGE_VW, dst.data,
    Camera::FORMAT_GRAY);
    display_app.SendJpeg(camera.getJpegAdr(), jpegSize);
    loop_time = millis();
  } 
  else if (g_mode == 2) {
    ///////////////////////////////////
    // MODE2: Drawing
    ///////////////////////////////////
    static int x = 0, y = 0, ax = 10, ay = 10;
    Scalar red(0, 0, 255), green(0, 255, 0), blue(255, 0, 0);
    Scalar yellow = red + green;
    Scalar white = Scalar::all(255);
    Scalar pink = Scalar(154, 51, 255);
 
    Mat img_raw(IMAGE_VW, IMAGE_HW, CV_8UC2, camera.getImageAdr());
 
    Mat src;
    cvtColor(img_raw, src, COLOR_YUV2BGR_YUYV); //covert YUV to RGB
 
    x += ax;
    y += ay;
    if (x > (src.cols - 10) || x < 10) {
      ax *= -1;
    }
    if (y > (src.rows - 10) || y < 10) {
      ay *= -1;
    }
 
    line(src, Point(10, 10), Point(src.cols - 10, 10), blue, 3, LINE_AA); //Line
    line(src, Point(10, src.rows - 10), Point(src.cols - 10, src.rows - 10),
    blue, 3, LINE_AA); //Line
    rectangle(src, Point(10, 30), Point(src.cols - 10, 60), white, FILLED);
    putText(src, "Gadget Renesas", Point(15, 55), FONT_HERSHEY_COMPLEX, 1,
    pink, 2);
    circle(src, Point(x, y), 10, yellow, FILLED);
 
    stringstream ss;
    ss << x << ", " << y;
    putText(src, ss.str(), Point(10, src.rows - 20),
    FONT_HERSHEY_SCRIPT_SIMPLEX, 1, white, 1);
 
    size_t jpegSize = camera.createJpeg(IMAGE_HW, IMAGE_VW, src.data,
    Camera::FORMAT_RGB888);
    display_app.SendJpeg(camera.getJpegAdr(), jpegSize);
    loop_time = millis();
  } 
  else if (g_mode == 3) {
    ///////////////////////////////////
    // MODE3: MovingDetection
    ///////////////////////////////////
    Mat img_raw(IMAGE_VW, IMAGE_HW, CV_8UC2, camera.getImageAdr());
    Mat src, diff, srcFloat, dstFloat, diffFloat;
    dstFloat.create(IMAGE_VW, IMAGE_HW, CV_32FC1);
    dstFloat.setTo(0.0);
 
    while (g_mode == 3) {
      cvtColor(img_raw, src, COLOR_YUV2GRAY_YUYV); //covert from YUV to GRAY
 
      src.convertTo(srcFloat, CV_32FC1, 1 / 255.0);
      addWeighted(srcFloat, 0.01, dstFloat, 0.99, 0, dstFloat, -1);
      absdiff(srcFloat, dstFloat, diffFloat);
      diffFloat.convertTo(diff, CV_8UC1, 255.0);
 
      size_t jpegSize = camera.createJpeg(IMAGE_HW, IMAGE_VW, diff.data,
      Camera::FORMAT_GRAY);
      display_app.SendJpeg(camera.getJpegAdr(), jpegSize);
      loop_time = millis();
    }
 
  }
 
}

USBケーブルをGR-LYCHEEのUSB0につなぎ、DisplayAppで画像を確認します。UB0ボタンを押すと以下のようにモードが切り替わり、OpenCVのエッジ検出、描画、差分検出処理を見ることができます。

sp-opencv-multiple

顔検出

次に顔検出を試してみます。カスケードファイル「lbpcascade_frontalface.xml」をSDカードに保存します。

保存後、GR-LYCHEEにSDカードを差し込んで以下のスケッチを実行してください。


/* Face detection example with OpenCV */
/* Public Domain             */
#include <Arduino.h>
#include  <Camera.h>
#include  <opencv.hpp>
#include  <DisplayApp.h>
 
// To monitor realtime on PC, you need DisplayApp on following site.
// Connect USB0(not for mbed interface) to your PC
// https://os.mbed.com/users/dkato/code/DisplayApp/
#include "mbed.h"
#include "SdUsbConnect.h"
 
#define IMAGE_HW 320
#define IMAGE_VW 240
using namespace cv;
 
/* FACE DETECTOR Parameters */
#define DETECTOR_SCALE_FACTOR (2)
#define DETECTOR_MIN_NEIGHBOR (4)
#define DETECTOR_MIN_SIZE     (30)
#define FACE_DETECTOR_MODEL     "/storage/lbpcascade_frontalface.xml"
 
 
static Camera camera(IMAGE_HW, IMAGE_VW);
static DisplayApp  display_app;
static CascadeClassifier detector_classifier;
 
void setup() {
    pinMode(PIN_LED_GREEN, OUTPUT);
    pinMode(PIN_LED_RED, OUTPUT);
 
    // Camera
    camera.begin();
 
    // SD & USB
    SdUsbConnect storage("storage");
    storage.wait_connect();
 
    // Load the cascade classifier file
    detector_classifier.load(FACE_DETECTOR_MODEL);
 
    if (detector_classifier.empty()) {
        digitalWrite(PIN_LED_RED, HIGH); // Error
        CV_Assert(0);
        mbed_die();
    }
}
 
void loop(){
    Mat img_raw(IMAGE_VW, IMAGE_HW, CV_8UC2, camera.getImageAdr());
 
    Mat src;
    cvtColor(img_raw, src, COLOR_YUV2GRAY_YUYV); //covert from YUV to GRAY
 
    // Detect a face in the frame
    Rect face_roi;
    if (detector_classifier.empty()) {
        digitalWrite(PIN_LED_RED, HIGH); // Error
    }
 
    // Perform detected the biggest face
    std::vector <Rect> rect_faces;
    detector_classifier.detectMultiScale(src, rect_faces,
                                         DETECTOR_SCALE_FACTOR,
                                         DETECTOR_MIN_NEIGHBOR,
                                         CASCADE_FIND_BIGGEST_OBJECT,
                                         Size(DETECTOR_MIN_SIZE, DETECTOR_MIN_SIZE));
 
    if (rect_faces.size() > 0) {
        // A face is detected
        face_roi = rect_faces[0];
    } else {
        // No face is detected, set an invalid rectangle
        face_roi.x = -1;
        face_roi.y = -1;
        face_roi.width = -1;
        face_roi.height = -1;
    }
 
    if (face_roi.width > 0 && face_roi.height > 0) {   // A face is detected
        digitalWrite(PIN_LED_GREEN, HIGH);
        printf("Detected a face X:%d Y:%d W:%d H:%d\n",face_roi.x, face_roi.y, face_roi.width, face_roi.height);
        digitalWrite(PIN_LED_GREEN, LOW);
    } else {
    }
 
    Mat dst;
    cvtColor(img_raw, dst, COLOR_YUV2BGR_YUYV); //covert from YUV to BGR
    Scalar red(0, 0, 255), green(0, 255, 0), blue(255, 0, 0);
 
    rectangle(dst, Point(face_roi.x, face_roi.y), Point(face_roi.x + face_roi.width, face_roi.y + face_roi.height), red, 2);
    size_t jpegSize = camera.createJpeg(IMAGE_HW, IMAGE_VW, dst.data, Camera::FORMAT_RGB888);
    display_app.SendJpeg(camera.getJpegAdr(), jpegSize);
}

USBケーブルをGR-LYCHEEのUSB0につなぎ、DisplayAppで画像を確認してください。以下のように顔検出を行います。

sp-opencv-face