Skip to content

Commit

Permalink
feat: 컴포넌트 분리 및 마크업 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
G-hoon committed Aug 3, 2024
1 parent 7b717c1 commit bd02611
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 107 deletions.
50 changes: 50 additions & 0 deletions src/components/CameraContianer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react"
import Camera from "./Camera"
import ControlPanel from "./ControlPanel"

interface CameraContianerProps {
detectStart: (video: HTMLVideoElement) => void
canvasRef: React.LegacyRef<HTMLCanvasElement> | undefined
isModelLoaded: boolean
onChangeMode: React.ChangeEventHandler<HTMLSelectElement>
onChangeTranslation: React.ChangeEventHandler<HTMLInputElement>
}

export default function CameraContianer(props: CameraContianerProps) {
const { detectStart, canvasRef, isModelLoaded, onChangeMode, onChangeTranslation } = props
return (
<div
style={{
display: "flex",
padding: "20px",
}}
>
<div
style={{
position: "relative",
}}
>
<Camera onStreamReady={detectStart} />
<canvas
ref={canvasRef}
width="640"
height="480"
style={{
position: "absolute",
top: 0,
left: 0,
border: "1px solid black",
}}
/>
</div>
<div
style={{
paddingLeft: "20px",
width: "150px",
}}
>
{isModelLoaded && <ControlPanel onChangeMode={onChangeMode} onChangeTranslation={onChangeTranslation} />}
</div>
</div>
)
}
22 changes: 22 additions & 0 deletions src/components/ControlPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react"

interface ControlPanelProps {
onChangeTranslation: React.ChangeEventHandler<HTMLInputElement>
onChangeMode: React.ChangeEventHandler<HTMLSelectElement>
}

export default function ControlPanel(props: ControlPanelProps) {
const { onChangeTranslation, onChangeMode } = props
return (
<div>
<div>
<div>좌우 이동</div>
<input id="horizontal" type="range" min={-100} max={100} onChange={onChangeTranslation}></input>
<div>상하 이동</div>
<input id="vertical" type="range" min={-100} max={100} onChange={onChangeTranslation}></input>
<div>크기 변경</div>
<input id="scale" type="range" min={0} max={100} onChange={onChangeTranslation}></input>
</div>
</div>
)
}
204 changes: 97 additions & 107 deletions src/components/PoseDetector.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useState, useEffect, useRef, useCallback } from "react"
import { Camera } from "."
import usePushNotification from "@/hooks/usePushNotification"
import type { pose } from "@/utils/detector"
import { detectSlope, detectTextNeck } from "@/utils/detector"
import { drawPose } from "@/utils/drawer"
import usePushNotification from "@/hooks/usePushNotification"
import { worker } from "@/utils/worker"
import { useCallback, useEffect, useRef, useState } from "react"
import CameraContianer from "./CameraContianer"
import TrackingResult from "./TrackingResult"

const PoseDetector: React.FC = () => {
const [isScriptLoaded, setIsScriptLoaded] = useState<boolean>(false)
Expand All @@ -14,7 +15,7 @@ const PoseDetector: React.FC = () => {
const [isModelLoaded, setIsModelLoaded] = useState<boolean>(false)
const [mode, setMode] = useState<string>("snapshot")
const [canInit, setCanInit] = useState<boolean>(false)
const [isSnapSaved, setIsSnapSaved] = useState<boolean>(false);
const [isSnapSaved, setIsSnapSaved] = useState<boolean>(false)
const modelRef = useRef<any>(null)
const snapRef = useRef<pose[] | null>(null)
const resultRef = useRef<pose[] | null>(null)
Expand Down Expand Up @@ -69,22 +70,15 @@ const PoseDetector: React.FC = () => {
await window.ml5.setBackend("webgl")
}

const canInitCallback = (canInit : boolean) => {
const canInitCallback = (canInit: boolean) => {
setCanInit(canInit)
}

const detect = useCallback(
(results: pose[]): void => {
resultRef.current = results
if (canvasRef.current) {
drawPose(
results,
canvasRef.current,
dx.current,
dy.current,
scale.current,
canInitCallback,
!!snapRef.current)
drawPose(results, canvasRef.current, dx.current, dy.current, scale.current, canInitCallback, !!snapRef.current)
}
if (snapRef.current) {
const _slope = detectSlope(snapRef.current, results, mode === "snapshot")
Expand Down Expand Up @@ -116,7 +110,7 @@ const PoseDetector: React.FC = () => {

const detectStart = useCallback(
async (video: HTMLVideoElement): Promise<void> => {
worker.onmessage = ({ }: any) => {
worker.onmessage = ({}: any) => {
if (modelRef.current) {
modelRef.current.detect(video, detect)
}
Expand Down Expand Up @@ -162,21 +156,21 @@ const PoseDetector: React.FC = () => {
}

const onChangeTranslation = (e: React.ChangeEvent<HTMLInputElement>) => {
const id = e.target.id ;
if(e.target.value){
const value = Number.parseInt(e.target.value)
switch(id){
case 'vertical' :
dy.current = value
return
case 'horizontal' :
dx.current = value
return
case 'scale' :
scale.current = value / 100 * 2
return
default :
}
const id = e.target.id
if (e.target.value) {
const value = Number.parseInt(e.target.value)
switch (id) {
case "vertical":
dy.current = value
return
case "horizontal":
dx.current = value
return
case "scale":
scale.current = (value / 100) * 2
return
default:
}
}
}

Expand All @@ -185,99 +179,95 @@ const PoseDetector: React.FC = () => {
}

return (
<div>
<>
{isScriptError ? (
"스크립트 불러오기 실패"
) : !isScriptLoaded ? (
"스크립트 불러오는 중"
) : (
<>
<Camera onStreamReady={detectStart} />
<canvas
ref={canvasRef}
width="640"
height="480"
style={{
position: "absolute",
top: 0,
left: 0,
border: "1px solid black",
}}
<div className="flex flex-col items-center justify-center">
<CameraContianer
detectStart={detectStart}
canvasRef={canvasRef}
isModelLoaded={isModelLoaded}
onChangeMode={onChangeMode}
onChangeTranslation={onChangeTranslation}
/>
{isModelLoaded && (
<>
<div>
<div>좌우 이동</div>
<input id="horizontal" type="range" min={-100} max={100} onChange={onChangeTranslation}></input>
<div>상하 이동</div>
<input id="vertical" type="range" min={-100} max={100} onChange={onChangeTranslation}></input>
<div>크기 변경</div>
<input id="scale" type="range" min={0} max={100} onChange={onChangeTranslation}></input>
</div>
<div className="font-bold text-red-500">본 화면은 좌우가 반대로 보이고 있으니 주의하세요!</div>
<div>
<select className="rounded border border-gray-400 bg-white p-2" onChange={onChangeMode}>
<option value={"snapshot"}>스냅샷 모드 (올바른 자세 촬영 후, 해당 자세 기준으로 측정)</option>
<option value={"skeleton"}>자동 모드 (올바른 자세 기준으로 측정)</option>
</select>
</div>
{mode === "snapshot" && (
<>
<div className="p-10 font-bold">
스냅샷 모드입니다. 올바른 자세를 하신 후에, 버튼을 눌러 촬영을 하면 해당 자세를 기준으로 부적절한
자세를 추적합니다!
</div>
{
canInit ?
isSnapSaved ?
<button className="rounded bg-blue-500 px-4 py-2 font-bold text-white" onClick={initializePoseMonitoring}>
<div
style={{
display: "flex",
}}
>
<div className="text-center">
<div className="font-bold text-red-500">본 화면은 좌우가 반대로 보이고 있으니 주의하세요!</div>
<div>
<select className="rounded border border-gray-400 bg-white p-2" onChange={onChangeMode}>
<option value={"snapshot"}>스냅샷 모드 (올바른 자세 촬영 후, 해당 자세 기준으로 측정)</option>
<option value={"skeleton"}>자동 모드 (올바른 자세 기준으로 측정)</option>
</select>
</div>
{mode === "snapshot" && (
<div
style={{
paddingTop: "5px",
}}
>
{canInit ? (
isSnapSaved ? (
<button
className="rounded bg-blue-500 px-4 py-2 font-bold text-white"
onClick={initializePoseMonitoring}
>
스냅샷 다시 찍기
</button> :
</button>
) : (
<button className="rounded bg-blue-500 px-4 py-2 font-bold text-white" onClick={getInitSnap}>
올바른 자세를 촬영한 후 자세 측정 시작!
</button> :
<div className="font-bold text-red-500">스냅샷을 찍을 수 없습니다. 가이드 라인에 맞게 자세를 잡아주세요.</div>
}
</>
)}
{mode === "skeleton" && (
<>
<div className="p-10 font-bold">자동 모드입니다. 자동으로 부적절한 자세를 추적합니다.</div>
<div className="flex gap-10">
<button
className="rounded bg-blue-500 px-4 py-2 font-bold text-white disabled:bg-gray-400"
onClick={getInitSnap}
// disabled={snapShoptPose.length > 0}
>
자세 모니터링 시작!
</button>
<button
className="rounded bg-red-500 px-4 py-2 font-bold text-white disabled:bg-gray-400"
onClick={onCancelAutoPoseMonitoring}
// disabled={snapShoptPose.length === 0}
>
자세 모니터링 취소
</button>
</button>
)
) : (
<div className="font-bold text-red-500">
스냅샷을 찍을 수 없습니다. 가이드 라인에 맞게 자세를 잡아주세요.
</div>
)}
</div>
</>
)}

<div>
거북목 상태:&nbsp;
{isTextNeck === null ? (
"상태를 확인할 수 없습니다."
) : isTextNeck ? (
<span className="font-extrabold text-red-500">"거북목 상태 입니다"</span>
) : (
"정상적인 자세 입니다"
)}
{mode === "skeleton" && (
<>
<div className="p-1 font-bold">자동 모드입니다. 자동으로 부적절한 자세를 추적합니다.</div>
<div className="flex gap-10">
<button
className="rounded bg-blue-500 px-4 py-2 font-bold text-white disabled:bg-gray-400"
onClick={getInitSnap}
// disabled={snapShoptPose.length > 0}
>
자세 모니터링 시작!
</button>
<button
className="rounded bg-red-500 px-4 py-2 font-bold text-white disabled:bg-gray-400"
onClick={onCancelAutoPoseMonitoring}
// disabled={snapShoptPose.length === 0}
>
자세 모니터링 취소
</button>
</div>
</>
)}
</div>
<div
style={{
paddingLeft: "20px",
paddingTop: "20px",
}}
>
<TrackingResult isTextNeck={isTextNeck} slope={slope} />
</div>
<div>어깨 기울기: {slope === null ? "상태를 확인할 수 없습니다." : slope}</div>
</>
</div>
)}
</>
</div>
)}
</div>
</>
)
}

Expand Down
32 changes: 32 additions & 0 deletions src/components/TrackingResult.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
interface TrackingResultProps {
isTextNeck: boolean | null
slope: null | string
}

export default function TrackingResult(props: TrackingResultProps) {
const { isTextNeck, slope } = props
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "10px",
}}
>
<div>
거북목 상태:&nbsp;
{isTextNeck === null ? (
"상태를 확인할 수 없습니다."
) : isTextNeck ? (
<span className="font-extrabold text-red-500">거북목 상태 입니다</span>
) : (
"정상적인 자세 입니다"
)}
</div>
<div>
어깨 기울기: {slope === null && <span>상태를 확인할 수 없습니다.</span>}
{slope && <span className={`${!slope.includes("올바른") ? "font-extrabold text-red-500" : ""}`}>{slope}</span>}
</div>
</div>
)
}

0 comments on commit bd02611

Please sign in to comment.