简介
将图像发布到使用Flask制成的Web服务器时,将使用OpenCV处理该图像,并将结果显示在浏览器上。
我很难用axios发布图像并将其反映在View中,所以记下
粗流量
步骤
https://github.com/toshi1127/My-tutorial-flask-and-react.git
使用示例
应用服务器代码
server.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import os import io import time import numpy as np import cv2 import base64 from flask import Flask, render_template, request, redirect, url_for, Response from io import BytesIO from werkzeug import secure_filename import math import sys app = Flask(__name__) UPLOAD_FOLDER = './uploads' IMAGE_FOLDER = './image' ALLOWED_EXTENSIONS = set(['png', 'jpg', 'PNG', 'JPG']) IMAGE_WIDTH = 640 app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['IMAGE_FOLDER'] = IMAGE_FOLDER app.config['SECRET_KEY'] = os.urandom(24) def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS @app.route('/') def index(): return render_template('index.html') @app.route('/send', methods=['GET', 'POST']) def send(): if request.method == 'POST': img_file = request.files['img_file'] # 変なファイル弾き if img_file and allowed_file(img_file.filename): filename = secure_filename(img_file.filename) else: return ''' <p>許可されていない拡張子です</p> ''' # BytesIOで読み込んでOpenCVで扱える型にする f = img_file.stream.read() bin_data = io.BytesIO(f) file_bytes = np.asarray(bytearray(bin_data.read()), dtype=np.uint8) img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR) # ここでOpenCVで好きな処理をする raw_img = cv2.resize(img, (IMAGE_WIDTH, int(IMAGE_WIDTH*img.shape[0]/img.shape[1]))) gray_img = cv2.cvtColor(raw_img, cv2.COLOR_BGR2GRAY) retval, buffer = cv2.imencode('.png', gray_img) jpg_as_text = base64.b64encode(buffer) return Response(response=jpg_as_text, content_type='image/jpeg') else: return redirect(url_for('index')) if __name__ == '__main__': app.debug = True app.run() |
应用程序客户端代码流
前端/ index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import React, { Component } from 'react'; import ReactDOM from 'react-dom' import { BrowserRouter, Route, Switch } from 'react-router-dom' import App from './app'; const ImageApp = () => ( <BrowserRouter> <div> <Route path='/' component={App} /> </div> </BrowserRouter> ) ReactDOM.render( <ImageApp/>,document.getElementById('root') ); |
前端/ app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import React from "react"; import ImageSend from './container/imageSend'; export default class App extends React.Component { constructor(props) { super(props); } render() { return ( <div> <ImageSend/> </div> ); } } |
容器/imageSend.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | import React from "react"; import axios from "axios"; import Loading from "../component/roadComponent"; export default class ImageSend extends React.Component { constructor(props) { super(props); this.state = { imageDate: null, receiveDate: null, isLoad: false }; this.onSubmit = this.onSubmit.bind(this); this.onInput = this.onInput.bind(this); } onSubmit(e) { const { imageDate } = this.state; const formData = new FormData(); formData.append("img_file", this.state.imageDate, "testImage.png"); this.setState({ isLoad: !this.state.isLoad }); axios({ method: "post", url: "/send", data: formData, config: { headers: { "Content-Type": "multipart/form-data" } } }).then((response) => { this.setState({ receiveDate: response.data, isLoad: !this.state.isLoad }); }); } onInput(e) { const files = e.target.files; this.setState({ imageDate: files[0] }); } render() { const { receiveDate, isLoad } = this.state; if (!isLoad) { return ( <div> <input type="file" name="upfile" id="upfile" accept="image/*" capture="camera" onInput={this.onInput} /> <button onClick={this.onSubmit}>送信</button> {receiveDate && <img src={`data:image/png;base64,${receiveDate}`} />} </div> ); } return <Loading />; } } |
用axios敲击API并绘制,直到返回响应为止。
组件/ roadComponent.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import React from "react"; import styled, { keyframes } from "styled-components"; import { media } from "../utile/Helper"; const rotate360 = keyframes` from { transform: rotate(0); } to { transform: rotate(360deg); } `; export const LoadingContainer = styled.div` display: flex; justify-content: center; align-items: center; width: 100%; height: 400px; `; export const LoadingContent = styled.p` width: 70px; height: 70px; border: 4px solid #00f; border-top-color: #fff; border-radius: 100%; animation: ${rotate360} 0.6s linear infinite; background-color: #fff; box-sizing: border-box; `; const Loading = () => ( <LoadingContainer> <LoadingContent /> </LoadingContainer> ); export default Loading; |
templates.html
1 2 3 4 5 6 7 8 9 10 11 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> File </head> <body> <div id="root"></div> <script src="./static/bundle.js" ></script> </body> </html> |
执行结果
