|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +# https://developers.google.com/web/fundamentals/media/capturing-images |
| 4 | +# https://github.com/jhuckaby/webcamjs |
| 5 | +# https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob |
| 6 | +# https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL |
| 7 | +# https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL |
| 8 | + |
| 9 | +from flask import Flask, render_template_string, request, make_response |
| 10 | +import cv2 |
| 11 | +import numpy as np |
| 12 | +import datetime |
| 13 | + |
| 14 | +app = Flask(__name__) |
| 15 | + |
| 16 | + |
| 17 | +@app.route('/') |
| 18 | +def index(): |
| 19 | + return render_template_string(''' |
| 20 | +<video id="video" width="320" height="240" autoplay style="background-color: grey"></video> |
| 21 | +<button id="send">Take & Send Photo</button> |
| 22 | +<canvas id="canvas" width="320" height="240" style="background-color: grey"></canvas> |
| 23 | +<img id="image" src="" width="320" height="240" style="background-color: grey"></img> |
| 24 | +
|
| 25 | +<script> |
| 26 | +
|
| 27 | +// Elements for taking the snapshot |
| 28 | +var video = document.getElementById('video'); |
| 29 | +
|
| 30 | +// Element to display snapshot |
| 31 | +
|
| 32 | + // you need canvas to get image - canvas can be hiden using `createElement("canvas")` |
| 33 | + // var canvas = document.createElement("canvas"); |
| 34 | + |
| 35 | +var canvas = document.getElementById('canvas'); |
| 36 | +var context = canvas.getContext('2d'); |
| 37 | +
|
| 38 | +var image = document.getElementById('image'); |
| 39 | +var image64 = document.getElementById('image64'); |
| 40 | +
|
| 41 | +// Get access to the camera! |
| 42 | +if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { |
| 43 | + // Not adding `{ audio: true }` since we only want video now |
| 44 | + navigator.mediaDevices.getUserMedia({ video: true }).then(function(stream) { |
| 45 | + //video.src = window.URL.createObjectURL(stream); |
| 46 | + video.srcObject = stream; |
| 47 | + video.play(); |
| 48 | + |
| 49 | + //console.log('setInterval') |
| 50 | + window.setInterval(function() { |
| 51 | + //context.drawImage(video, 0, 0); |
| 52 | + context.drawImage(video, 0, 0, 320, 240); // better use size because camera may gives data in different size then <video> is displaying |
| 53 | + |
| 54 | + image64.src = canvas.toDataURL(); |
| 55 | + canvas.toBlob(upload, "image/jpeg"); |
| 56 | + }, 100); |
| 57 | + }); |
| 58 | +} |
| 59 | +
|
| 60 | +// Trigger photo take |
| 61 | +document.getElementById("send").addEventListener("click", function() { |
| 62 | + //context.drawImage(video, 0, 0); |
| 63 | + context.drawImage(video, 0, 0, 320, 240); // better use size because camera may gives data in different size then <video> is displaying |
| 64 | + image64.src = canvas.toDataURL(); |
| 65 | +
|
| 66 | + canvas.toBlob(upload, "image/jpeg"); |
| 67 | +}); |
| 68 | +
|
| 69 | +function upload(file) { |
| 70 | +
|
| 71 | + // create form |
| 72 | + var formdata = new FormData(); |
| 73 | + |
| 74 | + // add file to form |
| 75 | + formdata.append("snap", file); |
| 76 | + |
| 77 | + // create AJAX connection |
| 78 | + var xhr = new XMLHttpRequest(); |
| 79 | + xhr.open("POST", "{{ url_for('upload') }}", true); |
| 80 | + xhr.responseType = 'blob'; |
| 81 | + // define function which get response |
| 82 | + xhr.onload = function() { |
| 83 | + |
| 84 | + if(this.status = 200) { |
| 85 | + //console.log(this.response); |
| 86 | + } else { |
| 87 | + console.error(xhr); |
| 88 | + } |
| 89 | + |
| 90 | + //alert(this.response); |
| 91 | +
|
| 92 | + //img.onload = function(){ |
| 93 | + // ctx.drawImage(img, 0, 0) |
| 94 | + //} |
| 95 | +
|
| 96 | + image.src = URL.createObjectURL(this.response); // blob |
| 97 | + }; |
| 98 | + |
| 99 | + // send form in AJAX |
| 100 | + xhr.send(formdata); |
| 101 | + |
| 102 | + //image.src = URL.createObjectURL(file); |
| 103 | +} |
| 104 | +
|
| 105 | + |
| 106 | +</script> |
| 107 | +''') |
| 108 | + |
| 109 | +def send_file_data(data, mimetype='image/jpeg', filename='output.jpg'): |
| 110 | + # https://stackoverflow.com/questions/11017466/flask-to-return-image-stored-in-database/11017839 |
| 111 | + |
| 112 | + response = make_response(data) |
| 113 | + response.headers.set('Content-Type', mimetype) |
| 114 | + response.headers.set('Content-Disposition', 'attachment', filename=filename) |
| 115 | + |
| 116 | + return response |
| 117 | + |
| 118 | +@app.route('/upload', methods=['GET', 'POST']) |
| 119 | +def upload(): |
| 120 | + if request.method == 'POST': |
| 121 | + #fs = request.files['snap'] # it raise error when there is no `snap` in form |
| 122 | + fs = request.files.get('snap') |
| 123 | + if fs: |
| 124 | + #print('FileStorage:', fs) |
| 125 | + #print('filename:', fs.filename) |
| 126 | + |
| 127 | + # https://stackoverflow.com/questions/27517688/can-an-uploaded-image-be-loaded-directly-by-cv2 |
| 128 | + # https://stackoverflow.com/a/11017839/1832058 |
| 129 | + img = cv2.imdecode(np.frombuffer(fs.read(), np.uint8), cv2.IMREAD_UNCHANGED) |
| 130 | + #print('Shape:', img.shape) |
| 131 | + # rectangle(image, start_point, end_point, color, thickness) |
| 132 | + img = cv2.rectangle(img, (20, 20), (300, 220), (0, 0, 255), 2) |
| 133 | + |
| 134 | + text = datetime.datetime.now().strftime('%Y.%m.%d %H.%M.%S.%f') |
| 135 | + img = cv2.putText(img, text, (5, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA) |
| 136 | + #cv2.imshow('image', img) |
| 137 | + #cv2.waitKey(1) |
| 138 | + |
| 139 | + # https://jdhao.github.io/2019/07/06/python_opencv_pil_image_to_bytes/ |
| 140 | + ret, buf = cv2.imencode('.jpg', img) |
| 141 | + |
| 142 | + #return f'Got Snap! {img.shape}' |
| 143 | + return send_file_data(buf.tobytes()) |
| 144 | + else: |
| 145 | + return 'You forgot Snap!' |
| 146 | + |
| 147 | + return 'Hello World!' |
| 148 | + |
| 149 | + |
| 150 | +if __name__ == '__main__': |
| 151 | + app.run(debug=True, port=5000) |
| 152 | + |
0 commit comments