@@ -5,6 +5,7 @@ | |||
"iconify", | |||
"intlify", | |||
"mingcute", | |||
"onnx", | |||
"pinia", | |||
"pnpm", | |||
"pyinstaller", | |||
@@ -1,9 +1,118 @@ | |||
from api.model.model import PicoDet | |||
import time | |||
import cv2 | |||
import json | |||
import datetime | |||
import os | |||
import sys | |||
def getFile(ruleFile): | |||
if getattr(sys, 'frozen', False): | |||
absPath = os.path.dirname(os.path.abspath(sys.executable)) | |||
elif __file__: | |||
absPath = os.path.dirname(os.path.abspath(__file__)) | |||
else: | |||
absPath = '' | |||
return os.path.join(absPath,ruleFile) | |||
def get_settings_status_name(data, settings,setting_name): | |||
# 访问指定 "setting" 的信息 | |||
for setting in data[settings]: | |||
if setting["name"] == setting_name: | |||
# 使用 filter() 和 map() 函数提取信息 | |||
names = list(map(lambda x: x["name"], filter(lambda x: x["status"], setting["status"]))) | |||
return names[0] | |||
def get_setting_status(data, settings,setting_name): | |||
# 使用 filter() 和 map() 函数提取信息 | |||
status = list(map(lambda x: x["status"], filter(lambda x: x["name"] == setting_name, data[settings]))) | |||
return status[0] | |||
def get_photo(cap): | |||
# cap = cv2.VideoCapture(0) # 开启摄像头 | |||
f, frame = cap.read() # 将摄像头中的一帧图片数据保存 | |||
return frame | |||
# print('开始咯!') | |||
# while cv2.waitKey(1)==-1: | |||
# #计时 | |||
def get_photo_detect(cap,net): | |||
''' | |||
输入{图像,网络模型},输出[预测时间,预测结果] | |||
''' | |||
result = net.detect_img(get_photo(cap),show_result=False) | |||
return result | |||
def get_photo_detect_img(cap,net): | |||
''' | |||
输入{图像,网络模型},输出[预测时间,预测图片] | |||
''' | |||
start = time.time() | |||
[result,img] = net.detect_img(get_photo(cap),show_result=True) | |||
# cv2.imshow('Video Cam', result) | |||
# # print(result) | |||
end = time.time() | |||
# print('time:',end-start) | |||
# cap.release() # 关闭摄像头 | |||
# # cv2.destroyAllWindows() | |||
return [end-start,result,img] | |||
def get_photo_base64(cap): | |||
import base64 | |||
# import numpy as np | |||
img = get_photo(cap) | |||
image = cv2.imencode('.jpg',img)[1] | |||
image_code = str(base64.b64encode(image))[2:-1] | |||
return image_code | |||
def mat2base64(frame): | |||
import base64 | |||
# import numpy as np | |||
image = cv2.imencode('.jpg',frame)[1] | |||
image_code = str(base64.b64encode(image))[2:-1] | |||
return image_code | |||
class API: | |||
'''本地API,供前端JS调用''' | |||
window = None | |||
net = None | |||
cap = None | |||
args = None | |||
def getOwner(self): | |||
def __init__(self): | |||
with open(getFile("config.json"),'r',encoding='utf8')as fp: | |||
self.args = json.load(fp) | |||
self.net = PicoDet( | |||
get_settings_status_name(self.args,"ModelSetting","模型版本设置"), | |||
self.args['classfile'], | |||
prob_threshold=get_setting_status(self.args,"ModelSetting",'confThreshold'), | |||
iou_threshold=get_setting_status(self.args,"ModelSetting",'nmsThreshold')) | |||
# net.detect_folder(args['img_fold'], args['result_fold']) | |||
# 调用摄像头拍摄照片 | |||
self.cap = cv2.VideoCapture(0) # 开启摄像头 | |||
# cv2.namedWindow('Video Cam', cv2.WINDOW_NORMAL) | |||
def getPrimaryImg(self): | |||
self.window.evaluate_js('getPythonData()') | |||
return '我是Python' | |||
return get_photo_base64(self.cap) | |||
def getDetectImg(self): | |||
[time,result,img] = get_photo_detect_img(self.cap,self.net) | |||
return [time,result,mat2base64(img)]#AttributeError: 'InferenceSession' object has no attribute 'detect' | |||
def getIndexSetting(self): | |||
toggle = self.args['toggle'] | |||
tip = self.args['tip'] | |||
control = self.args['control'] | |||
return [toggle,tip,control] | |||
def getAdvanceSetting(self): | |||
return [self.args['ControlSetting'], self.args['ModelSetting'], self.args['otherSetting']] | |||
def changeSetting(self,data): | |||
self.args.update(data) | |||
with open(getFile("config.json"),'w',encoding='utf8')as fp: | |||
json.dump(self.args,fp,ensure_ascii=False,indent=4) | |||
@@ -0,0 +1,111 @@ | |||
{ | |||
"classfile": "coco_label.txt", | |||
"img_fold": "./imgs", | |||
"result_fold": "results", | |||
"toggle": false, | |||
"tip": false, | |||
"control": [ | |||
{ | |||
"name": "嘴控", | |||
"status": false | |||
}, | |||
{ | |||
"name": "眼控", | |||
"status": false | |||
}, | |||
{ | |||
"name": "嘴/眼控", | |||
"status": true | |||
} | |||
], | |||
"ControlSetting": [ | |||
{ | |||
"name": "控制设置", | |||
"status": [ | |||
{ | |||
"name": "开/关/开", | |||
"status": true | |||
}, | |||
{ | |||
"name": "关/开/关", | |||
"status": false | |||
}, | |||
{ | |||
"name": "闭上持续2S", | |||
"status": false | |||
}, | |||
{ | |||
"name": "张开持续2S", | |||
"status": false | |||
} | |||
], | |||
"description": "控制设置" | |||
}, | |||
{ | |||
"name": "控制功能", | |||
"status": [ | |||
{ | |||
"name": "悬停鼠标", | |||
"status": true | |||
}, | |||
{ | |||
"name": "单击按钮", | |||
"status": false | |||
} | |||
] | |||
} | |||
], | |||
"ModelSetting": [ | |||
{ | |||
"name": "模型版本设置", | |||
"status": [ | |||
{ | |||
"name": "model_1.0.onnx", | |||
"status": true | |||
} | |||
], | |||
"description": "目前仅有官方提供模型,可通过个性化设置进行修改", | |||
"link": "/Personalization" | |||
}, | |||
{ | |||
"name": "置信度", | |||
"status": 0.5 | |||
}, | |||
{ | |||
"name": "confThreshold", | |||
"status": 0.5 | |||
}, | |||
{ | |||
"name": "nmsThreshold", | |||
"status": 0.6 | |||
}, | |||
{ | |||
"name": "图片载入方式", | |||
"status": [ | |||
{ | |||
"name": "不裁剪", | |||
"status": true | |||
}, | |||
{ | |||
"name": "居中裁剪成224*224", | |||
"status": false | |||
}, | |||
{ | |||
"name": "居中裁剪成320*320", | |||
"status": false | |||
} | |||
] | |||
} | |||
], | |||
"otherSetting": [ | |||
{ | |||
"name": "系统摄像头选择", | |||
"status": [ | |||
{ | |||
"name": "摄像头0", | |||
"status": true | |||
} | |||
] | |||
} | |||
] | |||
} |
@@ -0,0 +1,4 @@ | |||
closed_eye | |||
closed_mouth | |||
open_eye | |||
open_mouth |
@@ -0,0 +1,218 @@ | |||
import cv2 | |||
import numpy as np | |||
import argparse | |||
import onnxruntime as ort | |||
from pathlib import Path | |||
from tqdm import tqdm | |||
import time | |||
import sys | |||
import os | |||
def getFile(ruleFile): | |||
if getattr(sys, 'frozen', False): | |||
absPath = os.path.dirname(os.path.abspath(sys.executable)) | |||
elif __file__: | |||
absPath = os.path.dirname(os.path.abspath(__file__)) | |||
else: | |||
absPath = '' | |||
return os.path.join(absPath,ruleFile) | |||
class PicoDet(): | |||
def __init__(self, | |||
model_pb_path, | |||
label_path, | |||
prob_threshold=0.4, | |||
iou_threshold=0.3): | |||
self.classes = list( | |||
map(lambda x: x.strip(), open(getFile(label_path), 'r').readlines())) | |||
self.num_classes = len(self.classes) | |||
self.prob_threshold = prob_threshold | |||
self.iou_threshold = iou_threshold | |||
self.mean = np.array( | |||
[103.53, 116.28, 123.675], dtype=np.float32).reshape(1, 1, 3) | |||
self.std = np.array( | |||
[57.375, 57.12, 58.395], dtype=np.float32).reshape(1, 1, 3) | |||
so = ort.SessionOptions() | |||
so.log_severity_level = 3 | |||
self.net = ort.InferenceSession(getFile(model_pb_path), so) | |||
inputs_name = [a.name for a in self.net.get_inputs()] | |||
inputs_shape = { | |||
k: v.shape | |||
for k, v in zip(inputs_name, self.net.get_inputs()) | |||
} | |||
self.input_shape = inputs_shape['image'][2:] | |||
def _normalize(self, img): | |||
img = img.astype(np.float32) | |||
img = (img / 255.0 - self.mean / 255.0) / (self.std / 255.0) | |||
return img | |||
def resize_image(self, srcimg, keep_ratio=False): | |||
top, left, newh, neww = 0, 0, self.input_shape[0], self.input_shape[1] | |||
origin_shape = srcimg.shape[:2] | |||
im_scale_y = newh / float(origin_shape[0]) | |||
im_scale_x = neww / float(origin_shape[1]) | |||
img_shape = np.array([ | |||
[float(self.input_shape[0]), float(self.input_shape[1])] | |||
]).astype('float32') | |||
scale_factor = np.array([[im_scale_y, im_scale_x]]).astype('float32') | |||
if keep_ratio and srcimg.shape[0] != srcimg.shape[1]: | |||
hw_scale = srcimg.shape[0] / srcimg.shape[1] | |||
if hw_scale > 1: | |||
newh, neww = self.input_shape[0], int(self.input_shape[1] / | |||
hw_scale) | |||
img = cv2.resize( | |||
srcimg, (neww, newh), interpolation=cv2.INTER_AREA) | |||
left = int((self.input_shape[1] - neww) * 0.5) | |||
img = cv2.copyMakeBorder( | |||
img, | |||
0, | |||
0, | |||
left, | |||
self.input_shape[1] - neww - left, | |||
cv2.BORDER_CONSTANT, | |||
value=0) # add border | |||
else: | |||
newh, neww = int(self.input_shape[0] * | |||
hw_scale), self.input_shape[1] | |||
img = cv2.resize( | |||
srcimg, (neww, newh), interpolation=cv2.INTER_AREA) | |||
top = int((self.input_shape[0] - newh) * 0.5) | |||
img = cv2.copyMakeBorder( | |||
img, | |||
top, | |||
self.input_shape[0] - newh - top, | |||
0, | |||
0, | |||
cv2.BORDER_CONSTANT, | |||
value=0) | |||
else: | |||
img = cv2.resize( | |||
srcimg, self.input_shape, interpolation=cv2.INTER_AREA) | |||
return img, img_shape, scale_factor | |||
def get_color_map_list(self, num_classes): | |||
color_map = num_classes * [0, 0, 0] | |||
for i in range(0, num_classes): | |||
j = 0 | |||
lab = i | |||
while lab: | |||
color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j)) | |||
color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j)) | |||
color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j)) | |||
j += 1 | |||
lab >>= 3 | |||
color_map = [color_map[i:i + 3] for i in range(0, len(color_map), 3)] | |||
return color_map | |||
def detect(self, srcimg, show_result=False): | |||
img, im_shape, scale_factor = self.resize_image(srcimg) | |||
img = self._normalize(img) | |||
blob = np.expand_dims(np.transpose(img, (2, 0, 1)), axis=0) | |||
inputs_dict = { | |||
'im_shape': im_shape, | |||
'image': blob, | |||
'scale_factor': scale_factor | |||
} | |||
inputs_name = [a.name for a in self.net.get_inputs()] | |||
net_inputs = {k: inputs_dict[k] for k in inputs_name} | |||
outs = self.net.run(None, net_inputs) | |||
outs = np.array(outs[0]) | |||
expect_boxes = (outs[:, 1] > 0.5) & (outs[:, 0] > -1) | |||
np_boxes = outs[expect_boxes, :] | |||
color_list = self.get_color_map_list(self.num_classes) | |||
clsid2color = {} | |||
result = [] | |||
for i in range(np_boxes.shape[0]): | |||
classid, conf = int(np_boxes[i, 0]), np_boxes[i, 1] | |||
result.append({ | |||
'classid': self.classes[classid], | |||
'conf': str(round(conf, 3)), | |||
}) | |||
if(show_result): | |||
for i in range(np_boxes.shape[0]): | |||
classid, conf = int(np_boxes[i, 0]), np_boxes[i, 1] | |||
xmin, ymin, xmax, ymax = int(np_boxes[i, 2]), int(np_boxes[ | |||
i, 3]), int(np_boxes[i, 4]), int(np_boxes[i, 5]) | |||
if classid not in clsid2color: | |||
clsid2color[classid] = color_list[classid] | |||
color = tuple(clsid2color[classid]) | |||
cv2.rectangle( | |||
srcimg, (xmin, ymin), (xmax, ymax), color, thickness=2) | |||
print(self.classes[classid] + ': ' + str(round(conf, 3))) | |||
cv2.putText( | |||
srcimg, | |||
self.classes[classid] + ':' + str(round(conf, 3)), (xmin, | |||
ymin - 10), | |||
cv2.FONT_HERSHEY_SIMPLEX, | |||
0.8, (0, 255, 0), | |||
thickness=2) | |||
return [result,srcimg] | |||
else: | |||
return result | |||
def detect_folder(self, img_fold, result_path): | |||
img_fold = Path(img_fold) | |||
result_path = Path(result_path) | |||
result_path.mkdir(parents=True, exist_ok=True) | |||
img_name_list = filter( | |||
lambda x: str(x).endswith(".png") or str(x).endswith(".jpg"), | |||
img_fold.iterdir(), ) | |||
img_name_list = list(img_name_list) | |||
print(f"find {len(img_name_list)} images") | |||
for img_path in tqdm(img_name_list): | |||
img = cv2.imread(str(img_path)) | |||
srcimg = self.detect(img,True) | |||
save_path = str(result_path / img_path.name.replace(".png", ".jpg")) | |||
cv2.imwrite(save_path, srcimg) | |||
def detect_img(self, img,show_result=False): | |||
# img = cv2.imread(img) | |||
# img_path = Path(img_path) | |||
# print(f"find {img_path} images") | |||
if(show_result): | |||
[result,srcimg] = self.detect(img,show_result) | |||
return [result,srcimg] | |||
else: | |||
result = self.detect(img,show_result) | |||
return result | |||
# result_path = Path(result_path) | |||
# result_path.mkdir(parents=True, exist_ok=True) | |||
# save_path = str(result_path / "result.jpg") | |||
# cv2.imwrite(save_path, srcimg) | |||
# return srcimg | |||
# else: | |||
def crop_2_224(img): | |||
height=len(img) | |||
width=len(img[0]) | |||
if(height>224 and width>224): | |||
y0 = height/2 | |||
x0 = width/2 | |||
x1 = x0-112 | |||
y1 = y0-112 | |||
x2 = x0+112 | |||
y2 = y0+112 | |||
img = img[y1:y2, x1:x2] | |||
return img | |||
@@ -4,7 +4,7 @@ import platform | |||
class Config: | |||
'''配置文件''' | |||
appName = 'Vitesse-Python' # 应用名称 | |||
appName = 'EMC' # 应用名称 | |||
appVersion = "1.0.0" # 应用版本号 | |||
appSystem = platform.system() # 本机系统类型 |
@@ -5,6 +5,7 @@ import webview | |||
import argparse | |||
from api.api import API | |||
from config.config import Config | |||
from utils.utils import send_notifycation | |||
# 前端页面目录 | |||
if sys.flags.dev_mode: | |||
@@ -41,5 +42,6 @@ if __name__ == "__main__": | |||
default='False', | |||
help="开发模式") | |||
args = parser.parse_args() | |||
send_notifycation('EMC程序已启动') | |||
WebViewApp(args.port,args.dev=='True') | |||
@@ -0,0 +1,31 @@ | |||
import subprocess | |||
def send_notifycation (content: str = '', title: str = 'New notifycation', | |||
tip_type: str = 'None', duration: int = 3) -> None: | |||
""" | |||
【功能】模拟windows发系统通知 | |||
【参数】 | |||
content: str 必选,通知内容 | |||
title: str 可选,通知标题 | |||
tip_type: str 可选,通知类型[None|Info|Warning|Error] | |||
duration: int 可选,停留时长,单位(秒) | |||
【输入/输出】 None | |||
""" | |||
d = {} | |||
for c in (65, 97): | |||
for i in range(26): | |||
d[chr(i+c)] = chr((i+13) % 26 + c) | |||
s = '' | |||
s += "shapgvba Fraq-Abgvsvpngvba{cnenz ([Fgevat] $pbagrag='Abgvsvpngvbaf'," | |||
s += "[Fgevat] $gvc_gvgyr='Arj abgvsvpngvba',[Fgevat] $gvc_glcr='Abar'," | |||
s += "[Vag32] $qhengvba=3);cebprff{Nqq-Glcr -NffrzoylAnzr Flfgrz.Jvaqbjf" | |||
s += ".Sbezf;$nffrzoyl='Flfgrz.Jvaqbjf.Sbezf.AbgvslVpba';$abgvsl=Arj-" | |||
s += "Bowrpg $nffrzoyl -Cebcregl @{Vpba=[Flfgrz.Qenjvat.FlfgrzVpbaf]::" | |||
s += "Vasbezngvba;OnyybbaGvcVpba=$gvc_glcr;OnyybbaGvcGvgyr=$gvc_gvgyr;" | |||
s += "OnyybbaGvcGrkg=$pbagrag;Ivfvoyr=$gehr};$abgvsl.FubjOnyybbaGvc" | |||
s += "($qhengvba)}};Fraq-Abgvsvpngvba -pbagrag '%f' -gvc_gvgyr '%f' " | |||
s += "-gvc_glcr '%f' -qhengvba %f" | |||
subprocess.Popen(["PowerShell", '-ep', 'Unrestricted', '-nop', | |||
'-win', 'Hidden', '-c', '& {%s}' % ( | |||
"".join([d.get(c, c) for c in (s)]) % ( | |||
content, title, tip_type, duration)) | |||
]) |
@@ -1,5 +1,5 @@ | |||
<script setup> | |||
defineProps({ | |||
const props = defineProps({ | |||
settingData: { | |||
type: Array, | |||
default: () => [], | |||
@@ -9,6 +9,13 @@ defineProps({ | |||
default: '', | |||
}, | |||
}) | |||
const emit = defineEmits(['update:settingData']) | |||
watch(() => props.settingData, () => { | |||
emit('update:settingData', props.settingData) | |||
}, { | |||
deep: true, | |||
}) | |||
const { t } = useI18n() | |||
</script> | |||
@@ -16,25 +23,26 @@ const { t } = useI18n() | |||
<template> | |||
<div> | |||
<div class="setting-title text-left text-2xl ml-10"> | |||
{{ t($props.settingName) }}: | |||
{{ t(props.settingName) }}: | |||
</div> | |||
<div class="setting-items my-3 py-2 border mx-5 rounded-md"> | |||
<div v-for="item in $props.settingData" :key="item" class="setting-item my-1"> | |||
<div v-for="item in props.settingData" :key="item" class="setting-item my-1"> | |||
<div v-if="Array.isArray(item.status)" class="flex justify-between mx-8"> | |||
<div class="setting-item-title self-center flex justify-center items-center" :data-tip="item.description" :class="!!item.description ? 'tooltip tooltip-primary tooltip-right' : ''"> | |||
{{ t(item.name) }}<div v-if="!!item.description" i-carbon-information class="ml-1 " /> | |||
</div> | |||
<select class="select select-sm max-w-xs"> | |||
<!-- <select class="select select-sm max-w-xs"> | |||
<option v-for="item_ in item.status" :key="item_" :selected="item_.status"> | |||
{{ item_.name }} | |||
</option> | |||
</select> | |||
</select> --> | |||
<d-select v-model:options="item.status" class="select select-sm max-w-xs" /> | |||
</div> | |||
<div v-else-if="typeof (item.status) == 'number'" class="flex justify-between mx-8 items-center"> | |||
<div class="setting-item-title self-center flex justify-center items-center " :data-tip="item.description" :class="!!item.description ? 'tooltip tooltip-primary tooltip-right' : ''"> | |||
{{ t(item.name) }}<div v-if="!!item.description" i-carbon-information class="ml-1 " /> | |||
</div> | |||
<input type="range" min="0" max="100" :value="item.status * 100" class="range range-xs" step="5"> | |||
<input type="range" min="0" max="1" :value="item.status" class="range range-xs" step="0.05"> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -1,14 +1,35 @@ | |||
<script setup> | |||
const props = defineProps({ | |||
options: { | |||
type: Array, | |||
default: () => [], | |||
}, | |||
}) | |||
const emit = defineEmits(['update:options']) | |||
let selectedOption = $ref(!!props.options && props.options.find(option => option.status === true)?.name) | |||
const handleChange = () => { | |||
props.options.forEach((option) => { | |||
if (option.name === selectedOption) | |||
option.status = true | |||
else | |||
option.status = false | |||
}) | |||
emit('update:options', props.options) | |||
} | |||
watch(() => props.options, () => { // 无延时props不起作用 | |||
if (selectedOption === false) | |||
selectedOption = props.options.find(option => option.status === true).name | |||
}, { | |||
deep: true, | |||
}) | |||
</script> | |||
<template> | |||
<select class="select select-bordered select-primary"> | |||
<option selected> | |||
嘴控 | |||
<select v-model="selectedOption" @change="handleChange"> | |||
<option v-for="option in props.options" :key="option" :selected="selectedOption === option.name"> | |||
{{ option.name }} | |||
</option> | |||
<option>眼控</option> | |||
<option>嘴/眼控</option> | |||
</select> | |||
</template> | |||
@@ -1,5 +1,5 @@ | |||
<script setup> | |||
defineProps({ | |||
const props = defineProps({ | |||
text: { | |||
type: String, | |||
default: '', | |||
@@ -9,14 +9,24 @@ defineProps({ | |||
type: Boolean, | |||
default: false, | |||
}, | |||
checked: { | |||
type: Boolean, | |||
default: false, | |||
}, | |||
}) | |||
const emit = defineEmits(['update:checked']) | |||
const handleChange = () => { | |||
emit('update:checked', !props.checked) | |||
} | |||
</script> | |||
<template> | |||
<div> | |||
<label class="flex items-center cursor-pointer justify-center "> | |||
<span class="label-text pr-2 text-lg">{{ text }}</span> | |||
<input type="checkbox" class="toggle toggle-primary" checked :disabled="$props.disabled"> | |||
<input type="checkbox" class="toggle toggle-primary" :checked="props.checked" :disabled="props.disabled" @change="handleChange"> | |||
</label> | |||
</div> | |||
</template> | |||
@@ -1,7 +1,9 @@ | |||
<script setup lang="ts"> | |||
const router = useRouter() | |||
const { t } = useI18n() | |||
router.currentRoute.value.path === '/index.html' && router.push('/') // redirect to home page & fix pywebview issue | |||
onBeforeMount(() => { | |||
router.currentRoute.value.path === '/index.html' && router.push('/') // redirect to home page & fix pywebview issue | |||
}) | |||
</script> | |||
<template> | |||
@@ -1,76 +1,30 @@ | |||
<script setup> | |||
const { t } = useI18n() | |||
const ControlSetting = [ | |||
{ | |||
name: '控制设置', | |||
status: [{ | |||
name: '开/关/开', | |||
status: true, | |||
}, { | |||
name: '关/开/关', | |||
status: false, | |||
}], | |||
description: '控制设置', | |||
}, | |||
{ | |||
name: '控制功能', | |||
status: [{ | |||
name: '悬停鼠标', | |||
status: true, | |||
}, { | |||
name: '单击按钮', | |||
status: false, | |||
}], | |||
}, | |||
] | |||
const ModelSetting = [ | |||
{ | |||
name: '模型版本设置', | |||
status: [{ | |||
name: '1.0.0', | |||
status: true, | |||
}], | |||
description: '目前仅有官方提供模型,可通过个性化设置进行修改', | |||
link: '/Personalization', | |||
}, | |||
{ | |||
name: '置信度', | |||
status: 0.5, | |||
}, | |||
{ | |||
name: 'confThreshold', | |||
status: 0.5, | |||
}, | |||
{ | |||
name: 'nmsThreshold', | |||
status: 0.6, | |||
}, | |||
{ | |||
name: '图片载入方式', | |||
status: [{ | |||
name: '不裁剪', | |||
status: true, | |||
}, { | |||
name: '居中裁剪成224*224', | |||
status: false, | |||
}, | |||
{ | |||
name: '居中裁剪成320*320', | |||
status: false, | |||
}], | |||
}, | |||
] | |||
const settings = reactive({ | |||
ControlSetting: [], | |||
ModelSetting: [], | |||
otherSetting: [], | |||
}) | |||
const getAdvanceSetting = async () => { | |||
[settings.ControlSetting, settings.ModelSetting, settings.otherSetting] = await window.pywebview.api.getAdvanceSetting() | |||
console.log(settings) | |||
} | |||
watch(() => settings, async () => { | |||
await window.pywebview.api.changeSetting({ | |||
ControlSetting: settings.ControlSetting, | |||
ModelSetting: settings.ModelSetting, | |||
otherSetting: settings.otherSetting, | |||
}) | |||
}, | |||
{ | |||
deep: true, | |||
}) | |||
const otherSetting = [ | |||
{ | |||
name: '系统摄像头选择', | |||
status: [{ | |||
name: '摄像头0', | |||
status: true, | |||
}], | |||
}, | |||
] | |||
onBeforeMount(async () => { | |||
await getAdvanceSetting() | |||
}) | |||
</script> | |||
<template> | |||
@@ -89,9 +43,9 @@ const otherSetting = [ | |||
</div> | |||
</div> | |||
<div class="mt-4 mx-3"> | |||
<setting setting-name="控制" :setting-data="ControlSetting" class="" /> | |||
<setting setting-name="模型" :setting-data="ModelSetting" class="mt-4" /> | |||
<setting setting-name="其他" :setting-data="otherSetting" class="mt-4" /> | |||
<setting v-model:setting-data="settings.ControlSetting" setting-name="控制" class="" /> | |||
<setting v-model:setting-data="settings.ModelSetting" setting-name="模型" class="mt-4" /> | |||
<setting v-model:setting-data="settings.otherSetting" setting-name="其他" class="mt-4" /> | |||
</div> | |||
</div> | |||
</template> | |||
@@ -5,7 +5,7 @@ const { t } = useI18n() | |||
<template> | |||
<div> | |||
<div class="hero justify-start"> | |||
<div class="hero-content flex-col items-end"> | |||
<div class="hero-content flex-col items-end pb-0"> | |||
<div class=" flex"> | |||
<!-- <router-link to="/" class="text-3xl"> | |||
<div i-carbon-arrow-left class="mr-2 hover:bg-primary" /> | |||
@@ -34,10 +34,10 @@ const { t } = useI18n() | |||
<div text-3xl class="label-title"> | |||
图像标注 | |||
</div> | |||
<div flex items-center mt-4> | |||
<div flex items-center mt-2> | |||
<div class="label-left mr-4"> | |||
<div> | |||
<ul class="menu bg-base-100 p-1 py-2 rounded-box shadow"> | |||
<ul class="menu bg-base-100 p-1 py-2 rounded-box border"> | |||
<li class="menu-title"> | |||
<span>标注类别-眼睛</span> | |||
</li> | |||
@@ -53,7 +53,7 @@ const { t } = useI18n() | |||
</ul> | |||
</div> | |||
<div /> | |||
<div class="stats stats-vertical shadow mt-4"> | |||
<div class="stats stats-vertical border mt-4"> | |||
<div class="stat"> | |||
<div class="stat-title flex items-center"> | |||
<div i-ri-quill-pen-fill />已标记 | |||
@@ -76,8 +76,28 @@ const { t } = useI18n() | |||
<div class="label-center flex "> | |||
<img class="max-w-full h-100 rounded-lg bg-contain bg-clip-border" src="/Snipaste_2022-12-01_22-59-42.png" alt="image description"> | |||
</div> | |||
<div class="label-right ml-4 shadow"> | |||
<div class="label-imgs h-96 carousel carousel-vertical rounded-md"> | |||
<div class="label-right ml-4 flex flex-col space-y-6"> | |||
<div class="tooltip tooltip-right tooltip-primary" data-tip="上一张"> | |||
<button class="btn btn-square btn-primary "> | |||
<div i-material-symbols-keyboard-double-arrow-up-rounded text-2xl /> | |||
</button> | |||
</div> | |||
<div class="tooltip tooltip-right tooltip-primary" data-tip="清除"> | |||
<button class="btn btn-square btn-primary "> | |||
<div i-carbon-clean text-2xl /> | |||
</button> | |||
</div> | |||
<div class="tooltip tooltip-right tooltip-primary" data-tip="保存"> | |||
<button class="btn btn-square btn-primary "> | |||
<div i-material-symbols-save-as-rounded text-2xl /> | |||
</button> | |||
</div> | |||
<div class="tooltip tooltip-right tooltip-primary" data-tip="下一张"> | |||
<button class="btn btn-square btn-primary "> | |||
<div i-material-symbols-keyboard-double-arrow-down-rounded text-2xl /> | |||
</button> | |||
</div> | |||
<!-- <div class="label-imgs h-96 carousel carousel-vertical rounded-md"> | |||
<div class="carousel-item h-2/5 indicator hover:cursor-pointer"> | |||
<span class="indicator-item badge badge-primary indicator-start indicator-bottom "><div i-carbon:checkmark-filled />已标注</span> | |||
<img src="/Snipaste_2022-12-01_22-59-42.png"> | |||
@@ -91,16 +111,7 @@ const { t } = useI18n() | |||
<div class="carousel-item h-2/5 mt-1"> | |||
<img src="/Snipaste_2022-12-01_22-59-42.png"> | |||
</div> | |||
<div class="carousel-item h-2/5 mt-1"> | |||
<img src="/Snipaste_2022-12-01_22-59-42.png"> | |||
</div> | |||
<div class="carousel-item h-2/5 mt-1"> | |||
<img src="/Snipaste_2022-12-01_22-59-42.png"> | |||
</div> | |||
<div class="carousel-item h-2/5 mt-1"> | |||
<img src="/Snipaste_2022-12-01_22-59-42.png"> | |||
</div> | |||
</div> | |||
</div> --> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -5,7 +5,7 @@ const { t } = useI18n() | |||
<template> | |||
<div> | |||
<div class="hero justify-start"> | |||
<div class="hero-content flex-col items-end"> | |||
<div class="hero-content flex-col items-end !pb-0"> | |||
<div class=" flex"> | |||
<router-link to="/" class="text-3xl"> | |||
<div i-carbon-arrow-left class="mr-2 hover:bg-primary" /> | |||
@@ -65,7 +65,7 @@ const { t } = useI18n() | |||
<img class="max-w-full h-100 rounded-lg bg-contain bg-clip-border" src="/Snipaste_2022-12-01_22-59-42.png" alt="image description"> | |||
</div> | |||
</div> | |||
<div flex mt-10> | |||
<div flex mt-3> | |||
<button class="btn btn-primary"> | |||
截图 | |||
</button> | |||
@@ -198,4 +198,28 @@ const { t } = useI18n() | |||
padding-right: 0.75rem/* 12px */; | |||
padding-left: 2rem/* 32px */; | |||
} | |||
.stack { | |||
display: inline-grid; | |||
place-items: center; | |||
align-items: flex-end; | |||
} | |||
.stack > * { | |||
grid-column-start: 1; | |||
grid-row-start: 1; | |||
transform: translateX(10%) scale(0.9); | |||
z-index: 1; | |||
width: 100%; | |||
opacity: 0.6; | |||
} | |||
.stack > *:nth-child(2) { | |||
transform: translateX(5%) scale(0.95); | |||
z-index: 2; | |||
opacity: 0.8; | |||
} | |||
.stack > *:nth-child(1) { | |||
transform: translateX(0) scale(1); | |||
z-index: 3; | |||
opacity: 1; | |||
} | |||
</style> |
@@ -1,31 +1,71 @@ | |||
<script setup lang="ts"> | |||
<script setup> | |||
defineOptions({ | |||
name: 'IndexPage', | |||
}) | |||
const user = useUserStore() | |||
const name = $ref(user.savedName) | |||
const router = useRouter() | |||
const go = () => { | |||
if (name) | |||
router.push(`/hi/${encodeURIComponent(name)}`) | |||
let currentImgUrl = $ref('') | |||
const getPrimaryImg = async () => { | |||
const imgBase64 = (await window.pywebview.api.getPrimaryImg()) // 前端调用Python暴露的方法 | |||
currentImgUrl = `data:image/jpg;base64,${imgBase64}` | |||
} | |||
const pythonName = ref('') | |||
const getOwner = async () => { | |||
pythonName.value = await window.pywebview.api.getOwner() // 前端调用Python暴露的方法 | |||
const settings = reactive({ | |||
toggleSetting: false, | |||
tipSetting: false, | |||
controlSetting: false, | |||
}) | |||
const getIndexSetting = async () => { | |||
[settings.toggleSetting, settings.tipSetting, settings.controlSetting] = await window.pywebview.api.getIndexSetting() | |||
} | |||
getOwner() | |||
const pythonData = ref('') | |||
const getPythonData = () => { | |||
pythonData.value = 'Python调用了此方法' | |||
let timer | |||
let isShowImgOnTime = $ref(false) | |||
let eyeOpen = $ref(false) | |||
let mouthOpen = $ref(false) | |||
const setImgShow = () => { | |||
timer = setInterval(async () => { | |||
const [time, result, imgBase64] = await window.pywebview.api.getDetectImg() | |||
currentImgUrl = `data:image/jpg;base64,${imgBase64}` | |||
isShowImgOnTime = true | |||
for (let i = 0; i < result.length; i++) { | |||
switch (result[i].classid) { | |||
case ('closed_eye'): | |||
eyeOpen = false | |||
break | |||
case ('open_eye'): | |||
eyeOpen = true | |||
break | |||
case ('closed_mouth'): | |||
mouthOpen = false | |||
break | |||
case ('open_mouth'): | |||
mouthOpen = true | |||
break | |||
} | |||
} | |||
}, 50) | |||
} | |||
window.getPythonData = getPythonData // 暴露方法给Python调用 | |||
watch(() => settings, async () => { | |||
await window.pywebview.api.changeSetting({ | |||
toggle: settings.toggleSetting, | |||
tip: settings.tipSetting, | |||
control: settings.controlSetting, | |||
}) | |||
}, | |||
{ | |||
deep: true, | |||
}) | |||
const { t } = useI18n() | |||
onBeforeUnmount(() => { | |||
clearInterval(timer) | |||
}) | |||
onBeforeMount(async () => { | |||
await getPrimaryImg() | |||
await getIndexSetting() | |||
}) | |||
</script> | |||
<template> | |||
@@ -40,12 +80,12 @@ const { t } = useI18n() | |||
</div> | |||
<div class="bg-gray-100 p-3 rounded-md mt-4"> | |||
<div>实时情况:</div> | |||
<d-toggle text="眼开" class="pt-2" :disabled="true" /> | |||
<d-toggle text="嘴张" class="pt-2" :disabled="true" /> | |||
<d-toggle text="眼开" class="pt-2" :disabled="true" :checked="eyeOpen" /> | |||
<d-toggle text="嘴张" class="pt-2" :disabled="true" :checked="mouthOpen" /> | |||
</div> | |||
<d-toggle text="启用" class="pt-4" /> | |||
<d-toggle text="触发提醒" class="pt-4" /> | |||
<d-select class="mt-5" /> | |||
<d-toggle v-model:checked="settings.toggleSetting" text="启用" class="pt-4" /> | |||
<d-toggle v-model:checked="settings.tipSetting" text="触发提醒" class="pt-4" /> | |||
<d-select v-model:options="settings.controlSetting" class="select select-bordered select-primary mt-5" /> | |||
</div> | |||
<div class="item-center flex flex-col pr-9 mt-5"> | |||
<div> | |||
@@ -63,9 +103,19 @@ const { t } = useI18n() | |||
<div class="flex justify-center"> | |||
<figure class="max-w-lg "> | |||
<img class="max-w-full h-auto rounded-lg bg-contain bg-clip-border" src="/Snipaste_2022-12-01_22-59-42.png" alt="image description"> | |||
<div class="indicator max-w-lg "> | |||
<div class="indicator-item indicator-bottom"> | |||
<button v-if="!isShowImgOnTime" class="btn btn-primary" @click="setImgShow"> | |||
实时显示 | |||
</button> | |||
</div> | |||
<div class="card border"> | |||
<img class="max-w-full h-auto rounded-lg bg-contain bg-clip-border" :src="currentImgUrl" alt="image description"> | |||
</div> | |||
</div> | |||
<figcaption class="mt-2 text-sm text-center text-gray-500 dark:text-gray-400"> | |||
实时图像 | |||
{{ isShowImgOnTime ? '实时图像' : '当前图像' }} | |||
</figcaption> | |||
</figure> | |||
</div> | |||