ArUco ๋ง์ปค ๊ฑฐ๋ฆฌ์ธก์ ์ค์ต ๊ฐ์ด๋
์ด ๊ฐ์ด๋๋ OpenCV๋ฅผ ์ฌ์ฉํ์ฌ ArUco ๋ง์ปค๋ฅผ ์ด์ฉํ ๊ฑฐ๋ฆฌ ์ธก์ ์์คํ ์ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ๋จ๊ณ๋ณ๋ก ์๋ดํฉ๋๋ค.
๊ฐ์
ArUco ๋ง์ปค๋ ์ฆ๊ฐํ์ค๊ณผ ์ปดํจํฐ ๋น์ ์์ ๋๋ฆฌ ์ฌ์ฉ๋๋ ์ ์ฌ๊ฐํ ๋ง์ปค๋ก, ๊ฐ ๋ง์ปค๋ ๊ณ ์ ํ ID๋ฅผ ๊ฐ์ง๋ฉฐ ์นด๋ฉ๋ผ๋ก ์ฝ๊ฒ ์ธ์ํ ์ ์์ต๋๋ค. ์ด ์ค์ต์์๋ ์นด๋ฉ๋ผ ์ผ๋ฆฌ๋ธ๋ ์ด์ ๋ถํฐ ๊ฑฐ๋ฆฌ ์ธก์ ๊น์ง์ ์ ์ฒด ๊ณผ์ ์ ๋ค๋ฃน๋๋ค.
์ฌ์ ์ค๋น์ฌํญ
ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
pip install opencv-python==4.6.0.66
pip install opencv-contrib-python==4.6.0.66
pip install numpy
์ฒด์ค๋ณด๋ ํจํด ์ค๋น
-
9x6 ๋ด๋ถ ์ฝ๋๋ฅผ ๊ฐ์ง ์ฒด์ค๋ณด๋ ํจํด ์ธ์ : https://calib.io/pages/camera-calibration-pattern-generator
-
ํํํ ํ๋ฉด์ ๋ถ์ฐฉ (์ค์: ํ์ด์ง๋ฉด ์๋จ)
-
๊ถ์ฅ ํฌ๊ธฐ: A4 ์ฉ์ง ํฌ๊ธฐ
ArUco ๋ง์ปค ์ค๋น
-
5x4 ArUco ๋ง์ปค ์ธ์ (์จ๋ผ์ธ ์์ฑ๊ธฐ ์ฌ์ฉ ๊ฐ๋ฅ)
-
์ ํํ ํฌ๊ธฐ ์ธก์ (์: 5cm x 5cm)
1๋จ๊ณ: ์นด๋ฉ๋ผ ์ผ๋ฆฌ๋ธ๋ ์ด์ ์ฉ ์ฌ์ง ์ดฌ์
์นด๋ฉ๋ผ ์ผ๋ฆฌ๋ธ๋ ์ด์ ์ ์ํด์๋ ๋ค์ํ ๊ฐ๋์ ์์น์์ ์ดฌ์ํ ์ฒด์ค๋ณด๋ ์ด๋ฏธ์ง๊ฐ ํ์ํฉ๋๋ค.
์ดฌ์ ์คํฌ๋ฆฝํธ ์์ฑ
import cv2
import os
# ์นด๋ฉ๋ผ ์ด๊ธฐํ
cap = cv2.VideoCapture(0)
# ์ด๋ฏธ์ง ์ ์ฅ ๊ฒฝ๋ก ์ค์
save_path = "calibration_images"
if not os.path.exists(save_path):
os.makedirs(save_path)
image_count = 0
print("์ฒด์ค๋ณด๋๋ฅผ ๋ค์ํ ๊ฐ๋๋ก ์ดฌ์ํ์ธ์.")
print("'s' ํค๋ฅผ ๋๋ฌ ์ ์ฅ, 'q' ํค๋ฅผ ๋๋ฌ ์ข
๋ฃ")
while True:
ret, frame = cap.read()
if not ret:
break
# ํ๋ฉด์ ์ด๋ฏธ์ง ํ์
cv2.imshow('Camera Calibration', frame)
key = cv2.waitKey(1) & 0xFF
# 's' ํค๋ฅผ ๋๋ฅด๋ฉด ์ด๋ฏธ์ง ์ ์ฅ
if key == ord('s'):
filename = f"{save_path}/calib_{image_count:02d}.jpg"
cv2.imwrite(filename, frame)
print(f"์ด๋ฏธ์ง ์ ์ฅ๋จ: {filename}")
image_count += 1
# 'q' ํค๋ฅผ ๋๋ฅด๋ฉด ์ข
๋ฃ
elif key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
print(f"์ด {image_count}์ฅ์ ์ด๋ฏธ์ง๊ฐ ์ ์ฅ๋์์ต๋๋ค.")
์ดฌ์ ๊ฐ์ด๋๋ผ์ธ
ํ์ ์กฐ๊ฑด
-
์ต์ 10-15์ฅ์ ์ด๋ฏธ์ง ํ์ (20์ฅ ๊ถ์ฅ)
-
๋ค์ํ ๊ฐ๋์์ ์ดฌ์ (0ยฐ, 15ยฐ, 30ยฐ, 45ยฐ ๋ฑ)
-
ํ๋ฉด ์ ์ฒด์ ์ฒด์ค๋ณด๋๊ฐ ๋ณด์ด๋๋ก ์ดฌ์
-
ํ๋ฉด ๋ชจ์๋ฆฌ ๋ถ๋ถ๋ ํฌํจํ์ฌ ์ดฌ์
ํผํด์ผ ํ ๊ฒ
-
ํ๋ฆฟํ๊ฑฐ๋ ์์ง์์ด ์๋ ์ฌ์ง
-
์กฐ๋ช ์ด ๋๋ฌด ์ด๋ก๊ฑฐ๋ ๋ฐ์ ์ฌ์ง
-
์ฒด์ค๋ณด๋๊ฐ ํ์ด์ง ์ํ๋ก ์ดฌ์๋ ์ฌ์ง
2๋จ๊ณ: ์นด๋ฉ๋ผ ์ผ๋ฆฌ๋ธ๋ ์ด์ ์งํ
์ดฌ์๋ ์ด๋ฏธ์ง๋ค์ ์ฌ์ฉํ์ฌ ์นด๋ฉ๋ผ์ ๋ด๋ถ ๋งค๊ฐ๋ณ์์ ์๊ณก ๊ณ์๋ฅผ ๊ณ์ฐํฉ๋๋ค.
์ผ๋ฆฌ๋ธ๋ ์ด์ ์คํฌ๋ฆฝํธ
import cv2
import numpy as np
import glob
import pickle
# ์ฒด์ค๋ณด๋ ์ค์
CHECKERBOARD = (9, 6) # ๋ด๋ถ ์ฝ๋ ์ (๊ฐ๋ก, ์ธ๋ก)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 3D ์ ์ค๋น
objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
# ์ ๋ค์ ์ ์ฅํ ๋ฐฐ์ด
objpoints = [] # 3D ์
imgpoints = [] # 2D ์
# ์ผ๋ฆฌ๋ธ๋ ์ด์
์ด๋ฏธ์ง ๋ก๋
images = glob.glob('calibration_images/*.jpg')
print(f"์ด {len(images)}์ฅ์ ์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.")
successful_images = 0
failed_images = []
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ์ฒด์ค๋ณด๋ ์ฝ๋ ์ฐพ๊ธฐ
ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, None)
if ret:
successful_images += 1
objpoints.append(objp)
# ์ฝ๋ ์ ๋ฐ๋ ํฅ์
corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
imgpoints.append(corners2)
# ์ฝ๋ ์๊ฐํ (์ ํ์ฌํญ)
img_with_corners = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret)
cv2.imshow('Corners', img_with_corners)
cv2.waitKey(100)
else:
failed_images.append(fname)
print(f"์ฝ๋๋ฅผ ์ฐพ์ ์ ์์: {fname}")
cv2.destroyAllWindows()
print(f"์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌ๋ ์ด๋ฏธ์ง: {successful_images}์ฅ")
print(f"์คํจํ ์ด๋ฏธ์ง: {len(failed_images)}์ฅ")
if successful_images < 10:
print("๊ฒฝ๊ณ : ์ผ๋ฆฌ๋ธ๋ ์ด์
์ ์ํด ์ต์ 10์ฅ์ ์ด๋ฏธ์ง๊ฐ ํ์ํฉ๋๋ค.")
exit()
# ์นด๋ฉ๋ผ ์ผ๋ฆฌ๋ธ๋ ์ด์
์ํ
print("์นด๋ฉ๋ผ ์ผ๋ฆฌ๋ธ๋ ์ด์
์ ์ํํ๋ ์ค...")
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
print(f"์ผ๋ฆฌ๋ธ๋ ์ด์
์๋ฃ!")
print(f"์ฌํฌ์ ์ค์ฐจ (RMS): {ret:.4f}")
print(f"์นด๋ฉ๋ผ ๋งคํธ๋ฆญ์ค:\n{mtx}")
print(f"์๊ณก ๊ณ์:\n{dist}")
# ๊ฒฐ๊ณผ ์ ์ฅ
calibration_data = {
'camera_matrix': mtx,
'distortion_coefficients': dist,
'rotation_vectors': rvecs,
'translation_vectors': tvecs,
'rms_error': ret
}
with open('camera_calibration.pkl', 'wb') as f:
pickle.dump(calibration_data, f)
print("์ผ๋ฆฌ๋ธ๋ ์ด์
๋ฐ์ดํฐ๊ฐ 'camera_calibration.pkl'์ ์ ์ฅ๋์์ต๋๋ค.")
# ์๊ณก ๋ณด์ ํ
์คํธ
test_img = cv2.imread(images[0])
h, w = test_img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
# ์๊ณก ๋ณด์ ์ ์ฉ
dst = cv2.undistort(test_img, mtx, dist, None, newcameramtx)
# ๊ฒฐ๊ณผ ๋น๊ต ํ์
comparison = np.hstack((test_img, dst))
cv2.imshow('Original vs Undistorted', comparison)
cv2.waitKey(0)
cv2.destroyAllWindows()
์ผ๋ฆฌ๋ธ๋ ์ด์ ํ์ง ํ์ธ
์ข์ ์ผ๋ฆฌ๋ธ๋ ์ด์ ์ ์งํ
-
RMS ์ค์ฐจ๊ฐ 1.0 ๋ฏธ๋ง
-
์ด๋ฏธ์ง ์๊ณก์ด ๋์ ๋๊ฒ ๊ฐ์ ๋จ
-
์ง์ ์ด ์ค์ ๋ก ์ง์ ์ผ๋ก ๋ณด์
์ผ๋ฆฌ๋ธ๋ ์ด์ ๊ฐ์ ๋ฐฉ๋ฒ
-
๋ ๋ง์ ์ด๋ฏธ์ง ์ดฌ์
-
๋ค์ํ ๊ฑฐ๋ฆฌ์์ ์ดฌ์
-
ํ๋ฉด ์ ์ฒด ์์ญ์ ๊ณ ๋ฅด๊ฒ ์ปค๋ฒ
3๋จ๊ณ: ArUco ๋ง์ปค ์ธ์
์ผ๋ฆฌ๋ธ๋ ์ด์ ๋ ์นด๋ฉ๋ผ๋ฅผ ์ฌ์ฉํ์ฌ ArUco ๋ง์ปค๋ฅผ ์ธ์ํ๊ณ ํฌ์ฆ๋ฅผ ์ถ์ ํฉ๋๋ค.
ArUco ๋ง์ปค ์ธ์ ์คํฌ๋ฆฝํธ
import cv2
import numpy as np
import pickle
# ์ผ๋ฆฌ๋ธ๋ ์ด์
๋ฐ์ดํฐ ๋ก๋
with open('camera_calibration.pkl', 'rb') as f:
calibration_data = pickle.load(f)
camera_matrix = calibration_data['camera_matrix']
dist_coeffs = calibration_data['distortion_coefficients']
# ArUco ์ค์
aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_4X4_50)
parameters = cv2.aruco.DetectorParameters_create()
# ๋ง์ปค ํฌ๊ธฐ (์ค์ ํฌ๊ธฐ, ๋จ์: ๋ฏธํฐ)
marker_size = 0.05 # 5cm = 0.05m
# ์นด๋ฉ๋ผ ์ด๊ธฐํ
cap = cv2.VideoCapture(0)
print("ArUco ๋ง์ปค ์ธ์์ ์์ํฉ๋๋ค.")
print("'q' ํค๋ฅผ ๋๋ฌ ์ข
๋ฃ")
while True:
ret, frame = cap.read()
if not ret:
break
# ์๊ณก ๋ณด์
frame = cv2.undistort(frame, camera_matrix, dist_coeffs, None, camera_matrix)
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# ArUco ๋ง์ปค ๊ฒ์ถ
corners, ids, rejected = cv2.aruco.detectMarkers(gray, aruco_dict, parameters=parameters)
if ids is not None:
# ๋ง์ปค ๊ทธ๋ฆฌ๊ธฐ
cv2.aruco.drawDetectedMarkers(frame, corners, ids)
# ํฌ์ฆ ์ถ์
rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers(
corners, marker_size, camera_matrix, dist_coeffs)
for i in range(len(ids)):
# ์ขํ์ถ ๊ทธ๋ฆฌ๊ธฐ
cv2.aruco.drawAxis(frame, camera_matrix, dist_coeffs,
rvecs[i], tvecs[i], 0.03)
# ๋ง์ปค ์ค์ฌ์ ๊ณ์ฐ
marker_center = np.mean(corners[i][0], axis=0).astype(int)
# ๊ฑฐ๋ฆฌ ๊ณ์ฐ (์ ํด๋ฆฌ๋ ๊ฑฐ๋ฆฌ)
distance = np.linalg.norm(tvecs[i][0])
# ์ ๋ณด ํ์
cv2.putText(frame, f"ID: {ids[i][0]}",
(marker_center[0] - 50, marker_center[1] - 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.putText(frame, f"Distance: {distance:.2f}m",
(marker_center[0] - 50, marker_center[1] - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
# ๋ง์ปค ์ค์ฌ์ ์ ๊ทธ๋ฆฌ๊ธฐ
cv2.circle(frame, tuple(marker_center), 5, (0, 0, 255), -1)
# ๊ฒฐ๊ณผ ํ์
cv2.imshow('ArUco Marker Detection', frame)
# ์ข
๋ฃ ์กฐ๊ฑด
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
ArUco ๋ง์ปค ์ธ์ ์ต์ ํ
์ธ์ ์ฑ๋ฅ ํฅ์ ๋ฐฉ๋ฒ
-
์ ์ ํ ์กฐ๋ช ํ๊ฒฝ ์ ์ง
-
๋ง์ปค๊ฐ ํ๋ฉด์ ํํํ๊ฒ ์์น
-
๋ง์ปค์ ์นด๋ฉ๋ผ ๊ฐ ์ ์ ํ ๊ฑฐ๋ฆฌ ์ ์ง
๋ฌธ์ ํด๊ฒฐ
-
๋ง์ปค๊ฐ ์ธ์๋์ง ์๋ ๊ฒฝ์ฐ: ์กฐ๋ช ํ์ธ, ๋ง์ปค ์ํ ์ ๊ฒ
-
๊ฑฐ๋ฆฌ ๊ฐ์ด ๋ถ์ ํํ ๊ฒฝ์ฐ: ์ผ๋ฆฌ๋ธ๋ ์ด์ ์ฌ์ํ, ๋ง์ปค ํฌ๊ธฐ ์ฌ์ธก์
4๋จ๊ณ: ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ๊ฒฝ๊ณ ๋ฌธ ์ถ๋ ฅ
์ธก์ ๋ ๊ฑฐ๋ฆฌ๋ฅผ ๋ฐํ์ผ๋ก ๊ฒฝ๊ณ ์์คํ ์ ๊ตฌํํฉ๋๋ค.
๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ ๊ฒฝ๊ณ ์์คํ ์คํฌ๋ฆฝํธ
import cv2
import numpy as np
import pickle
import time
# ์ผ๋ฆฌ๋ธ๋ ์ด์
๋ฐ์ดํฐ ๋ก๋
with open('camera_calibration.pkl', 'rb') as f:
calibration_data = pickle.load(f)
camera_matrix = calibration_data['camera_matrix']
dist_coeffs = calibration_data['distortion_coefficients']
# ArUco ์ค์
aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_4X4_50)
parameters = cv2.aruco.DetectorParameters_create()
# ๋ง์ปค ํฌ๊ธฐ (์ค์ ํฌ๊ธฐ, ๋จ์: ๋ฏธํฐ)
marker_size = 0.05 # 5cm
# ๊ฑฐ๋ฆฌ ์๊ณ๊ฐ ์ค์ (๋ฏธํฐ)
WARNING_DISTANCES = {
'DANGER': 0.2, # 20cm ์ดํ - ์ํ
'CAUTION': 0.5, # 50cm ์ดํ - ์ฃผ์
'SAFE': 1.0 # 1m ์ด์ - ์์
}
# ๊ฒฝ๊ณ ์์ ์ค์
WARNING_COLORS = {
'DANGER': (0, 0, 255), # ๋นจ๊ฐ์
'CAUTION': (0, 165, 255), # ์ฃผํฉ์
'SAFE': (0, 255, 0) # ์ด๋ก์
}
# ์นด๋ฉ๋ผ ์ด๊ธฐํ
cap = cv2.VideoCapture(0)
# ์๋ฆผ ๊ด๋ จ ๋ณ์
last_warning_time = 0
warning_interval = 2.0 # 2์ด๋ง๋ค ๊ฒฝ๊ณ ์
def get_warning_level(distance):
"""๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ๊ฒฝ๊ณ ๋ ๋ฒจ ๋ฐํ"""
if distance <= WARNING_DISTANCES['DANGER']:
return 'DANGER'
elif distance <= WARNING_DISTANCES['CAUTION']:
return 'CAUTION'
else:
return 'SAFE'
def draw_warning_overlay(frame, warning_level, distance):
"""๊ฒฝ๊ณ ์ค๋ฒ๋ ์ด ๊ทธ๋ฆฌ๊ธฐ"""
height, width = frame.shape[:2]
# ๊ฒฝ๊ณ ๋ฉ์์ง ์ค์
if warning_level == 'DANGER':
message = "DANGER! TOO CLOSE!"
font_scale = 2.0
elif warning_level == 'CAUTION':
message = "CAUTION! BE CAREFUL!"
font_scale = 1.5
else:
message = "SAFE DISTANCE"
font_scale = 1.0
color = WARNING_COLORS[warning_level]
# ๋ฐฐ๊ฒฝ ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
overlay = frame.copy()
cv2.rectangle(overlay, (0, 0), (width, 100), color, -1)
cv2.addWeighted(overlay, 0.3, frame, 0.7, 0, frame)
# ํ
์คํธ ํฌ๊ธฐ ๊ณ์ฐ
text_size = cv2.getTextSize(message, cv2.FONT_HERSHEY_SIMPLEX, font_scale, 3)[0]
text_x = (width - text_size[0]) // 2
text_y = 60
# ํ
์คํธ ๊ทธ๋ฆฌ๊ธฐ (๊ฒ์์ ์ธ๊ณฝ์ )
cv2.putText(frame, message, (text_x-2, text_y-2),
cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 0), 5)
cv2.putText(frame, message, (text_x, text_y),
cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 255, 255), 3)
# ๊ฑฐ๋ฆฌ ์ ๋ณด ํ์
distance_text = f"Distance: {distance:.2f}m"
cv2.putText(frame, distance_text, (20, height - 30),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, color, 2)
def play_warning_sound(warning_level):
"""๊ฒฝ๊ณ ์ ์ถ๋ ฅ (์์คํ
์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์์)"""
global last_warning_time
current_time = time.time()
if current_time - last_warning_time > warning_interval:
if warning_level == 'DANGER':
# ์ฐ์ ๋นํ์ (์ํ)
print("\a" * 3) # ์์คํ
์๋ฆผ์
elif warning_level == 'CAUTION':
# ๋จ์ผ ๋นํ์ (์ฃผ์)
print("\a")
last_warning_time = current_time
print("๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ ๊ฒฝ๊ณ ์์คํ
์ ์์ํฉ๋๋ค.")
print("๊ฒฝ๊ณ ๋ ๋ฒจ:")
print(f"- ์ํ (๋นจ๊ฐ): {WARNING_DISTANCES['DANGER']}m ์ดํ")
print(f"- ์ฃผ์ (์ฃผํฉ): {WARNING_DISTANCES['CAUTION']}m ์ดํ")
print(f"- ์์ (์ด๋ก): {WARNING_DISTANCES['SAFE']}m ์ด์")
print("'q' ํค๋ฅผ ๋๋ฌ ์ข
๋ฃ")
while True:
ret, frame = cap.read()
if not ret:
break
# ์๊ณก ๋ณด์
frame = cv2.undistort(frame, camera_matrix, dist_coeffs, None, camera_matrix)
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# ArUco ๋ง์ปค ๊ฒ์ถ
corners, ids, rejected = cv2.aruco.detectMarkers(gray, aruco_dict, parameters=parameters)
if ids is not None:
# ๋ง์ปค ๊ทธ๋ฆฌ๊ธฐ
cv2.aruco.drawDetectedMarkers(frame, corners, ids)
# ํฌ์ฆ ์ถ์
rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers(
corners, marker_size, camera_matrix, dist_coeffs)
# ๊ฐ์ฅ ๊ฐ๊น์ด ๋ง์ปค ์ฐพ๊ธฐ
min_distance = float('inf')
closest_marker_idx = 0
for i in range(len(ids)):
distance = np.linalg.norm(tvecs[i][0])
if distance < min_distance:
min_distance = distance
closest_marker_idx = i
# ์ขํ์ถ ๊ทธ๋ฆฌ๊ธฐ
cv2.aruco.drawAxis(frame, camera_matrix, dist_coeffs,
rvecs[i], tvecs[i], 0.03)
# ๋ง์ปค ์ค์ฌ์ ๊ณผ ID ํ์
marker_center = np.mean(corners[i][0], axis=0).astype(int)
cv2.putText(frame, f"ID: {ids[i][0]}",
(marker_center[0] - 30, marker_center[1] + 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
# ๊ฐ์ฅ ๊ฐ๊น์ด ๋ง์ปค์ ๋ํ ๊ฒฝ๊ณ ์ฒ๋ฆฌ
warning_level = get_warning_level(min_distance)
# ๊ฒฝ๊ณ ์ค๋ฒ๋ ์ด ๊ทธ๋ฆฌ๊ธฐ
draw_warning_overlay(frame, warning_level, min_distance)
# ๊ฒฝ๊ณ ์ ์ถ๋ ฅ
if warning_level in ['DANGER', 'CAUTION']:
play_warning_sound(warning_level)
else:
# ๋ง์ปค๊ฐ ์์ ๋ ๋ฉ์์ง ํ์
cv2.putText(frame, "No ArUco marker detected", (20, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2)
# ์ฌ์ฉ๋ฒ ์๋ด
cv2.putText(frame, "Press 'q' to quit", (20, frame.shape[0] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
# ๊ฒฐ๊ณผ ํ์
cv2.imshow('Distance Warning System', frame)
# ์ข
๋ฃ ์กฐ๊ฑด
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
print("๊ฑฐ๋ฆฌ ๊ฒฝ๊ณ ์์คํ
์ ์ข
๋ฃํฉ๋๋ค.")
๊ฒฝ๊ณ ์์คํ ์ปค์คํฐ๋ง์ด์ง
๊ฑฐ๋ฆฌ ์๊ณ๊ฐ ์กฐ์
# ์ฉ๋์ ๋ง๊ฒ ์๊ณ๊ฐ ๋ณ๊ฒฝ
WARNING_DISTANCES = {
'DANGER': 0.1, # 10cm - ๋งค์ฐ ์ํ
'CAUTION': 0.3, # 30cm - ์ฃผ์ ํ์
'SAFE': 0.8 # 80cm - ์์ ๊ฑฐ๋ฆฌ
}
์ถ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ ์์ด๋์ด
-
๋ก๊ทธ ํ์ผ์ ๊ฑฐ๋ฆฌ ๋ฐ์ดํฐ ๊ธฐ๋ก
-
๋คํธ์ํฌ๋ฅผ ํตํ ์๊ฒฉ ์๋ฆผ
-
๋ค์ค ๋ง์ปค ๋์ ๋ชจ๋ํฐ๋ง
-
๊ฑฐ๋ฆฌ ๋ณํ์จ ๊ธฐ๋ฐ ๊ฒฝ๊ณ
๋ฌธ์ ํด๊ฒฐ ๊ฐ์ด๋
์ผ๋ฐ์ ์ธ ๋ฌธ์ ๋ค
๋ง์ปค๊ฐ ์ธ์๋์ง ์๋ ๊ฒฝ์ฐ
-
์กฐ๋ช ์ํ ํ์ธ (๋๋ฌด ๋ฐ๊ฑฐ๋ ์ด๋์ฐ๋ฉด ์๋จ)
-
๋ง์ปค์ ๋ฌผ๋ฆฌ์ ์ํ ์ ๊ฒ (๊ตฌ๊ฒจ์ง๊ฑฐ๋ ๋๋ฌ์์ง)
-
์นด๋ฉ๋ผ ํฌ์ปค์ค ์กฐ์
-
ArUco ๋์ ๋๋ฆฌ ํ์ ํ์ธ
๊ฑฐ๋ฆฌ ์ธก์ ์ด ๋ถ์ ํํ ๊ฒฝ์ฐ
-
์นด๋ฉ๋ผ ์ผ๋ฆฌ๋ธ๋ ์ด์ ์ฌ์ํ
-
๋ง์ปค ํฌ๊ธฐ ์ฌ์ธก์ (์ ํํ ๋ฌผ๋ฆฌ์ ํฌ๊ธฐ ํ์)
-
์ผ๋ฆฌ๋ธ๋ ์ด์ ์ ์ฌ์ฉํ ์ด๋ฏธ์ง ํ์ง ํ์ธ
์ฑ๋ฅ์ด ๋๋ฆฐ ๊ฒฝ์ฐ
-
์ด๋ฏธ์ง ํด์๋ ๋ฎ์ถ๊ธฐ
-
์นด๋ฉ๋ผ FPS ์กฐ์
-
๋ถํ์ํ ์ฒ๋ฆฌ ๊ณผ์ ์ ๊ฑฐ
์ ํ๋ ํฅ์ ๋ฐฉ๋ฒ
์ผ๋ฆฌ๋ธ๋ ์ด์ ํ์ง ๊ฐ์
-
๋ ๋ง์ ์ผ๋ฆฌ๋ธ๋ ์ด์ ์ด๋ฏธ์ง ์ฌ์ฉ (20์ฅ ์ด์)
-
๋ค์ํ ๊ฐ๋์ ๊ฑฐ๋ฆฌ์์ ์ดฌ์
-
๊ณ ํด์๋ ์ด๋ฏธ์ง ์ฌ์ฉ
๋ง์ปค ์ธ์ ์ ํ๋ ํฅ์
-
๊ณ ํ์ง ๋ง์ปค ์ธ์ (์ ๋ช ํ๊ณ ๋๋น๊ฐ ๋๋ ทํจ)
-
์ ์ ํ ๋ง์ปค ํฌ๊ธฐ ์ ํ (๋๋ฌด ์๊ฑฐ๋ ํฌ์ง ์๊ฒ)
-
๊ท ์ผํ ์กฐ๋ช ํ๊ฒฝ ์กฐ์ฑ