Browse Source

feat: selectSetting

main
少轻狂 2 years ago
parent
commit
b4838137e5
No known key found for this signature in database GPG Key ID: 80E176093D33A9AB
17 changed files with 687 additions and 131 deletions
  1. +1
    -0
      .vscode/settings.json
  2. +111
    -2
      app/api/api.py
  3. +111
    -0
      app/api/config.json
  4. +4
    -0
      app/api/model/coco_label.txt
  5. +218
    -0
      app/api/model/model.py
  6. BIN
      app/api/model/model_1.0.onnx
  7. +1
    -1
      app/config/config.py
  8. +3
    -1
      app/main.py
  9. +31
    -0
      app/utils/utils.py
  10. +14
    -6
      src/components/Setting.vue
  11. +26
    -5
      src/components/daisy/DSelect.vue
  12. +12
    -2
      src/components/daisy/DToggle.vue
  13. +3
    -1
      src/layouts/404.vue
  14. +26
    -72
      src/pages/AdvancedSettings.vue
  15. +27
    -16
      src/pages/Personalization/annotations.vue
  16. +26
    -2
      src/pages/Personalization/index.vue
  17. +73
    -23
      src/pages/index.vue

+ 1
- 0
.vscode/settings.json View File

@@ -5,6 +5,7 @@
"iconify", "iconify",
"intlify", "intlify",
"mingcute", "mingcute",
"onnx",
"pinia", "pinia",
"pnpm", "pnpm",
"pyinstaller", "pyinstaller",


+ 111
- 2
app/api/api.py View File

@@ -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: class API:
'''本地API,供前端JS调用''' '''本地API,供前端JS调用'''
window = None 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()') 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)

+ 111
- 0
app/api/config.json View File

@@ -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
}
]
}
]
}

+ 4
- 0
app/api/model/coco_label.txt View File

@@ -0,0 +1,4 @@
closed_eye
closed_mouth
open_eye
open_mouth

+ 218
- 0
app/api/model/model.py View File

@@ -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

BIN
app/api/model/model_1.0.onnx View File


+ 1
- 1
app/config/config.py View File

@@ -4,7 +4,7 @@ import platform
class Config: class Config:
'''配置文件''' '''配置文件'''


appName = 'Vitesse-Python' # 应用名称
appName = 'EMC' # 应用名称
appVersion = "1.0.0" # 应用版本号 appVersion = "1.0.0" # 应用版本号


appSystem = platform.system() # 本机系统类型 appSystem = platform.system() # 本机系统类型

+ 3
- 1
app/main.py View File

@@ -5,6 +5,7 @@ import webview
import argparse import argparse
from api.api import API from api.api import API
from config.config import Config from config.config import Config
from utils.utils import send_notifycation


# 前端页面目录 # 前端页面目录
if sys.flags.dev_mode: if sys.flags.dev_mode:
@@ -41,5 +42,6 @@ if __name__ == "__main__":
default='False', default='False',
help="开发模式") help="开发模式")
args = parser.parse_args() args = parser.parse_args()
send_notifycation('EMC程序已启动')
WebViewApp(args.port,args.dev=='True') WebViewApp(args.port,args.dev=='True')

+ 31
- 0
app/utils/utils.py View File

@@ -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))
])

+ 14
- 6
src/components/Setting.vue View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
defineProps({
const props = defineProps({
settingData: { settingData: {
type: Array, type: Array,
default: () => [], default: () => [],
@@ -9,6 +9,13 @@ defineProps({
default: '', default: '',
}, },
}) })
const emit = defineEmits(['update:settingData'])

watch(() => props.settingData, () => {
emit('update:settingData', props.settingData)
}, {
deep: true,
})


const { t } = useI18n() const { t } = useI18n()
</script> </script>
@@ -16,25 +23,26 @@ const { t } = useI18n()
<template> <template>
<div> <div>
<div class="setting-title text-left text-2xl ml-10"> <div class="setting-title text-left text-2xl ml-10">
{{ t($props.settingName) }}:
{{ t(props.settingName) }}:
</div> </div>
<div class="setting-items my-3 py-2 border mx-5 rounded-md"> <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 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' : ''"> <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 " /> {{ t(item.name) }}<div v-if="!!item.description" i-carbon-information class="ml-1 " />
</div> </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"> <option v-for="item_ in item.status" :key="item_" :selected="item_.status">
{{ item_.name }} {{ item_.name }}
</option> </option>
</select>
</select> -->
<d-select v-model:options="item.status" class="select select-sm max-w-xs" />
</div> </div>
<div v-else-if="typeof (item.status) == 'number'" class="flex justify-between mx-8 items-center"> <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' : ''"> <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 " /> {{ t(item.name) }}<div v-if="!!item.description" i-carbon-information class="ml-1 " />
</div> </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> </div>
</div> </div>


+ 26
- 5
src/components/daisy/DSelect.vue View File

@@ -1,14 +1,35 @@
<script setup> <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> </script>


<template> <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>嘴/眼控</option>
</select> </select>
</template> </template>




+ 12
- 2
src/components/daisy/DToggle.vue View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
defineProps({
const props = defineProps({
text: { text: {
type: String, type: String,
default: '', default: '',
@@ -9,14 +9,24 @@ defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
checked: {
type: Boolean,
default: false,
},
}) })

const emit = defineEmits(['update:checked'])

const handleChange = () => {
emit('update:checked', !props.checked)
}
</script> </script>


<template> <template>
<div> <div>
<label class="flex items-center cursor-pointer justify-center "> <label class="flex items-center cursor-pointer justify-center ">
<span class="label-text pr-2 text-lg">{{ text }}</span> <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> </label>
</div> </div>
</template> </template>


+ 3
- 1
src/layouts/404.vue View File

@@ -1,7 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
const router = useRouter() const router = useRouter()
const { t } = useI18n() 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> </script>


<template> <template>


+ 26
- 72
src/pages/AdvancedSettings.vue View File

@@ -1,76 +1,30 @@
<script setup> <script setup>
const { t } = useI18n() 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> </script>


<template> <template>
@@ -89,9 +43,9 @@ const otherSetting = [
</div> </div>
</div> </div>
<div class="mt-4 mx-3"> <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>
</div> </div>
</template> </template>


+ 27
- 16
src/pages/Personalization/annotations.vue View File

@@ -5,7 +5,7 @@ const { t } = useI18n()
<template> <template>
<div> <div>
<div class="hero justify-start"> <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"> <div class=" flex">
<!-- <router-link to="/" class="text-3xl"> <!-- <router-link to="/" class="text-3xl">
<div i-carbon-arrow-left class="mr-2 hover:bg-primary" /> <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 text-3xl class="label-title">
图像标注 图像标注
</div> </div>
<div flex items-center mt-4>
<div flex items-center mt-2>
<div class="label-left mr-4"> <div class="label-left mr-4">
<div> <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"> <li class="menu-title">
<span>标注类别-眼睛</span> <span>标注类别-眼睛</span>
</li> </li>
@@ -53,7 +53,7 @@ const { t } = useI18n()
</ul> </ul>
</div> </div>
<div /> <div />
<div class="stats stats-vertical shadow mt-4">
<div class="stats stats-vertical border mt-4">
<div class="stat"> <div class="stat">
<div class="stat-title flex items-center"> <div class="stat-title flex items-center">
<div i-ri-quill-pen-fill />已标记 <div i-ri-quill-pen-fill />已标记
@@ -76,8 +76,28 @@ const { t } = useI18n()
<div class="label-center flex "> <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"> <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 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"> <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> <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"> <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"> <div class="carousel-item h-2/5 mt-1">
<img src="/Snipaste_2022-12-01_22-59-42.png"> <img src="/Snipaste_2022-12-01_22-59-42.png">
</div> </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> </div>
</div> </div>


+ 26
- 2
src/pages/Personalization/index.vue View File

@@ -5,7 +5,7 @@ const { t } = useI18n()
<template> <template>
<div> <div>
<div class="hero justify-start"> <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"> <div class=" flex">
<router-link to="/" class="text-3xl"> <router-link to="/" class="text-3xl">
<div i-carbon-arrow-left class="mr-2 hover:bg-primary" /> <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"> <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> </div>
<div flex mt-10>
<div flex mt-3>
<button class="btn btn-primary"> <button class="btn btn-primary">
截图 截图
</button> </button>
@@ -198,4 +198,28 @@ const { t } = useI18n()
padding-right: 0.75rem/* 12px */; padding-right: 0.75rem/* 12px */;
padding-left: 2rem/* 32px */; 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> </style>

+ 73
- 23
src/pages/index.vue View File

@@ -1,31 +1,71 @@
<script setup lang="ts">
<script setup>
defineOptions({ defineOptions({
name: 'IndexPage', 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() const { t } = useI18n()

onBeforeUnmount(() => {
clearInterval(timer)
})
onBeforeMount(async () => {
await getPrimaryImg()
await getIndexSetting()
})
</script> </script>


<template> <template>
@@ -40,12 +80,12 @@ const { t } = useI18n()
</div> </div>
<div class="bg-gray-100 p-3 rounded-md mt-4"> <div class="bg-gray-100 p-3 rounded-md mt-4">
<div>实时情况:</div> <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> </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>
<div class="item-center flex flex-col pr-9 mt-5"> <div class="item-center flex flex-col pr-9 mt-5">
<div> <div>
@@ -63,9 +103,19 @@ const { t } = useI18n()


<div class="flex justify-center"> <div class="flex justify-center">
<figure class="max-w-lg "> <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"> <figcaption class="mt-2 text-sm text-center text-gray-500 dark:text-gray-400">
实时图像
{{ isShowImgOnTime ? '实时图像' : '当前图像' }}
</figcaption> </figcaption>
</figure> </figure>
</div> </div>


Loading…
Cancel
Save