GR-PEACH Special Project: Omron HVC-P2 Human Recognition Camera Module

Part 2. Saving a Photo to an SD Card

Overview

This project introduces a sample program for how to save an image to a Micro SD card after having detected a person with HVC-P2.


Preparation

You will need a GR-PEACH board, HVC-P2, two USB cables (Micro B type), and a USB conversion adapter (Type A). Refer to the image on the right for how to connect everything together.

Then, insert the Micro SD card into a socket located on the back side of GR-PEACH for storage.

Place a short jumper (JP3) in the center part of GR-PEACH in the circuit. This allows the VBUS to receive power supply when it’s connected as a USB host.


Sample Program to Save a Detected Image to an SD Card

Build to execute the following sample program with a web compiler. Double-click on a source, whole document will be selected.


//* 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() {
}

The picture can be saved on a Micro SD card when this sample program works normally. Since it is slightly long, here is a brief description.

First, the detection starts after the initial setting of HVC-P2. After detection, the gray image will be generated with Y 8-bit. Execute buffering it in RGB 888 form temporarily, which is suitable for drawing a square.

Draw a square in RGB image using the DrawSquare function based on detected position, size. Convert it into YUV422 form once to make a JPEG image afterwards. By the way, square vertical lines will be collapsed a little by conversion because YUV422 has only one color information in two pixels. Should be able to convert to JPEG image as RGB form, but please note that it is not supported as of now.

In addition, this default sample program targets all of the parts such as face, body and hands for detection, so its speed is slightly slow. If the words #define ALL_DETECTION is deleted, only the face detection becomes available and then detection speed will be faster.