|
| 1 | +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | +#include "esp_http_server.h" |
| 15 | +#include "esp_timer.h" |
| 16 | +#include "esp_camera.h" |
| 17 | +#include "img_converters.h" |
| 18 | +#include "camera_index.h" |
| 19 | +#include "Arduino.h" |
| 20 | + |
| 21 | +#include "fb_gfx.h" |
| 22 | +#include "fd_forward.h" |
| 23 | +#include "fr_forward.h" |
| 24 | + |
| 25 | +#define ENROLL_CONFIRM_TIMES 5 |
| 26 | +#define FACE_ID_SAVE_NUMBER 7 |
| 27 | + |
| 28 | +#define FACE_COLOR_WHITE 0x00FFFFFF |
| 29 | +#define FACE_COLOR_BLACK 0x00000000 |
| 30 | +#define FACE_COLOR_RED 0x000000FF |
| 31 | +#define FACE_COLOR_GREEN 0x0000FF00 |
| 32 | +#define FACE_COLOR_BLUE 0x00FF0000 |
| 33 | +#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN) |
| 34 | +#define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN) |
| 35 | +#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED) |
| 36 | + |
| 37 | +typedef struct { |
| 38 | + size_t size; //number of values used for filtering |
| 39 | + size_t index; //current value index |
| 40 | + size_t count; //value count |
| 41 | + int sum; |
| 42 | + int * values; //array to be filled with values |
| 43 | +} ra_filter_t; |
| 44 | + |
| 45 | +typedef struct { |
| 46 | + httpd_req_t *req; |
| 47 | + size_t len; |
| 48 | +} jpg_chunking_t; |
| 49 | + |
| 50 | +#define PART_BOUNDARY "123456789000000000000987654321" |
| 51 | +static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; |
| 52 | +static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; |
| 53 | +static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; |
| 54 | + |
| 55 | +static ra_filter_t ra_filter; |
| 56 | +httpd_handle_t stream_httpd = NULL; |
| 57 | +httpd_handle_t camera_httpd = NULL; |
| 58 | + |
| 59 | +static mtmn_config_t mtmn_config = {0}; |
| 60 | +static int8_t detection_enabled = 0; |
| 61 | +static int8_t recognition_enabled = 0; |
| 62 | +static int8_t is_enrolling = 0; |
| 63 | +static face_id_list id_list = {0}; |
| 64 | + |
| 65 | +static ra_filter_t * ra_filter_init(ra_filter_t * filter, size_t sample_size){ |
| 66 | + memset(filter, 0, sizeof(ra_filter_t)); |
| 67 | + |
| 68 | + filter->values = (int *)malloc(sample_size * sizeof(int)); |
| 69 | + if(!filter->values){ |
| 70 | + return NULL; |
| 71 | + } |
| 72 | + memset(filter->values, 0, sample_size * sizeof(int)); |
| 73 | + |
| 74 | + filter->size = sample_size; |
| 75 | + return filter; |
| 76 | +} |
| 77 | + |
| 78 | +static int ra_filter_run(ra_filter_t * filter, int value){ |
| 79 | + if(!filter->values){ |
| 80 | + return value; |
| 81 | + } |
| 82 | + filter->sum -= filter->values[filter->index]; |
| 83 | + filter->values[filter->index] = value; |
| 84 | + filter->sum += filter->values[filter->index]; |
| 85 | + filter->index++; |
| 86 | + filter->index = filter->index % filter->size; |
| 87 | + if (filter->count < filter->size) { |
| 88 | + filter->count++; |
| 89 | + } |
| 90 | + return filter->sum / filter->count; |
| 91 | +} |
| 92 | + |
| 93 | +static void rgb_print(dl_matrix3du_t *image_matrix, uint32_t color, const char * str){ |
| 94 | + fb_data_t fb; |
| 95 | + fb.width = image_matrix->w; |
| 96 | + fb.height = image_matrix->h; |
| 97 | + fb.data = image_matrix->item; |
| 98 | + fb.bytes_per_pixel = 3; |
| 99 | + fb.format = FB_BGR888; |
| 100 | + fb_gfx_print(&fb, (fb.width - (strlen(str) * 14)) / 2, 10, color, str); |
| 101 | +} |
| 102 | + |
| 103 | +static int rgb_printf(dl_matrix3du_t *image_matrix, uint32_t color, const char *format, ...){ |
| 104 | + char loc_buf[64]; |
| 105 | + char * temp = loc_buf; |
| 106 | + int len; |
| 107 | + va_list arg; |
| 108 | + va_list copy; |
| 109 | + va_start(arg, format); |
| 110 | + va_copy(copy, arg); |
| 111 | + len = vsnprintf(loc_buf, sizeof(loc_buf), format, arg); |
| 112 | + va_end(copy); |
| 113 | + if(len >= sizeof(loc_buf)){ |
| 114 | + temp = (char*)malloc(len+1); |
| 115 | + if(temp == NULL) { |
| 116 | + return 0; |
| 117 | + } |
| 118 | + } |
| 119 | + vsnprintf(temp, len+1, format, arg); |
| 120 | + va_end(arg); |
| 121 | + rgb_print(image_matrix, color, temp); |
| 122 | + if(len > 64){ |
| 123 | + free(temp); |
| 124 | + } |
| 125 | + return len; |
| 126 | +} |
| 127 | + |
| 128 | +static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id){ |
| 129 | + int x, y, w, h, i; |
| 130 | + uint32_t color = FACE_COLOR_YELLOW; |
| 131 | + if(face_id < 0){ |
| 132 | + color = FACE_COLOR_RED; |
| 133 | + } else if(face_id > 0){ |
| 134 | + color = FACE_COLOR_GREEN; |
| 135 | + } |
| 136 | + fb_data_t fb; |
| 137 | + fb.width = image_matrix->w; |
| 138 | + fb.height = image_matrix->h; |
| 139 | + fb.data = image_matrix->item; |
| 140 | + fb.bytes_per_pixel = 3; |
| 141 | + fb.format = FB_BGR888; |
| 142 | + for (i = 0; i < boxes->len; i++){ |
| 143 | + // rectangle box |
| 144 | + x = (int)boxes->box[i].box_p[0]; |
| 145 | + y = (int)boxes->box[i].box_p[1]; |
| 146 | + w = (int)boxes->box[i].box_p[2] - x + 1; |
| 147 | + h = (int)boxes->box[i].box_p[3] - y + 1; |
| 148 | + fb_gfx_drawFastHLine(&fb, x, y, w, color); |
| 149 | + fb_gfx_drawFastHLine(&fb, x, y+h-1, w, color); |
| 150 | + fb_gfx_drawFastVLine(&fb, x, y, h, color); |
| 151 | + fb_gfx_drawFastVLine(&fb, x+w-1, y, h, color); |
| 152 | +#if 0 |
| 153 | + // landmark |
| 154 | + int x0, y0, j; |
| 155 | + for (j = 0; j < 10; j+=2) { |
| 156 | + x0 = (int)boxes->landmark[i].landmark_p[j]; |
| 157 | + y0 = (int)boxes->landmark[i].landmark_p[j+1]; |
| 158 | + fb_gfx_fillRect(&fb, x0, y0, 3, 3, color); |
| 159 | + } |
| 160 | +#endif |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +static int run_face_recognition(dl_matrix3du_t *image_matrix, box_array_t *net_boxes){ |
| 165 | + dl_matrix3du_t *aligned_face = NULL; |
| 166 | + int matched_id = 0; |
| 167 | + |
| 168 | + aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3); |
| 169 | + if(!aligned_face){ |
| 170 | + Serial.println("Could not allocate face recognition buffer"); |
| 171 | + return matched_id; |
| 172 | + } |
| 173 | + if (align_face(net_boxes, image_matrix, aligned_face) == ESP_OK){ |
| 174 | + if (is_enrolling == 1){ |
| 175 | + int8_t left_sample_face = enroll_face(&id_list, aligned_face); |
| 176 | + |
| 177 | + if(left_sample_face == (ENROLL_CONFIRM_TIMES - 1)){ |
| 178 | + Serial.printf("Enrolling Face ID: %d\n", id_list.tail); |
| 179 | + } |
| 180 | + Serial.printf("Enrolling Face ID: %d sample %d\n", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face); |
| 181 | + rgb_printf(image_matrix, FACE_COLOR_CYAN, "ID[%u] Sample[%u]", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face); |
| 182 | + if (left_sample_face == 0){ |
| 183 | + is_enrolling = 0; |
| 184 | + Serial.printf("Enrolled Face ID: %d\n", id_list.tail); |
| 185 | + } |
| 186 | + } else { |
| 187 | + matched_id = recognize_face(&id_list, aligned_face); |
| 188 | + if (matched_id >= 0) { |
| 189 | + Serial.printf("Match Face ID: %u\n", matched_id); |
| 190 | + rgb_printf(image_matrix, FACE_COLOR_GREEN, "Hello Subject %u", matched_id); |
| 191 | + } else { |
| 192 | + Serial.println("No Match Found"); |
| 193 | + rgb_print(image_matrix, FACE_COLOR_RED, "Intruder Alert!"); |
| 194 | + matched_id = -1; |
| 195 | + } |
| 196 | + } |
| 197 | + } else { |
| 198 | + Serial.println("Face Not Aligned"); |
| 199 | + //rgb_print(image_matrix, FACE_COLOR_YELLOW, "Human Detected"); |
| 200 | + } |
| 201 | + |
| 202 | + dl_matrix3du_free(aligned_face); |
| 203 | + return matched_id; |
| 204 | +} |
| 205 | + |
| 206 | +static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){ |
| 207 | + jpg_chunking_t *j = (jpg_chunking_t *)arg; |
| 208 | + if(!index){ |
| 209 | + j->len = 0; |
| 210 | + } |
| 211 | + if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){ |
| 212 | + return 0; |
| 213 | + } |
| 214 | + j->len += len; |
| 215 | + return len; |
| 216 | +} |
| 217 | + |
| 218 | +static esp_err_t capture_handler(httpd_req_t *req){ |
| 219 | + camera_fb_t * fb = NULL; |
| 220 | + esp_err_t res = ESP_OK; |
| 221 | + int64_t fr_start = esp_timer_get_time(); |
| 222 | + |
| 223 | + fb = esp_camera_fb_get(); |
| 224 | + if (!fb) { |
| 225 | + Serial.println("Camera capture failed"); |
| 226 | + httpd_resp_send_500(req); |
| 227 | + return ESP_FAIL; |
| 228 | + } |
| 229 | + |
| 230 | + httpd_resp_set_type(req, "image/jpeg"); |
| 231 | + httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg"); |
| 232 | + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); |
| 233 | + |
| 234 | + size_t out_len, out_width, out_height; |
| 235 | + uint8_t * out_buf; |
| 236 | + bool s; |
| 237 | + bool detected = false; |
| 238 | + int face_id = 0; |
| 239 | + if(!detection_enabled || fb->width > 400){ |
| 240 | + size_t fb_len = 0; |
| 241 | + if(fb->format == PIXFORMAT_JPEG){ |
| 242 | + fb_len = fb->len; |
| 243 | + res = httpd_resp_send(req, (const char *)fb->buf, fb->len); |
| 244 | + } else { |
| 245 | + jpg_chunking_t jchunk = {req, 0}; |
| 246 | + res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL; |
| 247 | + httpd_resp_send_chunk(req, NULL, 0); |
| 248 | + fb_len = jchunk.len; |
| 249 | + } |
| 250 | + esp_camera_fb_return(fb); |
| 251 | + int64_t fr_end = esp_timer_get_time(); |
| 252 | + Serial.printf("JPG: %uB %ums\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000)); |
| 253 | + return res; |
| 254 | + } |
| 255 | + |
| 256 | + dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); |
| 257 | + if (!image_matrix) { |
| 258 | + esp_camera_fb_return(fb); |
| 259 | + Serial.println("dl_matrix3du_alloc failed"); |
| 260 | + httpd_resp_send_500(req); |
| 261 | + return ESP_FAIL; |
| 262 | + } |
| 263 | + |
| 264 | + out_buf = image_matrix->item; |
| 265 | + out_len = fb->width * fb->height * 3; |
| 266 | + out_width = fb->width; |
| 267 | + out_height = fb->height; |
| 268 | + |
| 269 | + s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf); |
| 270 | + esp_camera_fb_return(fb); |
| 271 | + if(!s){ |
| 272 | + dl_matrix3du_free(image_matrix); |
| 273 | + Serial.println("to rgb888 failed"); |
| 274 | + httpd_resp_send_500(req); |
| 275 | + return ESP_FAIL; |
| 276 | + } |
| 277 | + |
| 278 | + box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config); |
| 279 | + |
| 280 | + if (net_boxes){ |
| 281 | + detected = true; |
| 282 | + if(recognition_enabled){ |
| 283 | + face_id = run_face_recognition(image_matrix, net_boxes); |
| 284 | + } |
| 285 | + draw_face_boxes(image_matrix, net_boxes, face_id); |
| 286 | + free(net_boxes->score); |
| 287 | + free(net_boxes->box); |
| 288 | + free(net_boxes->landmark); |
| 289 | + free(net_boxes); |
| 290 | + } |
| 291 | + |
| 292 | + jpg_chunking_t jchunk = {req, 0}; |
| 293 | + s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk); |
| 294 | + dl_matrix3du_free(image_matrix); |
| 295 | + if(!s){ |
| 296 | + Serial.println("JPEG compression failed"); |
| 297 | + return ESP_FAIL; |
| 298 | + } |
| 299 | + |
| 300 | + int64_t fr_end = esp_timer_get_time(); |
| 301 | + Serial.printf("FACE: %uB %ums %s%d\n", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start)/1000), detected?"DETECTED ":"", face_id); |
| 302 | + return res; |
| 303 | +} |
| 304 | + |
| 305 | +static esp_err_t stream_handler(httpd_req_t *req){ |
| 306 | + camera_fb_t * fb = NULL; |
| 307 | + esp_err_t res = ESP_OK; |
| 308 | + size_t _jpg_buf_len = 0; |
| 309 | + uint8_t * _jpg_buf = NULL; |
| 310 | + char * part_buf[64]; |
| 311 | + dl_matrix3du_t *image_matrix = NULL; |
| 312 | + bool detected = false; |
| 313 | + int face_id = 0; |
| 314 | + int64_t fr_start = 0; |
| 315 | + int64_t fr_ready = 0; |
| 316 | + int64_t fr_face = 0; |
| 317 | + int64_t fr_recognize = 0; |
| 318 | + int64_t fr_encode = 0; |
| 319 | + |
| 320 | + static int64_t last_frame = 0; |
| 321 | + if(!last_frame) { |
| 322 | + last_frame = esp_timer_get_time(); |
| 323 | + } |
| 324 | + |
| 325 | + res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); |
| 326 | + if(res != ESP_OK){ |
| 327 | + return res; |
| 328 | + } |
| 329 | + |
| 330 | + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); |
| 331 | + |
| 332 | + while(true){ |
| 333 | + detected = false; |
| 334 | + face_id = 0; |
| 335 | + fb = esp_camera_fb_get(); |
| 336 | + if (!fb) { |
| 337 | + Serial.println("Camera capture failed"); |
| 338 | + res = ESP_FAIL; |
| 339 | + } else { |
| 340 | + fr_start = esp_timer_get_time(); |
| 341 | + fr_ready = fr_start; |
| 342 | + fr_face = fr_start; |
| 343 | + fr_encode = fr_start; |
| 344 | + fr_recognize = fr_start; |
| 345 | + if(!detection_enabled || fb->width > 400){ |
| 346 | + if(fb->format != PIXFORMAT_JPEG){ |
| 347 | + bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); |
| 348 | + esp_camera_fb_return(fb); |
| 349 | + fb = NULL; |
| 350 | + if(!jpeg_converted){ |
| 351 | + Serial.println("JPEG compression failed"); |
| 352 | + res = ESP_FAIL; |
| 353 | + } |
| 354 | + } else { |
| 355 | + _jpg_buf_len = fb->len; |
| 356 | + _jpg_buf = fb->buf; |
| 357 | + } |
| 358 | + } else { |
| 359 | + |
| 360 | + image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); |
| 361 | + |
| 362 | + if (!image_matrix) { |
| 363 | + Serial.println("dl_matrix3du_alloc failed"); |
| 364 | + res = ESP_FAIL; |
| 365 | + } else { |
| 366 | + if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item)){ |
| 367 | + Serial.println("fmt2rgb888 failed"); |
| 368 | + res = ESP_FAIL; |
| 369 | + } else { |
| 370 | + fr_ready = esp_timer_get_time(); |
| 371 | + box_array_t *net_boxes = NULL; |
| 372 | + if(detection_enabled){ |
| 373 | + net_boxes = face_detect(image_matrix, &mtmn_config); |
| 374 | + } |
| 375 | + fr_face = esp_timer_get_time(); |
| 376 | + fr_recognize = fr_face; |
| 377 | + if (net_boxes || fb->format != PIXFORMAT_JPEG){ |
| 378 | + if(net_boxes){ |
| 379 | + detected = true; |
| 380 | + if(recognition_enabled){ |
| 381 | + face_id = run_face_recognition(image_matrix, net_boxes); |
| 382 | + } |
| 383 | + fr_recognize = esp_timer_get_time(); |
| 384 | + draw_face_boxes(image_matrix, net_boxes, face_id); |
| 385 | + free(net_boxes->score); |
| 386 | + free(net_boxes->box); |
| 387 | + free(net_boxes->landmark); |
| 388 | + free(net_boxes); |
| 389 | + } |
| 390 | + if(!fmt2jpg(image_matrix->item, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len)){ |
| 391 | + Serial.println("fmt2jpg failed"); |
| 392 | + res = ESP_FAIL; |
| 393 | + } |
| 394 | + esp_camera_fb_return(fb); |
| 395 | + fb = NULL; |
| 396 | + } else { |
| 397 | + _jpg_buf = fb->buf; |
| 398 | + _jpg_buf_len = fb->len; |
| 399 | + } |
| 400 | + fr_encode = esp_timer_get_time(); |
| 401 | + } |
| 402 | + dl_matrix3du_free(image_matrix); |
| 403 | + } |
| 404 | + } |
| 405 | + } |
| 406 | + if(res == ESP_OK){ |
| 407 | + size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); |
| 408 | + res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); |
| 409 | + } |
| 410 | + if(res == ESP_OK){ |
| 411 | + res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); |
| 412 | + } |
| 413 | + if(res == ESP_OK){ |
| 414 | + res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); |
| 415 | + } |
| 416 | + if(fb){ |
| 417 | + esp_camera_fb_return(fb); |
| 418 | + fb = NULL; |
| 419 | + _jpg_buf = NULL; |
| 420 | + } else if(_jpg_buf){ |
| 421 | + free(_jpg_buf); |
| 422 | + _jpg_buf = NULL; |
| 423 | + } |
| 424 | + if(res != ESP_OK){ |
| 425 | + break; |
| 426 | + } |
| 427 | + int64_t fr_end = esp_timer_get_time(); |
| 428 | + |
| 429 | + int64_t ready_time = (fr_ready - fr_start)/1000; |
| 430 | + int64_t face_time = (fr_face - fr_ready)/1000; |
| 431 | + int64_t recognize_time = (fr_recognize - fr_face)/1000; |
| 432 | + int64_t encode_time = (fr_encode - fr_recognize)/1000; |
| 433 | + int64_t process_time = (fr_encode - fr_start)/1000; |
| 434 | + |
| 435 | + int64_t frame_time = fr_end - last_frame; |
| 436 | + last_frame = fr_end; |
| 437 | + frame_time /= 1000; |
| 438 | + uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time); |
| 439 | + Serial.printf("MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps), %u+%u+%u+%u=%u %s%d\n", |
| 440 | + (uint32_t)(_jpg_buf_len), |
| 441 | + (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time, |
| 442 | + avg_frame_time, 1000.0 / avg_frame_time, |
| 443 | + (uint32_t)ready_time, (uint32_t)face_time, (uint32_t)recognize_time, (uint32_t)encode_time, (uint32_t)process_time, |
| 444 | + (detected)?"DETECTED ":"", face_id |
| 445 | + ); |
| 446 | + } |
| 447 | + |
| 448 | + last_frame = 0; |
| 449 | + return res; |
| 450 | +} |
| 451 | + |
| 452 | +static esp_err_t cmd_handler(httpd_req_t *req){ |
| 453 | + char* buf; |
| 454 | + size_t buf_len; |
| 455 | + char variable[32] = {0,}; |
| 456 | + char value[32] = {0,}; |
| 457 | + |
| 458 | + buf_len = httpd_req_get_url_query_len(req) + 1; |
| 459 | + if (buf_len > 1) { |
| 460 | + buf = (char*)malloc(buf_len); |
| 461 | + if(!buf){ |
| 462 | + httpd_resp_send_500(req); |
| 463 | + return ESP_FAIL; |
| 464 | + } |
| 465 | + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { |
| 466 | + if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK && |
| 467 | + httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) { |
| 468 | + } else { |
| 469 | + free(buf); |
| 470 | + httpd_resp_send_404(req); |
| 471 | + return ESP_FAIL; |
| 472 | + } |
| 473 | + } else { |
| 474 | + free(buf); |
| 475 | + httpd_resp_send_404(req); |
| 476 | + return ESP_FAIL; |
| 477 | + } |
| 478 | + free(buf); |
| 479 | + } else { |
| 480 | + httpd_resp_send_404(req); |
| 481 | + return ESP_FAIL; |
| 482 | + } |
| 483 | + |
| 484 | + int val = atoi(value); |
| 485 | + sensor_t * s = esp_camera_sensor_get(); |
| 486 | + int res = 0; |
| 487 | + |
| 488 | + if(!strcmp(variable, "framesize")) { |
| 489 | + if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val); |
| 490 | + } |
| 491 | + else if(!strcmp(variable, "quality")) res = s->set_quality(s, val); |
| 492 | + else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val); |
| 493 | + else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val); |
| 494 | + else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val); |
| 495 | + else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val); |
| 496 | + else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val); |
| 497 | + else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val); |
| 498 | + else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val); |
| 499 | + else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val); |
| 500 | + else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val); |
| 501 | + else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val); |
| 502 | + else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val); |
| 503 | + else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val); |
| 504 | + else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val); |
| 505 | + else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val); |
| 506 | + else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val); |
| 507 | + else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val); |
| 508 | + else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val); |
| 509 | + else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val); |
| 510 | + else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val); |
| 511 | + else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val); |
| 512 | + else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val); |
| 513 | + else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val); |
| 514 | + else if(!strcmp(variable, "face_detect")) { |
| 515 | + detection_enabled = val; |
| 516 | + if(!detection_enabled) { |
| 517 | + recognition_enabled = 0; |
| 518 | + } |
| 519 | + } |
| 520 | + else if(!strcmp(variable, "face_enroll")) is_enrolling = val; |
| 521 | + else if(!strcmp(variable, "face_recognize")) { |
| 522 | + recognition_enabled = val; |
| 523 | + if(recognition_enabled){ |
| 524 | + detection_enabled = val; |
| 525 | + } |
| 526 | + } |
| 527 | + else { |
| 528 | + res = -1; |
| 529 | + } |
| 530 | + |
| 531 | + if(res){ |
| 532 | + return httpd_resp_send_500(req); |
| 533 | + } |
| 534 | + |
| 535 | + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); |
| 536 | + return httpd_resp_send(req, NULL, 0); |
| 537 | +} |
| 538 | + |
| 539 | +static esp_err_t status_handler(httpd_req_t *req){ |
| 540 | + static char json_response[1024]; |
| 541 | + |
| 542 | + sensor_t * s = esp_camera_sensor_get(); |
| 543 | + char * p = json_response; |
| 544 | + *p++ = '{'; |
| 545 | + |
| 546 | + p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); |
| 547 | + p+=sprintf(p, "\"quality\":%u,", s->status.quality); |
| 548 | + p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); |
| 549 | + p+=sprintf(p, "\"contrast\":%d,", s->status.contrast); |
| 550 | + p+=sprintf(p, "\"saturation\":%d,", s->status.saturation); |
| 551 | + p+=sprintf(p, "\"sharpness\":%d,", s->status.sharpness); |
| 552 | + p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect); |
| 553 | + p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode); |
| 554 | + p+=sprintf(p, "\"awb\":%u,", s->status.awb); |
| 555 | + p+=sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain); |
| 556 | + p+=sprintf(p, "\"aec\":%u,", s->status.aec); |
| 557 | + p+=sprintf(p, "\"aec2\":%u,", s->status.aec2); |
| 558 | + p+=sprintf(p, "\"ae_level\":%d,", s->status.ae_level); |
| 559 | + p+=sprintf(p, "\"aec_value\":%u,", s->status.aec_value); |
| 560 | + p+=sprintf(p, "\"agc\":%u,", s->status.agc); |
| 561 | + p+=sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain); |
| 562 | + p+=sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling); |
| 563 | + p+=sprintf(p, "\"bpc\":%u,", s->status.bpc); |
| 564 | + p+=sprintf(p, "\"wpc\":%u,", s->status.wpc); |
| 565 | + p+=sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma); |
| 566 | + p+=sprintf(p, "\"lenc\":%u,", s->status.lenc); |
| 567 | + p+=sprintf(p, "\"vflip\":%u,", s->status.vflip); |
| 568 | + p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror); |
| 569 | + p+=sprintf(p, "\"dcw\":%u,", s->status.dcw); |
| 570 | + p+=sprintf(p, "\"colorbar\":%u,", s->status.colorbar); |
| 571 | + p+=sprintf(p, "\"face_detect\":%u,", detection_enabled); |
| 572 | + p+=sprintf(p, "\"face_enroll\":%u,", is_enrolling); |
| 573 | + p+=sprintf(p, "\"face_recognize\":%u", recognition_enabled); |
| 574 | + *p++ = '}'; |
| 575 | + *p++ = 0; |
| 576 | + httpd_resp_set_type(req, "application/json"); |
| 577 | + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); |
| 578 | + return httpd_resp_send(req, json_response, strlen(json_response)); |
| 579 | +} |
| 580 | + |
| 581 | +static esp_err_t index_handler(httpd_req_t *req){ |
| 582 | + httpd_resp_set_type(req, "text/html"); |
| 583 | + httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); |
| 584 | + sensor_t * s = esp_camera_sensor_get(); |
| 585 | + if (s->id.PID == OV3660_PID) { |
| 586 | + return httpd_resp_send(req, (const char *)index_ov3660_html_gz, index_ov3660_html_gz_len); |
| 587 | + } |
| 588 | + return httpd_resp_send(req, (const char *)index_ov2640_html_gz, index_ov2640_html_gz_len); |
| 589 | +} |
| 590 | + |
| 591 | +void startCameraServer(){ |
| 592 | + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); |
| 593 | + |
| 594 | + httpd_uri_t index_uri = { |
| 595 | + .uri = "/", |
| 596 | + .method = HTTP_GET, |
| 597 | + .handler = index_handler, |
| 598 | + .user_ctx = NULL |
| 599 | + }; |
| 600 | + |
| 601 | + httpd_uri_t status_uri = { |
| 602 | + .uri = "/status", |
| 603 | + .method = HTTP_GET, |
| 604 | + .handler = status_handler, |
| 605 | + .user_ctx = NULL |
| 606 | + }; |
| 607 | + |
| 608 | + httpd_uri_t cmd_uri = { |
| 609 | + .uri = "/control", |
| 610 | + .method = HTTP_GET, |
| 611 | + .handler = cmd_handler, |
| 612 | + .user_ctx = NULL |
| 613 | + }; |
| 614 | + |
| 615 | + httpd_uri_t capture_uri = { |
| 616 | + .uri = "/capture", |
| 617 | + .method = HTTP_GET, |
| 618 | + .handler = capture_handler, |
| 619 | + .user_ctx = NULL |
| 620 | + }; |
| 621 | + |
| 622 | + httpd_uri_t stream_uri = { |
| 623 | + .uri = "/stream", |
| 624 | + .method = HTTP_GET, |
| 625 | + .handler = stream_handler, |
| 626 | + .user_ctx = NULL |
| 627 | + }; |
| 628 | + |
| 629 | + |
| 630 | + ra_filter_init(&ra_filter, 20); |
| 631 | + |
| 632 | + mtmn_config.type = FAST; |
| 633 | + mtmn_config.min_face = 80; |
| 634 | + mtmn_config.pyramid = 0.707; |
| 635 | + mtmn_config.pyramid_times = 4; |
| 636 | + mtmn_config.p_threshold.score = 0.6; |
| 637 | + mtmn_config.p_threshold.nms = 0.7; |
| 638 | + mtmn_config.p_threshold.candidate_number = 20; |
| 639 | + mtmn_config.r_threshold.score = 0.7; |
| 640 | + mtmn_config.r_threshold.nms = 0.7; |
| 641 | + mtmn_config.r_threshold.candidate_number = 10; |
| 642 | + mtmn_config.o_threshold.score = 0.7; |
| 643 | + mtmn_config.o_threshold.nms = 0.7; |
| 644 | + mtmn_config.o_threshold.candidate_number = 1; |
| 645 | + |
| 646 | + face_id_init(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES); |
| 647 | + |
| 648 | + Serial.printf("Starting web server on port: '%d'\n", config.server_port); |
| 649 | + if (httpd_start(&camera_httpd, &config) == ESP_OK) { |
| 650 | + httpd_register_uri_handler(camera_httpd, &index_uri); |
| 651 | + httpd_register_uri_handler(camera_httpd, &cmd_uri); |
| 652 | + httpd_register_uri_handler(camera_httpd, &status_uri); |
| 653 | + httpd_register_uri_handler(camera_httpd, &capture_uri); |
| 654 | + } |
| 655 | + |
| 656 | + config.server_port += 1; |
| 657 | + config.ctrl_port += 1; |
| 658 | + Serial.printf("Starting stream server on port: '%d'\n", config.server_port); |
| 659 | + if (httpd_start(&stream_httpd, &config) == ESP_OK) { |
| 660 | + httpd_register_uri_handler(stream_httpd, &stream_uri); |
| 661 | + } |
| 662 | +} |
0 commit comments