CS log
[Capstone project] SPitching ๋ณธ๋ฌธ
๐ฃ๏ธ 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์ ์ฃผ์์ฒ๋ฆฌ ํด์ค ํ ์คํ์ ํ๋ฉด ์์ ๊ฐ์ด ์ฑ๊ณต~
์ ์๋ ๊ฐ๋ฅํ๋ค. ์์ง ์๋ฌด๊ฒ๋ ๊ฐ๋ฐํ์ง ์์ ๋จ๋ ๊ฑด ์๋ค.
์ด๋ ๊ฒ ์ด๊ธฐ ์ค์ ๋ ๋ ๐๐ปโ๏ธ