The coronavirus disease 2019 (COVID-19) pandemic is currently perceived as one of the greatest global threats to public health and well-being and global economic and social stability. COVID-19 spreads mainly from person to person through respiratory droplets. Respiratory droplets travel into the air when you cough, sneeze, talk, shout or sing. These droplets can then land in the mouths or noses of people who are near you or they may breathe these droplets in. Masks are a simple barrier to help prevent your respiratory droplets from reaching others. Studies show that masks reduce the spray of droplets when worn over the nose and mouth.
We will make this project using ESP32-Cam. For programming ESP32-Cam, we will use ESP8266. You can learn more about ESP8266 by going through ESP8266 projects.
We have built some face detection projects previously,
- Real Time Face Recognition with Raspberry Pi and OpenCV
- ESP32-CAM Face Recognition Door Lock System
- Real Life Object Detection using OpenCV – Detecting objects in Live Video
- Real Time Face Detection and Tracking Robot using Arduino
Component Requirement for Face Mask Detection
Project Used Hardware
- ESP8266,
- ESP32-CAM
Project Used Software
- Arduino IDE,
- Teachable machine
Project Hardware Software Selection
Developed Face mask detection image classification model using Teachable machine. The link of the model is used in our code. Here we can also add a Relay module or Servo motor for Gate.
Circuit Diagram
ESP8266 is used for programming ESP32-CAM.
const char* ssid = "nbsuthar"; //your network SSID const char* password = "nbsuthar"; //your network password const char* apssid = "ESP32-CAM"; const char* appassword = "12345678"; #include <WiFi.h> #include <esp32-hal-ledc.h> #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" #include "esp_camera.h" #include "esp_http_server.h" #include "esp_timer.h" #include "img_converters.h" #include "fb_gfx.h" #include "fd_forward.h" String Feedback=""; String Command=""; String cmd=""; String P1=""; String P2=""; String P3=""; String P4=""; String P5=""; String P6=""; String P7=""; String P8=""; String P9=""; byte ReceiveState=0; byte cmdState=1; byte strState=1; byte questionstate=0; byte equalstate=0; byte semicolonstate=0; typedef struct { size_t size; size_t index; size_t count; int sum; int * values; } ra_filter_t; typedef struct { httpd_req_t *req; size_t len; } jpg_chunking_t; #define PART_BOUNDARY "123456789000000000000987654321" static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; static ra_filter_t ra_filter; httpd_handle_t stream_httpd = NULL; httpd_handle_t camera_httpd = NULL; static ra_filter_t * ra_filter_init(ra_filter_t * filter, size_t sample_size){ memset(filter, 0, sizeof(ra_filter_t)); filter->values = (int *)malloc(sample_size * sizeof(int)); if(!filter->values){ return NULL; } memset(filter->values, 0, sample_size * sizeof(int)); filter->size = sample_size; return filter; } static int ra_filter_run(ra_filter_t * filter, int value){ if(!filter->values){ return value; } filter->sum -= filter->values[filter->index]; filter->values[filter->index] = value; filter->sum += filter->values[filter->index]; filter->index++; filter->index = filter->index % filter->size; if (filter->count < filter->size) { filter->count++; } return filter->sum / filter->count; } #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 0 #define SIOD_GPIO_NUM 26 #define SIOC_GPIO_NUM 27 #define Y9_GPIO_NUM 35 #define Y8_GPIO_NUM 34 #define Y7_GPIO_NUM 39 #define Y6_GPIO_NUM 36 #define Y5_GPIO_NUM 21 #define Y4_GPIO_NUM 19 #define Y3_GPIO_NUM 18 #define Y2_GPIO_NUM 5 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 void setup() { WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); Serial.begin(115200); Serial.setDebugOutput(true); Serial.println(); camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; config.pin_d3 = Y5_GPIO_NUM; config.pin_d4 = Y6_GPIO_NUM; config.pin_d5 = Y7_GPIO_NUM; config.pin_d6 = Y8_GPIO_NUM; config.pin_d7 = Y9_GPIO_NUM; config.pin_xclk = XCLK_GPIO_NUM; config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; config.pin_sscb_sda = SIOD_GPIO_NUM; config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; if(psramFound()){ config.frame_size = FRAMESIZE_UXGA; config.jpeg_quality = 10; //0-63 lower number means higher quality config.fb_count = 2; } else { config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; //0-63 lower number means higher quality config.fb_count = 1; } esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); delay(1000); ESP.restart(); } sensor_t * s = esp_camera_sensor_get(); s->set_framesize(s, FRAMESIZE_QVGA); //UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA ledcAttachPin(4, 4); ledcSetup(4, 5000, 8); WiFi.mode(WIFI_AP_STA); //WiFi.config(IPAddress(192, 168, 201, 100), IPAddress(192, 168, 201, 2), IPAddress(255, 255, 255, 0)); WiFi.begin(ssid, password); delay(1000); Serial.println(""); Serial.print("Connecting to "); Serial.println(ssid); long int StartTime=millis(); while (WiFi.status() != WL_CONNECTED) { delay(500); if ((StartTime+10000) < millis()) break; } if (WiFi.status() == WL_CONNECTED) { WiFi.softAP((WiFi.localIP().toString()+"_"+(String)apssid).c_str(), appassword); Serial.println(""); Serial.println("STAIP address: "); Serial.println(WiFi.localIP()); for (int i=0;i<5;i++) { ledcWrite(4,10); delay(200); ledcWrite(4,0); delay(200); } } else { WiFi.softAP((WiFi.softAPIP().toString()+"_"+(String)apssid).c_str(), appassword); for (int i=0;i<2;i++) { ledcWrite(4,10); delay(1000); ledcWrite(4,0); delay(1000); } } //WiFi.softAPConfig(IPAddress(192, 168, 4, 1), IPAddress(192, 168, 4, 1), IPAddress(255, 255, 255, 0)); Serial.println(""); Serial.println("APIP address: "); Serial.println(WiFi.softAPIP()); startCameraServer(); pinMode(4, OUTPUT); pinMode(33,OUTPUT); digitalWrite(4, LOW); pinMode(16,OUTPUT); } void loop() { if(P1=="helmet"){ Serial.println("wah..."); } } static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){ jpg_chunking_t *j = (jpg_chunking_t *)arg; if(!index){ j->len = 0; } if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){ return 0; } j->len += len; return len; } static esp_err_t capture_handler(httpd_req_t *req){ camera_fb_t * fb = NULL; esp_err_t res = ESP_OK; int64_t fr_start = esp_timer_get_time(); fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed"); httpd_resp_send_500(req); return ESP_FAIL; } httpd_resp_set_type(req, "image/jpeg"); httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg"); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); size_t out_len, out_width, out_height; uint8_t * out_buf; bool s; if(fb->width > 400){ size_t fb_len = 0; if(fb->format == PIXFORMAT_JPEG){ fb_len = fb->len; res = httpd_resp_send(req, (const char *)fb->buf, fb->len); } else { jpg_chunking_t jchunk = {req, 0}; res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL; httpd_resp_send_chunk(req, NULL, 0); fb_len = jchunk.len; } esp_camera_fb_return(fb); int64_t fr_end = esp_timer_get_time(); Serial.printf("JPG: %uB %ums\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000)); return res; } dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); if (!image_matrix) { esp_camera_fb_return(fb); Serial.println("dl_matrix3du_alloc failed"); httpd_resp_send_500(req); return ESP_FAIL; } out_buf = image_matrix->item; out_len = fb->width * fb->height * 3; out_width = fb->width; out_height = fb->height; s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf); esp_camera_fb_return(fb); if(!s){ dl_matrix3du_free(image_matrix); Serial.println("to rgb888 failed"); httpd_resp_send_500(req); return ESP_FAIL; } jpg_chunking_t jchunk = {req, 0}; s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk); dl_matrix3du_free(image_matrix); if(!s){ Serial.println("JPEG compression failed"); return ESP_FAIL; } int64_t fr_end = esp_timer_get_time(); return res; } static esp_err_t stream_handler(httpd_req_t *req){ camera_fb_t * fb = NULL; esp_err_t res = ESP_OK; size_t _jpg_buf_len = 0; uint8_t * _jpg_buf = NULL; char * part_buf[64]; dl_matrix3du_t *image_matrix = NULL; int64_t fr_start = 0; int64_t fr_ready = 0; static int64_t last_frame = 0; if(!last_frame) { last_frame = esp_timer_get_time(); } res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); if(res != ESP_OK){ return res; } httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); while(true){ fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed"); res = ESP_FAIL; } else { fr_start = esp_timer_get_time(); fr_ready = fr_start; if(fb->width > 400){ if(fb->format != PIXFORMAT_JPEG){ bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); esp_camera_fb_return(fb); fb = NULL; if(!jpeg_converted){ Serial.println("JPEG compression failed"); res = ESP_FAIL; } } else { _jpg_buf_len = fb->len; _jpg_buf = fb->buf; } } else { image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); if (!image_matrix) { Serial.println("dl_matrix3du_alloc failed"); res = ESP_FAIL; } else { if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item)){ Serial.println("fmt2rgb888 failed"); res = ESP_FAIL; } else { fr_ready = esp_timer_get_time(); if (fb->format != PIXFORMAT_JPEG){ if(!fmt2jpg(image_matrix->item, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len)){ Serial.println("fmt2jpg failed"); res = ESP_FAIL; } esp_camera_fb_return(fb); fb = NULL; } else { _jpg_buf = fb->buf; _jpg_buf_len = fb->len; } } dl_matrix3du_free(image_matrix); } } } if(res == ESP_OK){ size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); } if(res == ESP_OK){ res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); } if(res == ESP_OK){ res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); } if(fb){ esp_camera_fb_return(fb); fb = NULL; _jpg_buf = NULL; } else if(_jpg_buf){ free(_jpg_buf); _jpg_buf = NULL; } if(res != ESP_OK){ break; } int64_t fr_end = esp_timer_get_time(); int64_t ready_time = (fr_ready - fr_start)/1000; int64_t frame_time = fr_end - last_frame; last_frame = fr_end; frame_time /= 1000; uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time); Serial.printf("MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps), %u+%u+%u+%u=%u %s%d\n", (uint32_t)(_jpg_buf_len), (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time, avg_frame_time, 1000.0 / avg_frame_time ); } last_frame = 0; return res; } static esp_err_t cmd_handler(httpd_req_t *req){ char* buf; size_t buf_len; char variable[128] = {0,}; char value[128] = {0,}; String myCmd = ""; buf_len = httpd_req_get_url_query_len(req) + 1; if (buf_len > 1) { buf = (char*)malloc(buf_len); if(!buf){ httpd_resp_send_500(req); return ESP_FAIL; } if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK && httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) { } else { myCmd = String(buf); } } } else { httpd_resp_send_404(req); return ESP_FAIL; } Feedback="";Command="";cmd="";P1="";P2="";P3="";P4="";P5="";P6="";P7="";P8="";P9=""; ReceiveState=0,cmdState=1,strState=1,questionstate=0,equalstate=0,semicolonstate=0; if (myCmd.length()>0) { myCmd = "?"+myCmd; for (int i=0;i<myCmd.length();i++) { getCommand(char(myCmd.charAt(i))); } } if (cmd.length()>0) { Serial.println(""); //Serial.println("Command: "+Command); Serial.println("cmd= "+cmd+" ,P1= "+P1+" ,P2= "+P2+" ,P3= "+P3+" ,P4= "+P4+" ,P5= "+P5+" ,P6= "+P6+" ,P7= "+P7+" ,P8= "+P8+" ,P9= "+P9); Serial.println(""); if (cmd=="your cmd") { // You can do anything // Feedback="<font color=\"red\">Hello World</font>"; } else if (cmd=="ip") { Feedback="AP IP: "+WiFi.softAPIP().toString(); Feedback+="<br>"; Feedback+="STA IP: "+WiFi.localIP().toString(); } else if (cmd=="mac") { Feedback="STA MAC: "+WiFi.macAddress(); } else if (cmd=="restart") { ESP.restart(); } else if (cmd=="digitalwrite") { ledcDetachPin(P1.toInt()); pinMode(P1.toInt(), OUTPUT); digitalWrite(P1.toInt(), P2.toInt()); } else if (cmd=="digitalread") { Feedback=String(digitalRead(P1.toInt())); } else if (cmd=="analogwrite") { if (P1=="4") { ledcAttachPin(4, 4); ledcSetup(4, 5000, 8); ledcWrite(4,P2.toInt()); } else { ledcAttachPin(P1.toInt(), 9); ledcSetup(9, 5000, 8); ledcWrite(9,P2.toInt()); } } else if (cmd=="analogread") { Feedback=String(analogRead(P1.toInt())); } else if (cmd=="touchread") { Feedback=String(touchRead(P1.toInt())); } else if (cmd=="flash") { ledcAttachPin(4, 4); ledcSetup(4, 5000, 8); int val = P1.toInt(); ledcWrite(4,val); } else if (cmd=="serial") { if (P1!=""&P1!="stop") Serial.println(P1); if (P2!=""&P2!="stop") Serial.println(P2); Serial.println(); } else { Feedback="Command is not defined"; } if (Feedback=="") Feedback=Command; const char *resp = Feedback.c_str(); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); return httpd_resp_send(req, resp, strlen(resp)); } else { int val = atoi(value); sensor_t * s = esp_camera_sensor_get(); int res = 0; if(!strcmp(variable, "framesize")) { if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val); } else if(!strcmp(variable, "quality")) res = s->set_quality(s, val); else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val); else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val); else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val); else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val); else if(!strcmp(variable, "flash")) { ledcAttachPin(4, 4); ledcSetup(4, 5000, 8); ledcWrite(4,val); } else { res = -1; } if(res){ return httpd_resp_send_500(req); } if (buf) { Feedback = String(buf); const char *resp = Feedback.c_str(); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); return httpd_resp_send(req, resp, strlen(resp)); } else { httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); return httpd_resp_send(req, NULL, 0); } } } static esp_err_t status_handler(httpd_req_t *req){ static char json_response[1024]; sensor_t * s = esp_camera_sensor_get(); char * p = json_response; *p++ = '{'; p+=sprintf(p, "\"flash\":%d,", 0); p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); p+=sprintf(p, "\"quality\":%u,", s->status.quality); p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); p+=sprintf(p, "\"contrast\":%d,", s->status.contrast); p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror); p+=sprintf(p, "\"vflip\":%u", s->status.vflip); *p++ = '}'; *p++ = 0; httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); return httpd_resp_send(req, json_response, strlen(json_response)); } static const char PROGMEM INDEX_HTML[] = R"rawliteral(<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <meta http-equiv="Access-Control-Allow-Headers" content="Origin, X-Requested-With, Content-Type, Accept"> <meta http-equiv="Access-Control-Allow-Methods" content="GET,POST,PUT,DELETE,OPTIONS"> <meta http-equiv="Access-Control-Allow-Origin" content="*"> <title>Teachable Machine-Nitesh Suthar</title> <style> body{font-family:Arial,Helvetica,sans-serif;background:#181818;color:#EFEFEF;font-size:16px}h2{font-size:18px}section.main{display:flex}#menu,section.main{flex-direction:column}#menu{display:flex;flex-wrap:nowrap;min-width:340px;background:#363636;padding:8px;border-radius:4px;margin-top:-10px;margin-right:10px}#content{display:flex;flex-wrap:wrap;align-items:stretch}figure{padding:0;margin:0;-webkit-margin-before:0;margin-block-start:0;-webkit-margin-after:0;margin-block-end:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:0;margin-inline-end:0}figure img{display:block;width:100%;height:auto;border-radius:4px;margin-top:8px}@media (min-width: 800px) and (orientation:landscape){#content{display:flex;flex-wrap:nowrap;align-items:stretch}figure img{display:block;max-width:100%;max-height:calc(100vh - 40px);width:auto;height:auto}figure{padding:0;margin:0;-webkit-margin-before:0;margin-block-start:0;-webkit-margin-after:0;margin-block-end:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:0;margin-inline-end:0}}section#buttons{display:flex;flex-wrap:nowrap;justify-content:space-between}#nav-toggle{cursor:pointer;display:block}#nav-toggle-cb{outline:0;opacity:0;width:0;height:0}#nav-toggle-cb:checked+#menu{display:none}.input-group{display:flex;flex-wrap:nowrap;line-height:22px;margin:5px 0}.input-group>label{display:inline-block;padding-right:10px;min-width:47%}.input-group input,.input-group select{flex-grow:1}.range-max,.range-min{display:inline-block;padding:0 5px}button{display:block;margin:5px;padding:0 12px;border:0;line-height:28px;cursor:pointer;color:#fff;background:#ff3034;border-radius:5px;font-size:16px;outline:0}button:hover{background:#ff494d}button:active{background:#f21c21}button.disabled{cursor:default;background:#a0a0a0}input[type=range]{-webkit-appearance:none;width:100%;height:22px;background:#363636;cursor:pointer;margin:0}input[type=range]:focus{outline:0}input[type=range]::-webkit-slider-runnable-track{width:100%;height:2px;cursor:pointer;background:#EFEFEF;border-radius:0;border:0 solid #EFEFEF}input[type=range]::-webkit-slider-thumb{border:1px solid rgba(0,0,30,0);height:22px;width:22px;border-radius:50px;background:#ff3034;cursor:pointer;-webkit-appearance:none;margin-top:-11.5px}input[type=range]:focus::-webkit-slider-runnable-track{background:#EFEFEF}input[type=range]::-moz-range-track{width:100%;height:2px;cursor:pointer;background:#EFEFEF;border-radius:0;border:0 solid #EFEFEF}input[type=range]::-moz-range-thumb{border:1px solid rgba(0,0,30,0);height:22px;width:22px;border-radius:50px;background:#ff3034;cursor:pointer}input[type=range]::-ms-track{width:100%;height:2px;cursor:pointer;background:0 0;border-color:transparent;color:transparent}input[type=range]::-ms-fill-lower{background:#EFEFEF;border:0 solid #EFEFEF;border-radius:0}input[type=range]::-ms-fill-upper{background:#EFEFEF;border:0 solid #EFEFEF;border-radius:0}input[type=range]::-ms-thumb{border:1px solid rgba(0,0,30,0);height:22px;width:22px;border-radius:50px;background:#ff3034;cursor:pointer;height:2px}input[type=range]:focus::-ms-fill-lower{background:#EFEFEF}input[type=range]:focus::-ms-fill-upper{background:#363636}.switch{display:block;position:relative;line-height:22px;font-size:16px;height:22px}.switch input{outline:0;opacity:0;width:0;height:0}.slider{width:50px;height:22px;border-radius:22px;cursor:pointer;background-color:grey}.slider,.slider:before{display:inline-block;transition:.4s}.slider:before{position:relative;content:"";border-radius:50%;height:16px;width:16px;left:4px;top:3px;background-color:#fff}input:checked+.slider{background-color:#ff3034}input:checked+.slider:before{-webkit-transform:translateX(26px);transform:translateX(26px)}select{border:1px solid #363636;font-size:14px;height:22px;outline:0;border-radius:5px}.image-container{position:relative;min-width:160px}.close{position:absolute;right:5px;top:5px;background:#ff3034;width:16px;height:16px;border-radius:100px;color:#fff;text-align:center;line-height:18px;cursor:pointer}.hidden{display:none} </style> <script src="https:\/\/ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> <script src="https:\/\/cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.3.1/dist/tf.min.js"></script> <script src="https:\/\/cdn.jsdelivr.net/npm/@teachablemachine/image@0.8/dist/teachablemachine-image.min.js"></script> <script src="https:\/\/cdn.jsdelivr.net/npm/@teachablemachine/pose@0.8/dist/teachablemachine-pose.min.js"></script> </head> <body> <section class="main"> <figure> <div id="stream-container" class="image-container hidden"> <div class="close" id="close-stream" style="display:none">×</div> <img id="stream" src="" style="display:none" crossorigin="anonymous"> <canvas id="canvas" width="0" height="0"></canvas> </div> </figure> <section id="buttons"> <table> <tr><td><button id="restart" onclick="try{fetch(document.location.origin+'/control?restart');}catch(e){}">Restart</button></td><td><button id="get-still" style="display:none">Get Still</button></td><td><button id="toggle-stream" style="display:none"></td></tr> </table> </section> <div id="logo"> <label for="nav-toggle-cb" id="nav-toggle">☰ Toggle settings</label> </div> <div id="content"> <div id="sidebar"> <input type="checkbox" id="nav-toggle-cb"> <nav id="menu"> <div class="input-group"> <label for="kind">Kind</label> <select id="kind"> <option value="image" selected>image</option> <option value="pose">pose</option> </select> </div> <div class="input-group" style="display:none;"> <label for="modelPath">Model Path</label> <input type="text" id="modelPath" value=https://teachablemachine.withgoogle.com/models/KhigiCCoU/> </div> <div class="input-group" style="display:none;"> <label for="btnModel"></label> <button type="button" id="btnModel" onclick="LoadModel();">Start Recognition</button> </div> <div class="input-group" id="flash-group"> <label for="flash">Flash</label> <div class="range-min">0</div> <input type="range" id="flash" min="0" max="255" value="0" class="default-action"> <div class="range-max">255</div> </div> <div class="input-group" id="framesize-group"> <label for="framesize">Resolution</label> <select id="framesize" class="default-action"> <option value="10">UXGA(1600x1200)</option> <option value="9">SXGA(1280x1024)</option> <option value="8">XGA(1024x768)</option> <option value="7">SVGA(800x600)</option> <option value="6">VGA(640x480)</option> <option value="5" selected="selected">CIF(400x296)</option> <option value="4">QVGA(320x240)</option> <option value="3">HQVGA(240x176)</option> <option value="0">QQVGA(160x120)</option> </select> </div> <div class="input-group" id="quality-group"> <label for="quality">Quality</label> <div class="range-min">10</div> <input type="range" id="quality" min="10" max="63" value="10" class="default-action"> <div class="range-max">63</div> </div> <div class="input-group" id="brightness-group"> <label for="brightness">Brightness</label> <div class="range-min">-2</div> <input type="range" id="brightness" min="-2" max="2" value="0" class="default-action"> <div class="range-max">2</div> </div> <div class="input-group" id="contrast-group"> <label for="contrast">Contrast</label> <div class="range-min">-2</div> <input type="range" id="contrast" min="-2" max="2" value="0" class="default-action"> <div class="range-max">2</div> </div> <div class="input-group" id="hmirror-group"> <label for="hmirror">H-Mirror</label> <div class="switch"> <input id="hmirror" type="checkbox" class="default-action" checked="checked"> <label class="slider" for="hmirror"></label> </div> </div> <div class="input-group" id="vflip-group"> <label for="vflip">V-Flip</label> <div class="switch"> <input id="vflip" type="checkbox" class="default-action" checked="checked"> <label class="slider" for="vflip"></label> </div> </div> </nav> </div> </div> </section> <br> <div id="result" style="color:red"><div> <script> document.addEventListener('DOMContentLoaded', function (event) { var baseHost = document.location.origin var streamUrl = baseHost + ':81' const hide = el => { el.classList.add('hidden') } const show = el => { el.classList.remove('hidden') } const disable = el => { el.classList.add('disabled') el.disabled = true } const enable = el => { el.classList.remove('disabled') el.disabled = false } const updateValue = (el, value, updateRemote) => { updateRemote = updateRemote == null ? true : updateRemote let initialValue if (el.type === 'checkbox') { initialValue = el.checked value = !!value el.checked = value } else { initialValue = el.value el.value = value } if (updateRemote && initialValue !== value) { updateConfig(el); } } function updateConfig (el) { let value switch (el.type) { case 'checkbox': value = el.checked ? 1 : 0 break case 'range': case 'select-one': value = el.value break case 'button': case 'submit': value = '1' break default: return } const query = `${baseHost}/control?var=${el.id}&val=${value}` fetch(query) .then(response => { console.log(`request to ${query} finished, status: ${response.status}`) }) } document .querySelectorAll('.close') .forEach(el => { el.onclick = () => { hide(el.parentNode) } }) // read initial values fetch(`${baseHost}/status`) .then(function (response) { return response.json() }) .then(function (state) { document .querySelectorAll('.default-action') .forEach(el => { updateValue(el, state[el.id], false) }) }) const view = document.getElementById('stream') const viewContainer = document.getElementById('stream-container') const stillButton = document.getElementById('get-still') const streamButton = document.getElementById('toggle-stream') const closeButton = document.getElementById('close-stream') const stopStream = () => { //window.stop(); view.src=""; streamButton.innerHTML = 'Start Stream' } const startStream = () => { view.src = `${streamUrl}/stream` show(viewContainer) streamButton.innerHTML = 'Stop Stream' } // Attach actions to buttons stillButton.onclick = () => { stopStream() try{ view.src = `${baseHost}/capture?_cb=${Date.now()}` } catch(e) { view.src = `${baseHost}/capture?_cb=${Date.now()}` } show(viewContainer) } closeButton.onclick = () => { stopStream() hide(viewContainer) } streamButton.onclick = () => { const streamEnabled = streamButton.innerHTML === 'Stop Stream' if (streamEnabled) { stopStream() } else { startStream() } } // Attach default on change action document .querySelectorAll('.default-action') .forEach(el => { el.onchange = () => updateConfig(el) }) }) </script> <script> var getStill = document.getElementById('get-still'); var ShowImage = document.getElementById('stream'); var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); var modelPath = document.getElementById('modelPath'); var result = document.getElementById('result'); var kind = document.getElementById('kind'); let Model; async function LoadModel() { if (modelPath.value=="") { result.innerHTML = "Please input model path."; return; } result.innerHTML = "Please wait for loading model."; const URL = modelPath.value; const modelURL = URL + "model.json"; const metadataURL = URL + "metadata.json"; if (kind.value=="image") { Model = await tmImage.load(modelURL, metadataURL); } else if (kind.value=="pose") { Model = await tmPose.load(modelURL, metadataURL); } maxPredictions = Model.getTotalClasses(); result.innerHTML = ""; getStill.style.display = "block"; getStill.click(); } LoadModel(); async function predict() { var data = ""; var maxClassName = ""; var maxProbability = ""; canvas.setAttribute("width", ShowImage.width); canvas.setAttribute("height", ShowImage.height); context.drawImage(ShowImage, 0, 0, ShowImage.width, ShowImage.height); if (kind.value=="image") var prediction = await Model.predict(canvas); else if (kind.value=="pose") { var { pose, posenetOutput } = await Model.estimatePose(canvas); var prediction = await Model.predict(posenetOutput); } if (maxPredictions>0) { for (let i = 0; i < maxPredictions; i++) { if (i==0) { maxClassName = prediction[i].className; maxProbability = prediction[i].probability; a = prediction[1].probability.toFixed(2); } else { if (prediction[i].probability>maxProbability) { maxClassName