python Flask框架,调用MobileNetV2图像分类模型,实现前端上传图像分类

本文主要是介绍python Flask框架,调用MobileNetV2图像分类模型,实现前端上传图像分类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

python Flask框架,调用MobileNetV2图像分类模型,实现前端上传图像分类

今天博主介绍一个图像分类的小项目
基于flask 和mobileNetV2模型的前端图像分类项目

环境配置如下:

python版本==3.7.6
安装库的版本如下:
tensorflow 2.11.0
Flask 2.2.5
gevent 1.4.0
Werkzeug 2.2.3
numpy 1.21.6
Pillow 9.5.0
keras 2.12.0

下面我们开始介绍项目:

这个是我们的项目文件结构图:
在这里插入图片描述
app.py:

import os
import sys# Flask
from flask import Flask, redirect, url_for, request, render_template, Response, jsonify, redirect
from werkzeug.utils import secure_filename
from gevent.pywsgi import WSGIServer# TensorFlow and tf.keras
import tensorflow as tf
from tensorflow import kerasfrom tensorflow.keras.applications.imagenet_utils import preprocess_input, decode_predictions
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image# Some utilites
import numpy as np
from util import base64_to_pil# Declare a flask app
app = Flask(__name__)# You can use pretrained model from Keras
# Check https://keras.io/applications/
# or https://www.tensorflow.org/api_docs/python/tf/keras/applicationsfrom tensorflow.keras.applications.mobilenet_v2 import MobileNetV2model = MobileNetV2(weights='imagenet')print('Model loaded. Check http://127.0.0.1:5000/')# Model saved with Keras model.save()
MODEL_PATH = 'models/your_model.h5'# Load your own trained model
# model = load_model(MODEL_PATH)
# model._make_predict_function()          # Necessary
# print('Model loaded. Start serving...')def model_predict(img, model):# print(img.shape())print(img.size)img = img.resize((224, 224))# Preprocessing the imagex = image.img_to_array(img)x=x[:,:,0:3]# x = np.true_divide(x, 255)print("fds",x.shape)x = np.expand_dims(x, axis=0)print("fds",x.shape)# Be careful how your trained model deals with the input# otherwise, it won't make correct prediction!x = preprocess_input(x, mode='tf')print(x.size)preds = model.predict(x)return preds@app.route('/', methods=['GET'])
def index():# Main pagereturn render_template('index.html')@app.route('/predict', methods=['GET', 'POST'])
def predict():if request.method == 'POST':# Get the image from post requestimg = base64_to_pil(request.json)# Save the image to ./uploads# img.save("./uploads/image.png")# Make predictionpreds = model_predict(img, model)# Process your result for humanpred_proba = "{:.3f}".format(np.amax(preds))    # Max probabilitypred_class = decode_predictions(preds, top=1)   # ImageNet Decoderesult = str(pred_class[0][0][1])               # Convert to stringresult = result.replace('_', ' ').capitalize()# Serialize the result, you can add additional fieldsreturn jsonify(result=result, probability=pred_proba)return Noneif __name__ == '__main__':# app.run(port=5002, threaded=False)# Serve the app with geventhttp_server = WSGIServer(('0.0.0.0', 5000), app)http_server.serve_forever()

util.py:

"""Utilities
"""
import re
import base64import numpy as npfrom PIL import Image
from io import BytesIOdef base64_to_pil(img_base64):"""Convert base64 image data to PIL image"""image_data = re.sub('^data:image/.+;base64,', '', img_base64)pil_image = Image.open(BytesIO(base64.b64decode(image_data)))return pil_imagedef np_to_base64(img_np):"""Convert numpy image (RGB) to base64 string"""img = Image.fromarray(img_np.astype('uint8'), 'RGB')buffered = BytesIO()img.save(buffered, format="PNG")return u"data:image/png;base64," + base64.b64encode(buffered.getvalue()).decode("ascii")

base.html

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><title>Demo</title><meta name="viewport" content="width=device-width, initial-scale=1" /><link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='main.css') }}" /></head><!-- GitHub Corner --><a href="https://github.com/imfing/keras-flask-deploy-webapp" class="github-corner" aria-label="View source on GitHub"><svg width="60" height="60" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style><body>{% block content %}{% endblock %}</body><footer><script src="{{ url_for('static',filename='main.js') }}"></script></footer>
</html>

index.html

{% extends "base.html" %} {% block content %}<div class="main"><div class="title"><h3>Image Classifier</h3><!-- <p><small>A web app demo</small></p> --></div><div class="panel"><input id="file-upload" class="hidden" type="file" accept="image/x-png,image/gif,image/jpeg" /><label for="file-upload" id="file-drag" class="upload-box"><div id="upload-caption">Drop image here or click to select</div><img id="image-preview" class="hidden" /></label></div><div style="margin-bottom: 2rem;"><input type="button" value="Submit" class="button" onclick="submitImage();" /><input type="button" value="Clear" class="button" onclick="clearImage();" /></div><div id="image-box"><img id="image-display" /><div id="pred-result" class="hidden"></div><svg id="loader" class="hidden" viewBox="0 0 32 32" width="32" height="32"><circle id="spinner" cx="16" cy="16" r="14" fill="none"></circle></svg></div>
</div>{% endblock %}

main.css

body {font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;-webkit-font-smoothing: antialiased;background-color: #f8f8f8;
}/* Global button style */
.button {font-family: inherit;text-align: center;cursor: pointer;border: none;text-decoration: none;outline: none;color: #ffffff;background-color: rgb(0, 120, 212);padding: 0.5rem 1.2rem;border-radius: 2px;font-size: 1rem;min-width: 6rem;
}.button:hover {background-color: rgb(16, 110, 190);
}.button.disabled {pointer-events: none;background-color: #cccccc;color: #666666;
}/* Main section */.main {box-sizing: border-box;display: flex;flex-direction: column;align-items: center;
}.main .title h3 {font-size: 2.3rem;font-weight: 300;margin: 0.8rem 0;
}.hidden {display: none;
}.reveal {opacity: 0;
}.reveal:hover {opacity: 0.2;
}/* Upload box */
.upload-box {font-size: 0.8rem;color: #666666;cursor: pointer;width: 16rem;height: 10rem;background: #fff;border: 0.1rem dashed #838388;border-radius: 0.4rem;display: flex;justify-content: center;align-items: center;flex-direction: column;margin: 1rem 0 2rem 0;
}.upload-box.dragover {/* background-color: grey; */color: #eeeeee;border: 0.1rem solid rgb(0, 120, 212);box-shadow: inset 0 0 0 0.1rem rgb(0, 120, 212);
}.upload-box:hover {border-color: rgb(0, 120, 212);
}.upload-box #image-preview {max-width: 14rem;max-height: 8rem;box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.19);
}#image-result {box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);max-height: 20rem;
}#image-box {position: relative;width: auto;float: left;margin-bottom: 2rem;
}#image-display {box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);max-height: 20rem;
}#image-display.loading {filter: brightness(30%);
}#pred-result {color: white;font-size: 1.5rem;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);
}#loader {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 10;margin: 0 auto;
}/* Animation */
#spinner {box-sizing: border-box;stroke: #cccccc;stroke-width: 3px;transform-origin: 50%;animation: line 1.6s cubic-bezier(0.4, 0, 0.2, 1) infinite,rotate 1.6s linear infinite;
}
@keyframes rotate {from {transform: rotate(0);}to {transform: rotate(450deg);}
}
@keyframes line {0% {stroke-dasharray: 2, 85.964;transform: rotate(0);}50% {stroke-dasharray: 65.973, 21.9911;stroke-dashoffset: 0;}100% {stroke-dasharray: 2, 85.964;stroke-dashoffset: -65.973;transform: rotate(90deg);}
}

main.js

//========================================================================
// Drag and drop image handling
//========================================================================var fileDrag = document.getElementById("file-drag");
var fileSelect = document.getElementById("file-upload");// Add event listeners
fileDrag.addEventListener("dragover", fileDragHover, false);
fileDrag.addEventListener("dragleave", fileDragHover, false);
fileDrag.addEventListener("drop", fileSelectHandler, false);
fileSelect.addEventListener("change", fileSelectHandler, false);function fileDragHover(e) {// prevent default behavioure.preventDefault();e.stopPropagation();fileDrag.className = e.type === "dragover" ? "upload-box dragover" : "upload-box";
}function fileSelectHandler(e) {// handle file selectingvar files = e.target.files || e.dataTransfer.files;fileDragHover(e);for (var i = 0, f; (f = files[i]); i++) {previewFile(f);}
}//========================================================================
// Web page elements for functions to use
//========================================================================var imagePreview = document.getElementById("image-preview");
var imageDisplay = document.getElementById("image-display");
var uploadCaption = document.getElementById("upload-caption");
var predResult = document.getElementById("pred-result");
var loader = document.getElementById("loader");//========================================================================
// Main button events
//========================================================================function submitImage() {// action for the submit buttonconsole.log("submit");if (!imageDisplay.src || !imageDisplay.src.startsWith("data")) {window.alert("Please select an image before submit.");return;}loader.classList.remove("hidden");imageDisplay.classList.add("loading");// call the predict function of the backendpredictImage(imageDisplay.src);
}function clearImage() {// reset selected filesfileSelect.value = "";// remove image sources and hide themimagePreview.src = "";imageDisplay.src = "";predResult.innerHTML = "";hide(imagePreview);hide(imageDisplay);hide(loader);hide(predResult);show(uploadCaption);imageDisplay.classList.remove("loading");
}function previewFile(file) {// show the preview of the imageconsole.log(file.name);var fileName = encodeURI(file.name);var reader = new FileReader();reader.readAsDataURL(file);reader.onloadend = () => {imagePreview.src = URL.createObjectURL(file);show(imagePreview);hide(uploadCaption);// resetpredResult.innerHTML = "";imageDisplay.classList.remove("loading");displayImage(reader.result, "image-display");};
}//========================================================================
// Helper functions
//========================================================================function predictImage(image) {fetch("/predict", {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify(image)}).then(resp => {if (resp.ok)resp.json().then(data => {displayResult(data);});}).catch(err => {console.log("An error occured", err.message);window.alert("Oops! Something went wrong.");});
}function displayImage(image, id) {// display image on given id <img> elementlet display = document.getElementById(id);display.src = image;show(display);
}function displayResult(data) {// display the result// imageDisplay.classList.remove("loading");hide(loader);predResult.innerHTML = data.result;show(predResult);
}function hide(el) {// hide an elementel.classList.add("hidden");
}function show(el) {// show an elementel.classList.remove("hidden");
}

其他的东西其实用不到了。主要是这个六个文件。
下面看一下代码运行情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
全部都预测对了
Labrador retriever是拉布拉多寻回犬
Tabby是斑猫的意思
感兴趣的可以学习一下这个项目。

这篇关于python Flask框架,调用MobileNetV2图像分类模型,实现前端上传图像分类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/390248

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G