Browse Source

1233

master
lichong 9 months ago
parent
commit
a5cb2e08bc
  1. 75
      app.py
  2. 7
      front/package-lock.json
  3. 2
      front/package.json
  4. 226
      front/src/App copy.vue
  5. 313
      front/src/App.vue
  6. 6
      front/src/assets/config/imgconf.json
  7. BIN
      front/src/assets/imgs/bilstm/1.png
  8. 2
      front/src/main.js
  9. 17
      front/src/style.css
  10. BIN
      front/vite600.zip
  11. 161
      inference copy.py
  12. 175
      inference.py
  13. BIN
      results_1.jpg
  14. BIN
      results_2.jpg

75
app.py

@ -1,75 +0,0 @@
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)

7
front/package-lock.json

@ -9,8 +9,10 @@
"version": "0.0.0",
"dependencies": {
"axios": "^1.6.8",
"dayjs": "^1.11.11",
"element-plus": "^2.7.2",
"lodash": "^4.17.21",
"nprogress": "^0.2.0",
"vue": "^3.4.21"
},
"devDependencies": {
@ -1522,6 +1524,11 @@
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
},
"node_modules/nprogress": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz",
"integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",

2
front/package.json

@ -10,8 +10,10 @@
},
"dependencies": {
"axios": "^1.6.8",
"dayjs": "^1.11.11",
"element-plus": "^2.7.2",
"lodash": "^4.17.21",
"nprogress": "^0.2.0",
"vue": "^3.4.21"
},
"devDependencies": {

226
front/src/App copy.vue

@ -0,0 +1,226 @@
<template>
<div class="bgimg">
<el-row class="inputClass">
<el-col :span="8">
<span>IoU阈值</span>
<el-input-number v-model="iou" :min="0.01" :max="1" :step="0.01" :disabled="checkLoading">
<template #decrease-icon>
<el-icon>
<ArrowDown />
</el-icon>
</template>
<template #increase-icon>
<el-icon>
<ArrowUp />
</el-icon>
</template>
</el-input-number>
</el-col>
<el-col :span="8">
<span>置信度阈值</span>
<el-input-number v-model="conf" :min="0.01" :max="1" :step="0.01" :disabled="checkLoading">
<template #decrease-icon>
<el-icon>
<ArrowDown />
</el-icon>
</template>
<template #increase-icon>
<el-icon>
<ArrowUp />
</el-icon>
</template>
</el-input-number>
</el-col>
<el-col :span="4">
<el-upload :show-file-list="false" :before-upload="beforeAvatarUpload" :http-request="successSubmit"
accept=".png,.jpg,.jpeg">
<el-button type="success" :disabled="checkLoading">
<span>上传图片</span>
</el-button>
</el-upload>
</el-col>
</el-row>
<el-row class="outClass">
<el-col>
<el-descriptions direction="vertical" :column="3" border>
<template #title>
<span class="spanClass">检测结果</span>
</template>
<el-descriptions-item label="毛囊群数" align="center">
<template v-if="_.isEmpty(resResult)"><el-tag type="info">暂无数据</el-tag></template>
<template v-else>
<el-tag type="success">{{ resResult.num_follicle_groups }}</el-tag>
</template>
</el-descriptions-item>
<el-descriptions-item label="大毛囊群" align="center">
<template v-if="_.isEmpty(resResult)"><el-tag type="info">暂无数据</el-tag></template>
<template v-else>
<el-tag type="success">{{ resResult.num_big_follicles }}</el-tag>
</template>
</el-descriptions-item>
<el-descriptions-item label="小毛囊群" align="center">
<template v-if="_.isEmpty(resResult)"><el-tag type="info">暂无数据</el-tag></template>
<template v-else>
<el-tag type="success">{{ resResult.num_small_follicles }}</el-tag>
</template>
</el-descriptions-item>
</el-descriptions>
</el-col>
</el-row>
<el-row class="imgClass">
<el-col :span="8">
<div>
<template v-if="_.isEmpty(resResult)"><span class="noDataClass">暂无数据</span></template>
<template v-else>
<el-image style="width: 500px; height: 500px"
:src="`${baseUrl}/api/getPng/${_.split(resResult.originalImgPath, '/')[1]}`" :zoom-rate="1.2"
:max-scale="7" :min-scale="0.2" fit="cover" />
</template>
</div>
</el-col>
<el-col :span="8">
<div>
<template v-if="_.isEmpty(resResult)"><span class="noDataClass">暂无数据</span></template>
<template v-else>
<el-image style="width: 500px; height: 500px"
:src="`${baseUrl}/api/getPng/${_.split(resResult.result1Path, '/')[1]}`" :zoom-rate="1.2" :max-scale="7"
:min-scale="0.2" fit="cover" />
</template>
</div>
</el-col>
<el-col :span="8">
<div>
<template v-if="_.isEmpty(resResult)"><span class="noDataClass">暂无数据</span></template>
<template v-else>
<el-image style="width: 500px; height: 500px"
:src="`${baseUrl}/api/getPng/${_.split(resResult.result2Path, '/')[1]}`" :zoom-rate="1.2" :max-scale="7"
:min-scale="0.2" fit="cover" />
</template>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import _ from "lodash";
import dayjs from "dayjs";
import axios from "axios";
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue'
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { ElMessage } from 'element-plus'
export default {
name: "app",
components: { ArrowDown, ArrowUp },
data() {
return {
_: _,
dayjs: dayjs,
iou: 0.1,
conf: 0.25,
checkLoading: false,
resResult: {},
baseUrl: "http://127.0.0.1:8006",
};
},
methods: {
/**
* 上传检查图片
*/
beforeAvatarUpload(rawFile) {
let imgList = ["image/jpeg", "image/png"]
if (imgList.indexOf(rawFile.type) === -1) {
this.$msgbox.alert('请上传.png,.jpg,.jpeg格式的图片!')
return false
} else if (rawFile.size / 1024 / 1024 > 50) {
this.$msgbox.alert('图片文件的大小为小于50MB,过大时会处理过慢')
return true
}
return true
},
//
async successSubmit(opts) {
ElMessage({
showClose: true,
duration: 0,
type: "success",
message: '正在检测,请耐心等待后,稍后查看结果',
})
this.resResult = {}
NProgress.configure({ showSpinner: false });
NProgress.configure({ parent: '.bgimg' });
this.checkLoading = true
NProgress.start();
NProgress.inc();
let that = this
let file = opts.file
let fileReader = new FileReader()
fileReader.onload = async function (e) {
let formData = new FormData();
formData.append('file', file);
formData.append('iou', that.iou);
formData.append('conf', that.conf);
let res = await axios.post('http://127.0.0.1:8006/api/checkPng', formData, {
headers: { 'content-type': 'multipart/form-data' },
})
that.resResult = res.data
that.checkLoading = false
ElMessage.closeAll()
NProgress.done();
}
fileReader.onerror = function (error) {
console.error('Error reading file:', error)
}
fileReader.readAsArrayBuffer(file)
},
//
handlePictureCardPreview(file) {
console.log(7777, file)
},
//
async checkPng() {
let res = await axios.get('http://127.0.0.1:8006/api/checkResult')
console.log(8977, res)
}
},
async mounted() {
},
watch: {},
computed: {},
};
</script>
<style scoped>
.bgimg {
height: calc(100vh - 20px);
width: calc(100vw - 16px);
padding: 12px 8px 8px 8px;
background-color: #ecf5ff;
}
.inputClass {
text-align: center;
margin: 60px 0 20px 0;
}
.outClass {
text-align: center;
margin: 20px 0;
}
.imgClass {
margin: 20px;
text-align: center;
}
.spanClass {
font-size: 3em;
}
.noDataClass {
font-size: 2em;
color: #909399;
}
</style>

313
front/src/App.vue

@ -1,50 +1,106 @@
<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-row class="inputClass">
<el-col :span="4">
<el-upload :show-file-list="false" :before-upload="beforeAvatarUpload" :http-request="successSubmit"
accept=".png,.jpg,.jpeg">
<el-button :disabled="checkLoading" type="primary">
<span>上传图片</span>
</el-button>
</div>
</el-upload>
</el-col>
<el-col :span="8">
<span>IoU阈值</span>
<el-input-number v-model="iou" :min="0.01" :max="1" :step="0.01" :disabled="checkLoading">
<template #decrease-icon>
<el-icon>
<ArrowDown />
</el-icon>
</template>
<template #increase-icon>
<el-icon>
<ArrowUp />
</el-icon>
</template>
</el-input-number>
</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-col :span="8">
<span>置信度阈值</span>
<el-input-number v-model="conf" :min="0.01" :max="1" :step="0.01" :disabled="checkLoading">
<template #decrease-icon>
<el-icon>
<ArrowDown />
</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>
</template>
<template #increase-icon>
<el-icon>
<ArrowUp />
</el-icon>
</el-tag>
</template>
</el-input-number>
</el-col>
<el-col :span="4">
<el-button type="success" :disabled="checkLoading" @click="checkMaonang">
<span>提取毛囊</span>
</el-button>
</el-col>
</el-row>
<el-row class="outClass">
<el-col>
<el-descriptions direction="vertical" :column="3" border>
<template #title>
<span class="spanClass">检测结果</span>
</template>
<el-descriptions-item label="毛囊群数" align="center">
<template v-if="_.isEmpty(resResult)"><el-tag type="info">暂无数据</el-tag></template>
<template v-else>
<el-tag type="success">{{ resResult.num_follicle_groups }}</el-tag>
</template>
</el-descriptions-item>
<el-descriptions-item label="大毛囊群" align="center">
<template v-if="_.isEmpty(resResult)"><el-tag type="info">暂无数据</el-tag></template>
<template v-else>
<el-tag type="success">{{ resResult.num_big_follicles }}</el-tag>
</template>
</el-descriptions-item>
<el-descriptions-item label="小毛囊群" align="center">
<template v-if="_.isEmpty(resResult)"><el-tag type="info">暂无数据</el-tag></template>
<template v-else>
<el-tag type="success">{{ resResult.num_small_follicles }}</el-tag>
</template>
</el-descriptions-item>
</el-descriptions>
</el-col>
</el-row>
<el-row class="imgClass">
<el-col :span="8">
<div>
<template v-if="_.isEmpty(savePngResult)"><span class="noDataClass">暂无数据</span></template>
<template v-else>
<el-image style="width: 500px; height: 500px"
:src="`${baseUrl}/api/getPng/${_.split(savePngResult.originalImgPath, '/')[1]}`" :zoom-rate="1.2"
:max-scale="7" :min-scale="0.2" fit="cover" />
</template>
</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" />
</el-col>
<el-col :span="8">
<div>
<template v-if="_.isEmpty(resResult)"><span class="noDataClass">暂无数据</span></template>
<template v-else>
<el-image style="width: 500px; height: 500px"
:src="`${baseUrl}/api/getPng/${_.split(resResult.result1Path, '/')[1]}`" :zoom-rate="1.2" :max-scale="7"
:min-scale="0.2" fit="cover" />
</template>
</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>
<el-col :span="8">
<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 />
<template v-if="_.isEmpty(resResult)"><span class="noDataClass">暂无数据</span></template>
<template v-else>
<el-image style="width: 500px; height: 500px"
:src="`${baseUrl}/api/getPng/${_.split(resResult.result2Path, '/')[1]}`" :zoom-rate="1.2" :max-scale="7"
:min-scale="0.2" fit="cover" />
</template>
</div>
</el-col>
</el-row>
@ -52,118 +108,137 @@
</template>
<script>
import { result } from "lodash";
import imgPath from "./assets/config/imgconf.json";
import _ from "lodash";
import dayjs from "dayjs";
import axios from "axios";
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue'
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { ElMessage } from 'element-plus'
export default {
name: "app",
components: { ArrowDown, ArrowUp },
data() {
return {
result: "",
content: "",
percentage: 0,
isSend: false,
loading: false,
imgList: [],
sendmessage: null,
classZh: {
negative: "消极",
positive: "积极",
},
_: _,
dayjs: dayjs,
iou: 0.1,
conf: 0.25,
checkLoading: false,
savePngResult: {},
resResult: {},
baseUrl: "http://127.0.0.1:8006",
};
},
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;
/**
* 上传检查图片
*/
beforeAvatarUpload(rawFile) {
let imgList = ["image/jpeg", "image/png"]
if (imgList.indexOf(rawFile.type) === -1) {
this.$msgbox.alert('请上传.png,.jpg,.jpeg格式的图片!')
return false
} else if (rawFile.size / 1024 / 1024 > 50) {
this.$msgbox.alert('图片文件的大小为小于50MB,过大时会处理过慢')
return true
}
return true
},
},
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 {
//
async successSubmit(opts) {
ElMessage({
type: "success",
message: '正在上传,请稍后',
})
this.savePngResult = {}
NProgress.configure({ showSpinner: false });
NProgress.configure({ parent: '.bgimg' });
this.checkLoading = true
NProgress.start();
NProgress.inc();
let that = this
let file = opts.file
let fileReader = new FileReader()
fileReader.onload = async function (e) {
let formData = new FormData();
formData.append('file', file);
let res = await axios.post('http://127.0.0.1:8006/api/savePng', formData, {
headers: { 'content-type': 'multipart/form-data' },
})
that.savePngResult = res.data
that.checkLoading = false
ElMessage.closeAll()
NProgress.done();
}
fileReader.onerror = function (error) {
console.error('Error reading file:', error)
}
fileReader.readAsArrayBuffer(file)
},
result(nev, olv) {
this.loading = !nev;
//
async checkMaonang() {
ElMessage({
showClose: true,
duration: 0,
type: "success",
message: '正在检测,请耐心等待后,稍后查看结果',
})
this.resResult = {}
NProgress.configure({ showSpinner: false });
NProgress.configure({ parent: '.bgimg' });
this.checkLoading = true
NProgress.start();
NProgress.inc();
let formData = new FormData();
formData.append('iou', this.iou);
formData.append('conf', this.conf);
let res = await axios.post('http://127.0.0.1:8006/api/checkPng', formData, {
headers: { 'content-type': 'multipart/form-data' },
})
this.resResult = res.data
this.checkLoading = false
ElMessage.closeAll()
NProgress.done();
},
},
computed: {
duration() {
return Math.floor(this.percentage / 10);
},
async mounted() {
},
watch: {},
computed: {},
};
</script>
<style scoped>
.bgimg {
height: 100vh;
width: 100vw;
color: #fff;
background-image: url("@/assets/imgs/otherimgs/bg.jpg");
height: calc(100vh - 20px);
width: calc(100vw - 16px);
padding: 12px 8px 8px 8px;
background-color: #ecf5ff;
}
.sendButton {
align-content: center;
.inputClass {
text-align: center;
margin: 60px 0 60px 0;
}
.text-align {
.outClass {
text-align: center;
margin: 20px 30px 20px 30px;
}
.pa-2 {
padding: 4px;
.imgClass {
margin: 20px;
text-align: center;
}
.tagClass {
height: 1.6em;
.spanClass {
font-size: 3em;
}
.opacitybg {
opacity: 0.6;
.noDataClass {
font-size: 2em;
color: #909399;
}
</style>

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

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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

2
front/src/main.js

@ -9,5 +9,5 @@ import axios from "axios"
const app = createApp(App)
app.config.globalProperties.$_ = _
app.config.globalProperties.$axios = axios
app.use(ElementPlus)
app.use(ElementPlus, { size: "large" })
app.mount('#app')

17
front/src/style.css

@ -2,4 +2,21 @@ html,
body {
margin: 0;
padding: 0;
font-size: 1.2em;
}
#nprogress .bar {
background: #79bbff !important;
height: 6px;
}
.el-descriptions__body .el-descriptions__table .el-descriptions__cell {
font-size: 3em !important;
height: 64px;
}
.el-tag--large {
font-size: 26px;
line-height: 30px;
height: 48px;
}

BIN
front/vite600.zip

Binary file not shown.

161
inference copy.py

@ -0,0 +1,161 @@
import cv2
import torch
import numpy as np
from PIL import Image
from ultralytics import YOLO
from flask import Flask, request, send_file, make_response
from flask_cors import CORS
import time
from io import BytesIO
# 3个输入参数
img_path = "img0002.jpg"
iou = 0.1
conf = 0.25
originalImgPath = ""
result1Path = ""
result2Path = ""
app = Flask(__name__)
# 解决跨域问题
cors = CORS(
app,
resources={
r"/api/*": {
"origins": ["http://localhost:5173"],
"methods": ["GET", "POST"],
}
},
)
# 初始化模型
resResult = ""
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")
# 保存检测图片
@app.route("/api/checkPng", methods=["POST"])
def checkPng():
global follicle_groups_detector
global follicles_detector
global originalImgPath
global result1Path
global result2Path
global resResult
pngFile = request.files["file"]
img = Image.open(pngFile.stream)
iou = request.form.get("iou")
iou = float(iou)
conf = request.form.get("conf")
conf = float(conf)
nowtime1 = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time()))
originalImgPath = "images/original" + str(nowtime1) + ".png"
result1Path = "images/result1" + str(nowtime1) + ".png"
result2Path = "images/result2" + str(nowtime1) + ".png"
img.save(originalImgPath)
results = follicle_groups_detector(originalImgPath, 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(result1Path) # 输出结果图1
img_list, indexes, (height, width) = split_image(result1Path)
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(result2Path) # 输出结果图2
resResult = {
"hasError": False,
"num_follicle_groups": num_follicle_groups,
"num_big_follicles": num_big_follicles,
"num_small_follicles": num_small_follicles,
"originalImgPath": originalImgPath,
"result1Path": result1Path,
"result2Path": result2Path,
}
return resResult
# 检测结果返回
@app.route("/api/checkResult", methods=["GET"])
def checkResult():
global resResult
return resResult
# 图片查询
@app.route("/api/getPng/<pngPath>", methods=["GET"])
def getPng(pngPath):
# 打开或处理图像
img = Image.open("images/" + pngPath)
# 对图像进行处理,例如调整大小
img = img.resize((800, 600))
# 创建一个 BytesIO 对象来保存图像数据
img_byte_arr = BytesIO()
img.save(img_byte_arr, format="PNG")
img_byte_arr.seek(0)
# 使用 send_file 返回图像数据
# return send_file(img_byte_arr, mimetype='image/jpeg')
# 或者使用 make_response 来创建一个响应对象
response = make_response(img_byte_arr.getvalue())
response.headers.set("Content-Type", "image/jpeg")
response.headers.set("Content-Disposition", "attachment", filename=pngPath)
return response
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8006, debug=True)

175
inference.py

@ -3,12 +3,33 @@ import torch
import numpy as np
from PIL import Image
from ultralytics import YOLO
from flask import Flask, request, send_file, make_response
from flask_cors import CORS
import time
from io import BytesIO
# 3个输入参数
img_path = 'img0002.jpg'
img_path = "img0002.jpg"
iou = 0.1
conf = 0.25
originalImgPath = ""
result1Path = ""
result2Path = ""
nowtime1 = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time()))
app = Flask(__name__)
# 解决跨域问题
cors = CORS(
app,
resources={
r"/api/*": {
"origins": ["http://localhost:5173"],
"methods": ["GET", "POST"],
}
},
)
# 初始化模型
resResult = ""
def split_image(img_path, size=(800, 800)):
img = cv2.imread(img_path)
@ -36,46 +57,118 @@ def combine_images(pred_imgs, indexes, size=(800, 800), img_shape=(3000, 4000)):
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]
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)
follicle_groups_detector = YOLO("follicle_groups.pt")
follicles_detector = YOLO("follicles.pt")
# 保存图片
@app.route("/api/savePng", methods=["POST"])
def savePng():
global nowtime1
global originalImgPath
nowtime1 = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time()))
pngFile = request.files["file"]
img = Image.open(pngFile.stream)
originalImgPath = "images/original" + str(nowtime1) + ".png"
img.save(originalImgPath)
resResult = {
"hasError": False,
"originalImgPath": originalImgPath,
}
return resResult
# 检测图片
@app.route("/api/checkPng", methods=["POST"])
def checkPng():
global follicle_groups_detector
global follicles_detector
global originalImgPath
global nowtime1
global result1Path
global result2Path
global resResult
iou = request.form.get("iou")
iou = float(iou)
conf = request.form.get("conf")
conf = float(conf)
result1Path = "images/result1" + str(nowtime1) + ".png"
result2Path = "images/result2" + str(nowtime1) + ".png"
results = follicle_groups_detector(originalImgPath, 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()
num_follicle_groups = len(r.boxes)
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
im = Image.fromarray(im_array[..., ::-1])
im.save(result1Path) # 输出结果图1
img_list, indexes, (height, width) = split_image(result1Path)
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(result2Path) # 输出结果图2
resResult = {
"hasError": False,
"num_follicle_groups": num_follicle_groups,
"num_big_follicles": num_big_follicles,
"num_small_follicles": num_small_follicles,
"originalImgPath": originalImgPath,
"result1Path": result1Path,
"result2Path": result2Path,
}
return resResult
# 检测结果返回
@app.route("/api/checkResult", methods=["GET"])
def checkResult():
global resResult
return resResult
# 图片查询
@app.route("/api/getPng/<pngPath>", methods=["GET"])
def getPng(pngPath):
# 打开或处理图像
img = Image.open("images/" + pngPath)
# 对图像进行处理,例如调整大小
img = img.resize((800, 600))
# 创建一个 BytesIO 对象来保存图像数据
img_byte_arr = BytesIO()
img.save(img_byte_arr, format="PNG")
img_byte_arr.seek(0)
# 使用 send_file 返回图像数据
# return send_file(img_byte_arr, mimetype='image/jpeg')
# 或者使用 make_response 来创建一个响应对象
response = make_response(img_byte_arr.getvalue())
response.headers.set("Content-Type", "image/jpeg")
response.headers.set("Content-Disposition", "attachment", filename=pngPath)
return response
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8006, debug=True)

BIN
results_1.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 KiB

BIN
results_2.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

Loading…
Cancel
Save