首页 > 解决方案 > 如何使用 OpenCV 和 Flask 捕获图像?

问题描述

我正在尝试运行这个网站,该网站使用烧瓶,当你按下下一步时它会拍照,但它只在我第一次访问该 URL 时有效。当我被重定向到相同的网址时,它不再起作用。

请帮助我已经坚持了几个月了,不知道该怎么办了。

from camera import VideoCamera
import cv2
import time

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hello'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///drug1.sqlite'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

med_list = []
timestr = time.strftime("%Y%m%d-%H%M%S")



cam = cv2.VideoCapture(0)
video_stream = VideoCamera()


@app.route('/')
def index():
    return render_template('vid.html')

def gen(camera):
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')

@app.route('/video_feed')
def video_feed():


@app.route("/scan", methods =["POST","GET"])
def scan():
    drug_list = []
    if request.method == 'POST':
        bc = request.form['barcode']
        print(bc)

        if request.form['submit_button'] == 'Next':
            return redirect(url_for('scan2',image=image_link,drug=drug,strength=strength,med_list=med_list))
    return render_template("scan.html")


@app.route("/scan2", methods =["POST","GET"])
def scan2():
    drug = session.get("drug",None)
    image_link = session.get("image_link",None)
    strength = session.get("strength",None)
    
    if request.method == 'POST':
        if request.form['submit_button'] == 'Next':

            retval, frame = cam.read()
            cv2.imwrite('img{}.png'.format(timestr), frame)
            
            return redirect(url_for('scan'))
    return render_template("scan2.html",image=image_link,drug=drug,strength=strength)

标签: pythonopencvflaskcv2

解决方案


如果您想使用用户笔记本电脑中的内置摄像头拍照,那么您必须使用JavaScript,因为只有网络浏览器才能访问用户的摄像头 - 服务器无法访问用户的计算机。

这是显示来自用户相机的流的示例,它可以截屏。但它仍然需要 JavaScript 才能将其发送到服务器。

from flask import Flask, render_template_string


app = Flask(__name__)


@app.route('/')
def index():
    return render_template_string('''
<video id="video" width="640" height="480" autoplay style="background-color: grey"></video>
<button id="snap">Take Photo</button>
<canvas id="canvas" width="640" height="480" style="background-color: grey"></canvas>

<script>

// Elements for taking the snapshot
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

// Get access to the camera!
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    // Not adding `{ audio: true }` since we only want video now
    navigator.mediaDevices.getUserMedia({ video: true }).then(function(stream) {
        //video.src = window.URL.createObjectURL(stream);
        video.srcObject = stream; // assing stream to <video>
        video.play();             // play stream
    });
}

// Trigger photo take
document.getElementById("snap").addEventListener("click", function() {
    context.drawImage(video, 0, 0, 640, 480);  // copy video frame to canvas
});

</script>
''')

    
if __name__ == '__main__':    
    app.run(debug=True)

编辑:

这是可以在服务器上发送图像的示例。

以及我在这段代码中拥有的一些有用的链接

https://developers.google.com/web/fundamentals/media/capturing-images

https://github.com/jhuckaby/webcamjs

https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob

from flask import Flask, render_template_string, request


app = Flask(__name__)


@app.route('/')
def index():
    return render_template_string('''
<video id="video" width="640" height="480" autoplay style="background-color: grey"></video>
<button id="send">Take & Send Photo</button>
<canvas id="canvas" width="640" height="480" style="background-color: grey"></canvas>

<script>

// Elements for taking the snapshot
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

// Get access to the camera!
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    // Not adding `{ audio: true }` since we only want video now
    navigator.mediaDevices.getUserMedia({ video: true }).then(function(stream) {
        //video.src = window.URL.createObjectURL(stream);
        video.srcObject = stream;
        video.play();
    });
}

// Trigger photo take
document.getElementById("send").addEventListener("click", function() {
    context.drawImage(video, 0, 0, 640, 480); // copy frame from <video>
    canvas.toBlob(upload, "image/jpeg");  // convert to file and execute function `upload`
});

function upload(file) {
    // create form and append file
    var formdata =  new FormData();
    formdata.append("snap", file);
    
    // create AJAX requests POST with file
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "{{ url_for('upload') }}", true);
    xhr.onload = function() {
        if(this.status = 200) {
            console.log(this.response);
        } else {
            console.error(xhr);
        }
        alert(this.response);
    };
    xhr.send(formdata);
}

</script>
''')

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        #fs = request.files['snap'] # it raise error when there is no `snap` in form
        fs = request.files.get('snap')
        if fs:
            print('FileStorage:', fs)
            print('filename:', fs.filename)
            fs.save('image.jpg')
            return 'Got Snap!'
        else:
            return 'You forgot Snap!'
    
    return 'Hello World!'
    
    
if __name__ == '__main__':    
    app.run(debug=True, port=5000)

编辑:

我找到了将流发送到服务器的示例,它使用当前时间绘制红色边框和文本并将其发送回浏览器。

# https://developers.google.com/web/fundamentals/media/capturing-images
# https://github.com/jhuckaby/webcamjs
# https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
# https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
# https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL

from flask import Flask, render_template_string, request, make_response
import cv2
import numpy as np
import datetime

app = Flask(__name__)


@app.route('/')
def index():
    return render_template_string('''
<video id="video" width="320" height="240" autoplay style="background-color: grey"></video>
<button id="send">Take & Send Photo</button>
<canvas id="canvas" width="320" height="240" style="background-color: grey"></canvas>
<img id="image" src="" width="320" height="240" style="background-color: grey"></img>
<img id="image64" src="" width="320" height="240" style="background-color: grey"></img>

<script>

// Elements for taking the snapshot
var video = document.getElementById('video');

// Element to display snapshot

    // you need canvas to get image. canvas can be hidden using `createElement("canvas")`
    // var canvas = document.createElement("canvas");
   
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var image = document.getElementById('image');
var image64 = document.getElementById('image64');

// Get access to the camera!
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    // Not adding `{ audio: true }` since we only want video now
    navigator.mediaDevices.getUserMedia({ video: true }).then(function(stream) {
        //video.src = window.URL.createObjectURL(stream);
        video.srcObject = stream;
        video.play();
    
        //console.log('setInterval')
        window.setInterval(function() {
            //context.drawImage(video, 0, 0);
            context.drawImage(video, 0, 0, 320, 240); // better use size because camera may gives data in different size then <video> is displaying
            
            image64.src = canvas.toDataURL();  
            canvas.toBlob(upload, "image/jpeg");
        }, 100);    
    });
}

// Trigger photo take
document.getElementById("send").addEventListener("click", function() {
    //context.drawImage(video, 0, 0);
    context.drawImage(video, 0, 0, 320, 240); // better use size because camera may gives data in different size then <video> is displaying
    image64.src = canvas.toDataURL();  

    canvas.toBlob(upload, "image/jpeg");
});

function upload(file) {

    // create form 
    var formdata =  new FormData();
    
    // add file to form
    formdata.append("snap", file);
    
    // create AJAX connection
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "{{ url_for('upload') }}", true);
    xhr.responseType = 'blob';   
    // define function which get response
    xhr.onload = function() {
        
        if(this.status = 200) {
            //console.log(this.response);
        } else {
            console.error(xhr);
        }
        
        //alert(this.response);

        //img.onload = function(){
        //    ctx.drawImage(img, 0, 0)
        //}

        image.src = URL.createObjectURL(this.response); // blob
    };
    
    // send form in AJAX
    xhr.send(formdata);
    
    //image.src = URL.createObjectURL(file);
}

    
</script>
''')

def send_file_data(data, mimetype='image/jpeg', filename='output.jpg'):
    # https://stackoverflow.com/questions/11017466/flask-to-return-image-stored-in-database/11017839
    
    response = make_response(data)
    response.headers.set('Content-Type', mimetype)
    response.headers.set('Content-Disposition', 'attachment', filename=filename)
    
    return response
    
@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        #fs = request.files['snap'] # it raise error when there is no `snap` in form
        fs = request.files.get('snap')
        if fs:
            #print('FileStorage:', fs)
            #print('filename:', fs.filename)
            
            # https://stackoverflow.com/questions/27517688/can-an-uploaded-image-be-loaded-directly-by-cv2
            # https://stackoverflow.com/a/11017839/1832058
            img = cv2.imdecode(np.frombuffer(fs.read(), np.uint8), cv2.IMREAD_UNCHANGED)
            #print('Shape:', img.shape)
            # rectangle(image, start_point, end_point, color, thickness)
            img = cv2.rectangle(img, (20, 20), (300, 220), (0, 0, 255), 2)
            
            text = datetime.datetime.now().strftime('%Y.%m.%d %H.%M.%S.%f')
            img = cv2.putText(img, text, (5, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA) 
            #cv2.imshow('image', img)
            #cv2.waitKey(1)
            
            # https://jdhao.github.io/2019/07/06/python_opencv_pil_image_to_bytes/
            ret, buf = cv2.imencode('.jpg', img)
            
            #return f'Got Snap! {img.shape}'
            return send_file_data(buf.tobytes())
        else:
            return 'You forgot Snap!'
    
    return 'Hello World!'
    
    
if __name__ == '__main__':    
    app.run(debug=True, port=5000)

相机HTTP仅适用于地址127.0.0.1

0.0.0.0它需要,HTTPS但它需要。cert您可以使用测试,ssl_context='adhoc'但浏览器会警告您这是不安全的连接,您必须接受它。

app.run(host='0.0.0.0', port=5000, ssl_context='adhoc')

推荐阅读