[OpenCV]기본적인 이미지 핸들링
OpenCV를 활용한 기본적인 이미지 핸들링 방법에 대해 포스팅하겠습니다.
- 이미지 출력
- 동영상 출력
- 카메라 출력
- 도형 그리기
- 텍스트 넣기(한글 포함)
- 파일 저장
- 이미지 크기 조정(보간법)
- 이미지 자르기
- 이미지 대칭
- 이미지 회전
- 이미지 흑백 변환(RGB -> GRAY)
- 이미지 블러 처리
- 이미지 변형 (마우스 핸들링 포함)
- 이미지 이진화(흑 or 백)
- 경계선 검출(Canny Edge Detection)
- 윤곽선 검출(Otsu 알고리즘)
import cv2
import numpy as np
print(cv2.__version__)
print(np.__version__)
4.5.5
1.21.2
1. 이미지 출력
img = cv2.imread('img.jpg')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img_gray = cv2.imread('img.jpg', cv2.IMREAD_GRAYSCALE) # 또는 _COLOR, _UNCHANGED
cv2.imshow('gray', img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
shape
이미지의 height, width, channel 정보
img.shape
(390, 640, 3)
2.동영상 출력
여러 프레임이 움직이면서 보여주는 게 동영상
저장된 동영상 파일 출력
cap = cv2.VideoCapture('video.mp4')
while cap.isOpened(): #동영상 파일이 올바로 열렸는지 확인
ret, frame = cap.read() #ret(return값):성공여부, frame:받아온 이미지
if not ret:
print("더 이상 가져올 프레임이 없어요")
break
cv2.imshow('video', frame)
if cv2.waitKey(10) == ord('q'): #10ms만큼 기다림
print("사용자 입력에 의해 종료합니다")
break
cap.release() #자원 해제
cv2.destroyAllWindows()
사용자 입력에 의해 종료합니다
3.카메라 출력
cap = cv2.VideoCapture(0) # 0번 째 카메라 장치( Device ID)
if not cap.isOpened():
exit() # 프로그램 종료
while True:
ret, frame = cap.read()
if not ret:
break
cv2.imshow('camera', frame)
if cv2.waitKey(10) == ord('q'):
print("사용자 입력에 의해 종료합니다")
break
cap.release() #자원 해제
cv2.destroyAllWindows()
사용자 입력에 의해 종료합니다
4.도형 그리기
# 세로 480, 가로 640, 채널 3(RGB)에 해당하는 스케치북 만들기
img = np.zeros((480,640,3), dtype=np.uint8)
#전체 공간을 흰 색으로 채우기
img[:] = (255,255,255)
#일부 영역 색칠
img[0:200, 150:200] = (0,0,0) #행방향, 열방향
# 직선 긋기
col = (0,255,255)
thickness = 3
cv2.line(img, (0,0),(400,100), col,thickness, cv2.LINE_8) #출발점, 도착점의 x좌표,y좌표
# 원 그리기
radius = 50
cv2.circle(img,(200,100), radius, col ,cv2.FILLED,cv2.LINE_AA ) #꽉찬 원
cv2.circle(img,(500,400), radius, col ,thickness,cv2.LINE_AA ) #속이 빈 원
# 사각형
cv2.rectangle(img, (300,300), (500,500), col, thickness) #좌측 상단점, 우측 하단점의 좌표
cv2.rectangle(img, (200,300), (300,500), col, cv2.FILLED) #좌측 상단점, 우측 하단점의 좌표
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
5.텍스트 넣어보기
OpenCV에서 사용하는 글꼴 종류
- cv2.FONT_HERSHEY_SIMPLEX : 보통 크기의 산 세리프 글꼴
- cv2.FONT_HERSHEY_PLAIN : 작은 크기의 산 세리프 글꼴
- cv2.FONT_HERSHEY_SCRIPT_SIMPLEX : 필기체 스타일 글꼴
- cv2.FONT_HERSHEY_TRIPLEX : 보통 크기의 세리프 글꼴
- cv2.FONT_ITALIC : 기울임
img = np.zeros((480,640,3), dtype=np.uint8)
SCALE = 1 #글자 크기
COLOR = (255,255,255)
THICKNESS = 1 #글자 두께
# 그릴곳, 텍스트내용, 시작위치, 폰트종류, 크기, 색깔, 두께
cv2.putText(img, "Simplex", (20,50), cv2.FONT_HERSHEY_SIMPLEX, SCALE, COLOR, THICKNESS)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
한글 적는법
from PIL import Image, ImageFont, ImageDraw
def myPutText(src, text, pos, font_size, font_color):
img_pil = Image.fromarray(src)
draw = ImageDraw.Draw(img_pil)
font = ImageFont.truetype("font/gulim.ttc", font_size)
draw.text(pos, text, font=font, fill=font_color)
return np.array(img_pil)
img = np.zeros((480,640,3), dtype=np.uint8)
SCALE = 30 #글자 크기
COLOR = (255,255,255)
THICKNESS = 1 #글자 두께
# 그릴곳, 텍스트내용, 시작위치, 폰트종류, 크기, 색깔, 두께
img = myPutText(img, "식드리치", (20,50), SCALE, COLOR)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.파일 저장
# 사진
img = cv2.imread('img.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imwrite('img_gray.jpg', img) #.png적으면 png로 저장되는 거임
#동영상
cap = cv2.VideoCapture('video.mp4')
fourcc = cv2.VideoWriter_fourcc(*'DIVX') #코텍 정의
width = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) #저장할 동영상의 width정의
height = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) #저장할 동영상의 height정의
fps = cap.get(cv2.CAP_PROP_FPS) #저장할 동영상의 fps정의
out = cv2.VideoWriter('output.avi', fourcc, fps, (width, height)) #어떤식으로 동영상 저장할지 결정
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
out.write(frame) #영상 데이터만 저장(소리X)
cv2.imshow('video', frame)
if cv2.waitKey(10) == ord('q'):
break
out.release()
cap.release()
cv2.destroyAllWindows()
7.크기조정
고정 크기로 조정
#이미지
img = cv2.imread('img.jpg')
dst = cv2.resize(img, (400,500)) #width,height resize한 이미지
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
비율로 설정
#이미지
img = cv2.imread('img.jpg')
dst = cv2.resize(img, None, fx=0.5,fy=0.5) #비율을 이용해 resize한 이미지(0.5배)
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
보간법
- cv2.INTER_AREA : 크기 줄일 때 사용
- cv2.INTER_CUBIC : 크기 늘릴 때 사용 (속도 느림, 퀄리티 좋음)
- cv2.INTER_LINEAR : 크기 늘릴 때 사용 (기본값)
보간법을 적용하여 축소
#이미지
img = cv2.imread('img.jpg')
dst = cv2.resize(img, None, fx=0.5,fy=0.5, interpolation=cv2.INTER_AREA) #비율을 이용해 resize한 이미지(0.5배)
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
보간법을 적용하여 확대
#이미지
img = cv2.imread('img.jpg')
dst = cv2.resize(img, None, fx=2,fy=2, interpolation=cv2.INTER_CUBIC) #비율을 이용해 resize한 이미지(0.5배)
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
동영상은 중간중간 frame을 resize해주면 됨
8.이미지 자르기
img = cv2.imread('img.jpg')
img.shape #(390, 640, 3)
crop = img[100:200, 300:400] #img[행범위 , 열범위]
cv2.imshow('crop', crop)
cv2.waitKey(0)
cv2.destroyAllWindows()
9.이미지 대칭
cv2.flip에서 파라미터 flipCode에 대해
- flipCode > 0 : 좌우대칭
- flipCode == 0 : 상하대칭 1.flipCode < 0 :상하좌우대칭
# 좌우대칭
img = cv2.imread('img.jpg')
flip_horizontal = cv2.flip(img, flipCode=1)
cv2.imshow('img', img)
cv2.imshow('horizontal', flip_horizontal)
cv2.waitKey(0)
cv2.destroyAllWindows()
10.이미지 회전
# 90도 회전
img = cv2.imread('img.jpg')
rotate_90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
cv2.imshow('img', img)
cv2.imshow('rotate_90', rotate_90)
cv2.waitKey(0)
cv2.destroyAllWindows()
11.이미지 흑백 변환
불러온 이미지를 흑백으로 변경
img = cv2.imread('img.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('img',img)
cv2.imshow('img_gray',img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
12.블러 처리(이미지를 흐리게 변경) -> 외곽선 추출할 때 유용
가우시안 블러
커널 사이즈 또는 표준편차에 따라서 흐림 정도를 정할 수 있음 </br> (3,3) or (5,5) or (7,7)
img = cv2.imread('img.jpg')
kernel_3 = cv2.GaussianBlur(img, (3,3), 0) #커널사이즈, 표준편차
cv2.imshow('img',img)
cv2.imshow('kernel_3', kernel_3)
cv2.waitKey(0)
cv2.destroyAllWindows()
13.이미지 변형(원근)
사다리꼴 이미지 직사각형으로 펼치기
img = cv2.imread('newspaper.jpg')
width, height = 640, 240 #가로 640, 세로 240으로 결과물 출력
# 좌상, 우상, 우하, 좌하 (좌표 위치)
src = np.array([[246,189],[513,185],[559,289],[224,284]], dtype=np.float32) #input 4개 지점
dst = np.array([[0,0],[width,0],[width,height],[0,height]], dtype=np.float32) #output 4개 지점
matrix = cv2.getPerspectiveTransform(src,dst) #Matrix 얻어옴
result = cv2.warpPerspective(img, matrix, (width,height)) #matrix대로 변환을 함
cv2.imshow('img',img)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
회전된 이미지 올바로 세우기
img = cv2.imread('poker.jpg')
width, height = 530, 710 #가로 530, 세로 710으로 결과물 출력
# 좌상, 우상, 우하, 좌하 (좌표 위치 : 시계 방향으로 4지점 정의)
src = np.array([[702,189],[1133,414],[726,1007],[276,700]], dtype=np.float32) #input 4개 지점
dst = np.array([[0,0],[width,0],[width,height],[0,height]], dtype=np.float32) #output 4개 지점
matrix = cv2.getPerspectiveTransform(src,dst) #Matrix 얻어옴
result = cv2.warpPerspective(img, matrix, (width,height)) #matrix대로 변환을 함
cv2.imshow('img',img)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
미니 프로젝트 : 반자동 문서 스캐너
- 지점을 그림판에서 직접 얻는 게 아니라 이미지상에서 마우스 클릭으로 좌표를 얻을 수 있게끔 해보자
- 마우스 이벤트 등록
def mouse_handler(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN: #마우스 왼쪽 버튼 누르기
print('왼쪽 버튼 Down')
print(x,y)
elif event == cv2.EVENT_LBUTTONUP : #마우스 왼쪽 버튼 떼기
print('왼쪽 버튼 Up')
print(x,y)
elif event == cv2.EVENT_LBUTTONDBLCLK: #마우스 왼쪽 더블클릭
print('왼쪽 버튼 Double Click')
# elif event == cv2.EVENT_MOUSEMOVE: #마우스 이동
# print('마우스 이동')
elif event == cv2.EVENT_RBUTTONDOWN: #마우스 오른쪽 버튼 누르기
print('오른쪽 버튼 Down')
cv2.namedWindow('img') #img란 이름의 윈도우를 먼저 만들어두는 것, 여기에 마우스 이벤트를 처리하기위한 핸들러 적용할 거임
cv2.setMouseCallback('img', mouse_handler)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
왼쪽 버튼 Down
702 146
왼쪽 버튼 Up
702 146
- 미니 프로젝트 코드 완성
src_img = cv2.resize(cv2.imread('poker.jpg'), None, fx=0.5, fy=0.5)
point_list = []
COLOR = (255,0,255) #핑크색
def mouse_handler(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN: #마우스 왼쪽 버튼 누르기
point_list.append((x,y))
for point in point_list:
cv2.circle(src_img, point, 5, COLOR, cv2.FILLED)
if len(point_list) == 4:
show_result() #결과 출력
cv2.imshow('img',src_img) #왼쪽 버튼 누를 때마다 이미지 갱신
def show_result():
width, height = 530, 710
# 좌상, 우상, 우하, 좌하 (좌표 위치 : 시계 방향으로 4지점 정의)
src = np.float32(point_list) #input 4개 지점
dst = np.array([[0,0],[width,0],[width,height],[0,height]], dtype=np.float32) #output 4개 지점
matrix = cv2.getPerspectiveTransform(src,dst) #Matrix 얻어옴
result = cv2.warpPerspective(src_img, matrix, (width,height)) #matrix대로 변환을 함
cv2.imshow('result',result)
cv2.namedWindow('img') #img란 이름의 윈도우를 먼저 만들어두는 것, 여기에 마우스 이벤트를 처리하기위한 핸들러 적용할 거임
cv2.setMouseCallback('img', mouse_handler)
cv2.imshow('img',src_img) #img라는 윈도우에 src_img를 띄움
cv2.waitKey(0)
cv2.destroyAllWindows()
14.이미지 변형(이진화)
- 흰색과 검정색만 가지는 이미지로 변형시키는 것
img = cv2.resize(cv2.imread('book.jpg', cv2.IMREAD_GRAYSCALE),None, fx=0.1, fy=0.1)
# 127보다 크다고 하면 흰색(255)으로 아니면 검정색(0)으로
ret, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
cv2.imshow('img',img)
cv2.imshow('binary', binary)
cv2.waitKey(0)
cv2.destroyAllWindows()
Trackbar(값 변화에 따른 변형 확인)
def empty(pos):
#print(pos)
pass
name = 'Trackbar'
cv2.namedWindow(name) #Trackbar라는 이름의 윈도우를 먼저 생성함
cv2.createTrackbar('threshold', name, 127, 255, empty) #bar이름, 창의 이름, 초기값, 최대값, 이벤트 처리
while True:
thresh = cv2.getTrackbarPos('threshold',name) #bar 이름, 창 이름 (0~255값을 차례로 반환해줌)
ret, binary = cv2.threshold(img, thresh, 255, cv2.THRESH_BINARY)
if not ret:
break
cv2.imshow(name, binary)
if cv2.waitKey(1) == ord('q'):
break
cv2.destroyAllWindows()
Adaptive Threshold
- 이미지를 작은 영역으로 나누어서 임계치 적용하는 방법
def empty(pos):
#print(pos)
pass
name = 'Trackbar'
cv2.namedWindow(name)
#bar이름, 창의 이름, 초기값, 최대값, 이벤트 처리
cv2.createTrackbar('block_size', name, 25, 100, empty) # block_size는 홀수만 가능, 1보다는 큰 값
cv2.createTrackbar('c', name, 3, 10, empty) # c의 경우 일반적으로 양수의 값을 사용
while True:
block_size = cv2.getTrackbarPos('block_size',name) #bar 이름, 창 이름
c = cv2.getTrackbarPos('c',name)
if block_size <= 1:
block_size = 3
if block_size % 2 == 0:
block_size += 1
binary = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, block_size, c)
cv2.imshow(name, binary)
if cv2.waitKey(1) == ord('q'):
break
cv2.destroyAllWindows()
오츠 알고리즘
- 최적의 threshold값을 찾아주는 알고리즘
- Bimodal Image(쌍봉분포)에 사용하기에 적합함
img = cv2.resize(cv2.imread('book.jpg', cv2.IMREAD_GRAYSCALE),None, fx=0.1, fy=0.1)
# 127보다 크다고 하면 흰색(255)으로 아니면 검정색(0)으로
ret, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, otsu = cv2.threshold(img, -1, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print('otsu threshold', ret)
cv2.imshow('img',img)
cv2.imshow('binary', binary)
cv2.imshow('otsu', otsu)
cv2.waitKey(0)
cv2.destroyAllWindows()
otsu threshold 101.0
15.경계선 검출
Canny Edge Detection(Trackbar를 이용한)
img = cv2.resize(cv2.imread('snowman.png'),None,fx=0.5,fy=0.5)
def empty(pos):
pass
name = 'Trackbar'
cv2.namedWindow(name)
cv2.createTrackbar('threshold1', name, 0, 255, empty) #하위임계값
cv2.createTrackbar('threshold2', name, 0, 255, empty) #상위임계값
while True:
threshold1 = cv2.getTrackbarPos('threshold1', name)
threshold2 = cv2.getTrackbarPos('threshold2', name)
#대상 이미지, threshold1(하위임계값), threshold2(상위임계값)
canny = cv2.Canny(img, threshold1, threshold2)
cv2.imshow('img',img)
cv2.imshow(name, canny)
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
16.윤곽선 검출
- 윤곽선 : 경계선을 연결한 선
- 정확하게 하기 위해 전처리 해줌 (binary image로 변환 – 흰색 검정색만 있는 이미지)
img = cv2.imread('card.png')
target_img = img.copy() #사본 이미지
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #GRAY이미지로 변환
ret, otsu = cv2.threshold(gray, -1, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) #오츠알고리즘을 이용한 이진화
#반환되는 값 : 윤관선 정보, 계층구조
contours, hierarchy = cv2.findContours(otsu, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) #윤곽선 검출
#파라미터 : 이미지, 윤곽선 찾는 모드, 윤곽선 찾을 때 사용하는 근사치 방법(cv2.CHAIN_APPROX_SIMPLE or cv2.CHAIN_APPROX_NONE)
#윤곽선 그리기
COLOR = (0,200,0)
cv2.drawContours(target_img, contours, -1, COLOR, 2)
# 파라미터 : 대상 이미지, 윤곽선 정보, 인덱스(-1이면 전체), 색깔, 두께
cv2.imshow('img',img)
cv2.imshow('gray', gray)
cv2.imshow('otsu', otsu)
cv2.imshow('contour', target_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
윤곽선 찾기 모드
- cv2.RETR_EXTERNAL : 가장 외곽의 윤곽선만 찾음
- cv2.RETR_LIST : 모든 윤곽선 찾음 (계층 정보 없음)
- cv2.RETR_TREE : 모든 윤곽선 찾음 (계층 정보를 트리 구조로 생성)
경계 사각형
- 윤곽선의 경계면을 둘러싸는 사각형
boundingRect()
img = cv2.imread('card.png')
target_img = img.copy() #사본 이미지
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, otsu = cv2.threshold(gray, -1, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
#반환되는 값 : 윤관선 정보, 계층구조
contours, hierarchy = cv2.findContours(otsu, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) #윤곽선 검출
#파라미터 : 이미지, 윤곽선 찾는 모드, 윤곽선 찾을 때 사용하는 근사치 방법
COLOR = (0,200,0)
for cnt in contours:
if cv2.contourArea(cnt) > 100: #윤곽선의 면적이 100보다 클 때만 도형 그림
x,y,width,height = cv2.boundingRect(cnt) #윤곽선의 경계면을 둘러싸는 사각형에 대한 정보 제공
cv2.rectangle(target_img,(x,y),(x+width,y+height), COLOR, 2) #제공 받은 정보로 사각형 그리기 (두께2)
cv2.imshow('img',img)
cv2.imshow('gray', gray)
cv2.imshow('otsu', otsu)
cv2.imshow('contour', target_img)
cv2.waitKey(0)
cv2.destroyAllWindows()