阅读:61
本WebApp是基于django框架来实现对苹果树病虫智能识别App的web部署
苹果树病虫智能识别App请参考人工智能 机器学习中的相关文章
用到的工具
编辑器:pycharm,vscode(前端)
python版本:python 3.9 (带TensorFlow框架)
django版本:3.2.9
MySQL版本:MySQL80
数据库可视化工具:NaviCat
数据库我使用的是MySQL,如果不会创建MySQL数据库,请先自行学习
数据库配置如下
字段设置如下
数据库名为user
在创建完django项目后,在settings.py中修改我们的数据库配置
DATABASES = {
'default': {
# 配置使用mysql
'ENGINE': 'django.db.backends.mysql', # 数据库产品
'HOST': "localhost", # 数据库ip
'PORT': 3306, # 数据库端口
'USER': "root", # 你的数据库用户名
'PASSWORD': "mysql", # 你的数据库密码
'NAME': "densenet_db", # 数据库名
}
}
import pymysql
pymysql.install_as_MySQLdb()
没有pymysql就安装一下
pip3 install pymysql
在后面创建完model后,我们会对数据库进行测试
新建Django项目,在pycharm中选择Django并进行创建
新建Django项目后,我需要添加第一个APP,命令如下(在pycharm的cmd终端中运行)
python manage.py startapp DenseNetApp
应用创建完成后,我们还需要在 settings.py 配置文件中对其进行添加,把我们创建的应用添加到这个列表,如下所示:
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'DenseNetApp' //创建的App
]
在DenseNetApp目录下创建urls.py文件,作为App内部的路由,同时我们要将DenseNetApp的内部路由导入到总路由文件中,也就是下图所示的urls.py中
导入后的urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('DenseNetApp/',include('DenseNetApp.urls')),
]
在视图函数中,我们创建我们的方法
打开views.py,写入我们的测试方法hello
from django.http import HttpResponse
# 定义一个测试用例
def hello(request):
return HttpResponse("Hello world ! ")
通过HttpResponse直接返回一个页面,页面内容为 "Hello world ! "
定义好方法后,在app内的路由文件中,设置一下hello方法的路由,以便从前端能正确访问到
打开DenseNetApp的路由文件urls.py,设置方法路由
# 定义DenseNetApp的url模式
from django.urls import path
from . import views
app_name = 'DenseNetApp'
urlpatterns = [
# 主页
path('hello/',views.hello,name='hello'),
#URL后缀为hello,
#执行的是views.py中的hello方法
]
在path方法中
‘hello/’ 的含义是在浏览器中的地址的URL后缀为hello
views.hello的含义是执行的是views.py中的hello方法
到此为止,我们创建好了我们的第一个App,并为这个App写了一个简单的方法,下面我们来测试一下这个方法
首先点击pycharm右上角的绿三角,启动django服务器
服务器启动成功后会得到入口地址
默认的是本地地址http://127.0.0.1:8000/,这个地址可以修改,后面在说
点击地址或者复制到浏览器中打开,发现出错了,提示我们Page not found即404错误,这是因为我们没有设置默认地址的路由,所以访问默认地址会找不到页面,这个我们也在后面会解决,先测试我们的方法
还记得我们在主路由设置的地址吗?
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('DenseNetApp/',include('DenseNetApp.urls')),
]
我们要访问hello方法,就想要访问hello所在的App,再从App的路由文件中寻找这个方法
比如我们直接访问hello方法的内路由地址 http://127.0.0.1:8000/hello
同样找不到页面,因为从主路由找不到app内路由的方法地址,所以,我们加上app路由文件的地址再访问
http://127.0.0.1:8000/DenseNetApp/hello/
成功得到了hello方法执行后的页面
随后我们进行项目的部署
导入我们训练好的模型
将它放在pycharm中的新建目录下
新建一个空python文件 predict.py
此文件主要负责训练模型的导入和图像的预处理,包括图像的归一化,RGB格式转换等等,代码如下
import io
import base64
import numpy as np
from PIL import Image
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import img_to_array
model = load_model('./algorithm/models/densenet121.h5') #导入训练好的数据模型
#图像预处理
def preprocess_image(image,target_size): # 图像预处理
if image.mode != 'RGB':
image = image.convert('RGB')
image = image.resize(target_size)
image = img_to_array(image)
image = image/255.
image = np.expand_dims(image,axis=0)
return image
写完后,我们需要在views.py中调用我们的神经网络模型
但在调用之前,我需要测试的图片数据需要从前端获得,于是,我们先写一下我们的App前端页面
前端我用的是一个Bootstrap的框架,设置了POST方法,用于向后台发送图片数据,我取名为densenet.html,保存在templates文件夹内
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Quixlab - Bootstrap Admin Dashboard Template by Themefisher.com</title>
<!-- Favicon icon -->
<link rel="icon" type="../../static/image/png" sizes="16x16" href="../static/images/favicon.png">
<!-- Custom Stylesheet -->
<link href="../static/css/style.css" rel="stylesheet">
</head>
<body>
<!--*******************
Preloader start
********************-->
<div id="preloader">
<div class="loader">
<svg class="circular" viewBox="25 25 50 50">
<circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="3" stroke-miterlimit="10" />
</svg>
</div>
</div>
<!--*******************
Preloader end
********************-->
<!--**********************************
Main wrapper start
***********************************-->
<div id="main-wrapper">
<!--**********************************
Nav header start
***********************************-->
<div class="nav-header">
<div class="brand-logo">
<a href="index1.html">
<h3>识别系统</h3>
</a>
</div>
</div>
<!--**********************************
Nav header end
***********************************-->
<!--**********************************
Header start
***********************************-->
<div class="header">
<div class="header-content clearfix">
<div class="nav-control">
<div class="hamburger">
<span class="toggle-icon"><i class="icon-menu"></i></span>
</div>
</div>
<div class="header-left">
<div class="input-group icons">
<div class="drop-down d-md-none">
<form action="#">
<input type="text" class="form-control" placeholder="Search">
</form>
</div>
</div>
</div>
<div class="header-right">
<ul class="clearfix">
<li class="icons dropdown">
<div class="user-img c-pointer position-relative" data-toggle="dropdown">
<span class="activity active"></span>
<img src="../static/images/user/1.png" height="40" width="40" alt="">
</div>
<div class="drop-down dropdown-profile dropdown-menu">
<div class="dropdown-content-body">
<ul>
<li>
<a href="app-profile.html"><i class="icon-user"></i> <span>Profile</span></a>
</li>
<li>
<a href="email-inbox.html"><i class="icon-envelope-open"></i> <span>Inbox</span> <div class="badge gradient-3 badge-pill badge-primary">3</div></a>
</li>
<hr class="my-2">
<li>
<a href="page-lock.html"><i class="icon-lock"></i> <span>Lock Screen</span></a>
</li>
<li><a href="page-login.html"><i class="icon-key"></i> <span>Logout</span></a></li>
</ul>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<!--**********************************
Header end ti-comment-alt
***********************************-->
<!--**********************************
Sidebar start
***********************************-->
<div class="nk-sidebar">
<div class="nk-nav-scroll">
<ul class="metismenu" id="menu">
<li class="nav-label">设备管理</li>
</ul>
</div>
</div>
<!--**********************************
Sidebar end
***********************************-->
<!--**********************************
Content body start
***********************************-->
<div class="content-body">
<div class="row page-titles mx-0">
<div class="col p-md-0">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="javascript:void(0)">Dashboard</a></li>
<li class="breadcrumb-item active"><a href="javascript:void(0)">Home</a></li>
</ol>
</div>
</div>
<!-- row -->
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">上传文件</h5>
<div class="basic-form">
<form enctype="multipart/form-data" action="/uploadfile" method="post">
{% csrf_token %}
<div class="form-group">
<input type="file" name="myfile" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-dark mb-2" >进行预测</button>
</div>
</form>
<hr>
<h5 class="m-b-15">识别信息</h5>
<p><strong>healthy:</strong>{{ healthy }}</p>
<p><strong>multiple_diseases:</strong>{{ multiple_diseases }}</p>
<p><strong>rust:</strong>{{ rust }}</p>
<p><strong>scab:</strong>{{ scab }}</p>
<h5 class="m-b-5 p-t-15"></h5>
<p></p>
<hr>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- #/ container -->
</div>
<!--**********************************
Content body end
***********************************-->
<!--**********************************
Footer start
***********************************-->
<div class="footer">
<div class="copyright">
</div>
</div>
<!--**********************************
Footer end
***********************************-->
</div>
<!--**********************************
Main wrapper end
***********************************-->
<!--**********************************
Scripts
***********************************-->
<script src="../static/plugins/common/common.min.js"></script>
<script src="../static/js/custom.min.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/gleek.js"></script>
<script src="../static/js/styleSwitcher.js"></script>
<script src="../static/plugins/tables/js/jquery.dataTables.min.js"></script>
<script src="../static/plugins/tables/js/datatable/dataTables.bootstrap4.min.js"></script>
<script src="../static/plugins/tables/js/datatable-init/datatable-basic.min.js"></script>
</body>
</html>
框架中的静态资源(CSS,JavaScript,img等页面静态资源)是需要我们和HTML文件分开存放,放在static文件夹中
这时,我们就能成功的访问静态页面
还记得在前面运行服务器时默认访问http://127.0.0.1:8000/会报404吗?现在我们就为默认地址加上页面,就是我们的densenet.html,首先,我们要在主路由中设置一个空地址来代表默认地址
是不是觉得眼熟,没错,就是把前面的path(‘DenseNetApp/’,include(‘DenseNetApp.urls’))中的DenseNetApp/删掉,变成上图所示,这样,一启动服务器,就会直接跳转到DenseNetApp的内路由文件,我们在用同样的方法来设置内路由
将index方法作为默认启动方法
所以我们要在views中写一个index方法,来完成服务器页面初始化
def index(request):
return render(request, 'E:\\第五章_智能Webapp_django\\templates\\densenet.html')
图省事用的绝对路径,尽量不要学我⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄
再启动服务器,发现这次就不报错了,而是直接运行出我们的densenet.html
非常好用!
有了页面,我们还需要接口来接受我们前端上传的图片,于是需要在views中写入我们的接口,接口代码如下
@csrf_exempt
def uploadfile(request):
if request.method == "POST": # 请求方法为POST时,进行处理
myFile = request.FILES.get("myfile", None) # 获取上传的文件,如果没有文件,则默认为None
if not myFile:
return HttpResponse("no files for upload!")
destination = open(os.path.join("E:\\djangoProject_file", myFile.name), 'wb+') # 打开特定的文件进行二进制的写操作
for chunk in myFile.chunks(): # 分块写入文件
destination.write(chunk)
destination.close()
address = "E:\\djangoProject_file\\" + myFile.name
保存图片时我们需要在本地创建好存储图片的文件夹,我的文件夹叫djangoProject_file
然后为uploadfile方法写一下端口路由
path('uploadfile',views.uploadfile,name='uploadfile'),
注意,路由的地址要与前端表单提交的方法相对应
为了使模型的准确率尽量高,我们需要对图像进行预处理,这里我们进行灰度处理,需要的库有
from PIL import Image
灰度处理代码如下
image = Image.open(address)
processed_image = preprocess_image(image, target_size=(512, 512))
将所保存的图片路径(address)传入灰度处理方法让图片被处理
再调用模型进行识别,并返回四个识别结果的参数
processed_image = preprocess_image(image, target_size=(512, 512))
prediction = model.predict(processed_image)[0].tolist() # 预测
response = {
'prediction': {
'healthy': prediction[0],
'multiple_diseases': prediction[1],
'rust': prediction[2],
'scab': prediction[3]
}
}
并将结果返回给前端页面
return render(request, 'E:\\第五章_智能Webapp_django\\templates\\densenet.html',
{'healthy': response['prediction']['healthy'], 'multiple_diseases': response['prediction']['multiple_diseases'], 'rust': response['prediction']['rust'],
'scab': response['prediction']['scab']}
)
最后我们把这些方法整合到uploadfile方法里,表示一上传图片就进行识别检测
完整uploadfile方法代码
@csrf_exempt
def uploadfile(request):
if request.method == "POST": # 请求方法为POST时,进行处理
myFile = request.FILES.get("myfile", None) # 获取上传的文件,如果没有文件,则默认为None
if not myFile:
return HttpResponse("no files for upload!")
destination = open(os.path.join("E:\\djangoProject_file", myFile.name), 'wb+') # 打开特定的文件进行二进制的写操作
for chunk in myFile.chunks(): # 分块写入文件
destination.write(chunk)
destination.close()
address = "E:\\djangoProject_file\\" + myFile.name
image = Image.open(address)
processed_image = preprocess_image(image, target_size=(512, 512))
prediction = model.predict(processed_image)[0].tolist() # 预测
response = {
'prediction': {
'healthy': prediction[0],
'multiple_diseases': prediction[1],
'rust': prediction[2],
'scab': prediction[3]
}
}
print(response['prediction']['healthy'])
return render(request, 'E:\\第五章_智能Webapp_django\\templates\\densenet.html',
{'healthy': response['prediction']['healthy'], 'multiple_diseases': response['prediction']['multiple_diseases'], 'rust': response['prediction']['rust'],
'scab': response['prediction']['scab']}
)
至此,我们已经完成了苹果树病虫智能识别的初步部署,下面我们来测试一下
运行服务器,进入页面后我们选择一张树叶图片
点击进行检测
于是我们得到了识别结果
测试成功,苹果树病虫智能识别的App能成功运行在我们的django服务器上
等等,我们是不是忘了什么,没错,我们开头使用的数据库没有用上,在后面的文章中,我会对App进行改进,让它能搭配数据库进行使用以及考虑到服务器性能问题,我还会设计分布式服务器以满足不同模块对于性能需求不同
源码链接
链接:https://pan.baidu.com/s/1EBR9TgrHRfdYdz9KoiwphQ
提取码:ybbb
参考资料(打个小广告)
本文所使用的识别算法就来自此书,书中对python网络编程讲的十分详细,有兴趣的同学可以学习一下
see you next time!