Skip to content

Latest commit

 

History

History
141 lines (113 loc) · 4.8 KB

211015.md

File metadata and controls

141 lines (113 loc) · 4.8 KB

Day 14

파이썬을 이용한 머신러닝, 딥러닝 실전 앱 개발 (5일차)

OpenCV와 머신러닝 - 이미지/동영상 입문

윤곽 검출 - 엽서의 우편 번호 인식하기

윤곽 검출 과정

  1. 이미지 읽기
  2. 이미지 이진화 (그레이스케일로 변환 -> 블러 처리 -> 이진화)
  3. 윤곽 검출

이미지 블러 처리

GaussianBlur() - 화이트 노이즈 제거에 많이 사용

img = cv2.GaussianBlur(img, (ax, ay), sigma_x)

  • img : 원본 이미지
  • (ax, ay) : 블러 처리를 적용할 화소 크기를 픽셀 단위로 지정, 값에는 홀수를 지정
  • sigma_x : 가로 방향 표준 편차, 0으로 지정하면 전체 크기를 기반으로 자동 지정

블러 처리 함수를 적용하면 이미지 전체에 흐림 효과가 적용, cv2.bilateralFilter()는 엣지를 남기고 흐림 효과 적용

이미지 이진화

ret, img = cv2.threshold(img, thresh, maxval, type)

  • 이미지의 화소가 지정한 역치보다 크면 흰색, 작으면 검은색으로 만들어 줌
  • img : 그레이스케일 이미지
  • thresh : 역치 값
  • maxval : 역치 값 이상의 값에 설정할 값
  • type : 어떻게 역치값을 처리할 것인가 (THRESH_BINARY_INV - 역치 값보다 큰 값일 때 0, 그 외는 maxval 할당)

윤곽 검출

contours, hierarchy = cv2.findContours(imgae, mode, method)

  • mode |상수|의미| |:-----------:|:-------------:| |cv2.RETR_LIST|단순한 윤곽 검출| |cv2.RETR_EXTERNAL|가장 외곽에 있는 윤곽을 검출| |cv2.RETR_CCOMP|계층을 고려해 2레벨 윤곽을 검출| |cv2.RETR_TREE|모든 윤곽을 검출하고 계층 구조로 저장|

  • method |상수|의미| |:----------------:|:----------------:| |cv2.CHAIN_APPROX_NONE|윤곽 위에 있는 모든 점을 반환| |cv2.CHAIN_APPROX_SIMPLE|의미 없는 정보를 제거하고 반환|

엽서에서 우편 번호 영역 검출

import cv2
import matplotlib.pyplot as plt

# 엽서 이미지에서 우편 번호를 추출하는 함수
def detect_zipno(fname):
    # 이미지 읽어 들이기
    img = cv2.imread(fname)
    
    # 이미지 크기 구하기
    h, w = img.shape[:2]
    
    # 이미지의 오른쪽 윗부분만 추출하기 (우편 번호가 이미지의 우측 상단에 있으니 위의 1/2부분과 오른쪽의 2/3부분만 추출)
    img = img[0:h//2, w//3:]
    
    # 이미지 이진화하기
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 그레이스케일
    gray = cv2.GaussianBlur(gray, (3, 3), 0) # 흐림 처리
    im2 = cv2.threshold(gray, 140, 255, cv2.THRESH_BINARY_INV)[1] # 이진화
    
    # 윤곽 검출하기
    cnts = cv2.findContours(im2, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[0]
    
    # 추출한 이미지에서 윤곽 추출
    result = []
    for pt in cnts:
        x, y, w, h = cv2.boundingRect(pt) # 추출한 윤곽을 단순한 [x, y, 너비, 높이] 형태의 리스트로 변환
        
        # 너무 크거나 너무 작은 부분 제거하기
        if not(50 < w < 70): continue
        result.append([x, y, w, h])
    
    # 추출한 윤곽을 위치에 따라 정렬하기
    result = sorted(result, key=lambda x: x[0]) # 추출한 영역을 x 방향으로 정렬 (왼쪽부터 차례대로 영역을 추출하기 위함)
    
    # 추출한 윤곽이 너무 가까운 것들 제거하기
    result2 = []
    lastx = -100
    for x, y, w, h in result:
        if (x - lastx) < 10: continue
        result2.append([x, y, w, h])
        lastx = x
        
    # 초록색 테두리 출력하기
    for x, y, w, h in result2:
        cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 3)
    return result2, img

if __name__ == '__main__':
    # 이미지를 지정해서 우편번호 추출하기
    cnts, img = detect_zipno('./data/letter01.png')
    
    # 결과 출력하기
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.show()

추출한 숫자 이미지 판정하기

import matplotlib.pyplot as plt
import joblib

# 학습한 데이터 읽어 들이기
clf = joblib.load('./data/digits.pkl')

# 이미지에서 영역 읽어 들이기
cnts, img = detect_zipno('./data/letter01.png')

# 읽어 들인 데이터 출력
for i, pt in enumerate(cnts):
    x, y, w, h = pt
    # 윤곽으로 감싼 부분을 작게 만들기
    x += 8
    y += 8
    w -= 16
    h -= 16
    
    # 이미지 데이터 추출
    im2 = img[y:y+h, x:x+w]
    
    # 데이터를 학습에 적합하게 변환
    im2gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 그레이스케일
    im2gray = cv2.resize(im2gray, (8,8)) # 크기 변경
    im2gray = 15 - im2gray // 16 # 이진화
    im2gray = im2gray.reshape((-1, 64)) # 차원 변환
    
    # 데이터 예측
    res = clf.predict(im2gray)
    
    # 출력
    plt.subplot(1, 7, i+1)
    plt.imshow(im2)
    plt.axis('off')
    plt.title(res)
    
plt.show()