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.