From 258751b5b615ffdc2e0f43f8815b6ffbaeb3da03 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 20 Jun 2019 04:43:46 +0200 Subject: [PATCH] Add Save button to capture window (#78) * Add "Save" button to capture window Allows saving images from both stills and streams by clicking a button. * Make image filenames be the date and time of capture * Add script to compress the html pages for embedding --- .../camera_web_server/main/app_httpd.c | 3 ++ .../main/www/compress_pages.sh | 7 ++++ .../main/www/index_ov2640.html | 35 +++++++++++++++++- .../main/www/index_ov2640.html.gz | Bin 4316 -> 4679 bytes .../main/www/index_ov3660.html | 35 +++++++++++++++++- .../main/www/index_ov3660.html.gz | Bin 4408 -> 4776 bytes 6 files changed, 76 insertions(+), 4 deletions(-) create mode 100755 examples/single_chip/camera_web_server/main/www/compress_pages.sh diff --git a/examples/single_chip/camera_web_server/main/app_httpd.c b/examples/single_chip/camera_web_server/main/app_httpd.c index cb1a1bd..fd5145f 100644 --- a/examples/single_chip/camera_web_server/main/app_httpd.c +++ b/examples/single_chip/camera_web_server/main/app_httpd.c @@ -252,6 +252,7 @@ static esp_err_t capture_handler(httpd_req_t *req){ 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", "*"); #if CONFIG_ESP_FACE_DETECT_ENABLED size_t out_len, out_width, out_height; @@ -357,6 +358,8 @@ static esp_err_t stream_handler(httpd_req_t *req){ return res; } + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + while(true){ #if CONFIG_ESP_FACE_DETECT_ENABLED detected = false; diff --git a/examples/single_chip/camera_web_server/main/www/compress_pages.sh b/examples/single_chip/camera_web_server/main/www/compress_pages.sh new file mode 100755 index 0000000..f7c13fb --- /dev/null +++ b/examples/single_chip/camera_web_server/main/www/compress_pages.sh @@ -0,0 +1,7 @@ +#!/bin/bash +for file in `ls *.html`; do + echo "Compressing: $file" + cp "$file" "copy_$file" && \ + gzip -f "$file" && \ + mv "copy_$file" "$file" +done diff --git a/examples/single_chip/camera_web_server/main/www/index_ov2640.html b/examples/single_chip/camera_web_server/main/www/index_ov2640.html index 09024d8..8460f32 100644 --- a/examples/single_chip/camera_web_server/main/www/index_ov2640.html +++ b/examples/single_chip/camera_web_server/main/www/index_ov2640.html @@ -135,7 +135,7 @@ padding: 0 5px } - button { + button, .button { display: block; margin: 5px; padding: 0 12px; @@ -149,6 +149,17 @@ outline: 0 } + .save { + position: absolute; + right: 25px; + top: 0px; + height: 16px; + line-height: 16px; + padding: 0 4px; + text-decoration: none; + cursor: pointer + } + button:hover { background: #ff494d } @@ -543,8 +554,9 @@
@@ -657,6 +669,7 @@ document.addEventListener('DOMContentLoaded', function (event) { const streamButton = document.getElementById('toggle-stream') const enrollButton = document.getElementById('face_enroll') const closeButton = document.getElementById('close-stream') + const saveButton = document.getElementById('save-still') const stopStream = () => { window.stop(); @@ -694,6 +707,24 @@ document.addEventListener('DOMContentLoaded', function (event) { updateConfig(enrollButton) } + saveButton.onclick = () => { + var canvas = document.createElement("canvas"); + canvas.width = view.width; + canvas.height = view.height; + document.body.appendChild(canvas); + var context = canvas.getContext('2d'); + context.drawImage(view,0,0); + try { + var dataURL = canvas.toDataURL('image/jpeg'); + saveButton.href = dataURL; + var d = new Date(); + saveButton.download = d.getFullYear() + ("0"+(d.getMonth()+1)).slice(-2) + ("0" + d.getDate()).slice(-2) + ("0" + d.getHours()).slice(-2) + ("0" + d.getMinutes()).slice(-2) + ("0" + d.getSeconds()).slice(-2) + ".jpg"; + } catch (e) { + console.error(e); + } + canvas.parentNode.removeChild(canvas); + } + // Attach default on change action document .querySelectorAll('.default-action') diff --git a/examples/single_chip/camera_web_server/main/www/index_ov2640.html.gz b/examples/single_chip/camera_web_server/main/www/index_ov2640.html.gz index f6e19a456a57ee655f704e3db8bafb8ac02b6148..4d8c535114e958efdf87b84cf4e3be91649c2fe2 100644 GIT binary patch literal 4679 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 literal 4316 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
@@ -684,6 +696,7 @@ document.addEventListener('DOMContentLoaded', function (event) { const streamButton = document.getElementById('toggle-stream') const enrollButton = document.getElementById('face_enroll') const closeButton = document.getElementById('close-stream') + const saveButton = document.getElementById('save-still') const stopStream = () => { window.stop(); @@ -721,6 +734,24 @@ document.addEventListener('DOMContentLoaded', function (event) { updateConfig(enrollButton) } + saveButton.onclick = () => { + var canvas = document.createElement("canvas"); + canvas.width = view.width; + canvas.height = view.height; + document.body.appendChild(canvas); + var context = canvas.getContext('2d'); + context.drawImage(view,0,0); + try { + var dataURL = canvas.toDataURL('image/jpeg'); + saveButton.href = dataURL; + var d = new Date(); + saveButton.download = d.getFullYear() + ("0"+(d.getMonth()+1)).slice(-2) + ("0" + d.getDate()).slice(-2) + ("0" + d.getHours()).slice(-2) + ("0" + d.getMinutes()).slice(-2) + ("0" + d.getSeconds()).slice(-2) + ".jpg"; + } catch (e) { + console.error(e); + } + canvas.parentNode.removeChild(canvas); + } + // Attach default on change action document .querySelectorAll('.default-action') diff --git a/examples/single_chip/camera_web_server/main/www/index_ov3660.html.gz b/examples/single_chip/camera_web_server/main/www/index_ov3660.html.gz index cf63cd48dcc2253af1d6adb243e129af8eefe180..411189c4d218441b4bc7e63b809c6283550b1827 100644 GIT binary patch literal 4776 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