#Steps
#1. Reading and Resizing the Video
#2. Convert Colors from BGR to HSV color space
#3. Detecting the Pool Table Boundary
#4. Cleaning Up the Mask - Morphogical Operations
#5. Detecting Lines
#6. Classifying Lines
#7. Finding Table Corners
#8. Defining Regions of Interest
#9. Creating a Mask
#10. Detecting Balls
#11. Detecting and Marking Patterns
#12. Analyzing Circle Positions and Cue Line
#13. Collision and Reflection Prediction
#14. Timelimits and check Condition
#15. Condition for Writing Frames
1번 Resize & Read
읽어들이는 동영상의 크기가 크기 때문에 원하는 비율에 맞춰 사이즈를 조절한다.
cap = cv2.VideoCapture("이름") 동영상 파일 열기
cap.read() 프레임을 하나씩 가져오기
cap.get(cv2.CAP_PROP_FRAME_WIDTH) 동영상의 너비 가져오기
cap.get(cv2.CAP_PROP_FPS) 초당 프레임수 가져오기
cv2.resize(원본, 새로운 가로 세로)
cv2.imshow("이름", 프레임) 영상 출력
cv2.waitKey(1) 1ms 동안 키 입력을 기다림 ord('1') 1키를 누르면 종료
cap.release() 동영상 파일 닫기
cv2.destroyAllWindows() 모든 창 닫기
mport cv2
import math
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont
cap = cv2.VideoCapture("video.mp4")
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_rate = int(cap.get(cv2.CAP_PROP_FPS))
resize_width = 800
resize_height = int(frame_height*(resize_width/frame_width))
while True:
ret, frame = cap.read()
if ret:
resized_frame = cv2.resize(frame, (resize_width, resize_height))
cv2.imshow("Video", resized_frame)
if cv2.waitKey(1) & 0xFF ==ord('1'):
break
else:
break
cap.release()
cv2.destroyAllWindows()
2번 BGR → HSV 변환
BGR blue green red : open cv의 기본 색상 형식으로 카메라나 이미지에서 읽어온 원본 색상이다.
HSV 는 hue, saturation, value 색상 채도 밝기로 표현한다.
BGR은 빛의 영향을 많이 받아 색상이 일정하지 않지만, HSV에서는 색상 값만으로 특정한 색을 쉽게 찾을 수 있다. 또한 특정 색을 검출하기 쉽다.
hsv_space = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
3~Mask
당구대에서 공의 움직임을 예측하려면, 먼저 당구대 자체와 공을 정확하게 감지해야 한다. 이를 위해 배경과 필요 없는 요소를 제거하고 관심 있는 부분만 남겨야 하는데, 이때 마스크를 사용한다.
cv2.inRange(프레임, 시작범위, 마지막범위) ---> 특정 색깔 필터링
시작범위: 초록색의 최소 HSV값
마지막범위: 초록색의 최대 HSV값
이 범위에 해당하는 픽셀만 흰색(255)이고 나머지는 검은색(0)으로 마스크를 생성한다.
mask_green = cv2.inRange(hsv_space, np.array([56,161,38]), np.array([71,255,94]))
4~ Morphology Operation
cv2.morphologyEx 모폴로지연산 --> 노이즈 제거
imageopen = cv2.morphologyEx(mask_green, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, (5,5)))
imageclose= cv2.morphologyEx(imageopen, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_RECT, (8,8)))
MOTPH_OPEN 열기 연산 : 작은 잡은 노이즈를 제거한다.
cv2.getStructuringElement(cv2.MORPH_RECT, (5,5)) : (5,5) 크기의 사각형 커널을 이용해 연산 수행한다.
작은 점이나 노이즈를 제거(Erosion)하고, 남은 부분을 팽창(Dilation)한다.
MORPH_CLOSE 닫기 연산 : 객체 내부의 빈 구멍을 메워 경계를 더 선명하게 만든다.
cv2.getSTructuringElement(cv2.MORPH_RECT,(8,8)): (8,8) 커널을 사용해 연산수행
객체의 경계를 확장해서 구멍을 Dilation팽창하고, 원래 크기로 다시 Erosion 침식=조정한다.
5~ Hough 변환
Detecting Lines
직선을 검출하는 코드이다. 여기서 허프 변환Hough Transform을 이용해 직선을 검출한다. 점들의 패턴을 분석해 선을 찾는 방법이다. 해당 함수를 활용하면 당구대의 쿠션 라인이나 가장자리 선 검출이 용이하다.
# 5. Detecting Lines
lines = cv2.HoughLinesP(imageclose, 1, np.pi/180, threshold=100, minLineLength=100, maxLineGap=10)

threshold=100 : 100개 이상의 점이 일직선상에 있으면 선으로 인정한다.
minLineLength=100 : 100픽셀 이상이어야만 선으로 검출한다.
maxLineGap=10 : 10픽셀 이내의 끊어진 선은 같은 직선으로 연결
6~ 선 분류하기
np.arctan2(y2-y1, x2-x1) : 선의 기울기 계산 - 직선의 기울기에 대한 아크탄젠트값(라디안) 반환
*180 /np.pi 라디안을 degree로 변환
abs() 각도를 절댓값으로 반환(음수각도 안나오도록)
angle<0 : 0도에 가까운 선은 수평선 ---> 위 아래의 쿠션벽
90-threshold < angle < 90+threshold : 90도에 가까운 선은 수직선 ----> 왼쪽 오른쪽의 쿠션벽
#Horizontal : Angle close tp 0 degrees
#Vertical : angle near 90 degrees
angle_threshold = 1
horizontal_lines = []
vertical_lines = []
for line in lines:
x1,y1,x2,y2 = line[0]
angle = np.abs(np.arctan2(y2 - y1, x2-x1)*180/np.pi)
if angle <angle_threshold:
horizontal_lines.append(line)
elif angle > 90 - angle_threshold and angle < 90 + angle_threshold:
vertical_lines.append(line)
7~모서리 찾기
Finding Table Corners
수평선과 수직선을 분류해서 top, bottom, left, right 경계 찾기
# 7. Finding Table Corners
middle_y = frame.shape[0]/2
top_lines = [line for line in horizontal_lines if line[0][1] < middle_y]
bot_lines = [line for line in horizontal_lines if line[0][1] > middle_y]
if len(bot_lines) ==0 or len(top_lines) ==0:
out.write(frame)
continue
top_line = sorted(top_lines, key= lambda x: x[0][1])[0]
bot_line = sorted(bot_lines, key= lambda x: x[0][1])[-1]
middle_x = frame.shape[1]/2
left_lines = [line for line in vertical_lines if line[0][0] < middle_x]
right_lines = [line for line in vertical_lines if line[0][0] > middle_x]
if len(left_lines) == 0 or len(right_lines) == 0:
out.write(frame)
continue
left_line = sorted(left_lines, key=lambda x: x[0][1])[0]
right_line = sorted(right_lines, key=lambda x: x[0][1])[-1]
테이블의 코너 4군데 검출하기
top_left = [left_line[0][0], top_line[0][1]]
bot_left = [left_line[0][0], bot_line[0][1]]
top_right = [right_line[0][0], top_line[0][1]]
bot_right = [right_line[0][0], bot_line[0][1]]
corners = [top_left, bot_left, bot_right, top_right]
print(corners)

'머신러닝 > 영상인식' 카테고리의 다른 글
영상인식 기초, 기존 모델 가져와서 적용, ball detection, interpolate(), get() (0) | 2025.03.16 |
---|---|
영상인식 기초, pickle 저장하기, yolo11x.pt, player검출하기, __init__.py 역할 (0) | 2025.03.16 |
영상인식 기초 욜로 파이토치 CNN 학습시키기 Resnet50 (0) | 2025.03.16 |
영상인식, roboflow, 데이터라벨링, 가지고 있는 데이터를 이용해 모델 만들기 (1) | 2025.03.09 |
YOLO 기초, 영상 인식, 영상에서 사물인식하기 yolo11x, yolo11n (0) | 2025.03.08 |