概要
本文主要介绍利用前端Vue+后端Django框架搭建一个简单的web应用,集成训练好的目标检测模型,输出相关的物体类别信息。
整体架构流程
开发过程中使用Vue 3、TypeScript、Element Plus和Axios,确保已经安装了相关环境,此处只演示整个流程与最终结果,对安装细节不做讲解。
利用Vue3+TypeScript搭建前端图像上传模块,后端利用Django框架接收相关图像数据,加载相关的目标检测模型,将识别的后的目标信息回传给前端页面。
技术细节
前端搭建
首先在Vue项目中创建一个包含图片上传的组件
<div>
<el-card shadow="never" :style="elStyle">
<template #header>
<div class="card-header">
<span>当前检测的物体为:{{ message_V }}</span>
</div>
</template>
<el-form
label-position= "left"
label-width="100px"
:model="formLabelAlign"
style="max-width: 460px"
>
<el-form-item label="检测模型选择">
<el-select v-model="formLabelAlign.select" placeholder="Select">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
/>
</el-select>
</el-form-item>
<!-- <el-form-item label="Activity zone">
<el-input v-model="formLabelAlign.region" />
</el-form-item>
<el-form-item label="Activity form">
<el-input v-model="formLabelAlign.type" />
</el-form-item> -->
<el-form-item
label="上传图像:">
<el-upload
ref="upload"
list-type="picture-card"
:auto-upload="false"
:on-change="handleChange"
:on-exceed="handleExceed"
:limit="1"
>
</el-upload>
</el-form-item>
<el-button type="primary" @click="handleUpload">
Upload
</el-button>
</el-form>
</el-card>
</div>
可视化效果如下
这里我们采用的是densenet121识别模型
前端接口
这里我们将获取到的图像信息通过 getImage_Info 接口传输至baseUrlApi封装后的后端路由中,本地测试中存在跨域问题,可以进行跨域代理
export const baseUrlApi = (url: string) => `/api/${url}`;
type Result = {
success: boolean;
data: Array<any>;
};
export const getImage_Info = (data?: object) => {
return http.request<Result>("post", baseUrlApi("getAsyncRoutes_2/"), { data });
};
这里我们使用的是Vite进行相关的代理,具体可以查看相关的说明文档
// 服务端渲染
server: {
// 是否开启 https
https: false,
// 端口号
port: VITE_PORT,
host: "0.0.0.0",
// 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy
proxy: {
"/api": {
// 这里填写后端地址
target: "http://127.0.0.1:8000",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, "")
}
}
},
后端接口
后端Django相关的安装教程很多,此处略去,直接进入接口编写。
申请好相关的APP应用后,在setting.py内加入以下语句,保证可以接收到前端的相关请求
CSRF_TRUSTED_ORIGINS = [
"http://localhost:8080", # 发起请求的地址,不需要添加http和端口
]
在urls.py内加入前端请求的接口地址 getAsyncRoutes_2
from app01.views import index
#这里的app01是已经创建好的应用,index是app01中定义的一个函数 前端传输的信息会分配给app01的index函数
urlpatterns = [
path("admin/", admin.site.urls),
path('getAsyncRoutes_2/', index, name='index'),
]
后端模型
将模型封装至get_prediction函数中,输出相关图像的标签信息
def index(request):
image_uri = None
predicted_label = None
Temp_Msg = None
if request.method == 'POST':
Temp_Msg = 'true'
image_bytes = request.body # 二进制图像数据
img = Image.open(BytesIO(image_bytes))
buf = BytesIO()
img.save(buf, 'JPEG')
buf.seek(0)
img_data = buf.read()
buf.close()
try:
# image_data = transform_image_M(img_data)
predicted_label = get_prediction(img_data)
except RuntimeError as re:
print(re)
# predicted_label = "Prediction Error"
else:
Temp_Msg = 'false'
# form = ImageUploadForm()
print(predicted_label)
context = {
'success': Temp_Msg,
# 'image_uri': image_uri,
'data': predicted_label,
}
return JsonResponse(context)
# return render(request,"Index.html", context)
图像处理细节
model = models.densenet121(pretrained=True)
model.eval()
json_path = os.path.join('.\static/', "imagenet_class_index.json")#标签相关的文件
imagenet_mapping = json.load(open(json_path))
def transform_image(image_bytes):
"""
Transform image into required DenseNet format: 224x224 with 3 RGB channels and normalized.
Return the corresponding tensor.
"""
my_transforms = transforms.Compose([transforms.Resize(255),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
[0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])])
image = Image.open(io.BytesIO(image_bytes))
return my_transforms(image).unsqueeze(0)
def get_prediction(image_bytes):
"""For given image bytes, predict the label using the pretrained DenseNet"""
tensor = transform_image(image_bytes)
outputs = model.forward(tensor)
_, y_hat = outputs.max(1)
predicted_idx = str(y_hat.item())
class_name, human_label = imagenet_mapping[predicted_idx]
return human_label
前端展示
小结
模型封装还是不够数量,还有一些细节处理的不够好 后续慢慢优化。
详细代码