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에서 사용하는 글꼴 종류

  1. cv2.FONT_HERSHEY_SIMPLEX : 보통 크기의 산 세리프 글꼴
  2. cv2.FONT_HERSHEY_PLAIN : 작은 크기의 산 세리프 글꼴
  3. cv2.FONT_HERSHEY_SCRIPT_SIMPLEX : 필기체 스타일 글꼴
  4. cv2.FONT_HERSHEY_TRIPLEX : 보통 크기의 세리프 글꼴
  5. 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()

보간법

  1. cv2.INTER_AREA : 크기 줄일 때 사용
  2. cv2.INTER_CUBIC : 크기 늘릴 때 사용 (속도 느림, 퀄리티 좋음)
  3. 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에 대해

  1. flipCode > 0 : 좌우대칭
  2. 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()

미니 프로젝트 : 반자동 문서 스캐너

  1. 마우스 이벤트 등록
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
  1. 미니 프로젝트 코드 완성
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()

13.이미지 변형(이진화)

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()    

오츠 알고리즘

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

14.경계선 검출

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()

15.윤곽선 검출

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()

윤곽선 찾기 모드

  1. cv2.RETR_EXTERNAL : 가장 외곽의 윤곽선만 찾음
  2. cv2.RETR_LIST : 모든 윤곽선 찾음 (계층 정보 없음)
  3. cv2.RETR_TREE : 모든 윤곽선 찾음 (계층 정보를 트리 구조로 생성)

경계 사각형

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()