--- 본 포스팅은 데이콘 서포터즈 "데이크루 2기" 활동의 일환입니다 ---
- 안녕하세요 데이콘 서포터즈 데이크루 2기 포스(POS)팀의 Rahites입니다 :)
- POS팀은 Python OpenCV Study의 약자로 활동 기간동안 저희 팀은 '파이썬으로 만드는 OpenCV 프로젝트' 책을 가지고 OpenCV를 공부해보고 프로젝트를 진행할 것입니다.
- 자세한 스터디 계획과 운영 방안은 아래의 포스팅에서 확인하실 수 있습니다.
https://dacon.io/codeshare/4759?utm_source=dacrew&utm_medium=432727&utm_campaign=dacrew_2
https://dacon.io/codeshare/4837?utm_source=dacrew&utm_medium=432727&utm_campaign=dacrew_2
- 그러면 2주차 활동 시작하겠습니다.
1. OpenCV 설치¶
본격적인 OpenCV실습 전에 우선 OpenCV를 설치합니다.
# !pip install opencv-python
# !pip install opencv-contrib-python
import cv2
cv2.__version__
'4.5.5'
2. 기본 입출력¶
2.1. 이미지와 비디오 입출력¶
2.1.1. 이미지 읽기¶
OpenCV로 이미지를 읽고 화면에 표시하는 코드입니다.
import cv2
img_file = "../img/girl.jpg" # 이미지 경로
img = cv2.imread(img_file) # 이미지를 numpy배열로 읽어서 img 변수에 할당
if img is not None:
cv2.imshow('girl', img) # ''로 이미지와 함께 전달할 문자열 표시, 읽은 이미지를 화면에 표시
cv2.waitKey() # 키가 입력될 때 까지 대기
cv2.destroyAllWindows() # 창 모두 닫기
else:
print('No image file.')
cv2.imread() 함수는 기본적으로 R,G,B로 구성된 3개 채널 스케일로 읽어들입니다.
이를 필요에 따라 gray scale이나 파일 그대로 읽을 수 있습니다.
img_file = "../img/girl.jpg"
img = cv2.imread(img_file, cv2.IMREAD_GRAYSCALE)
if img is not None:
cv2.imshow('girl', img)
cv2.waitKey()
cv2.destroyAllWindows()
else:
print('No image file.')
2.1.2. 이미지 저장하기¶
: cv2.imwrite(file_path, img)
# color 이미지를 읽어 gray scale로 저장
img_file = '../img/girl.jpg'
save_file = '../img/girl_gray.jpg' # 저장할 경로, 파일명
img = cv2.imread(img_file, cv2.IMREAD_GRAYSCALE)
cv2.imshow(img_file, img)
cv2.imwrite(save_file, img) #파일로 저장, 포맷은 확장에 따름
cv2.waitKey()
cv2.destroyAllWindows()
2.1.3. 동영상 및 카메라 프레임 읽기¶
OpenCV는 동영상 파일이나 컴퓨터에 연결한 카메라 장치로부터 연속된 이미지 프레임을 읽을 수 있는 API를 제공합니다.
- cap = cv2.VideoCapture(file_path or index) : 비디오 캡처 객체 생성
file_path : 동영상 파일 경로, index : 카메라 장치 번호로 0부터 순차적으로 증가, cap : VideoCapture 객체 - ret = cap.isOpened() : 객체 초기화 확인 ret : 초기화 여부 ( True / False )
- ret, img = cap.read() : 영상 프레임 읽기 ret : 프레임 읽기 성공 or 실패, img : 프레임 이미지 numpy배열 or None
- cap.set(id, value) : 프로퍼티 변경
- cap.get(id) : 프로퍼티 확인
- cap.release() : 캡처 자원 반납
2.1.4. 동영상 파일 읽기¶
video_file = "../img_1/big_buck.avi" # 동영상 파일 경로
cap = cv2.VideoCapture(video_file) # 동영상 캡쳐 객체 생성 ---①
if cap.isOpened(): # 캡쳐 객체 초기화 확인
while True:
ret, img = cap.read() # 다음 프레임 읽기 --- ②
if ret: # 프레임 읽기 정상
cv2.imshow(video_file, img) # 화면에 표시 --- ③
cv2.waitKey(25) # 25ms 지연(40fps로 가정) --- ④ ( 지연시간 = 1000 / fps )
# 각 프레임을 화면에 표시하는 시간이 너무 빠르면 우리 눈으로 볼 수 없기 때문
else: # 다음 프레임 읽을 수 없슴,
break # 재생 완료
else:
print("can't open video.") # 캡쳐 객체 초기화 실패
cap.release() # 캡쳐 자원 반납
cv2.destroyAllWindows()
2.1.5. 카메라(웹캠) 프레임 읽기¶
cap = cv2.VideoCapture(0) # 0번 카메라 장치 연결 ---①
if cap.isOpened(): # 캡쳐 객체 연결 확인
while True:
ret, img = cap.read() # 다음 프레임 읽기
if ret:
cv2.imshow('camera', img) # 다음 프레임 이미지 표시
if cv2.waitKey(1) != -1: # 1ms 동안 키 입력 대기 ---② ( 무한 루프를 빠져나오기 위해)
break # 아무 키라도 입력이 있으면 중지
else:
print('no frame')
break
else:
print("can't open camera.")
cap.release() # 자원 반납
cv2.destroyAllWindows()
2.1.6. 카메라 비디오 속성 제어¶
속성을 나타내는 아이디는 cv2.CAP_PROP_FRAME_으로 시작하는 상수로 정의됩니다. 각 속성 아이디를 get함수에 전달하면 해당 속성의 값을 구할 수 있고, set함수에 값과 함께 전달하면 원하는 값으로 지정할 수 있습니다.
- cv2.CAP_PROP_FRAME_WIDTH : 프레임 폭
- cv2.CAP_PROP_FRAME_HEIGHT : 프레임 높이
- cv2.CAP_PROP_FRAME_FPS : 초당 프레임 수
- cv2.CAP_PROP_FRAME_MSEC : 동영상 파일의 프레임 위치
- cv2.CAP_PROP_FRAME_AVI_RATIO : 동영상 파일의 상대 위치( 0 : 시작, 1 : 끝 )
- cv2.CAP_PROP_FRAME_FOURCC : 동영상 파일 코덱 문자
- cv2.CAP_PROP_FRAME_AUTOFOCUS : 카메라 자동 초점 조절
- cv2.CAP_PROP_FRAME_ZOOM : 카메라 줌
# 초당 프레임 수 구해 지연시간 계산하기
fps = cap.get(cv2.CAP_PROP_FPS)
delay = int(1000/fps)
# FPS를 지정해서 동영상 재생
video_file = "../img_1/big_buck.avi" # 동영상 파일 경로
cap = cv2.VideoCapture(video_file) # 동영상 캡쳐 객체 생성
if cap.isOpened(): # 캡쳐 객체 초기화 확인
fps = cap.get(cv2.CAP_PROP_FPS) # 프레임 수 구하기
delay = int(1000/fps)
print("FPS: %f, Delay: %dms" %(fps, delay))
while True:
ret, img = cap.read() # 다음 프레임 읽기
if ret: # 프레임 읽기 정상
cv2.imshow(video_file, img) # 화면에 표시
cv2.waitKey(delay) # fps에 맞게 시간 지연
else:
break # 다음 프레임 읽을 수 없슴, 재생 완료
else:
print("can't open video.") # 캡쳐 객체 초기화 실패
cap.release() # 캡쳐 자원 반납
cv2.destroyAllWindows()
FPS: 24.000000, Delay: 41ms
# 카메라 프레임 크기 설정 - 카메라에서 읽은 영상이 너무 고화질인 경우 픽셀 수가 많아 시간이 많이 걸리기 때문
cap = cv2.VideoCapture(0) # 카메라 0번 장치 연결
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) # 프레임 폭 값 구하기
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 프레임 높이 값 구하기
print("Original width: %d, height:%d" % (width, height) )
cap.release()
Original width: 640, height:480
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320) # 프레임 폭을 320으로 설정
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) # 프레임 높이를 240으로 설정
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) # 재지정한 프레임 폭 값 구하기
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 재지정한 프레임 폭 값 구하기
print("Resized width: %d, height:%d" % (width, height) )
if cap.isOpened():
while True:
ret, img = cap.read()
if ret:
cv2.imshow('camera', img)
if cv2.waitKey(1) != -1:
break
else:
print('no frame!')
break
else:
print("can't open camera!")
cap.release()
cv2.destroyAllWindows()
Resized width: 320, height:240
2.1.7. 비디오 파일 저장하기 ( 웹캠으로 사진찍기 )¶
cap = cv2.VideoCapture(0) # 0번 카메라 연결
if cap.isOpened() :
while True:
ret, frame = cap.read() # 카메라 프레임 읽기
if ret:
cv2.imshow('camera',frame) # 프레임 화면에 표시
if cv2.waitKey(1) != -1: # 아무 키나 누르면
cv2.imwrite('video_photo.jpg', frame) # 프레임을 저장
break
else:
print('no frame!')
break
else:
print('no camera!')
cap.release()
cv2.destroyAllWindows()
여러 프레임을 동영상으로 저장하고 싶을 때 사용하는 함수
- VideoWriter()
- wirter = cv2.VideoWriter(file_path, fourcc, fps, (width, height)) : 비디오 저장 클래스 생성자 함수
file_path : 비디오 파일 저장 경로, fourcc : 비디오 인코딩 형식 4글자, fps : 초당 프레임 수, (width, height) : 프레임 폭, 높이 - writer.write(frame) : 프레임 저장
frame : 저장할 프레임 - writer.set(id, value) :프로퍼티 변경
- writer.get(id) : 프로퍼티 확인
- ret = writer.fourcc(c1,c2,c3,c4) : fourcc코드 생성
c1,c2,c3,c4 : 인코딩 형식 4글자ㅏ ( 'MJPG', 'DIVX' 등 ), ret : fourcc 코드 - cv2.VideoWriter_fourcc(c1,c2,c3,c4) : cv2/VideoWriter.fourcc()와 동일
- wirter = cv2.VideoWriter(file_path, fourcc, fps, (width, height)) : 비디오 저장 클래스 생성자 함수
fourcc는 4개의 인코딩 포맷 문자를 한 문자씩 8비트를 사용해 각 자릿수에 맞게 표현한 것입니다.
cap = cv2.VideoCapture(0) # 0번 카메라 연결
if cap.isOpened:
file_path = '../img_1/record.avi' # 저장할 파일 경로 이름 ---①
fps = 30.0 # FPS, 초당 프레임 수
fourcc = cv2.VideoWriter_fourcc(*'DIVX') # 인코딩 포맷 문자
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
size = (int(width), int(height)) # 프레임 크기
print("width: %d, height:%d" % (width, height) )
out = cv2.VideoWriter(file_path, fourcc, fps, size) # VideoWriter 객체 생성
while True:
ret, frame = cap.read()
if ret:
cv2.imshow('camera-recording',frame)
out.write(frame) # 파일 저장
if cv2.waitKey(int(1000/fps)) != -1:
break
else:
print("no frame!")
break
out.release() # 파일 닫기
else:
print("can't open camera!")
cap.release()
cv2.destroyAllWindows()
# 아무키나 누르기 전까지 녹화!!
width: 640, height:480
2.2. 그림 그리기¶
객체나 얼굴을 인식해서 그 영역에 사각형을 그려서 표시하고 그 이름을 글씨로 표시하는 등의 용도로 자주 활용됩니다. 왼쪽 위 (0,0)
# 500 * 500 크기의 하얀 이미지 생성
import cv2
import numpy as np
img = np.full((500,500,3), 255, dtype = np.uint8)
cv2.imwrite('../img_1/blank_500.jpg', img)
True
2.2.1. 직선 그리기¶
cv2.line(img, start, end, color, [,thickness, lineType])
import matplotlib.pyplot as plt
img = cv2.imread('../img_1/blank_500.jpg')
cv2.line(img, (50, 50), (150, 50), (255,0,0)) # 파란색 1픽셀 선
cv2.line(img, (200, 50), (300, 50), (0,255,0)) # 초록색 1픽셀 선
cv2.line(img, (350, 50), (450, 50), (0,0,255)) # 빨간색 1픽셀 선 , 웹에서 사용하는 RGB와 반대라는 특징
# 하늘색(파랑+초록) 10픽셀 선
cv2.line(img, (100, 100), (400, 100), (255,255,0), 10)
# 분홍(파랑+빨강) 10픽셀 선
cv2.line(img, (100, 150), (400, 150), (255,0,255), 10)
# 노랑(초록+빨강) 10픽셀 선
cv2.line(img, (100, 200), (400, 200), (0,255,255), 10)
# 회색(파랑+초록+빨강) 10픽셀 선
cv2.line(img, (100, 250), (400, 250), (200,200,200), 10)
# 검정 10픽셀 선
cv2.line(img, (100, 300), (400, 300), (0,0,0), 10)
# 4연결 선
cv2.line(img, (100, 350), (400, 400), (0,0,255), 20, cv2.LINE_4)
# 8연결 선
cv2.line(img, (100, 400), (400, 450), (0,0,255), 20, cv2.LINE_8)
# 안티에일리어싱 선
cv2.line(img, (100, 450), (400, 500), (0,0,255), 20, cv2.LINE_AA)
# 이미지 전체에 대각선
cv2.line(img, (0,0), (500,500), (0,0,255))
plt.imshow(img[:,:,(2,1,0)]) # 이미지 표시
plt.xticks([])
plt.yticks([])
plt.show()
cv2.imshow('lines', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.2.2. 사각형 그리기¶
cv2.rectangle(img, start, end, color, [,thickness, lineType])
img = cv2.imread('../img_1/blank_500.jpg')
# 좌상, 우하 좌표로 사각형 그리기
cv2.rectangle(img, (50, 50), (150, 150), (255,0,0) )
# 우하, 좌상 좌표로 사각형 그리기
cv2.rectangle(img, (300, 300), (100, 100), (0,255,0), 10 )
# 우상, 좌하 좌표로 사각형 채워 그리기
cv2.rectangle(img, (450, 200), (200, 450), (0,0,255), -1 ) # thickness에 -1을 입력하면 채우기!!
plt.imshow(img[:,:,(2,1,0)]) # 이미지 표시
plt.xticks([])
plt.yticks([])
plt.show()
cv2.imshow('rectangle', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 어느 지점이든 시작 지점과 그 반대 지점을 사용한다
2.2.3. 다각형 그리기¶
cv2.polylines(img, points, isClosed, color, [,thickness, lineType])
- points : 꼭짓점 좌표, numpy 배열 리스트
- isClosed : 닫힌 도형 여부 ( True : 첫 꼭짓점과 마지막 꼭짓점 연결, False : 단순히 여러 꼭짓점을 잇는 선 )
polylines는 thickness에 -1로 채우기 효과를 내는 것을 지원하지 않습니다.
import cv2
import numpy as np
img = cv2.imread('../img_1/blank_500.jpg')
# Numpy array로 좌표 생성
# 번개 모양 선 좌표
pts1 = np.array([[50,50], [150,150], [100,140],[200,240]], dtype=np.int32)
# 삼각형 좌표
pts2 = np.array([[350,50], [250,200], [450,200]], dtype=np.int32)
# 삼각형 좌표
pts3 = np.array([[150,300], [50,450], [250,450]], dtype=np.int32)
# 5각형 좌표
pts4 = np.array([[350,250], [450,350], [400,450], [300,450], [250,350]],\
dtype=np.int32)
# 다각형 그리기
cv2.polylines(img, [pts1], False, (255,0,0)) # 번개 모양 선 그리기
cv2.polylines(img, [pts2], False, (0,0,0), 10) # 3각형 열린 선 그리기 ---④
cv2.polylines(img, [pts3], True, (0,0,255), 10) # 3각형 닫힌 도형 그리기 ---⑤
cv2.polylines(img, [pts4], True, (0,0,0)) # 5각형 닫힌 도형 그리기
plt.imshow(img[:,:,(2,1,0)]) # 이미지 표시
plt.xticks([])
plt.yticks([])
plt.show()
cv2.imshow('polyline', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.2.4. 원, 타원, 호 그리기¶
cv2.circle(img, center, radius, color, [,thickness, lineType])
- center : 원점 좌표
- radius : 원의 반지름
cv2.ellipse(img, center, axes, angle, from, to, color, [,thickness, lineType])
- axes : 기준 축 길이
- angle : 기준 축 회전 각도
- from, to : 호를 그릴 시작 각도와 끝 각도
img = cv2.imread('../img_1/blank_500.jpg')
# 원점(150,150), 반지름 100 ---①
cv2.circle(img, (150, 150), 100, (255,0,0))
# 원점(300,150), 반지름 70 ---②
cv2.circle(img, (300, 150), 70, (0,255,0), 5)
# 원점(400,150), 반지름 50, 채우기 ---③
cv2.circle(img, (400, 150), 50, (0,0,255), -1)
# 원점(50,300), 반지름(50), 회전 0, 0도 부터 360도 그리기 ---④
cv2.ellipse(img, (50, 300), (50, 50), 0, 0, 360, (0,0,255))
# 원점(150, 300), 아래 반원 그리기 ---⑤
cv2.ellipse(img, (150, 300), (50, 50), 0, 0, 180, (255,0,0))
#원점(200, 300), 윗 반원 그리기 ---⑥
cv2.ellipse(img, (200, 300), (50, 50), 0, 181, 360, (0,0,255))
# 원점(325, 300), 반지름(75,50) 납작한 타원 그리기 ---⑦
cv2.ellipse(img, (325, 300), (75, 50), 0, 0, 360, (0,255,0))
# 원점(450,300), 반지름(50,75) 홀쭉한 타원 그리기 ---⑧
cv2.ellipse(img, (450, 300), (50, 75), 0, 0, 360, (255,0,255))
# 원점(50, 425), 반지름(50,75), 회전 15도 ---⑨
cv2.ellipse(img, (50, 425), (50, 75), 15, 0, 360, (0,0,0))
# 원점(200,425), 반지름(50,75), 회전 45도 ---⑩
cv2.ellipse(img, (200, 425), (50, 75), 45, 0, 360, (0,0,0))
# 원점(350,425), 홀쭉한 타원 45도 회전 후 아랫 반원 그리기 ---⑪
cv2.ellipse(img, (350, 425), (50, 75), 45, 0, 180, (0,0,255))
# 원점(400,425), 홀쭉한 타원 45도 회전 후 윗 반원 그리기 ---⑫
cv2.ellipse(img, (400, 425), (50, 75), 45, 181, 360, (255,0,0))
plt.imshow(img[:,:,(2,1,0)]) # 이미지 표시
plt.xticks([])
plt.yticks([])
plt.show()
cv2.imshow('circle', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.2.5. 글씨 그리기¶
cv2.putText(img, text, point, fontFace, fontSize, color [,thickness, lineType])
- text : 표시할 문자열
- point : 글씨를 표시할 좌표 ( 좌측 하단을 기준으로 지정 )
- fontFace : 글꼴
img = cv2.imread('../img_1/blank_500.jpg')
# sans-serif small
cv2.putText(img, "Plain", (50, 30), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0,0))
# sans-serif normal
cv2.putText(img, "Simplex", (50, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0,0))
# sans-serif bold
cv2.putText(img, "Duplex", (50, 110), cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0,0))
# sans-serif normall X2 ---①
cv2.putText(img, "Simplex", (200, 110), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,250))
# serif small
cv2.putText(img, "Complex Small", (50, 180), cv2.FONT_HERSHEY_COMPLEX_SMALL, \
1, (0, 0,0))
# serif normal
cv2.putText(img, "Complex", (50, 220), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0,0))
# serif bold
cv2.putText(img, "Triplex", (50, 260), cv2.FONT_HERSHEY_TRIPLEX, 1, (0, 0,0))
# serif normal X2 ---②
cv2.putText(img, "Complex", (200, 260), cv2.FONT_HERSHEY_TRIPLEX, 2, (0,0,255))
# hand-wringing sans-serif
cv2.putText(img, "Script Simplex", (50, 330), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, \
1, (0, 0,0))
# hand-wringing serif
cv2.putText(img, "Script Complex", (50, 370), cv2.FONT_HERSHEY_SCRIPT_COMPLEX, \
1, (0, 0,0))
# sans-serif + italic ---③
cv2.putText(img, "Plain Italic", (50, 430), \
cv2.FONT_HERSHEY_PLAIN | cv2.FONT_ITALIC, 1, (0, 0,0))
# sarif + italic
cv2.putText(img, "Complex Italic", (50, 470), \
cv2.FONT_HERSHEY_COMPLEX | cv2.FONT_ITALIC, 1, (0, 0,0))
plt.imshow(img[:,:,(2,1,0)]) # 이미지 표시
plt.xticks([])
plt.yticks([])
plt.show()
cv2.imshow('draw text', img)
cv2.waitKey()
cv2.destroyAllWindows()
2.3. 창 관리¶
한 개 이상의 이미지를 여러 창에 띄우거나 각 창에 키보드와 마우스 이벤트를 처리하기 위해 창을 관리하는 기능이 필요합니다.
cv2.nameWindow(title [,option]) : 이름을 갖는 창 열기
- title : 창 이름
- option : 창 옵션 (cv2.WINDOW_NORMAL : 임의의 크기, 사용자 창 크기 조정 가능, cv2.WINDOW_AUTOSIZE : 이미지와 같은 크기, 창 크기 재조정 불가)
cv2.moveWIndow(title, x, y) : 창 위치 이동
- title : 위치를 변경할 창의 이름
- x, y : 이동할 창의 위치
cv2.resizeWindow(title, width, height) : 창 크기 변경
- title : 크기를 변경할 창의 이름
- width, height : 크기를 변경할 창의 폭과 높이
cv2.destroyWindow(title) : 창 닫기
cv2.destroyAllWindoews() : 열린 모든 창 닫기
# 창 관리 API 이용하기
file_path = '../img/girl.jpg'
img = cv2.imread(file_path) # 이미지를 기본 값으로 읽기
img_gray = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE) # 이미지를 그레이 스케일로 읽기
cv2.namedWindow('origin') # origin 이름으로 창 생성
cv2.namedWindow('gray', cv2.WINDOW_NORMAL) # gray 이름으로 창 생성
cv2.imshow('origin', img) # origin 창에 이미지 표시
cv2.imshow('gray', img_gray) # gray 창에 이미지 표시
cv2.moveWindow('origin', 0, 0) # 창 위치 변경
cv2.moveWindow('gray', 100, 100) # 창 위치 변경
cv2.waitKey(0) # 아무키나 누르면
cv2.resizeWindow('origin', 200, 200) # 창 크기 변경 (변경 안됨)
cv2.resizeWindow('gray', 100, 100) # 창 크기 변경 (변경 됨))
cv2.waitKey(0) # 아무키나 누르면
cv2.destroyWindow("gray") # gray 창 닫기
cv2.waitKey(0) # 아무키나 누르면
cv2.destroyAllWindows() # 모든 창 닫기
2.4. 이벤트 처리¶
2.4.1. 키보드 이벤트¶
cv2.waitkey(delay) 함수를 쓰면 키보드의 입력을 알아낼 수 있습니다. ms단위로 숫자를 전달하면 해당 시간동안 프로그램을 멈추고 대기하다 키보드의 눌린 키에 대응하는 코드 값을 정수로 반환합니다. 이 때 지정한 시간까지 키보드 입력이 없으면 -1을 반환합니다. delay 인자에 0을 전달하면 대기 시간을 무한대로 하겠다는 의미로 키를 누를 때까지 프로그램은 멈추고 이때는 -1을 반환할 일이 없숩니다. 키보드에서 어떤 키를 눌렀는지를 알아내려면 cv2.waitKey() 함수의 반환값을 출력해보면 됩니다.
import cv2
img_file = "../img/girl.jpg"
img = cv2.imread(img_file)
title = 'IMG' # 창 이름
x, y = 100, 100 # 최초 좌표
while True:
cv2.imshow(title, img)
cv2.moveWindow(title, x, y)
key = cv2.waitKey(0) & 0xFF # 키보드 입력을 무한 대기, 8비트 마스크처리
print(key, chr(key)) # 키보드 입력 값, 문자 값 출력
if key == ord('a'): # 'a' 키 이면 좌로 이동
x -= 10
elif key == ord('s'): # 's' 키 이면 아래로 이동
y += 10
elif key == ord('w'): # 'w' 키 이면 위로 이동
y -= 10
elif key == ord('d'): # 'd' 키 이면 오른쪽으로 이동
x += 10
elif key == ord('q') or key == 27: # 'q' 이거나 'esc' 이면 종료
cv2.destroyAllWindows()
break
cv2.moveWindow(title, x, y ) # 새로운 좌표로 창 이동
100 d 97 a 115 s 100 d 119 w 97 a 115 s 100 d 119 w 27
2.4.2. 마우스 이벤트¶
마우스에서 입력을 받으려면 이벤트를 처리할 함수를 미리 선언해 놓고 cv2.setMouseCallback() 함수에 그 함수를 전달합니다.
cv2.setMouseCallback(win_name, onMouse, [, param]): onMouse 함수를 등록
- win_name : 이벤트를 처리할 윈도우 이름
- onMouse : 이벤트 처리를 위해 미리 선언해 높은 콜백 함수
- param : 필요에 따라 onMouse 함수에 전달할 인자
MouseCallback(event, x, y, flags, param) : 콜백 함수 선언 부
- event : 마우스 이벤트 종류, cv2.EVENT로 시작하는 상수 (12가지)
_MOSEMOVE : 마우스 움직임
_LBUTTONDOWN : 왼쪽 버튼 누름
_RBUTTONDOWN : 오른쪽 버튼 누름
_MBUTTONDOWN : 가운데 버튼 누름
_LBUTTONUP : 왼쪽 버튼 뗌
_RBUTTONUP : 오른쪽 버튼 뗌
_MBUTTONUP : 가운데 버튼 뗌
_LBUTTONDBLCLK : 왼쪽 버튼 더블 클릭
_RBUTTONDBLCLK : 오른쪽 버튼 더블 클릭
_MBUTTONDBLCLK : 가운데 버튼 더블 클릭
_MOUSEWHEEL : 휠 스크롤
_MOUSEHWHEEL : 휠 가로 스크롤 - x, y : 마우스 좌표
- flags : 마우스 동작과 함께 일어난 상태, cv2.EVENT_FLAG로 시작하는 상수 ( 6가지 )
_LBUTTON(1) : 왼쪽 버튼 누름
_RBUTTON(2) : 오른쪽 버튼 누름
_MBUTTON(4) : 가운데 버튼 누름
_CTRLKEY(8) : Ctrl 키 누름
_SHIFTKEY(16) : Shift 키 누름
_ALTKEY(32) : Alt 키 누름 - param : cv2.setMouseCallback() 함수에서 전달한 인자
# 마우스 이벤트로 동그라미 그리기
title = 'mouse event' # 창 제목
img = cv2.imread('../img_1/blank_500.jpg') # 백색 이미지 읽기
cv2.imshow(title, img) # 백색 이미지 표시
def onMouse(event, x, y, flags, param): # 마우스 콜백 함수 구현 ---① 함수에 사용되지 않더라도 모든 인자 기입 필수
if event == cv2.EVENT_LBUTTONDOWN: # 왼쪽 버튼 누름인 경우 ---②
cv2.circle(img, (x,y), 30, (0,0,0), -1) # 지름 30 크기의 검은색 원을 해당 좌표에 그림
cv2.imshow(title, img) # 그려진 이미지를 다시 표시 ---③
cv2.setMouseCallback(title, onMouse) # 마우스 콜백 함수를 GUI 윈도우에 등록 ---④
while True:
if cv2.waitKey(0) & 0xFF == 27: # esc로 종료
break
plt.imshow(img[:,:,(2,1,0)]) # 이미지 표시
plt.xticks([])
plt.yticks([])
plt.show()
cv2.destroyAllWindows()
flag는 시프트 키와 커트롤 키를 함께 누른 상태에서 여러 가지 상태를 하나의 값으로 한꺼번에 나타낼 수 있어야 합니다. 따라서 2진수 비트 자릿수에 맞는 값을 각각 갖고 함수의 인자로 전달되는 값은 여러 상태를 나타내는 값을 조합한 것으로, 어떤 상태인지 알기 위해서는 비트 단위 논리곱(&) 또는 논리합(|) 연산을 사용해야 합니다.
ex.) if flags & cv2.EVENT_FLAG_LBUTTON : 마우스 왼쪽 버튼 눌림
# 플래그를 이용한 동그라미 그리기
title = 'mouse event' # 창 제목
img = cv2.imread('../img_1/blank_500.jpg') # 백색 이미지 읽기
cv2.imshow(title, img) # 백색 이미지 표시
colors = {'black':(0,0,0),
'red' : (0,0,255),
'blue':(255,0,0),
'green': (0,255,0) } # 색상 미리 정의
def onMouse(event, x, y, flags, param): # 마우스 콜백 함수 구현 ---①
# print(event, x, y, flags) # 파라미터 출력, 입력시 마우스의 이동좌표 또한 출력
color = colors['black']
if event == cv2.EVENT_LBUTTONDOWN: # 왼쪽 버튼 누름인 경우 ---②
# 컨트롤키와 쉬프트 키를 모두 누른 경우
if flags & cv2.EVENT_FLAG_CTRLKEY and flags & cv2.EVENT_FLAG_SHIFTKEY :
color = colors['green']
elif flags & cv2.EVENT_FLAG_SHIFTKEY : # 쉬프트 키를 누른 경우
color = colors['blue']
elif flags & cv2.EVENT_FLAG_CTRLKEY : # 컨트롤 키를 누른 경우
color = colors['red']
# 지름 30 크기의 검은색 원을 해당 좌표에 그림
cv2.circle(img, (x,y), 30, color, -1)
cv2.imshow(title, img) # 그려진 이미지를 다시 표시 ---③
cv2.setMouseCallback(title, onMouse) # 마우스 콜백 함수를 GUI 윈도우에 등록 ---④
while True:
if cv2.waitKey(0) & 0xFF == 27: # esc로 종료
break
plt.imshow(img[:,:,(2,1,0)]) # 이미지 표시
plt.xticks([])
plt.yticks([])
plt.show()
cv2.destroyAllWindows()
2.4.3. 트랙바¶
: 슬라이드 모양의 인터페이스를 마우스로 움직여서 값을 입력 받는 GUI요소
cv2.createTrack() 함수로 생성하면서 보여지기를 원하는 창의 이름을 지정합니다. 이때 마우스 이벤트의 방식과 마찬가지로 트랙바를 움직였을 때 동작할 함수를 미리 준비해서 함께 전달합니다. 이 때 트랙바의 값을 얻기 위한 cv2.getTrackbarPos()함수도 함께 사용합니다.
cv2.createTrackbar(trackbar_name, win_name, value, count, onChange) : 트랙바 생성
- trackbar_name : 트랙바 이름
- win_name : 트랙바를 표시할 창 이름
- value : 트랙바 초기 값, 0 ~ count 사이의 값
- count : 트랙바 눈금의 개수, 트랙바가 표시할 수 있는 최대 값
- onChange : TracbarCallback, 트랙바 이벤트 핸들러 함수
TrackbarCallback(value) : 트랙바 이벤트 콜백 함수
- value : 트랙바가 움직인 새 위치 값
pos = cv2.getTrackbarPos(trackbar_name, win_name)
- trackbar_name : 찾고자 하는 트랙바 이름
- win_name : 트랙바가 있는 창의 이름
- pos : 트랙바 위치 값
# 트랙바를 이용한 이미지 색 조정
import cv2
import numpy as np
win_name = 'Trackbar' # 창 이름
img = cv2.imread('../img_1/blank_500.jpg')
cv2.imshow(win_name,img) # 초기 이미지를 창에 표시
# 트랙바 이벤트 처리 함수 선언 ---①
def onChange(x):
# print(x) # 트랙바 새로운 위치 값 --- ②
# 'R', 'G', 'B' 각 트랙바 위치 값 --- ③
r = cv2.getTrackbarPos('R',win_name)
g = cv2.getTrackbarPos('G',win_name)
b = cv2.getTrackbarPos('B',win_name)
img[:] = [b,g,r] # 기존 이미지에 새로운 픽셀 값 적용 --- ④
cv2.imshow(win_name, img) # 새 이미지 창에 표시
# 트랙바 생성 --- ⑤
cv2.createTrackbar('R', win_name, 255, 255, onChange)
cv2.createTrackbar('G', win_name, 255, 255, onChange)
cv2.createTrackbar('B', win_name, 255, 255, onChange)
while True:
if cv2.waitKey(0) & 0xFF == 27:
break
cv2.destroyAllWindows()
- 지금까지 기본적인 OpenCV에 대한 소개와 OpenCV로 이미지를 불러오는 법에 대해 알아보았습니다.
- 또한, OpenCV를 이용해 그림을 직접 그려보거나 영상을 다뤄보는 법을 배워보았습니다.
- 이를 활용하여 학습에 필요한 이미지를 쉽게 다룰 수 있을 것입니다.
감사합니다 :)
'대외활동 > DACrew 2기' 카테고리의 다른 글
[ 파이썬으로 만드는 OpenCV 프로젝트🔥] 5장. 기하학적 변환 (0) | 2022.05.03 |
---|---|
[ 파이썬으로 만드는 OpenCV 프로젝트🔥] 4장. 이미지 프로세싱 기초 (1) | 2022.04.28 |
[ 파이썬으로 만드는 OpenCV 프로젝트🔥] 3장. Numpy와 Matplotlib (0) | 2022.04.14 |
댓글