# 오늘의 고민 : 로봇이 놓인 방향을 어떻게 구별할 것인가



코드 해석
def __init__(self, camera):
self.camera = camera
self.blue_lower = np.array([100, 150, 50]) # 파란색 하한값
self.blue_upper = np.array([140, 255, 255]) # 파란색 상한값
self.red_lower = np.array([170, 140, 150]) # 빨간색 하한값
self.red_upper = np.array([180, 255, 255]) # 빨간색 상한값
def get_image(self):
ret, frame = self.camera.read()
if not ret:
print("Failed to capture image")
return None
return frame
def preprocess_image(self, image, lower, upper):
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower, upper)
return mask
camera 파라미터를 받아오고, 파란색과 빨간색을 검출하기 위한 HSV 범위를 설정한다.
__init__ 매서드는 객체 초기화를 수행.
get_image 매서드에서,
read() 매서드는 cv2.VideoCapture 객체의 메서드로, 비디오 스트림으로부터 프레임을 읽어와서 두 개의 값을 반환 두 개의 값을 반환한다.
- ret : 프레임이 성공적으로 읽혔는지를 나타내는 불리언(boolean) 값 (True or False)
- frame : 읽어온 비디오 프레임(이미지)
preprocess_image 매서드에서,
전처리를 위해 이미지를 HSV 색 공간으로 변환하고, 주어진 색 범위(lower, upper)에 해당하는 영역을 마스크로 추출한다.
여기에서 mask 부분은 코드 아래의 run 매서드에서 preprocess_image 메서드를 호출하여 파란색과 빨간색 마스크를 생성하는데 사용된다.
def find_dots(self, mask):
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
dots = []
for contour in contours:
if cv2.contourArea(contour) > 10: # 작은 노이즈 제거
M = cv2.moments(contour)
if M['m00'] != 0:
cX = int(M['m10'] / M['m00'])
cY = int(M['m01'] / M['m00'])
dots.append((cX, cY))
return dots, contours
cv2.RETR_EXTERNAL은 중첩된 윤곽선은 무시하고 외부 윤곽선만 찾는다.
(counters가 찾은 윤곽선들의 리스트이고, _는 계층구조,, 부분인데 사용하지 않으므로 저렇게 해둔 것이다.)
for contour in contours: 내에서는,
윤곽선의 면적을 계산하여 작은 노이즈를 제거하고, 면적이 10보다 큰 윤곽선만 처리하도록 했다.
윤곽선의 모멘트를 계산하여 중심 좌표를 구하고, 이를 dots 리스트에 추가하여 중심 좌표들과 윤곽선 리스트를 반환한다.
cv2.moments()는 모멘트 값들을 dictionary형태로 반환하고,
if M['m00'] != 0:: 모멘트의 영(moment of order zero) 값이 0이 아닌 경우에만 중심 좌표를 계산한다. ( M['m00']는 윤곽선의 면적)
- M['m10']와 M['m01']는 각각 첫 번째 모멘트로, x와 y 좌표의 합계
- cX = int(M['m10'] / M['m00']): x 좌표의 합계를 면적으로 나누어 중심 x 좌표를 계산
- cY = int(M['m01'] / M['m00']): y 좌표의 합계를 면적으로 나누어 중심 y 좌표를 계산
def determine_direction(self, blue_dots, red_dot):
if len(blue_dots) < 2 or not red_dot:
return "Cannot determine direction. Not enough dots detected."
# 모든 점들을 포함한 직선 검출
dots = blue_dots + [red_dot]
# x 좌표 차이와 y 좌표 차이를 계산하여 방향 결정
# 리스트 컴프리헨션->반복문과 조건식을 한 줄로 작성하여 새로운 리스트를 생성
x_coords = [dot[0] for dot in dots]
y_coords = [dot[1] for dot in dots]
x_diff = max(x_coords) - min(x_coords)
y_diff = max(y_coords) - min(y_coords)
if x_diff > y_diff:
return "2번 방향" # 가로로 정렬됨
else:
return "1번 방향" # 세로로 정렬됨
determine_direction 메서드는 파란색 점이 최소 두 개 이상이고 빨간색 점이 하나 있을 때 점들의 위치를 바탕으로 방향을 결정한다. x 좌표 차이(x_diff)와 y 좌표 차이(y_diff)를 비교하여 점들이 가로로 정렬되었는지 세로로 정렬되었는지 판단하도록 했다.
dots = blue_dots + [red_dot]: 파란색 점들과 빨간색 점을 하나의 리스트로 결합하는데, 이렇게 한 이유는 파란색 점이 빨간색 공에 의해 가려지는 것을 고려하여 총 세 지점의 배치를 기준으로 방향을 결정할 것이기 때문이다.
cf. blue_dots는 리스트였고, red_dot은 튜플이었음
방향 정렬 계산 예시
ex. 세 점의 좌표가 [(30, 50), (70, 80), (50, 60)]일 때,
x_coords = [30, 70, 50],
y_coords = [50, 80, 60] 이므로
x_diff = max([30, 70, 50]) - min([30, 70, 50]) = 70 - 30 = 40,
y_diff = max([50, 80, 60]) - min([50, 80, 60]) = 80 - 50 = 30 이다.
이때, x 좌표의 범위가 y 좌표의 범위보다 크다면 점들이 가로로 더 넓게 퍼져 있음을 의미한다. 즉, 2번 방향이다.
def run(self):
while True:
image = self.get_image()
if image is None:
break
blue_mask = self.preprocess_image(image, self.blue_lower, self.blue_upper)
blue_dots, blue_contours = self.find_dots(blue_mask)
red_mask = self.preprocess_image(image, self.red_lower, self.red_upper)
red_dots, red_contours = self.find_dots(red_mask)
# 빨간색 점이 검출된 경우 첫 번째 점을 red_dot에 할당하고, 그렇지 않으면 None을 할당
red_dot = red_dots[0] if red_dots else None
direction = self.determine_direction(blue_dots, red_dot)
print(f"Robot is facing the {direction} of the field.")
# Draw contours around the blue and red dots
cv2.drawContours(image, blue_contours, -1, (0, 255, 0), 2)
cv2.drawContours(image, red_contours, -1, (0, 0, 255), 2)
for dot in blue_dots:
cv2.circle(image, dot, 5, (0, 255, 0), -1)
if red_dot:
cv2.circle(image, red_dot, 5, (0, 0, 255), -1)
cv2.imshow('Direction', image)
#cv2.imshow('Blue Mask', blue_mask)
#cv2.imshow('Red Mask', red_mask)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
run 메서드가 메인 실행 루프인데, 이 루프는 계속해서 이미지를 캡처하고, 전처리하고, 점들을 찾아 방향을 결정한다.
결정된 방향은 콘솔에 출력(print)하도록 하여, 어떤 방향인지 확인할 수 있다.
또한, 점들의 윤곽선과 중심을 이미지에 표시하고 화면에 출력하는 부분도 포함되어 있다.
camera = cv2.VideoCapture(1)
detector = DirectionDetector(camera)
detector.run()
camera.release()
DirectionDetector 객체를 생성하고 run 메서드를 호출하여 프로그램을 실행하는 부분 (python)
실행 결과



전체 코드
import cv2
import numpy as np
class DirectionDetector:
def __init__(self, camera):
self.camera = camera
self.blue_lower = np.array([100, 150, 50]) # 파란색 하한값
self.blue_upper = np.array([140, 255, 255]) # 파란색 상한값
self.red_lower = np.array([170, 140, 150]) # 빨간색 하한값
self.red_upper = np.array([180, 255, 255]) # 빨간색 상한값
def get_image(self):
ret, frame = self.camera.read()
if not ret:
print("Failed to capture image")
return None
return frame
def preprocess_image(self, image, lower, upper):
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower, upper)
return mask
def find_dots(self, mask):
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
dots = []
for contour in contours:
if cv2.contourArea(contour) > 10: # 작은 노이즈 제거
M = cv2.moments(contour)
if M['m00'] != 0:
cX = int(M['m10'] / M['m00'])
cY = int(M['m01'] / M['m00'])
dots.append((cX, cY))
return dots, contours
def determine_direction(self, blue_dots, red_dot):
if len(blue_dots) < 2 or not red_dot:
return "Cannot determine direction. Not enough dots detected."
# 모든 점들을 포함한 직선 검출
dots = blue_dots + [red_dot]
# x 좌표 차이와 y 좌표 차이를 계산하여 방향 결정
x_coords = [dot[0] for dot in dots]
y_coords = [dot[1] for dot in dots]
x_diff = max(x_coords) - min(x_coords)
y_diff = max(y_coords) - min(y_coords)
if x_diff > y_diff:
return "2번 방향" # 가로로 정렬됨
else:
return "1번 방향" # 세로로 정렬됨
def run(self):
while True:
image = self.get_image()
if image is None:
break
blue_mask = self.preprocess_image(image, self.blue_lower, self.blue_upper)
blue_dots, blue_contours = self.find_dots(blue_mask)
red_mask = self.preprocess_image(image, self.red_lower, self.red_upper)
red_dots, red_contours = self.find_dots(red_mask)
red_dot = red_dots[0] if red_dots else None
direction = self.determine_direction(blue_dots, red_dot)
print(f"Robot is facing the {direction} of the field.")
# Draw contours around the blue and red dots
cv2.drawContours(image, blue_contours, -1, (0, 255, 0), 2)
cv2.drawContours(image, red_contours, -1, (0, 0, 255), 2)
for dot in blue_dots:
cv2.circle(image, dot, 5, (0, 255, 0), -1)
if red_dot:
cv2.circle(image, red_dot, 5, (0, 0, 255), -1)
cv2.imshow('Direction', image)
cv2.imshow('Blue Mask', blue_mask)
cv2.imshow('Red Mask', red_mask)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
camera = cv2.VideoCapture(1)
detector = DirectionDetector(camera)
detector.run()
camera.release()'Embedded LAB Semina' 카테고리의 다른 글
| 공까지의 거리 추적하기(3) (0) | 2024.07.27 |
|---|---|
| 공까지의 거리 추적하기(2) :: 발돋움,, 이미지 캡쳐 + 카메라 캘리브레이션 (0) | 2024.07.26 |
| 공까지의 거리 추적하기(1) (0) | 2024.07.16 |
| 초록색 필드 구별하기(구별된 영역 위의 객체만 추적하도록) (0) | 2024.07.16 |
| 객체 위치에서 red_dot의 위치(경우의 수) 구별하기 (0) | 2024.07.12 |