fix: bone
This commit is contained in:
@ -41,7 +41,6 @@
|
||||
v-model="backendUrl"
|
||||
placeholder="http://192.168.2.184:8245"
|
||||
/>
|
||||
<!-- <button @click="testConnection" :disabled="isTesting">测试连接</button>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -92,6 +91,14 @@
|
||||
<input type="checkbox" v-model="showKeypoints" />
|
||||
关键点
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" v-model="showHandBones" />
|
||||
手部骨骼
|
||||
</label>
|
||||
<!-- <label>-->
|
||||
<!-- <input type="checkbox" v-model="showFootBones" />-->
|
||||
<!-- 脚部骨骼-->
|
||||
<!-- </label>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -196,35 +203,152 @@ const originalImage = ref<HTMLImageElement | null>(null)
|
||||
const imageWidth = ref(0)
|
||||
const imageHeight = ref(0)
|
||||
const averageConfidence = ref(0)
|
||||
const backendUrl = ref('http://192.168.2.184:8245')
|
||||
const backendUrl = ref('https://api.pose.weihua-iot.cn')
|
||||
const canvasWidth = ref(800)
|
||||
const canvasHeight = ref(600)
|
||||
const showBoundingBox = ref(true)
|
||||
const showSkeleton = ref(true)
|
||||
const showKeypoints = ref(true)
|
||||
const showHandBones = ref(false)
|
||||
const showFootBones = ref(false)
|
||||
const confidenceThreshold = ref(0.3)
|
||||
const selectColorIndex = ref<number | null>(null)
|
||||
const personColors = ref(['#FF5252', '#4CAF50', '#2196F3', '#FF9800', '#9C27B0', '#00BCD4'])
|
||||
const personColors = ref(['#1389CA', '#E74C3C', '#2ECC71', '#F39C12', '#9B59B6', '#1ABC9C'])
|
||||
|
||||
// 骨骼连接关系(COCO-WholeBody格式)
|
||||
const skeletonConnections = [
|
||||
[0, 1], [0, 2], [1, 3], [2, 4], [5, 6], [5, 7], [6, 8], [7, 9], [8, 10],
|
||||
[11, 12], [5, 11], [6, 12], [11, 13], [13, 15], [12, 14], [14, 16],
|
||||
[17, 18], [18, 19], [19, 20], [20, 21], [22, 23], [23, 24], [24, 25], [25, 26],
|
||||
[27, 28], [28, 29], [29, 30], [31, 32], [32, 33], [33, 34], [34, 35],
|
||||
[36, 37], [37, 38], [38, 39], [39, 40], [41, 42], [42, 43], [43, 44], [44, 45],
|
||||
[46, 47], [47, 48], [48, 49], [49, 50], [51, 52], [52, 53], [53, 54]
|
||||
// 颜色定义
|
||||
const COLOR_SPINE = '#8AC926' // 绿色,脊椎和头部
|
||||
const COLOR_ARMS = '#FFCA3A' // 黄色,手臂和肩膀
|
||||
const COLOR_LEGS = '#1982C4' // 蓝色,腿部和臀部
|
||||
const COLOR_FINGERS = '#FF595E' // 红色,手指
|
||||
const COLOR_FACE = '#FFCA3A' // 黄色,面部
|
||||
const COLOR_FOOT = '#FF924C' // 橙色,脚部
|
||||
const COLOR_HEAD = '#8AC926' // 绿色,头部
|
||||
|
||||
// 根据Python代码修复骨骼连接关系
|
||||
// 注意:Python代码中使用的是1-based索引,但JavaScript数组是0-based
|
||||
// 所以需要将Python中的索引减1
|
||||
|
||||
// 身体骨骼连接(基于Python中的body_bones定义)
|
||||
const bodyBones = [
|
||||
// 腿部
|
||||
{ start: 15, end: 13, color: COLOR_LEGS, name: "左胫骨" }, // left_tibia
|
||||
{ start: 13, end: 11, color: COLOR_LEGS, name: "左股骨" }, // left_femur
|
||||
{ start: 16, end: 14, color: COLOR_LEGS, name: "右胫骨" }, // right_tibia
|
||||
{ start: 14, end: 12, color: COLOR_LEGS, name: "右股骨" }, // right_femur
|
||||
{ start: 11, end: 12, color: COLOR_LEGS, name: "骨盆" }, // pelvis
|
||||
|
||||
// 躯干
|
||||
{ start: 5, end: 11, color: COLOR_SPINE, name: "左侧躯干轮廓" }, // left_contour
|
||||
{ start: 6, end: 12, color: COLOR_SPINE, name: "右侧躯干轮廓" }, // right_contour
|
||||
{ start: 5, end: 6, color: COLOR_SPINE, name: "锁骨" }, // clavicle
|
||||
|
||||
// 手臂
|
||||
{ start: 5, end: 7, color: COLOR_ARMS, name: "左肱骨" }, // left_humerus
|
||||
{ start: 7, end: 9, color: COLOR_ARMS, name: "左桡骨" }, // left_radius
|
||||
{ start: 6, end: 8, color: COLOR_ARMS, name: "右肱骨" }, // right_humerus
|
||||
{ start: 8, end: 10, color: COLOR_ARMS, name: "右桡骨" }, // right_radius
|
||||
|
||||
// 头部
|
||||
{ start: 0, end: 1, color: COLOR_HEAD, name: "鼻子-左眼" }, // 头部连接
|
||||
{ start: 0, end: 2, color: COLOR_HEAD, name: "鼻子-右眼" }, // 头部连接
|
||||
{ start: 1, end: 3, color: COLOR_HEAD, name: "左眼-左耳" }, // 左耳
|
||||
{ start: 2, end: 4, color: COLOR_HEAD, name: "右眼-右耳" }, // 右耳
|
||||
{ start: 1, end: 2, color: COLOR_HEAD, name: "左眼-右眼" }, // 双眼连接
|
||||
|
||||
// 脚部(基于Python中的body_bones定义,但注释中显示是foot_landmarks的连接)
|
||||
{ start: 15, end: 17, color: COLOR_FOOT, name: "左脚-大脚趾" }, // left foot toe
|
||||
{ start: 15, end: 18, color: COLOR_FOOT, name: "左脚-小脚趾" }, // left foot small toe
|
||||
{ start: 15, end: 19, color: COLOR_FOOT, name: "左脚-脚后跟" }, // left foot heel
|
||||
{ start: 16, end: 20, color: COLOR_FOOT, name: "右脚-大脚趾" }, // right foot toe
|
||||
{ start: 16, end: 21, color: COLOR_FOOT, name: "右脚-小脚趾" }, // right foot small toe
|
||||
{ start: 16, end: 22, color: COLOR_FOOT, name: "右脚-脚后跟" } // right foot heel
|
||||
]
|
||||
|
||||
// 观察置信度阈值变化
|
||||
watch(confidenceThreshold, () => {
|
||||
if (originalImage.value && poseData.value) {
|
||||
drawPoseResults(originalImage.value)
|
||||
}
|
||||
})
|
||||
// 手部骨骼连接(基于Python中的hand_bones定义)
|
||||
const handBones = [
|
||||
// 右手拇指
|
||||
{ start: 91, end: 92, color: COLOR_FINGERS, name: "右手拇指掌骨" },
|
||||
{ start: 92, end: 93, color: COLOR_FINGERS, name: "右手拇指近节指骨" },
|
||||
{ start: 93, end: 94, color: COLOR_FINGERS, name: "右手拇指远节指骨" },
|
||||
{ start: 94, end: 95, color: COLOR_FINGERS, name: "右手拇指指尖" },
|
||||
|
||||
// 观察显示选项变化
|
||||
watch([showBoundingBox, showSkeleton, showKeypoints], () => {
|
||||
// 右手食指
|
||||
{ start: 91, end: 96, color: COLOR_FINGERS, name: "右手食指掌骨" },
|
||||
{ start: 96, end: 97, color: COLOR_FINGERS, name: "右手食指近节指骨" },
|
||||
{ start: 97, end: 98, color: COLOR_FINGERS, name: "右手食指中节指骨" },
|
||||
{ start: 98, end: 99, color: COLOR_FINGERS, name: "右手食指远节指骨" },
|
||||
{ start: 99, end: 100, color: COLOR_FINGERS, name: "右手食指指尖" },
|
||||
|
||||
// 右手中指
|
||||
{ start: 91, end: 100, color: COLOR_FINGERS, name: "右手中指掌骨" },
|
||||
{ start: 100, end: 101, color: COLOR_FINGERS, name: "右手中指近节指骨" },
|
||||
{ start: 101, end: 102, color: COLOR_FINGERS, name: "右手中指中节指骨" },
|
||||
{ start: 102, end: 103, color: COLOR_FINGERS, name: "右手中指远节指骨" },
|
||||
{ start: 103, end: 104, color: COLOR_FINGERS, name: "右手中指指尖" },
|
||||
|
||||
// 右手无名指
|
||||
{ start: 91, end: 104, color: COLOR_FINGERS, name: "右手无名指掌骨" },
|
||||
{ start: 104, end: 105, color: COLOR_FINGERS, name: "右手无名指近节指骨" },
|
||||
{ start: 105, end: 106, color: COLOR_FINGERS, name: "右手无名指中节指骨" },
|
||||
{ start: 106, end: 107, color: COLOR_FINGERS, name: "右手无名指远节指骨" },
|
||||
{ start: 107, end: 108, color: COLOR_FINGERS, name: "右手无名指指尖" },
|
||||
|
||||
// 右手小指
|
||||
{ start: 91, end: 108, color: COLOR_FINGERS, name: "右手小指掌骨" },
|
||||
{ start: 108, end: 109, color: COLOR_FINGERS, name: "右手小指近节指骨" },
|
||||
{ start: 109, end: 110, color: COLOR_FINGERS, name: "右手小指中节指骨" },
|
||||
{ start: 110, end: 111, color: COLOR_FINGERS, name: "右手小指远节指骨" },
|
||||
{ start: 111, end: 112, color: COLOR_FINGERS, name: "右手小指指尖" },
|
||||
|
||||
// 左手拇指
|
||||
{ start: 112, end: 113, color: COLOR_FINGERS, name: "左手拇指掌骨" },
|
||||
{ start: 113, end: 114, color: COLOR_FINGERS, name: "左手拇指近节指骨" },
|
||||
{ start: 114, end: 115, color: COLOR_FINGERS, name: "左手拇指远节指骨" },
|
||||
{ start: 115, end: 116, color: COLOR_FINGERS, name: "左手拇指指尖" },
|
||||
|
||||
// 左手食指
|
||||
{ start: 112, end: 117, color: COLOR_FINGERS, name: "左手食指掌骨" },
|
||||
{ start: 117, end: 118, color: COLOR_FINGERS, name: "左手食指近节指骨" },
|
||||
{ start: 118, end: 119, color: COLOR_FINGERS, name: "左手食指中节指骨" },
|
||||
{ start: 119, end: 120, color: COLOR_FINGERS, name: "左手食指远节指骨" },
|
||||
{ start: 120, end: 121, color: COLOR_FINGERS, name: "左手食指指尖" },
|
||||
|
||||
// 左手中指
|
||||
{ start: 112, end: 121, color: COLOR_FINGERS, name: "左手中指掌骨" },
|
||||
{ start: 121, end: 122, color: COLOR_FINGERS, name: "左手中指近节指骨" },
|
||||
{ start: 122, end: 123, color: COLOR_FINGERS, name: "左手中指中节指骨" },
|
||||
{ start: 123, end: 124, color: COLOR_FINGERS, name: "左手中指远节指骨" },
|
||||
{ start: 124, end: 125, color: COLOR_FINGERS, name: "左手中指指尖" },
|
||||
|
||||
// 左手无名指
|
||||
{ start: 112, end: 125, color: COLOR_FINGERS, name: "左手无名指掌骨" },
|
||||
{ start: 125, end: 126, color: COLOR_FINGERS, name: "左手无名指近节指骨" },
|
||||
{ start: 126, end: 127, color: COLOR_FINGERS, name: "左手无名指中节指骨" },
|
||||
{ start: 127, end: 128, color: COLOR_FINGERS, name: "左手无名指远节指骨" },
|
||||
{ start: 128, end: 129, color: COLOR_FINGERS, name: "左手无名指指尖" },
|
||||
|
||||
// 左手小指
|
||||
{ start: 112, end: 129, color: COLOR_FINGERS, name: "左手小指掌骨" },
|
||||
{ start: 129, end: 130, color: COLOR_FINGERS, name: "左手小指近节指骨" },
|
||||
{ start: 130, end: 131, color: COLOR_FINGERS, name: "左手小指中节指骨" },
|
||||
{ start: 131, end: 132, color: COLOR_FINGERS, name: "左手小指远节指骨" },
|
||||
{ start: 132, end: 133, color: COLOR_FINGERS, name: "左手小指指尖" }
|
||||
]
|
||||
|
||||
// 合并所有骨骼连接
|
||||
const getAllBones = () => {
|
||||
const bones = [...bodyBones]
|
||||
if (showFootBones.value) {
|
||||
// 脚部骨骼已经在bodyBones中包含
|
||||
}
|
||||
if (showHandBones.value) {
|
||||
bones.push(...handBones)
|
||||
}
|
||||
return bones
|
||||
}
|
||||
|
||||
// 观察设置变化
|
||||
watch([confidenceThreshold, showBoundingBox, showSkeleton, showKeypoints, showHandBones, showFootBones], () => {
|
||||
if (originalImage.value && poseData.value) {
|
||||
drawPoseResults(originalImage.value)
|
||||
}
|
||||
@ -263,38 +387,6 @@ const handleDrop = async (e: DragEvent) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 测试后端连接
|
||||
const testConnection = async () => {
|
||||
isTesting.value = true
|
||||
connectionStatus.value = null
|
||||
error.value = ''
|
||||
|
||||
try {
|
||||
const response = await fetch(backendUrl.value + '/hpe', {
|
||||
method: 'OPTIONS' // 先发送OPTIONS请求检查CORS
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
connectionStatus.value = {
|
||||
type: 'success',
|
||||
message: '后端连接成功!'
|
||||
}
|
||||
} else {
|
||||
connectionStatus.value = {
|
||||
type: 'error',
|
||||
message: '后端连接失败,状态码:' + response.status
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
connectionStatus.value = {
|
||||
type: 'error',
|
||||
message: '无法连接到后端:' + (err as Error).message
|
||||
}
|
||||
} finally {
|
||||
isTesting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理图片分析
|
||||
const processImage = async (file: File) => {
|
||||
// 验证文件类型
|
||||
@ -349,7 +441,7 @@ const processImage = async (file: File) => {
|
||||
const response = await fetch(`${backendUrl.value}/hpe`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': file.type // 设置正确的Content-Type
|
||||
'Content-Type': file.type
|
||||
},
|
||||
body: arrayBuffer
|
||||
})
|
||||
@ -369,7 +461,6 @@ const processImage = async (file: File) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有检测结果
|
||||
const contentLength = response.headers.get('content-length')
|
||||
|
||||
if (response.status === 200 && contentLength !== '0' && parseInt(contentLength || '0') > 0) {
|
||||
@ -437,21 +528,29 @@ const drawPoseResults = (img: HTMLImageElement) => {
|
||||
const keypoints = poseData.value!.keypoints[personIndex]
|
||||
const confidences = poseData.value!.keypoints_confidence?.[personIndex] || []
|
||||
|
||||
// 获取所有要绘制的骨骼
|
||||
const bonesToDraw = getAllBones()
|
||||
|
||||
// 绘制骨骼连接
|
||||
if (showSkeleton.value) {
|
||||
ctx.strokeStyle = color
|
||||
ctx.lineWidth = 2
|
||||
bonesToDraw.forEach(bone => {
|
||||
if (bone.start < keypoints.length && bone.end < keypoints.length) {
|
||||
const startPoint = keypoints[bone.start]
|
||||
const endPoint = keypoints[bone.end]
|
||||
|
||||
skeletonConnections.forEach(([start, end]) => {
|
||||
if (start < keypoints.length && end < keypoints.length) {
|
||||
const startPoint = keypoints[start]
|
||||
const endPoint = keypoints[end]
|
||||
// 检查关键点是否存在
|
||||
if (!startPoint || !endPoint || startPoint.length < 2 || endPoint.length < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查关键点置信度
|
||||
const startConf = confidences[start] || 1
|
||||
const endConf = confidences[end] || 1
|
||||
const startConf = confidences[bone.start] || 1
|
||||
const endConf = confidences[bone.end] || 1
|
||||
|
||||
if (startConf > confidenceThreshold.value && endConf > confidenceThreshold.value) {
|
||||
ctx.strokeStyle = bone.color
|
||||
ctx.lineWidth = 2
|
||||
ctx.lineCap = 'round'
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(startPoint[0] * scaleX, startPoint[1] * scaleY)
|
||||
ctx.lineTo(endPoint[0] * scaleX, endPoint[1] * scaleY)
|
||||
@ -464,12 +563,50 @@ const drawPoseResults = (img: HTMLImageElement) => {
|
||||
// 绘制关键点
|
||||
if (showKeypoints.value) {
|
||||
keypoints.forEach((point, index) => {
|
||||
if (!point || point.length < 2) return
|
||||
|
||||
const confidence = confidences[index] || 1
|
||||
if (confidence > confidenceThreshold.value) {
|
||||
ctx.fillStyle = color
|
||||
// 根据身体部位设置关键点颜色
|
||||
let pointColor = color!
|
||||
|
||||
// 身体关键点(0-16)
|
||||
if (index <= 16) {
|
||||
// 头部和脊椎
|
||||
if (index <= 4) {
|
||||
pointColor = COLOR_HEAD
|
||||
}
|
||||
// 手臂
|
||||
else if (index <= 10) {
|
||||
pointColor = COLOR_ARMS
|
||||
}
|
||||
// 腿部
|
||||
else {
|
||||
pointColor = COLOR_LEGS
|
||||
}
|
||||
}
|
||||
// 脚部关键点(17-22)
|
||||
else if (index <= 22) {
|
||||
pointColor = COLOR_FOOT
|
||||
}
|
||||
// 手部关键点(92-132)
|
||||
else if (index >= 91 && index <= 132) {
|
||||
pointColor = COLOR_FINGERS
|
||||
}
|
||||
// 脸部关键点(23-90)
|
||||
else {
|
||||
pointColor = COLOR_FACE
|
||||
}
|
||||
|
||||
ctx.fillStyle = pointColor
|
||||
ctx.beginPath()
|
||||
ctx.arc(point[0] * scaleX, point[1] * scaleY, 1, 0, Math.PI * 2)
|
||||
ctx.arc(point[0] * scaleX, point[1] * scaleY, 2, 0, Math.PI * 2)
|
||||
ctx.fill()
|
||||
|
||||
// 绘制关键点轮廓
|
||||
ctx.strokeStyle = '#000000'
|
||||
ctx.lineWidth = 1
|
||||
ctx.stroke()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -488,8 +625,7 @@ const downloadResult = () => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化时测试后端连接
|
||||
// testConnection()
|
||||
// 组件挂载后的初始化
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -587,26 +723,6 @@ h1 {
|
||||
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.config-item button {
|
||||
padding: 8px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.config-item button:hover:not(:disabled) {
|
||||
background: #5a67d8;
|
||||
}
|
||||
|
||||
.config-item button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
|
||||
Reference in New Issue
Block a user