GR-PEACH 特設:オムロン人物認識カメラモジュールHVC-P2で遊ぶ

その2 人検出後に写真をSDに保存する

概要

この特設では、HVC-P2で人を検出した後に画像をマイクロSDに保存するサンプルを紹介します。


準備

以下の接続写真を参考に、GR-PEACH、HVC-P2のほかにUSBケーブル(マイクロBタイプ)を2本、USB(タイプA)変換アダプタを準備してください。

それから写真を保存するためにマイクロSDをGR-PEACHの裏面にあるソケットに差し込んでください。

また、GR-PEACHの中央部にあるジャンパー(JP3)をショートしてください。これによってUSBホストとして接続する際にVBUSに電源が供給されるようになります。


検出写真をSDに保存するサンプル

以下のサンプルをWebコンパイラでビルドし、実行してみてください。ソース上でダブルクリックすると全選択されます。


//* GR-PEACH Sketch Template for using HVC-P2
 * based on library version V1.06
 */
 
// GR-PEACH API
#include <arduino.h>
#include <SD.h>
#include <RTC.h>
#include <USBHostSerial.h>
#include <JPEG_Converter.h>
// HVC-P2 API
#include "HVCApi.h" // Omron sample
#include "HVCDef.h" // Omron sample
#include "HVCExtraUartFunc.h" // Omron sample
 
#define USB_SETTING_TIMEOUT 1000
#define USB_EXECUTE_TIMEOUT 7000
#define ALL_DETECTION
#define SENSOR_ROLL_ANGLE_DEFAULT            0            /* Camera angle setting (0°) */
#define BODY_THRESHOLD_DEFAULT             500            /* Threshold for Human Body Detection */
#define FACE_THRESHOLD_DEFAULT             500            /* Threshold for Face Detection */
#define HAND_THRESHOLD_DEFAULT             500            /* Threshold for Hand Detection */
#define REC_THRESHOLD_DEFAULT              500            /* Threshold for Face Recognition */
#define BODY_SIZE_RANGE_MIN_DEFAULT         30            /* Human Body Detection minimum detection size */
#define BODY_SIZE_RANGE_MAX_DEFAULT       8192            /* Human Body Detection maximum detection size */
#define HAND_SIZE_RANGE_MIN_DEFAULT         40            /* Hand Detection minimum detection size */
#define HAND_SIZE_RANGE_MAX_DEFAULT       8192            /* Hand Detection maximum detection size */
#define FACE_SIZE_RANGE_MIN_DEFAULT         64            /* Face Detection minimum detection size */
#define FACE_SIZE_RANGE_MAX_DEFAULT       8192            /* Face Detection maximum detection size */
#define FACE_POSE_DEFAULT                    0            /* Face Detection facial pose (frontal face)*/
#define FACE_ANGLE_DEFAULT                   0            /* Face Detection roll angle (±15°)*/
#define PIXEL_WIDTH                 (320)
#define PIXEL_HEIGHT                (240)
#define BYTE_PER_PIXEL_YUV   (2u)
#define BYTE_PER_PIXEL_RGB   (3u)
#define FRAME_STRIDE_YUV   (((PIXEL_WIDTH * BYTE_PER_PIXEL_YUV) + 31u) & ~31u)
#define FRAME_STRIDE_RGB   (((PIXEL_WIDTH * BYTE_PER_PIXEL_RGB) + 31u) & ~31u)
 
/*! 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. */
static uint8_t frame_buffer_YUV422[FRAME_STRIDE_YUV * PIXEL_HEIGHT]__attribute((section("NC_BSS"),aligned(32)));
static uint8_t frame_buffer_RGB888[FRAME_STRIDE_RGB * PIXEL_HEIGHT]__attribute((section("NC_BSS"),aligned(32)));
static uint8_t JpegBuffer[1024 * 200]__attribute((aligned(8))); //8 bytes aligned!;
static volatile int jcu_encoding = 1;
static volatile int jcu_encode_size = 1;
static JPEG_Converter Jcu;
static RTC rtc;
static int year, mon, day, hour, min, sec, week;
void sd_timestamp(uint16_t* date, uint16_t* time) {
    *date = FAT_DATE(year, mon, day);
    *time = FAT_TIME(hour, min, sec);
}
static USBHostSerial * p_cdc;
 
extern "C" int UART_SendData(int inDataSize, UINT8 *inData) {
    return p_cdc->writeBuf((char *) inData, inDataSize);
}
extern "C" int UART_ReceiveData(int inTimeOutTime, int inDataSize,
        UINT8 *outResult) {
    return p_cdc->readBuf((char *) outResult, inDataSize, inTimeOutTime);
}
void led_blink(int pin, int num) {
    for (int i = 0; i < num; i++) {
        digitalWrite(pin, HIGH);
        delay(200);
        digitalWrite(pin, LOW);
        delay(200);
    }
}
static void EraseImage(uint8_t* buf, uint32_t size) {
    uint32_t i = 0;
    while (i < size) {
        buf[i++] = 0x10;
        buf[i++] = 0x80;
    }
}
 
static void dcache_invalid(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 invalid
    for (addr = start_addr; addr < end_addr; addr += 0x20) {
        __v7_inv_dcache_mva((void *) addr);
    }
}
 
void JcuEncodeCallBackFunc(JPEG_Converter::jpeg_conv_error_t err_code) {
    jcu_encoding = 0;
}
 
size_t JpegEncode(uint8_t* buf, int len, int width, int height, uint8_t* inbuf) {
    size_t encode_size;
    JPEG_Converter::bitmap_buff_info_t bitmap_buff_info;
    JPEG_Converter::encode_options_t encode_options;
    bitmap_buff_info.width = width;
    bitmap_buff_info.height = height;
    bitmap_buff_info.format = JPEG_Converter::WR_RD_YCbCr422;
    bitmap_buff_info.buffer_address = (void *) inbuf;
    encode_options.encode_buff_size = sizeof(buf);
    encode_options.p_EncodeCallBackFunc = &JcuEncodeCallBackFunc;
    //    encode_options.input_swapsetting = JPEG_Converter::WR_RD_WRSWA_32_16_8BIT;
 
    jcu_encoding = 1; // flag for waiting callback function of JCU
    jcu_encode_size = 0;
    dcache_invalid(buf, len);
    if (Jcu.encode(&bitmap_buff_info, buf, &encode_size, &encode_options)
            != JPEG_Converter::JPEG_CONV_OK) {
        jcu_encode_size = 0;
        jcu_encoding = 0;
    }
 
    while (jcu_encoding == 1) {
        delay(1);
    }
 
    return encode_size;
}
static void DrawSquare(int x, int y, int size, uint32_t const colour, uint8_t* inbuf) {
    int wk_x;
    int wk_y;
    int wk_w = 0;
    int wk_h = 0;
    int idx_base;
    int wk_idx;
    int i;
    int j;
    uint8_t coller_pix[BYTE_PER_PIXEL_RGB];
    bool l_draw = true;
    bool r_draw = true;
    bool t_draw = true;
    bool b_draw = true;
 
    if ((x - (size / 2)) < 0) {
        l_draw = false;
        wk_w += x;
        wk_x = 0;
    } else {
        wk_w += (size / 2);
        wk_x = x - (size / 2);
    }
 
    if ((x + (size / 2)) >= 1600) {
        r_draw = false;
        wk_w += (1600 - x);
    } else {
        wk_w += (size / 2);
    }
 
    if ((y - (size / 2)) < 0) {
        t_draw = false;
        wk_h += y;
        wk_y = 0;
    } else {
        wk_h += (size / 2);
        wk_y = y - (size / 2);
    }
 
    if ((y + (size / 2)) >= 1200) {
        b_draw = false;
        wk_h += (1200 - y);
    } else {
        wk_h += (size / 2);
    }
 
    wk_x = wk_x / 5;
    wk_y = wk_y / 5;
    wk_w = wk_w / 5;
    wk_h = wk_h / 5;
 
    idx_base = (wk_x + (PIXEL_WIDTH * wk_y)) * BYTE_PER_PIXEL_RGB;
 
    /* Select color */
    coller_pix[0] = (colour >> 16) & 0xff;
    coller_pix[1] = (colour >> 8) & 0xff;
    coller_pix[2] = colour & 0xff;
 
    /* top */
    if (t_draw) {
        wk_idx = idx_base;
        for (j = 0; j < wk_w; j++) {
            inbuf[wk_idx++] = coller_pix[0];
            inbuf[wk_idx++] = coller_pix[1];
            inbuf[wk_idx++] = coller_pix[2];
        }
    }
 
    /* middle */
    for (i = 1; i < (wk_h - 1); i++) {
        wk_idx = idx_base + (PIXEL_WIDTH * BYTE_PER_PIXEL_RGB * i);
        if (l_draw) {
            inbuf[wk_idx + 0] = coller_pix[0];
            inbuf[wk_idx + 1] = coller_pix[1];
            inbuf[wk_idx + 2] = coller_pix[2];
        }
        wk_idx += (wk_w - 1) * 3;
        if (r_draw) {
            inbuf[wk_idx + 0] = coller_pix[0];
            inbuf[wk_idx + 1] = coller_pix[1];
            inbuf[wk_idx + 2] = coller_pix[2];
        }
    }
 
    /* bottom */
    if (b_draw) {
        wk_idx = idx_base + (PIXEL_WIDTH * BYTE_PER_PIXEL_RGB * (wk_h - 1));
        for (j = 0; j < wk_w; j++) {
            inbuf[wk_idx++] = coller_pix[0];
            inbuf[wk_idx++] = coller_pix[1];
            inbuf[wk_idx++] = coller_pix[2];
        }
    }
}
 
void setup() {
    INT32 ret = 0; /* Return code */
    UINT8 status;
    HVC_RESULT *pHVCResult = NULL;
    HVC_IMAGE *pImage = NULL;
 
    INT32 agleNo;
    HVC_THRESHOLD threshold;
    HVC_SIZERANGE sizeRange;
    INT32 pose;
    INT32 angle;
    INT32 timeOutTime;
    INT32 execFlag;
    const char *pExStr[] = { "?", "Neutral", "Happiness", "Surprise", "Anger",
            "Sadness" };
 
    int i;
    int ch = 0;
 
    pinMode(PIN_LED_RED, OUTPUT);
    pinMode(PIN_LED_GREEN, OUTPUT);
    pinMode(PIN_LED_BLUE, OUTPUT);
 
    Serial.begin(9600);
    Serial.println("Program starts");
 
    USBHostSerial cdc;
    p_cdc = &cdc; // for HVC P2 API
 
    Serial.print("SD & RTC initialization...");
    year = 2017;
    mon = 2;
    day = 15;
    hour = 11;
    min = 14;
    sec = 0;
    week = RTC_WEEK_WEDNESDAY;
    rtc.begin();
    rtc.setDateTime(year, mon, day, hour, min, sec, week);
 
    SdFile::dateTimeCallback(&sd_timestamp);
    if (!SD.begin()) {
        Serial.println("initialization failed!");
        return;
    }
    Serial.println("done.");
 
    Serial.print("Waiting USB device...");
    while (!p_cdc->connect()) { // wait for connecting HVC
        led_blink(PIN_LED_RED, 1);
        Thread::wait(500);
    }
    Serial.println("found.");
 
    led_blink(PIN_LED_GREEN, 2);
    p_cdc->baud(921600);
 
    /*********************************/
    /* Result Structure Allocation   */
    /*********************************/
    pHVCResult = (HVC_RESULT *) malloc(sizeof(HVC_RESULT));
    if (pHVCResult == NULL) { /* Error processing */
        Serial.println("Memory Allocation Error");
        mbed_die();
    }
 
    /* Image Structure allocation */
    pImage = (HVC_IMAGE *) malloc(sizeof(HVC_IMAGE));
    if (pImage == NULL) {
        printf("Memory Allocation Error : %08x\n", sizeof(HVC_RESULT));
        mbed_die();
    }
    /*********************************/
    /* Set Camera Angle              */
    /*********************************/
    agleNo = SENSOR_ROLL_ANGLE_DEFAULT;
    ret = HVC_SetCameraAngle(USB_SETTING_TIMEOUT, agleNo, &status);
    /*********************************/
    /* Set Threshold Values          */
    /*********************************/
    threshold.bdThreshold = BODY_THRESHOLD_DEFAULT;
    threshold.hdThreshold = HAND_THRESHOLD_DEFAULT;
    threshold.dtThreshold = FACE_THRESHOLD_DEFAULT;
    threshold.rsThreshold = REC_THRESHOLD_DEFAULT;
    ret = HVC_SetThreshold(USB_SETTING_TIMEOUT, &threshold, &status);
    /*********************************/
    /* Set Detection Size            */
    /*********************************/
    sizeRange.bdMinSize = BODY_SIZE_RANGE_MIN_DEFAULT;
    sizeRange.bdMaxSize = BODY_SIZE_RANGE_MAX_DEFAULT;
    sizeRange.hdMinSize = HAND_SIZE_RANGE_MIN_DEFAULT;
    sizeRange.hdMaxSize = HAND_SIZE_RANGE_MAX_DEFAULT;
    sizeRange.dtMinSize = FACE_SIZE_RANGE_MIN_DEFAULT;
    sizeRange.dtMaxSize = FACE_SIZE_RANGE_MAX_DEFAULT;
    ret = HVC_SetSizeRange(USB_SETTING_TIMEOUT, &sizeRange, &status);
    /*********************************/
    /* Set Face Angle                */
    /*********************************/
    pose = FACE_POSE_DEFAULT;
    angle = FACE_ANGLE_DEFAULT;
    ret = HVC_SetFaceDetectionAngle(USB_SETTING_TIMEOUT, pose, angle, &status);
    /*********************************/
    /* Execute Detection             */
    /*********************************/
    do {
        timeOutTime = USB_EXECUTE_TIMEOUT;
#ifdef ALL_DETECTION
        execFlag = HVC_ACTIV_BODY_DETECTION | HVC_ACTIV_HAND_DETECTION |
                HVC_ACTIV_FACE_DETECTION | HVC_ACTIV_FACE_DIRECTION |
                HVC_ACTIV_AGE_ESTIMATION | HVC_ACTIV_GENDER_ESTIMATION |
                HVC_ACTIV_GAZE_ESTIMATION | HVC_ACTIV_BLINK_ESTIMATION |
                HVC_ACTIV_EXPRESSION_ESTIMATION;
#else
        execFlag = HVC_ACTIV_FACE_DETECTION | HVC_ACTIV_FACE_DIRECTION |
                HVC_ACTIV_AGE_ESTIMATION | HVC_ACTIV_GENDER_ESTIMATION |
                HVC_ACTIV_GAZE_ESTIMATION | HVC_ACTIV_BLINK_ESTIMATION |
                HVC_ACTIV_EXPRESSION_ESTIMATION;
#endif
        Serial.println("Executing Detection");
        ret = HVC_ExecuteEx(timeOutTime, execFlag, HVC_EXECUTE_IMAGE_QVGA,
                pHVCResult, &status);
        if ((ret != 0) || (status != 0)) {
            Serial.println("HVC_ExecuteEx Error");
        }
 
        if(pHVCResult->bdResult.num | pHVCResult->hdResult.num | pHVCResult->fdResult.num){
            // Prepare image data
            // HVC-P2 outputs Y in order 4-3-2-1
            // To simplify data processing, convert from 4-3-2-1 to 1-2-3-4
            led_blink(PIN_LED_BLUE, 2);
            for (int i = 0; i < PIXEL_HEIGHT; i++){
                for (int j = 0; j < (PIXEL_WIDTH / 4); j++){
                    uint8_t temp[4];
                    temp[0] = pHVCResult->image.image[i * PIXEL_WIDTH + j * 4 + 3];
                    temp[1] = pHVCResult->image.image[i * PIXEL_WIDTH + j * 4 + 2];
                    temp[2] = pHVCResult->image.image[i * PIXEL_WIDTH + j * 4 + 1];
                    temp[3] = pHVCResult->image.image[i * PIXEL_WIDTH + j * 4 + 0];
                    pHVCResult->image.image[i * PIXEL_WIDTH + j * 4 + 0] = temp[0];
                    pHVCResult->image.image[i * PIXEL_WIDTH + j * 4 + 1] = temp[1];
                    pHVCResult->image.image[i * PIXEL_WIDTH + j * 4 + 2] = temp[2];
                    pHVCResult->image.image[i * PIXEL_WIDTH + j * 4 + 3] = temp[3];
                }
            }
            EraseImage(frame_buffer_YUV422, sizeof(frame_buffer_YUV422));
#if 0 // Y to YUV422
            for (int i = 0; i < PIXEL_HEIGHT; i++) {
                for (int j = 0; j < (PIXEL_WIDTH); j++) {
                    frame_buffer_YUV422[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_YUV)
                            + BYTE_PER_PIXEL_YUV * j] = pHVCResult->image.image[i * PIXEL_WIDTH + j];
                }
            }
#endif
            for (int i = 0; i < PIXEL_HEIGHT; i++) {
                for (int j = 0; j < PIXEL_WIDTH; j++) {
                    uint8_t Y = pHVCResult->image.image[i * PIXEL_WIDTH + j];
                    for (int k = 0; k < 3; k++){
                        frame_buffer_RGB888[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_RGB)
                                + BYTE_PER_PIXEL_RGB * j + k] = Y;
                    }
                }
            }
 
        }
        if(pHVCResult->executedFunc & HVC_ACTIV_BODY_DETECTION){
            /* Body Detection result string */
            Serial.print("Body result count:"); Serial.println(pHVCResult->bdResult.num);
            for(i = 0; i < pHVCResult->bdResult.num; i++){
                Serial.print("Index:"); Serial.println(i);
                Serial.print("X = "); Serial.print(pHVCResult->bdResult.bdResult[i].posX);
                Serial.print(" Y = "); Serial.print(pHVCResult->bdResult.bdResult[i].posY);
                Serial.print(" Size = "); Serial.print(pHVCResult->bdResult.bdResult[i].size);
                Serial.print(" Confidence = "); Serial.print(pHVCResult->bdResult.bdResult[i].confidence);
                Serial.println();
                // Draw red square
                DrawSquare(pHVCResult->bdResult.bdResult[i].posX,
                           pHVCResult->bdResult.bdResult[i].posY,
                           pHVCResult->bdResult.bdResult[i].size,
                           0x00f00000, frame_buffer_RGB888);
            }
        }
        if(pHVCResult->executedFunc & HVC_ACTIV_HAND_DETECTION){
            /* Hand Detection result string */
            Serial.print("Hand result count:"); Serial.println(pHVCResult->hdResult.num);
            for(i = 0; i < pHVCResult->hdResult.num; i++){
                Serial.print("Index:"); Serial.println(i);
                Serial.print("X = "); Serial.print(pHVCResult->hdResult.hdResult[i].posX);
                Serial.print(" Y = "); Serial.print(pHVCResult->hdResult.hdResult[i].posY);
                Serial.print(" Size = "); Serial.print(pHVCResult->hdResult.hdResult[i].size);
                Serial.print(" Confidence = "); Serial.print(pHVCResult->hdResult.hdResult[i].confidence);
                Serial.println();
                // Draw yellow square
                DrawSquare(pHVCResult->hdResult.hdResult[i].posX,
                           pHVCResult->hdResult.hdResult[i].posY,
                           pHVCResult->hdResult.hdResult[i].size,
                           0x00f0f000, frame_buffer_RGB888);
            }
        }
        /* Face Detection result string */
        if(pHVCResult->executedFunc &
                (HVC_ACTIV_FACE_DETECTION | HVC_ACTIV_FACE_DIRECTION |
                 HVC_ACTIV_AGE_ESTIMATION | HVC_ACTIV_GENDER_ESTIMATION |
                 HVC_ACTIV_GAZE_ESTIMATION | HVC_ACTIV_BLINK_ESTIMATION |
                 HVC_ACTIV_EXPRESSION_ESTIMATION | HVC_ACTIV_FACE_RECOGNITION)){
            Serial.print("Face result count:"); Serial.println(pHVCResult->fdResult.num);
            for(i = 0; i < pHVCResult->fdResult.num; i++){
                if(pHVCResult->executedFunc & HVC_ACTIV_FACE_DETECTION){
                    /* Detection */
                    Serial.print("Index:"); Serial.println(i);
                    Serial.print("X = "); Serial.print(pHVCResult->fdResult.fcResult[i].dtResult.posX);
                    Serial.print(" Y = "); Serial.print(pHVCResult->fdResult.fcResult[i].dtResult.posY);
                    Serial.print(" Size = "); Serial.print(pHVCResult->fdResult.fcResult[i].dtResult.size);
                    Serial.print(" Confidence = "); Serial.print(pHVCResult->fdResult.fcResult[i].dtResult.confidence);
                    Serial.println();
                }
                if(pHVCResult->executedFunc & HVC_ACTIV_FACE_DIRECTION){
                    /* Face Direction */
                    Serial.print("Face Direction : ");
                    Serial.print("LR = "); Serial.print(pHVCResult->fdResult.fcResult[i].dirResult.yaw);
                    Serial.print(" UD = "); Serial.print(pHVCResult->fdResult.fcResult[i].dirResult.pitch);
                    Serial.print(" Roll = "); Serial.print(pHVCResult->fdResult.fcResult[i].dirResult.roll);
                    Serial.print(" Confidence = "); Serial.print(pHVCResult->fdResult.fcResult[i].dirResult.confidence);
                    Serial.println();
                }
                if(pHVCResult->executedFunc & HVC_ACTIV_AGE_ESTIMATION){
                    /* Age */
                    if(-128 == pHVCResult->fdResult.fcResult[i].ageResult.age){
                        Serial.println("Age Estimation not possible");
                    } else {
                        Serial.print("Age = ");
                        Serial.print(pHVCResult->fdResult.fcResult[i].ageResult.age);
                        Serial.print(" Confidence = ");
                        Serial.print(pHVCResult->fdResult.fcResult[i].ageResult.confidence);
                        Serial.println();
                    }
                }
                if(pHVCResult->executedFunc & HVC_ACTIV_GENDER_ESTIMATION){
                    /* Gender */
                    if(-128 == pHVCResult->fdResult.fcResult[i].genderResult.gender){
                        Serial.println("Gender Estimation not possible");
                    }
                    else{
                        if(1 == pHVCResult->fdResult.fcResult[i].genderResult.gender){
                            Serial.print("Gender Male Confidence = ");
                            Serial.print(pHVCResult->fdResult.fcResult[i].genderResult.confidence);
                            Serial.println();
                            // Draw sky blue square
                            DrawSquare(pHVCResult->fdResult.fcResult[i].dtResult.posX,
                                       pHVCResult->fdResult.fcResult[i].dtResult.posY,
                                       pHVCResult->fdResult.fcResult[i].dtResult.size,
                                       0x0000f0f0, frame_buffer_RGB888);
                        }
                        else{
                            Serial.print("Gender Female Confidence = ");
                            Serial.print(pHVCResult->fdResult.fcResult[i].genderResult.confidence);
                            Serial.println();
                            // Draw pink square
                            DrawSquare(pHVCResult->fdResult.fcResult[i].dtResult.posX,
                                       pHVCResult->fdResult.fcResult[i].dtResult.posY,
                                       pHVCResult->fdResult.fcResult[i].dtResult.size,
                                       0x00f000f0, frame_buffer_RGB888);
                        }
                    }
                }
                if(pHVCResult->executedFunc & HVC_ACTIV_GAZE_ESTIMATION){
                    /* Gaze */
                    if((-128 == pHVCResult->fdResult.fcResult[i].gazeResult.gazeLR) ||
                        (-128 == pHVCResult->fdResult.fcResult[i].gazeResult.gazeUD)){
                        Serial.println("Gaze Estimation not possible");
                    }
                    else{
                        Serial.print("Gaze : ");
                        Serial.print("LR = "); Serial.print(pHVCResult->fdResult.fcResult[i].gazeResult.gazeLR);
                        Serial.print(" UD = "); Serial.print(pHVCResult->fdResult.fcResult[i].gazeResult.gazeUD);
                        Serial.println();
                    }
                }
                if(pHVCResult->executedFunc & HVC_ACTIV_BLINK_ESTIMATION){
                    /* Blink */
                    if((-128 == pHVCResult->fdResult.fcResult[i].blinkResult.ratioL) ||
                        (-128 == pHVCResult->fdResult.fcResult[i].blinkResult.ratioR)){
                        Serial.println("Gaze Estimation not possible");
                    }
                    else{
                        Serial.print("Blink : ");
                        Serial.print("Left = "); Serial.print(pHVCResult->fdResult.fcResult[i].blinkResult.ratioL);
                        Serial.print(" Right = "); Serial.print(pHVCResult->fdResult.fcResult[i].blinkResult.ratioR);
                        Serial.println();
                    }
                }
                if(pHVCResult->executedFunc & HVC_ACTIV_EXPRESSION_ESTIMATION){
                    /* Expression */
                    if(-128 == pHVCResult->fdResult.fcResult[i].expressionResult.score[0]){
                        Serial.println("Expression Estimation not possible");
                    }
                    else{
                        if(pHVCResult->fdResult.fcResult[i].expressionResult.topExpression > EX_SADNESS){
                            pHVCResult->fdResult.fcResult[i].expressionResult.topExpression = 0;
                        }
                        Serial.print("Expression : ");
                        Serial.print("Expression = "); Serial.print(pExStr[pHVCResult->fdResult.fcResult[i].expressionResult.topExpression]);
                        Serial.print(" Score = ");
                        Serial.print(pHVCResult->fdResult.fcResult[i].expressionResult.score[0]);
                        Serial.print(" ");
                        Serial.print(pHVCResult->fdResult.fcResult[i].expressionResult.score[1]);
                        Serial.print(" ");
                        Serial.print(pHVCResult->fdResult.fcResult[i].expressionResult.score[2]);
                        Serial.print(" ");
                        Serial.print(pHVCResult->fdResult.fcResult[i].expressionResult.score[3]);
                        Serial.print(" ");
                        Serial.print(pHVCResult->fdResult.fcResult[i].expressionResult.score[4]);
                        Serial.print(" Degree = ");
                        Serial.print(pHVCResult->fdResult.fcResult[i].expressionResult.degree);
                        Serial.println();
                    }
                }
            }
        }
 
        if(pHVCResult->bdResult.num | pHVCResult->hdResult.num | pHVCResult->fdResult.num){
            // Save image to SD
            for (int i = 0; i < PIXEL_HEIGHT; i++) {
                for (int j = 0; j < PIXEL_WIDTH; j+=2) { // processing 2 pixels
                    uint8_t r0,g0,b0,r1,g1,b1,y0,y1,u,v;
                    r1 = frame_buffer_RGB888[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_RGB)
                            + BYTE_PER_PIXEL_RGB * j + 5];
                    g1 = frame_buffer_RGB888[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_RGB)
                            + BYTE_PER_PIXEL_RGB * j + 4];
                    b1 = frame_buffer_RGB888[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_RGB)
                            + BYTE_PER_PIXEL_RGB * j + 3];
                    r0 = frame_buffer_RGB888[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_RGB)
                            + BYTE_PER_PIXEL_RGB * j + 2];
                    g0 = frame_buffer_RGB888[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_RGB)
                            + BYTE_PER_PIXEL_RGB * j + 1];
                    b0 = frame_buffer_RGB888[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_RGB)
                            + BYTE_PER_PIXEL_RGB * j + 0];
 
                    y0 = round( 0.256788 * r0 + 0.504129 * g0 + 0.097906 * b0) +  16;
                    y1 = round( 0.256788 * r1 + 0.504129 * g1 + 0.097906 * b1) +  16;
                    if(r0!=g0){ // it's not gray that means color line
                        u = round(-0.148223 * r0 - 0.290993 * g0 + 0.439216 * b0) + 128;
                        v = round( 0.439216 * r0 - 0.367788 * g0 - 0.071427 * b0) + 128;
                    } else if (r1!=g1){ // it's not gray that means color line
                        u = round(-0.148223 * r1 - 0.290993 * g1 + 0.439216 * b1) + 128;
                        v = round( 0.439216 * r1 - 0.367788 * g1 - 0.071427 * b1) + 128;
                    } else { // it's gray.
                        u = round(-0.148223 * r0 - 0.290993 * g0 + 0.439216 * b0) + 128;
                        v = round( 0.439216 * r0 - 0.367788 * g0 - 0.071427 * b0) + 128;
                    }
 
                    frame_buffer_YUV422[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_YUV)
                            + BYTE_PER_PIXEL_YUV * j + 0] = y0;
                    frame_buffer_YUV422[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_YUV)
                            + BYTE_PER_PIXEL_YUV * j + 1] = u;
                    frame_buffer_YUV422[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_YUV)
                            + BYTE_PER_PIXEL_YUV * j + 2] = y1;
                    frame_buffer_YUV422[(i * PIXEL_WIDTH * BYTE_PER_PIXEL_YUV)
                            + BYTE_PER_PIXEL_YUV * j + 3] = v;
                }
            }
 
            Serial.print("JPEG encoding...");
            int encode_size = JpegEncode(JpegBuffer, sizeof(JpegBuffer), PIXEL_WIDTH, PIXEL_HEIGHT, frame_buffer_YUV422);
            Serial.print(encode_size); Serial.println("byte");
 
            static int file_index;
            char filename[11];
            file_index++;
            sprintf(filename, "image%d.jpg", file_index);
            Serial.print(filename); Serial.print(" writing... ");
            File myFile = SD.open(filename, 0x13); //WRITE, READ, CREATE
            if (myFile) {
                for (int i = 0; i < encode_size; i++) {
                    myFile.write(JpegBuffer[i]);
                }
            } else {
                Serial.println("Fail to open file");
            }
            myFile.close();
            Serial.println("done.");
 
        }
        if (Serial.available()) {
            ch = Serial.read();
        }
 
    } while (ch != ' ');
 
    while (1)
        ;
 
}
 
void loop() {
}


正常に動作するとマイクロSDに写真が保存されます。少し長いプログラムですので簡単に流れを説明します。

まず、HVC-P2を初期設定後に検出がスタートします。検出後にY 8bitで出力されたグレー画像を一旦RGB888形式でバッファリングします。RGB形式にしているのは四角を描くためです。

検出された位置、サイズをもとにRGB画像にDrawSqare関数で四角を描きます。その後JPEG画像にするため、一旦YUV422形式に変換します。ちなみにYUV422では2ピクセルに1つのカラー情報しかないため、変換することで四角の縦の線が若干が崩れてしまいます。RGBのままJPEG変換できればよいのですが、未サポートになっています。

なお、デフォルトのサンプルでは顔、ボディ、手のすべてを検出対象にしており、検出速度はやや遅いです。#define ALL_DETECTIONをコメントアウトすると顔検出だけになり、検出速度が早くなります。