苹果树病虫智能识别的web部署,基于django框架

     阅读:30

苹果树病虫智能识别的web部署,基于django框架

本WebApp是基于django框架来实现对苹果树病虫智能识别App的web部署

苹果树病虫智能识别App请参考人工智能 机器学习中的相关文章

用到的工具

编辑器:pycharm,vscode(前端)

python版本:python 3.9 (带TensorFlow框架)

django版本:3.2.9

MySQL版本:MySQL80

数据库可视化工具:NaviCat

1.数据库设计

数据库我使用的是MySQL,如果不会创建MySQL数据库,请先自行学习

数据库配置如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JyLQ6zko-1642006774136)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220111225904922.png)]
字段设置如下
在这里插入图片描述

数据库名为user

2.数据库配置

在创建完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后,我们会对数据库进行测试

3.创建WebApp

新建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中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LCL0IVsU-1642006774137)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220111203747934.png)]

导入后的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服务器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lqqYQzhn-1642006774137)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220111223542477.png)]

服务器启动成功后会得到入口地址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WSFVX7jl-1642006774138)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220111223851294.png)]

默认的是本地地址http://127.0.0.1:8000/,这个地址可以修改,后面在说

点击地址或者复制到浏览器中打开,发现出错了,提示我们Page not found即404错误,这是因为我们没有设置默认地址的路由,所以访问默认地址会找不到页面,这个我们也在后面会解决,先测试我们的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUD2wmfw-1642006774138)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220111224008722.png)]

还记得我们在主路由设置的地址吗?

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0eUzebq6-1642006774138)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220111224802287.png)]

同样找不到页面,因为从主路由找不到app内路由的方法地址,所以,我们加上app路由文件的地址再访问

http://127.0.0.1:8000/DenseNetApp/hello/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rum2tgOP-1642006774139)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220111224948698.png)]

成功得到了hello方法执行后的页面

4苹果树病虫智能识别App部署

随后我们进行项目的部署

导入我们训练好的模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BEtFUZ5x-1642006774139)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113000357182.png)]

将它放在pycharm中的新建目录下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CoFK9lgX-1642006774140)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113000438366.png)]

新建一个空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前端页面

5前端页面绘制

前端我用的是一个Bootstrap的框架,设置了POST方法,用于向后台发送图片数据,我取名为densenet.html,保存在templates文件夹内

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6XSxB4hW-1642006774140)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113001607883.png)]

<!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文件夹中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CIlqLOZw-1642006774140)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113001757662.png)]

这时,我们就能成功的访问静态页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iw3IMzrU-1642006774141)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113001931226.png)]

还记得在前面运行服务器时默认访问http://127.0.0.1:8000/会报404吗?现在我们就为默认地址加上页面,就是我们的densenet.html,首先,我们要在主路由中设置一个空地址来代表默认地址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7CKTV62W-1642006774141)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113002115834.png)]

是不是觉得眼熟,没错,就是把前面的path(‘DenseNetApp/’,include(‘DenseNetApp.urls’))中的DenseNetApp/删掉,变成上图所示,这样,一启动服务器,就会直接跳转到DenseNetApp的内路由文件,我们在用同样的方法来设置内路由

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vN3l88Nc-1642006774141)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113002356401.png)]

将index方法作为默认启动方法

所以我们要在views中写一个index方法,来完成服务器页面初始化

def index(request):
    return render(request, 'E:\\第五章_智能Webapp_django\\templates\\densenet.html')

图省事用的绝对路径,尽量不要学我⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄

再启动服务器,发现这次就不报错了,而是直接运行出我们的densenet.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-crlYd9Lr-1642006774141)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113002847081.png)]

有了页面,我们还需要接口来接受我们前端上传的图片,于是需要在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'),

注意,路由的地址要与前端表单提交的方法相对应

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LaAKBRHo-1642006774142)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113003648125.png)]

6识别算法的应用

为了使模型的准确率尽量高,我们需要对图像进行预处理,这里我们进行灰度处理,需要的库有

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']}
                      )

至此,我们已经完成了苹果树病虫智能识别的初步部署,下面我们来测试一下

运行服务器,进入页面后我们选择一张树叶图片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IXpzlhU3-1642006774142)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113004623942.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UCgxeOpb-1642006774142)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113004706046.png)]

点击进行检测

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BjnnY5zA-1642006774142)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113004726997.png)]

于是我们得到了识别结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zQHHydD3-1642006774143)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113004757566.png)]

测试成功,苹果树病虫智能识别的App能成功运行在我们的django服务器上

等等,我们是不是忘了什么,没错,我们开头使用的数据库没有用上,在后面的文章中,我会对App进行改进,让它能搭配数据库进行使用以及考虑到服务器性能问题,我还会设计分布式服务器以满足不同模块对于性能需求不同

源码链接
链接:https://pan.baidu.com/s/1EBR9TgrHRfdYdz9KoiwphQ
提取码:ybbb

参考资料(打个小广告)

本文所使用的识别算法就来自此书,书中对python网络编程讲的十分详细,有兴趣的同学可以学习一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjWZUgoM-1642006774143)(C:\Users\91053\AppData\Roaming\Typora\typora-user-images\image-20220113005322486.png)]

see you next time!