Flask Web开发:图片处理界面

一、问题描述

制作一个抓取检测界面,在网页前端上传图片后,使用jQuery和AJAX将图片传到后端处理,再将处理后的图片返回到界面指定的位置。

初始界面如下:

 点击 选择图片 按钮上传图片后,图片展示在左边区域;点击 开始检测 按钮,将上传的图片传入后端处理后展示在右边区域。完成后界面如下(算法效果还不是太好):

 二、代码实现

2.1 图片上传

本文利用<input>和<label>标签以及jQuery实现图片上传,并展示在界面的 等待上传 区域。

HTML部分

<div class="d-grid" style="margin: 80px 30px">
    <input type="file" name="load_img01" id="load_img01" class="image_load"/>
    <label for="load_img01">选择图片</label>
</div>

<img src="../static/image/loading01.png" alt="等待上传" height="400" width="550" id="img_loaded01"/>

图片上传需要<input type="file"/>标签,之后对其进行美化。

CSS部分:

.image_load {
    width: 0.1px;
    height: 0.1px;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    z-index: -1;
}

.image_load + label {
    height: 38px;
    text-align: center;
    line-height: 38px;
    background-color: #0dcaf0;
    border-color: #0dcaf0;
    display: inline-block;
    border-radius: 3px;
}

.image_load + label:hover {
    background-color: #31d2f2;
    border-color: #25cff2;
    cursor: pointer;
}

JS部分:

// Load image and preview
$(function (){
    "use strict";
    $("#load_img01").change(function(){
        console.log(this.files[0]);
        let objUrl = getObjectURL(this.files[0]) ; // 获取文件信息
        console.log("objUrl = "+objUrl);
        if (objUrl) {
            $("#img_loaded01").attr("src", objUrl);
        }
    });
});


function getObjectURL(file) {
    "use strict";
    let url = null;
    if(Window.createObjectURL !== undefined) {
        url = window.createObjectURL(file) ;
    }else if (window.URL !== undefined) { // mozilla(firefox)
        url = window.URL.createObjectURL(file) ;
    }else if (window.webkitURL !== undefined) { // webkit or chrome
        url = window.webkitURL.createObjectURL(file) ;
    }
    return url ;
}

2.2 图片处理

将上传的图片传到后端进行处理,再将处理后的图片返回到界面指定的位置。这里主要是后端将处理完成后的图片编码成base64的形式发送到前端,再由前端进行解码。

HTML部分:

<button type="button" class="btn btn-info btn-block" onclick="detect()">开始检测</button>

 <img src="../static/image/loading02.png" alt="等待检测" height="400" width="550" id="img_detected"/>

JS部分:

function detect() {
    "use strict";
    let fileObj = $("#load_img01")[0].files[0];
    console.log(fileObj);
    let form = new FormData();
    let img_src;
    form.append("file", fileObj);
    $.ajax({
        type: 'POST',
        url: "imageDetect",
        data: form,
        async: false,
        processData: false,
        contentType: false,
        success: function (res){
            alert("检测完成!");
            console.log(res);
            img_src = "data:image/png;base64,"+res;
            console.log(img_src);
            $("#img_detected").attr("src",img_src);
        },
        error: function (){
            alert("检测失败!");
            console.log("后台处理错误");
        }
    });

}

后端部分:

@app.route("/imageDetect", methods=["POST"])
@torch.no_grad()
def imgDetect():
    image = request.files["file"]
    img_bytes = image.read()
    img_stream = get_detection(image_bytes=img_bytes)
    return img_stream
import io
import base64
from PIL import Image
import matplotlib.pyplot as plt
from torchvision import transforms


# Pre-process image
def transform_image(image_bytes):
    my_transforms = transforms.Compose([transforms.Resize(500),
                                        transforms.CenterCrop(300)])
    image = Image.open(io.BytesIO(image_bytes))

    if image.mode != "RGB":
        raise ValueError("input file does not RGB image...")

    return my_transforms(image)


# Detection
def get_detection(image_bytes):
    image = transform_image(image_bytes)

    fig = plt.figure()
    fig.set_size_inches(550 / 100, 400 / 100)
    ax = fig.add_subplot(1, 1, 1)
    ax.imshow(image)
    ax.axis('off')

    canvas = fig.canvas
    buffer = io.BytesIO()
    canvas.print_png(buffer)
    data = buffer.getvalue()
    buffer.write(data)

    img_stream = base64.b64encode(buffer.getvalue()).decode()

    return img_stream

2.3 代码汇总

文件夹结构:

  • static
    • css
      • detection_image.css
    • js
      • self
        • load_file.js
      • jquery-3.6.1.js
    • plugins
      • Bootstrap
  • templates
    • detection_image.html
  • app.py
  • detect.py
  • requirements.txt

app.py

from flask import Flask, render_template, request
from detect import get_detection

app = Flask(__name__)


@app.route('/')
def main():  # put application's code here
    return render_template('detection_image.html')


@app.route("/imageDetect", methods=["POST"])
def imgDetect():
    image = request.files["file"]
    img_bytes = image.read()
    img_stream = get_detection(image_bytes=img_bytes)
    return img_stream


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

detect.py

import io
import base64
from PIL import Image
import matplotlib.pyplot as plt
from torchvision import transforms


# Pre-process image
def transform_image(image_bytes):
    my_transforms = transforms.Compose([transforms.Resize(500),
                                        transforms.CenterCrop(300)])
    image = Image.open(io.BytesIO(image_bytes))

    if image.mode != "RGB":
        raise ValueError("input file does not RGB image...")

    return my_transforms(image)


# Detection
def get_detection(image_bytes):
    image = transform_image(image_bytes)

    fig = plt.figure()
    fig.set_size_inches(550 / 100, 400 / 100)
    ax = fig.add_subplot(1, 1, 1)
    ax.imshow(image)
    ax.axis('off')

    canvas = fig.canvas
    buffer = io.BytesIO()
    canvas.print_png(buffer)
    data = buffer.getvalue()
    buffer.write(data)

    img_stream = base64.b64encode(buffer.getvalue()).decode()

    return img_stream
detection_image.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>机械臂视觉抓取检测界面</title>

    <!-- Loading Bootstrap css -->
    <link rel="stylesheet" type="text/css" href="../static/plugins/Bootstrap/css/bootstrap.min.css">

    <!-- Loading user-defined css -->
    <link rel="stylesheet" type="text/css" href="../static/css/detection_image.css">

    <!-- Loading js -->
    <script src="../static/js/jquery-3.6.1.js"></script>
    <script src="../static/plugins/Bootstrap/js/bootstrap.bundle.min.js"></script>

</head>
<body>

<header>
    <img src="../static/image/TJ.png" alt="TJ" style="float: left; margin: 15px 15px" height="60.4" width="224.4">
    <h1>抓取检测模型测试界面</h1>
</header>

<div class="main">
    <div class="manipulation container">
        <div class="model_select">
            <form action="">
                <label for="sell" class="form-label" id="font_piggy">选择抓取检测模型:</label>
                <select class="form-select" id="sell" name="sellList">
                    <option value="1">TF-Grasp</option>
                    <option value="2">GG-CNN</option>
                </select>
            </form>
        </div>

        <div class="d-grid" style="margin: 80px 30px">
            <input type="file" name="load_img01" id="load_img01" class="image_load"/>
            <label for="load_img01">选择图片</label>
        </div>

        <div class="d-grid" style="margin: 80px 30px">
            <button type="button" class="btn btn-info btn-block" onclick="detect()">开始检测</button>
        </div>
    </div>

    <div class="display container">
        <div class="return">
            <div style="float: right">
                <a href="/" class="btn btn-info" role="button">返回首页</a>
            </div>

            <div style="float: right; margin-right: 10px">
                <a href="/cameraDetection" class="btn btn-info" role="button">相机检测</a>
            </div>
        </div>

        <div class="pictures_exhibit">
            <div class="picture_l">
                <img src="../static/image/loading01.png" alt="等待上传" height="400" width="550" id="img_loaded01"/>
            </div>
            <div class="picture_r">
                <img src="../static/image/loading02.png" alt="等待检测" height="400" width="550" id="img_detected"/>
            </div>
        </div>

        <div class="generation">
            <button type="button" class="btn btn-info btn-block" style="float: right">保存图片</button>
        </div>
    </div>
</div>

<!-- Loading js -->
<script src="../static/js/self/load_file.js"></script>

</body>
</html>
detection_image.css
/* layout */
* {
    margin: 0;
    padding: 0;
}

header {
    position: absolute;
    width: 100%;
    height: 12%;
    text-align: center;
    background-color: #2980b9;
}

.main {
    position: absolute;
    width: 100%;
    height: 88%;
    background: #3498db;
    bottom: 0;
}

.main:after {
    content: "";
    display: block;
    clear: both;
}

.main .manipulation {
    height: 100%;
    width: 20%;
    float: left;
    background: #2980b9;
}

.main .display {
    height: 100%;
    width: 80%;
    float: left;
    background: #3498db;
}

#font_piggy {
    color: #ecf0f1;
    font-family: "宋体", serif;
    font-size: larger;
}


/* header */
header h1 {
    color: #ecf0f1;
    font-weight: bold;
    font-family: "宋体", serif;
    line-height: 100px;
}


/* manipulation */
.manipulation .model_select {
    margin: 35px 30px;
}

.manipulation .image_load {
    width: 0.1px;
    height: 0.1px;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    z-index: -1;
}

.manipulation .image_load + label {
    height: 38px;
    text-align: center;
    line-height: 38px;
    background-color: #0dcaf0;
    border-color: #0dcaf0;
    display: inline-block;
    border-radius: 3px;
}

.manipulation .image_load + label:hover {
    background-color: #31d2f2;
    border-color: #25cff2;
    cursor: pointer;
}


/* display */
.display .return {
    margin: 30px 20px;
}

.display .return:after {
    content: "";
    display: table;
    clear: both;
}

.display .pictures_exhibit {
    padding: 10px 15px;
    height: 420px;
    width: 100%;
    background: #ecf0f1;
}

.display .pictures_exhibit:after {
    content: "";
    display: table;
    clear: both;
}

.display .pictures_exhibit .picture_l {
    float: left;
    height: 100%;
    width: 550px;
    background: #bdc3c7;
}

.display .pictures_exhibit .picture_r {
    float: right;
    height: 100%;
    width: 550px;
    background: #bdc3c7;
}

.display .generation {
    margin: 30px 30px;
}

.display .generation:after {
    content: "";
    display: table;
    clear: both;
}

load_file.js
// Load image and preview
$(function (){
    "use strict";
    $("#load_img01").change(function(){
        console.log(this.files[0]);
        let objUrl = getObjectURL(this.files[0]) ; // 获取文件信息
        console.log("objUrl = "+objUrl);
        if (objUrl) {
            $("#img_loaded01").attr("src", objUrl);
        }
    });
});


function getObjectURL(file) {
    "use strict";
    let url = null;
    if(Window.createObjectURL !== undefined) {
        url = window.createObjectURL(file) ;
    }else if (window.URL !== undefined) { // mozilla(firefox)
        url = window.URL.createObjectURL(file) ;
    }else if (window.webkitURL !== undefined) { // webkit or chrome
        url = window.webkitURL.createObjectURL(file) ;
    }
    return url ;
}


// Detect image
function detect() {
    "use strict";
    let fileObj = $("#load_img01")[0].files[0];
    console.log(fileObj);
    let form = new FormData();
    let img_src;
    form.append("file", fileObj);
    $.ajax({
        type: 'POST',
        url: "imageDetect",
        data: form,
        async: false,
        processData: false,
        contentType: false,
        success: function (res){
            alert("检测完成!");
            console.log(res);
            img_src = "data:image/png;base64,"+res;
            console.log(img_src);
            $("#img_detected").attr("src",img_src);
        },
        error: function (){
            alert("检测失败!");
            console.log("后台处理错误");
        }
    });

}

requirements.txt

Flask==2.2.2
matplotlib==3.6.2
Pillow==9.4.0
torchvision==0.14.0

项目文件链接:

链接:https://pan.baidu.com/s/1_KZv22XidLrmt5GLm5u3GA?pwd=0000 
提取码:0000 

三、参考资料

1. python的flask框架实现将python绘制的图像显示到web页面

https://www.cnblogs.com/qingnvsue/p/12988209.html

2. 获取图片上传路径方法

https://www.cnblogs.com/AaronNotes/p/6526479.html

3. 上传按钮<input type="file"/>智能多效美化

https://www.jianshu.com/p/07c27e576b26

4. 使用pytorch配合flask搭建简易web服务

https://www.bilibili.com/video/BV1Qv41117SR/?spm_id_from=333.999.0.0&vd_source=890b108744bd6d43c87d5d12ca12f740

评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值