Browse Source

1233

master
lc18518571399 9 months ago
commit
b85363c83c
  1. 75
      app.py
  2. BIN
      code.zip
  3. BIN
      follicle_groups.pt
  4. BIN
      follicles.pt
  5. 24
      front/.gitignore
  6. 7
      front/README.md
  7. 13
      front/index.html
  8. 1989
      front/package-lock.json
  9. 23
      front/package.json
  10. 1157
      front/public/vite.svg
  11. 169
      front/src/App.vue
  12. 6
      front/src/assets/config/imgconf.json
  13. BIN
      front/src/assets/imgs/bilstm/1.png
  14. BIN
      front/src/assets/imgs/otherimgs/bg.jpg
  15. 1157
      front/src/assets/vue.svg
  16. 13
      front/src/main.js
  17. 5
      front/src/style.css
  18. 24
      front/vite.config.js
  19. BIN
      front/vite600.zip
  20. 19
      front/项目说明文档.txt
  21. BIN
      img0002.jpg
  22. 81
      inference.py
  23. BIN
      results_1.jpg
  24. BIN
      results_2.jpg
  25. BIN
      网页.pptx

75
app.py

@ -0,0 +1,75 @@
import torch
from flask import Flask, request
import torch.nn as nn
import pickle
import jieba
app = Flask(__name__)
# 初始化模型
result = ""
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.embedding = nn.Embedding(10002, 300, padding_idx=10001)
self.lstm = nn.LSTM(300, 128, 2, bidirectional=True, batch_first=True, dropout=0.5)
self.fc = nn.Linear(128 * 2, 2)
def forward(self, x):
x, _ = x
out = self.embedding(x) # [batch_size, seq_len, embeding]=[128, 32, 300]
out, _ = self.lstm(out)
out = self.fc(out[:, -1, :]) # 句子最后时刻的 hidden state
return out
model = Model()
model.load_state_dict(torch.load('./THUCNews/saved_dict/TextRNN.ckpt', map_location=torch.device("cpu")))
model.eval()
stopwords = open('./THUCNews/data/hit_stopwords.txt', encoding='utf8').read().split('\n')[:-1]
vocab = pickle.load(open('./THUCNews/data/vocab.pkl', 'rb'))
classes = ['negative', 'positive']
s = '空调吵,住在电梯旁,电梯门口放垃圾箱,极臭,布草间没关门,也臭,臭到房间里,门下塞毛巾也挡不住臭味,开窗外面吵,关窗空调吵,楼下早餐桌子上摆满垃圾没人整理,不能再差的体验了'
# s = '东东还算不错。重装系统时,网上查不到怎么修改BIOS,才能安装?问题请帮忙解决!'
@app.route('/api/content', methods=["POST"])
def content():
get_json = request.get_json()
global model
global result
global classes
result = ""
s = get_json.get("content")[0]
print(6777, s)
try:
s = list(jieba.lcut(s))
s = [i for i in s if i not in stopwords]
s = [vocab.get(i, 10000) for i in s]
if len(s) > 64:
s = s[:64]
else:
for i in range(64 - len(s)):
s.append(vocab['<PAD>'])
outputs = model((torch.LongTensor(s).unsqueeze(0), None))
print(torch.argmax(outputs))
result = classes[torch.argmax(outputs)]
except Exception as e: # 未捕获到异常,程序直接报错
result = e
return "pridicting"
@app.route('/api/model_res', methods=['GET'])
def model_res():
global result
return str(result)
if __name__ == '__main__':
app.run(host="127.0.0.1", port=8006)

BIN
code.zip

Binary file not shown.

BIN
follicle_groups.pt

Binary file not shown.

BIN
follicles.pt

Binary file not shown.

24
front/.gitignore

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

7
front/README.md

@ -0,0 +1,7 @@
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously Volar) and disable Vetur

13
front/index.html

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>毛囊结果检测</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

1989
front/package-lock.json

File diff suppressed because it is too large

23
front/package.json

@ -0,0 +1,23 @@
{
"name": "vitebase",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.6.8",
"element-plus": "^2.7.2",
"lodash": "^4.17.21",
"vue": "^3.4.21"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.4",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.2.0"
}
}

1157
front/public/vite.svg

File diff suppressed because it is too large

After

Width:  |  Height:  |  Size: 87 KiB

169
front/src/App.vue

@ -0,0 +1,169 @@
<template>
<div class="bgimg">
<el-row class="pa-2">
<el-col :span="8" class="text-align">
<div>
<el-input class="opacitybg" v-model="content" :rows="15" type="textarea" placeholder="请输入文本进行预测" />
</div>
<div class="pa-2">
<el-button type="success" @click="sendData" :disabled="loading" :loading="loading">
开始预测
</el-button>
</div>
</el-col>
<el-col :span="16" class="sendButton text-align">
<div v-if="result">
<el-tag type="success" v-if="result === 'positive'" class="tagClass">
预测结果{{ classZh[result] }}
<el-icon style="width: 48px;font-size: 1.6em;position: relative;top: 20px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M13,9.94L14.06,11L15.12,9.94L16.18,11L17.24,9.94L15.12,7.82L13,9.94M8.88,9.94L9.94,11L11,9.94L8.88,7.82L6.76,9.94L7.82,11L8.88,9.94M12,17.5C14.33,17.5 16.31,16.04 17.11,14H6.89C7.69,16.04 9.67,17.5 12,17.5Z" />
</svg>
</el-icon>
</el-tag>
<el-tag type="danger" v-else-if="result === 'negative'" class="tagClass">
预测结果{{ classZh[result] }}
<el-icon style="width: 48px;font-size: 1.6em;position: relative;top: 20px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z" />
</svg>
</el-icon>
</el-tag>
</div>
<div v-else>
<el-progress class="opacitybg" :percentage="percentage" :stroke-width="15" :striped="percentage !== 100"
:striped-flow="percentage !== 100" :duration="duration" :status="percentage === 100 ? 'success' : ''"
type="circle" />
</div>
</el-col>
</el-row>
<el-row>
<el-col class="pa-2 text-align" :span="8" v-for="(item, index) in imgList" :index="index">
<div class="pa-2">{{ item.label }}</div>
<div>
<el-image style="height: calc(100vh - 420px)" :src="item.imgPath" :zoom-rate="1.2" :max-scale="7"
:min-scale="0.2" :preview-src-list="[item.imgPath]" :initial-index="4" fit="cover" close-on-press-escape />
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import { result } from "lodash";
import imgPath from "./assets/config/imgconf.json";
export default {
name: "app",
data() {
return {
result: "",
content: "",
percentage: 0,
isSend: false,
loading: false,
imgList: [],
sendmessage: null,
classZh: {
negative: "消极",
positive: "积极",
},
};
},
methods: {
async sendData() {
this.percentage = 0;
this.isSend = true;
this.loading = true;
this.result = "";
if (this.sendmessage) {
this.sendmessage.close()
}
this.sendmessage = this.$message({
showClose: true,
repeatNum: 1,
duration: 0,
message: "王一鸣202207100038",
type: 'success',
});
let data = {
content: [this.content],
};
let res = await this.$axios.post("/api/content", data);
if (res.status === 200) {
this.isSend = false;
}
},
},
async mounted() {
this.imgList = imgPath;
for (let index = 0; index < this.imgList.length; index++) {
let element = this.imgList[index];
element.imgPath = new URL(element.path, import.meta.url).href;
}
},
watch: {
async isSend(nev, olv) {
let total = 96;
if (nev) {
while (this.percentage < total) {
if (this.percentage < total) {
let random = Math.floor(Math.random() * 2);
this.percentage += random;
}
await new Promise((resolve) => setTimeout(resolve, 50));
}
if (this.percentage > total - 1) {
while (!this.result) {
let res = await this.$axios.get("/api/model_res");
this.percentage = 100;
this.result = res.data
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
} else {
}
},
result(nev, olv) {
this.loading = !nev;
},
},
computed: {
duration() {
return Math.floor(this.percentage / 10);
},
},
};
</script>
<style scoped>
.bgimg {
height: 100vh;
width: 100vw;
color: #fff;
background-image: url("@/assets/imgs/otherimgs/bg.jpg");
}
.sendButton {
align-content: center;
}
.text-align {
text-align: center;
}
.pa-2 {
padding: 4px;
}
.tagClass {
height: 1.6em;
font-size: 3em;
}
.opacitybg {
opacity: 0.6;
}
</style>

6
front/src/assets/config/imgconf.json

@ -0,0 +1,6 @@
[
{
"label": "f1-score",
"path": "assets/imgs/bilstm/1.png"
}
]

BIN
front/src/assets/imgs/bilstm/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
front/src/assets/imgs/otherimgs/bg.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

1157
front/src/assets/vue.svg

File diff suppressed because it is too large

After

Width:  |  Height:  |  Size: 87 KiB

13
front/src/main.js

@ -0,0 +1,13 @@
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import './style.css'
import _ from 'lodash'
import axios from "axios"
const app = createApp(App)
app.config.globalProperties.$_ = _
app.config.globalProperties.$axios = axios
app.use(ElementPlus)
app.mount('#app')

5
front/src/style.css

@ -0,0 +1,5 @@
html,
body {
margin: 0;
padding: 0;
}

24
front/vite.config.js

@ -0,0 +1,24 @@
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8006/api',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
resolve: {
alias: {
// @ 替代为 src
'@': resolve(__dirname, 'src'),
},
},
})

BIN
front/vite600.zip

Binary file not shown.

19
front/项目说明文档.txt

@ -0,0 +1,19 @@
项目启用:
后端启用:
使用pycharm打开项目文件夹,打开app.py(需要将缺失的包安装:例如flask、numpy等),在app.py中运行即可
前端启用:
使用vscode打开前端项目文件夹,在确保有node环境(百度搜索node,下载并安装18.20.0版本)后在终端(ctrl+shift+`)输入npm run dev,即可启用前端项目,如果缺失运行包的情况下,需要先npm install安装项目运行包。
项目前端开发流程:
1.安装node18.20.0版本,安装VScode最新版本,
2.打算使用vite(打包工具)+vue(前端框架)进行开发
3.在vscode终端中输入npm init vite@latest my-vue-app -- --template vue 搭建vue项目框架,
4.cd my-vue-app进入项目中,npm install安装项目运行包。
5.在终端输入npm run dev,即可启动项目,
6.使用axios进行请求,与后端进行数据交互。
7.使用element-plus进行前端页面开发。
8.使用lodash进行数据处理。加快开发。
9.开发的页面均在app.vue文件中。
前端页面开发的思想:
1.需要一个输入框和按钮发送请求(在发送后未出结果前,按钮不能再次点击,防止请求次数过多,造成资源浪费),以及后端数据处理过程中,页面上需要给出提示,及时反馈给用户
2.需要需要把训练的bilstm模型的参数放到页面上,便于用户查看,便于用户理解
3.在后端数据处理完以后,请求处理后的数据,在页面上显示出来

BIN
img0002.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 KiB

81
inference.py

@ -0,0 +1,81 @@
import cv2
import torch
import numpy as np
from PIL import Image
from ultralytics import YOLO
# 3个输入参数
img_path = 'img0002.jpg'
iou = 0.1
conf = 0.25
def split_image(img_path, size=(800, 800)):
img = cv2.imread(img_path)
height, width = img.shape[:2]
rows = (height + size[1] - 1) // size[1]
cols = (width + size[0] - 1) // size[0]
img_list = []
indexes = []
for r in range(rows):
for c in range(cols):
y1 = r * size[1]
y2 = min((r + 1) * size[1], height)
x1 = c * size[0]
x2 = min((c + 1) * size[0], width)
split = img[y1:y2, x1:x2]
img_list.append(split)
indexes.append((r, c))
return img_list, indexes, (height, width)
def combine_images(pred_imgs, indexes, size=(800, 800), img_shape=(3000, 4000)):
combined_img = np.zeros((img_shape[0], img_shape[1], 3), dtype=np.uint8)
for idx, (r, c) in enumerate(indexes):
y1 = r * size[1]
y2 = min((r + 1) * size[1], img_shape[0])
x1 = c * size[0]
x2 = min((c + 1) * size[0], img_shape[1])
combined_img[y1:y2, x1:x2] = pred_imgs[idx][:y2 - y1, :x2 - x1]
return combined_img
follicle_groups_detector = YOLO('follicle_groups.pt')
follicles_detector = YOLO('follicles.pt')
results = follicle_groups_detector(img_path, iou=iou, conf=conf)
for r in results:
num_follicle_groups = len(r.boxes)
im_array = r.plot()
im = Image.fromarray(im_array[..., ::-1])
im.save('results_1.jpg') #输出结果图1
img_list, indexes, (height, width) = split_image(img_path)
print(f"Number of image blocks: {len(img_list)}")
num_small_follicles = 0
num_big_follicles = 0
pred_imgs = []
for img in img_list:
results = follicles_detector(img, iou=iou, conf=conf)
for r in results:
num_small_follicles += torch.sum(r.boxes.cls == 0).item()
num_big_follicles += torch.sum(r.boxes.cls == 1).item()
im_array = r.plot()
pred_imgs.append(im_array)
# 输出的3个结果文本
print('毛囊群数量:', num_follicle_groups)
print('大毛囊数量:', num_big_follicles)
print('小毛囊数量:', num_small_follicles)
combined_img = combine_images(pred_imgs, indexes, size=(800, 800), img_shape=(height, width))
combined_image_pil = Image.fromarray(combined_img[..., ::-1])
combined_image_pil.save('results_2.jpg') #输出结果图2

BIN
results_1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 KiB

BIN
results_2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

BIN
网页.pptx

Binary file not shown.
Loading…
Cancel
Save