CS log

[Capstone project] SPitching ๋ณธ๋ฌธ

Development

[Capstone project] SPitching

sj.cath 2024. 11. 20. 20:13

๐Ÿ—ฃ๏ธ SPitching

์œ ์ฐฝํ•˜๊ณ  ๋Šฅ์ˆ™ํ•œ ๋ฐœํ‘œ๋ฅผ ์œ„ํ•œ ์‹œ์„  ์ถ”์ , ๋ชจ์…˜ ์ธ์‹, ์ƒ์„ฑํ˜• AI ๊ธฐ๋ฐ˜ ์ข…ํ•ฉ ๋ฐœํ‘œ ํŠธ๋ ˆ์ด๋„ˆ, SPitching

 

์ฃผ์ œ๋ฅผ ์ •ํ•˜๋Š” ๋ฐ์— ์‹œ๊ฐ„์ด ๊ฝค๋‚˜ ์˜ค๋ž˜ ๊ฑธ๋ ธ๋Š”๋‹ค.

์š”์ฆ˜ ์ฒญ๋…„๋“ค์—๊ฒŒ ๋„์›€์ด ๋˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์—ˆ๊ณ , ๊ทธ๋ ‡๊ฒŒ ๋‚ด๊ฐ€ ๋˜์ง„ '๋ฐœํ‘œ ์—ฐ์Šต'์ด๋ผ๋Š” ํ‚ค์›Œ๋“œ๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ ์ฑ„ํƒ์ด ๋˜์—ˆ๋‹ค! ์ฒ˜์Œ์—๋Š” ์ด ์ฃผ์ œ๊ฐ€ ๊ดœ์ฐฎ์€๊ฑธ๊นŒ ๊ณ ๋ฏผ์ด ๋งŽ์•˜๋Š”๋ฐ, ์˜คํžˆ๋ ค ๋” ํ™•์‹ ์„ ๊ฐ€์ง€๊ณ  ์•„์ด๋””์–ด๋ฅผ ํ•จ๊ป˜ develop ํ•ด์ค€ ํŒ€์›๋“ค์—๊ฒŒ ๊ณ ๋ง™๋‹ค๐Ÿฅน ์ฒญ๋…„๋“ค์„ ํƒ€์ผ“ํŒ…ํ•œ ์ฃผ์ œ์ด์ง€๋งŒ, ๋ฐœํ‘œ ๋ถˆ์•ˆ๊ฐ์„ ํ•ด๊ฒฐํ•˜๊ณ  ์‹ถ์€ ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๋„์›€์ด ๋˜๋Š” ์„œ๋น„์Šค์ด๋‹ค.

 

 

ME?

๋‚˜๋Š” AI ๋ฐ ๋ฐฑ์—”๋“œ ํŒŒํŠธ๋ฅผ ๋งก๊ฒŒ ๋˜์—ˆ๋‹ค.

๊ธฐ์ˆ  ์Šคํƒ์„ ์ •ํ•˜๋ฉด์„œ ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ทธ๋ ค๋ณด์•˜๋‹ค. ๊ทธ๋ž˜์„œ ํฌ๊ฒŒ spring & spring boot์™€ mysql๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ์„ ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. ๊ธฐ์กด์— ์Šคํ”„๋ง์œผ๋กœ ๊ฐœ๋ฐœ์„ ํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ์ง€๋งŒ ์ข€ ๋” ํด๋ฆฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์›๋ฆฌ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์•Œ๊ธฐ ์œ„ํ•ด ์Šคํ„ฐ๋””๋ฅผ ์ง„ํ–‰์ค‘์ด๋‹ค.

 

 

Head pose estimation

ํ˜„์žฌ 12์›” ์ค‘์ˆœ์— ํ•  ๊ธฐ์ˆ  ๊ฒ€์ฆ์„ ์œ„ํ•ด AI ๋ชจ๋ธ์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ๋‹ค.

import cv2
import dlib
import numpy as np
from imutils import face_utils

face_landmark_path = 'head-pose-estimation-1/shape_predictor_68_face_landmarks.dat'

K = [6.5308391993466671e+002, 0.0, 3.1950000000000000e+002,
     0.0, 6.5308391993466671e+002, 2.3950000000000000e+002,
     0.0, 0.0, 1.0]
D = [7.0834633684407095e-002, 6.9140193737175351e-002, 0.0, 0.0, -1.3073460323689292e+000]

cam_matrix = np.array(K).reshape(3, 3).astype(np.float32)
dist_coeffs = np.array(D).reshape(5, 1).astype(np.float32)

object_pts = np.float32([[6.825897, 6.760612, 4.402142],
                         [1.330353, 7.122144, 6.903745],
                         [-1.330353, 7.122144, 6.903745],
                         [-6.825897, 6.760612, 4.402142],
                         [5.311432, 5.485328, 3.987654],
                         [1.789930, 5.393625, 4.413414],
                         [-1.789930, 5.393625, 4.413414],
                         [-5.311432, 5.485328, 3.987654],
                         [2.005628, 1.409845, 6.165652],
                         [-2.005628, 1.409845, 6.165652],
                         [2.774015, -2.080775, 5.048531],
                         [-2.774015, -2.080775, 5.048531],
                         [0.000000, -3.116408, 6.097667],
                         [0.000000, -7.415691, 4.070434]])

reprojectsrc = np.float32([[10.0, 10.0, 10.0],
                           [10.0, 10.0, -10.0],
                           [10.0, -10.0, -10.0],
                           [10.0, -10.0, 10.0],
                           [-10.0, 10.0, 10.0],
                           [-10.0, 10.0, -10.0],
                           [-10.0, -10.0, -10.0],
                           [-10.0, -10.0, 10.0]])

line_pairs = [[0, 1], [1, 2], [2, 3], [3, 0],
              [4, 5], [5, 6], [6, 7], [7, 4],
              [0, 4], [1, 5], [2, 6], [3, 7]]


prev_euler_angle = np.zeros((3, 1), dtype=np.float32)  # ์ดˆ๊ธฐ ๊ฐ’

def get_head_pose(shape):
    image_pts = np.float32([shape[17], shape[21], shape[22], shape[26], shape[36],
                            shape[39], shape[42], shape[45], shape[31], shape[35],
                            shape[48], shape[54], shape[57], shape[8]])

    _, rotation_vec, translation_vec = cv2.solvePnP(object_pts, image_pts, cam_matrix, dist_coeffs)

    # ๋””๋ฒ„๊น…: rotation_vec์™€ translation_vec ํ™•์ธ
    print(f"Rotation Vec: {rotation_vec}")
    print(f"Translation Vec: {translation_vec}")

    reprojectdst, _ = cv2.projectPoints(reprojectsrc, rotation_vec, translation_vec, cam_matrix,
                                        dist_coeffs)

    # ๋””๋ฒ„๊น…: reprojectdst ํ™•์ธ
    print(f"Reprojected Points: {reprojectdst}")

    reprojectdst = tuple(map(tuple, reprojectdst.reshape(8, 2)))

    # calc euler angle
    rotation_mat, _ = cv2.Rodrigues(rotation_vec)
    pose_mat = cv2.hconcat((rotation_mat, translation_vec))
    _, _, _, _, _, _, euler_angle = cv2.decomposeProjectionMatrix(pose_mat)

    # ๋””๋ฒ„๊น…: euler_angle ํ™•์ธ
    print(f"Euler Angle: {euler_angle}")

    return reprojectdst, euler_angle


def main():
    # return
    cap = cv2.VideoCapture(0)
    if not cap.isOpened():
        print("Unable to connect to camera.")
        return
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor(face_landmark_path)

    while cap.isOpened():
        ret, frame = cap.read()
        if ret:
            face_rects = detector(frame, 0)

            if len(face_rects) > 0:
                shape = predictor(frame, face_rects[0])
                shape = face_utils.shape_to_np(shape)

                reprojectdst, euler_angle = get_head_pose(shape)

                for (x, y) in shape:
                    cv2.circle(frame, (x, y), 1, (0, 0, 255), -1)

                for start, end in line_pairs:
                    start_point = tuple(map(int, reprojectdst[start]))
                    end_point = tuple(map(int, reprojectdst[end]))
                    cv2.line(frame, start_point, end_point, (0, 0, 255), 2)

                
                #for start, end in line_pairs:
                    #cv2.line(frame, reprojectdst[start], reprojectdst[end], (0, 0, 255))
                    # Ensure that reprojectdst[start] and reprojectdst[end] are tuples (int or float)
                #    cv2.line(frame, tuple(reprojectdst[start]), tuple(reprojectdst[end]), (0, 0, 255), 2)
    
                cv2.putText(frame, "X: " + "{:7.2f}".format(euler_angle[0, 0]), (20, 20), cv2.FONT_HERSHEY_SIMPLEX,
                            0.75, (0, 0, 0), thickness=2)
                cv2.putText(frame, "Y: " + "{:7.2f}".format(euler_angle[1, 0]), (20, 50), cv2.FONT_HERSHEY_SIMPLEX,
                            0.75, (0, 0, 0), thickness=2)
                cv2.putText(frame, "Z: " + "{:7.2f}".format(euler_angle[2, 0]), (20, 80), cv2.FONT_HERSHEY_SIMPLEX,
                            0.75, (0, 0, 0), thickness=2)

            cv2.imshow("demo", frame)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                break


if __name__ == '__main__':
    main()

์ด ๋ชจ๋ธ์„ runํ•˜๋ฉด,

ํ˜„์žฌ๋Š” ์ด๋ ‡๊ฒŒ ์ถœ๋ ฅ์ด ๋œ๋‹ค.

์–ผ๊ตดํ˜•, ๋ˆˆ, ์ฝ”, ์ž…, ์ด๋งˆ ๋“ฑ์— ๋นจ๊ฐ„์ƒ‰ dot์ด ์ฐํžˆ๋ฉด์„œ head pose๋ฅผ ์ธ์‹ํ•ด X,Y,Z ๊ฐ’์„ ์ถœ๋ ฅํ•œ๋‹ค. ๊ฐ ๊ฐ’์˜ ๋œป์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

X (Pitch) = +30°: ๋จธ๋ฆฌ๊ฐ€ ์•ž์œผ๋กœ 30° ์ˆ™์—ฌ์ ธ ์žˆ๋‹ค๋Š” ๋œป
Y (Yaw) = -15°: ๋จธ๋ฆฌ๊ฐ€ ์™ผ์ชฝ์œผ๋กœ 15° ํšŒ์ „ํ•ด ์žˆ๋‹ค๋Š” ๋œป
Z (Roll) = +10°: ๋จธ๋ฆฌ๊ฐ€ ์˜ค๋ฅธ์ชฝ์œผ๋กœ 10° ๊ธฐ์šธ์–ด์ ธ ์žˆ๋‹ค๋Š” ๋œป

 

์•ˆ์ •์ ์œผ๋กœ x,y,z ๊ฐ’์„ ์ถœ๋ ฅํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋ชจ๋ธ ์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ ๋” tuning ํ•ด์•ผํ•œ๋‹ค. ๊ทธ๋ž˜๋„ ์šฐ๋ฆฌ ์„œ๋น„์Šค์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ์ด 1์ฐจ์ ์œผ๋กœ ๊ฐœ๋ฐœ์ด ๋˜์–ด์„œ ๋‹คํ–‰์ด๋‹ค!

 

 

Spring Repository START

๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ํ™˜๊ฒฝ ์„ธํŒ…์„ ๋ฐ”๋ฅด๊ฒŒ ์™„๋ฃŒํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด spring repo๋ฅผ ์ƒ์„ฑํ•ด๋‘์—ˆ๋‹ค.

๋”๋ณด๊ธฐ
  • Project: SpringBoot๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ํ˜„์žฌ๋Š” Gradle์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ถ”์„ธ์ด๋‹ค.
  • Language: ์‚ฌ์šฉํ•˜๊ณ ์žํ•˜๋Š” ์–ธ์–ด๋ฅผ ์„ ํƒํ•˜๋ฉด ๋œ๋‹ค. (์ผ๋ฐ˜์ ์œผ๋กœ Java๊ฐ€ ์‚ฌ์šฉ๋จ)
  • SpringBoot: ๋ฒ„์ „์„ ์„ ํƒํ•ด ์ค€๋‹ค. SNAPSHOT์€ ๋ฐ๋ชจ๋ฒ„์ „์ด๊ณ  ๋†’์€ ๋ฒ„์ „์€ ๋†’์€ ์ž๋ฐ”๋ฒ„์ „์„ ํ•„์š”๋กœ ํ•˜๋ฏ€๋กœ SNAPSHOT์ด ์—†๋Š” ๋‚ฎ์€ ๋ฒ„์ „์„ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • Group: ๊ธฐ์—… ๋„๋ฉ”์ธ๋ช…
  • Artifact: ๋นŒ๋“œ๋˜์–ด ๋‚˜์˜ฌ ๊ฒฐ๊ณผ๋ฌผ
  • Name: ํ”„๋กœ์ ํŠธ๋ช… (์ผ๋ฐ˜์ ์œผ๋กœ Artifact์™€ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•จ)
  • Description: ์„ค๋ช…Package name: ํŒจํ‚ค์ง€์ด๋ฆ„(Group๊ณผ Artifact๋ฅผ ์„ค์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.)
  • Packaging: ๊ธฐ๋ณธ์ด .jar์ด๋‹ค. (spring framework์™€ model2๋Š” .war๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค

 

  • SpringWeb (์ค‘์š”)
    ์›น ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ชจ๋“ˆ์ด๋‹ค. ๋‚ด์žฅ ํ†ฐ์บฃ ๋ฟ ์•„๋‹ˆ๋ผ Spring MVC ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด ๋Œ€๋ถ€๋ถ„ ๋“ค์–ด์žˆ๋‹ค. REST API์„œ๋ฒ„๋ฅผ ๋งŒ๋“ ๋‹ค๋ฉด ํ•„์ˆ˜์ด๋‹ค.
  • Lombok (์ถ”์ฒœ, ๊ฑฐ์˜ ํ•„์ˆ˜)
    Class์— getter, setter, toString, equals, constructor ๋“ฑ์˜ ๋ฉ”์†Œ๋“œ๋“ค์„ ๊ฐ„๋‹จํ•œ ์–ด๋…ธํ…Œ์ด์…˜(@)์œผ๋กœ ์ง€์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์–ด ์ž๋ฐ”ํŠน์œ ์˜ ์žฅํ™ฉํ•œ ํด๋ž˜์Šค๋ฅผ ์ค„์—ฌ์ค€๋‹ค.
  • Spring Data JPA ( ํ•„์ˆ˜ )
    JPA๋Š” Java Persistence API๋ผ๊ณ  ํ•˜๋Š”๋ฐ ์ž๋ฐ” ORM๊ธฐ์ˆ ์˜ ํ† ๋Œ€๋ฅผ ์ด๋ฃจ๋Š” ๊ธฐ์ˆ  ๋ช…์„ธ์ด๋‹ค. Spring์—์„œ DB๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฑฐ์˜ ํ‘œ์ค€ ๊ธฐ์ˆ ์ธ ์ƒํ™ฉ์ด๋‹ค. Class Entity๋ฅผ ๋งˆ์น˜ DB์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋งค์šฐ ์œ ์šฉํ•œ ๋„๊ตฌ์ด๋‹ค. (ํ•™์Šต๋Ÿ‰ ๋˜ํ•œ ์—„์ฒญ๋‚˜๋‹ค.)
  • MySQL Driver
    MySql์˜ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์ž๋™์œผ๋กœ ์—ฐ๊ฒฐํ•ด์ค€๋‹ค.
  • Spring Configuration Processor
    ์Šคํ”„๋ง ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด์„œ application.yml ๋˜๋Š” application.properties๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์ถ”์ฒœ์„ ๋ฐ›๊ณ  ์‹ถ์„ ๋•Œ ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • Spring Boot DevTools (์ทจํ–ฅ์— ๋”ฐ๋ผ ์‚ฌ์šฉ)
    DevTools๋Š” ์Šคํ”„๋ง ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋„์› ์„ ๋•Œ, ์žฌ์‹œ์ž‘ํ•˜์ง€ ์•Š๊ณ  ์ฝ”๋“œ์˜ ๋ณ€ํ™”๋ฅผ ๋ฐ˜์˜์‹œํ‚ฌ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. HTML ํŒŒ์ผ์˜ ๊ฒฝ์šฐ LiveReload ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์—ฌ ์ €์žฅํ•˜๋ฉด ๊ทธ ๋ณ€ํ™”๊ฐ€ ํŽ˜์ด์ง€์— ๋ฐ”๋กœ ๋‚˜ํƒ€๋‚˜๊ฒŒ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. (์ฐธ๊ณ   velog.io/@bread_dd/Spring-Boot-Devtools)
  • Thymeleaf
    View ํ…œํ”Œ๋ฆฟ์ธ Thymeleaf๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋ชจ๋“ˆ์ด๋‹ค. Spring์—์„œ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” JSP๋Š” Spring Boot์—์„œ๋Š” ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • Validation : ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ณ  ์œ ํšจํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œํ‚จ๋‹ค.

 

step1) ๊ทธ๋ฆฌ๊ณ  build.gradle์œผ๋กœ ์••์ถ•์„ ํ’€์–ด์ฃผ๊ณ 

์ฒ˜์Œ์—๋Š” run์ด ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋ฐ, ์•„์ง mysql๊ณผ์˜ ์—ฐ๊ฒฐ ์ •๋ณด๋ฅผ ์ ์–ด์ฃผ์ง€ ์•Š์•„์„œ ๊ทธ๋ ‡๋‹ค.

step 2) ๊ทธ๋ž˜์„œ 'build.gradle'์—์„œ jpa, security, mysql์€ ์ฃผ์„์ฒ˜๋ฆฌ ํ•ด์ค€ ํ›„ ์‹คํ–‰์„ ํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด ์„ฑ๊ณต~

 

 

์ ‘์†๋„ ๊ฐ€๋Šฅํ•˜๋‹ค. ์•„์ง ์•„๋ฌด๊ฒƒ๋„ ๊ฐœ๋ฐœํ•˜์ง€ ์•Š์•„ ๋œจ๋Š” ๊ฑด ์—†๋‹ค. 

์ด๋ ‡๊ฒŒ ์ดˆ๊ธฐ ์„ค์ •๋„ ๋ ๐Ÿ™†๐Ÿป‍โ™€๏ธ