Skip to content

Commit

Permalink
Merge pull request #4 from DDD-Community/feat/#2
Browse files Browse the repository at this point in the history
[feat#2] 자세 모니터링 기능 고도화
  • Loading branch information
lkhoony authored Jul 15, 2024
2 parents 4a3e7a8 + dd4e3ba commit e2715b3
Show file tree
Hide file tree
Showing 6 changed files with 861 additions and 652 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ dist/

# TypeScript
*.tsbuildinfo
*.d.ts
# *.d.ts
*.d.ts.map

# MacOS X finder files
Expand Down
15 changes: 0 additions & 15 deletions babel.config.js

This file was deleted.

67 changes: 61 additions & 6 deletions src/components/PoseDetector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ const PoseDetector: React.FC = () => {
const [isScriptLoaded, setIsScriptLoaded] = useState<boolean>(false)
const [isScriptError, setIsScriptError] = useState<boolean>(false)
const [slope, setSlope] = useState<string | null>(null)
const [isTextNeck, setIsTextNeck] = useState<boolean>(false)
const [isTextNeck, setIsTextNeck] = useState<boolean | null>(null)
const [isModelLoaded, setIsModelLoaded] = useState<boolean>(false)
const [mode, setMode] = useState<string>("snapshot")

const snapRef = useRef<pose[] | null>(null)
const resultRef = useRef<pose[] | null>(null)
Expand Down Expand Up @@ -47,9 +48,8 @@ const PoseDetector: React.FC = () => {
resultRef.current = results
if (canvasRef.current) drawPose(results, canvasRef.current)
if (snapRef.current) {
const _slope = detectSlope(snapRef.current, results)
const _slope = detectSlope(snapRef.current, results, mode === "snapshot")
const _isTextNeck = detectTextNeck(snapRef.current, results)

if (_slope !== null) setSlope(_slope)
if (_isTextNeck !== null) setIsTextNeck(_isTextNeck)

Expand Down Expand Up @@ -92,6 +92,12 @@ const PoseDetector: React.FC = () => {
getScript()
}, [])

const onChangeMode = (e: React.ChangeEvent<HTMLSelectElement>) => {
if (e.target.value) {
setMode(e.target.value)
}
}

return (
<div>
{isScriptError ? (
Expand All @@ -112,9 +118,58 @@ const PoseDetector: React.FC = () => {
border: "1px solid black",
}}
/>
{isModelLoaded ? <button onClick={getInitSnap}>get snap</button> : null}
<div>{`거북목 상태 ${isTextNeck}`}</div>
<div>{`어깨 기울기 ${slope}`}</div>
{isModelLoaded && (
<>
<div className="font-bold text-red-500">본 화면은 좌우가 반대로 보이고 있으니 주의하세요!</div>
<div className="w-1/2">
<select
className="w-full appearance-none rounded border border-gray-400 bg-white p-2"
onChange={onChangeMode}
>
<option value={"snapshot"}>스냅샷 모드 (올바른 자세 촬영 후, 해당 자세 기준으로 측정)</option>
<option value={"skeleton"}>스켈레톤 모드 (올바른 자세 제시 후, 해당 자세 기준으로 측정)</option>
</select>
</div>
{mode === "snapshot" && (
<>
<button
className="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
onClick={getInitSnap}
>
올바른 자세를 촬영한 후 자세 측정 시작!
</button>
<div>
거북목 상태:&nbsp;
{isTextNeck === null
? "상태를 확인할 수 없습니다."
: isTextNeck
? "거북목 상태 입니다"
: "정상적인 자세 입니다"}
</div>
<div>어깨 기울기: {slope === null ? "상태를 확인할 수 없습니다." : slope}</div>
</>
)}
{mode === "skeleton" && (
<>
<button
className="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
onClick={getInitSnap}
>
기준 그림에 몸을 맞춘 후 측정 시작!
</button>
<div>
거북목 상태:&nbsp;
{isTextNeck === null
? "상태를 확인할 수 없습니다."
: isTextNeck
? "거북목 상태 입니다"
: "정상적인 자세 입니다"}
</div>
<div>어깨 기울기: {slope === null ? "상태를 확인할 수 없습니다." : slope}</div>
</>
)}
</>
)}
</>
)}
</div>
Expand Down
37 changes: 30 additions & 7 deletions src/utils/detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,47 @@ export const detectTextNeck = (refer: pose[], comp: pose[]): boolean | null => {
* 어깨 기울기는 각 포즈 배열에서 왼쪽 어깨와 오른쪽 어깨의 좌표를 이용하여 계산
* @param refer 비교 기준이 되는 포즈 배열
* @param comp 비교할 대상이 되는 포즈 배열
* @param isSnapShotMode 스냅샷 촬영후, 해당 기준으로 자세를 측정할 지 아니면 스켈레톤을 토대로 측정할 지
* @returns 기울기가 왼쪽으로 치우쳤으면 "left", 오른쪽으로 치우쳤으면 "right"를 반환하며,
* 기울기를 계산할 수 없는 경우 null을 반환
*/
export const detectSlope = (refer: pose[], comp: pose[]): string | null => {
export const detectSlope = (refer: pose[], comp: pose[], isSnapShotMode: boolean = true): string | null => {
if (!refer || !comp) return null

const referLeftSoulder = getXYfromPose(refer, "left_shoulder")
const referRightSoulder = getXYfromPose(refer, "right_shoulder")
const compLeftSoulder = getXYfromPose(comp, "left_shoulder")
const compRightSoulder = getXYfromPose(comp, "right_shoulder")
const compLeftShoulder = getXYfromPose(comp, "left_shoulder")
const compRightShoulder = getXYfromPose(comp, "right_shoulder")

if (!isSnapShotMode && compLeftShoulder && compRightShoulder) {
const SHOULDER_DIFF_THRESHOLD = 30
const shoulderSlope = compLeftShoulder.y - compRightShoulder.y

if (Math.abs(shoulderSlope) < SHOULDER_DIFF_THRESHOLD) {
return "적절한 자세입니다"
} else if (shoulderSlope > 0) {
return "오른쪽 어깨가 올라갔습니다"
} else {
return "왼쪽 어깨가 올라갔습니다"
}
}

if (!referLeftSoulder || !referRightSoulder || !compLeftSoulder || !compRightSoulder) return null
if (!referLeftSoulder || !referRightSoulder || !compLeftShoulder || !compRightShoulder) return null

const referSlope = getSlopeFromPoints(referLeftSoulder, referRightSoulder)
const compSlope = getSlopeFromPoints(compLeftSoulder, compRightSoulder)
const compSlope = getSlopeFromPoints(compLeftShoulder, compRightShoulder)

if (referSlope === Infinity || compSlope === Infinity) return null

if (referSlope < compSlope) return "left"
else return "right"
// referSlope를 기준으로 10% 오차 미만이면, 정상 자세인 것으로 간주
const tenPercentOfReferSlope = Math.abs(referSlope) * 0.9
const slopeDifference = Math.abs(referSlope - compSlope)

if (slopeDifference <= tenPercentOfReferSlope) {
return "올바른 자세입니다"
} else if (referSlope < compSlope) {
return "왼쪽으로 치우쳐져 있습니다"
} else {
return "오른쪽으로 치우쳐져 있습니다"
}
}
15 changes: 14 additions & 1 deletion src/utils/drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,21 @@ export const drawPose = (poses: pose[], canvas: HTMLCanvasElement): void => {
ctx.clearRect(0, 0, canvas.width, canvas.height)

poses.forEach((pose) => {
// 왼쪽과 오른쪽 어깨 이어주는 선 그리기
const leftShoulder = pose.keypoints.find((kp) => kp.name === "left_shoulder")
const rightShoulder = pose.keypoints.find((kp) => kp.name === "right_shoulder")

if (leftShoulder && rightShoulder && leftShoulder.confidence > 0.2 && rightShoulder.confidence > 0.2) {
ctx.beginPath()
ctx.moveTo(leftShoulder.x, leftShoulder.y)
ctx.lineTo(rightShoulder.x, rightShoulder.y)
ctx.strokeStyle = "red"
ctx.lineWidth = 2
ctx.stroke()
}

pose.keypoints.forEach((keypoint) => {
if (keypoint.confidence > 0.1) {
if (keypoint.confidence > 0.25) {
ctx.beginPath()
ctx.arc(keypoint.x, keypoint.y, 5, 0, 2 * Math.PI)
ctx.fillStyle = "red"
Expand Down
Loading

0 comments on commit e2715b3

Please sign in to comment.