4강 OpenCV 명함 검출 및 인식-2

양말신은도비·2022년 7월 2일
0

객체 단위 분석

  • 객체 단위 분석

    • 흰색으로 표현된 객체를 분할하여 특징을 분석
    • 객체 위치 및 크기 정보, ROI 추출
  • 레이블링(Connected Component Labeling)

    • cv2.connectedComponent(), cv2.connectedComponentWithStats()
    • 서로 연결되어 있는 객체 픽셀에 고유한 번호를 지정
    • 각 객체의 바운딩 박스, 무게 중심 좌표로 함께 반환
  • 외곽선 검출(Contour Tracing)

    • cv2.findContours()
    • 각 객체의 외곽선 좌표를 모두 검출

외곽선 검출

  • 외곽선 검출이란?

    • 객체의 외곽선 좌표를 모두 추출하는 작업
    • 바깥쪽 & 안쪽 외곽선
    • 외곽선의 계층 구조도 표현 가능
  • 객체 하나의 외곽선 표현 방법

    • numpy.ndarray
    • shape=(K,1,2), dtype=int32
  • 여러 객체의 외곽선 표현 방법

    • "객체 하나의 외곽선"을 원소로 갖는 리스트
    • 리스트 길이 = 외곽선 개수

외곽선 검출 함수

  • 외곽선 검출
    cv2.findContours(image, mode, method, contours=None,
    								 hierarchy=None, offset=None) -> contours, hierarchy
    • image: 입력 영상. non-zero 픽셀을 객체로 간주함.
    • mode: 외곽선 검출 모드
    • method: 외곽선 근사화 방법
    • contours: 검출된 외곽선 좌표. numpy.ndarray로 구성된 리스트.
      len(contours)=N,
      contours[i].shape=(K, 1, 2)
    • hierarchy: 외곽선 계층 정보. numpy.ndarray.
      shape=(1, N, 4). hierarchy[0, i, 0~3]가 순서대로
      next, prev, child, parent 외곽선 인덱스를 가리킴
      해당 외곽선이 없으면 -1을 가짐
  • 세부 사항
    • Mode
      1. RETR_EXTERNAL: 가장 바깥쪽 외곽선만 검출
      2. RETR_LIST: 계층 관계없이 모든 외곽선 검출
      3. RETR_CCOMP: 2레벨 계층 구조로 외곽선 검출. 상위 레벨은 흰색 객체 외곽선, 하위 레벨은 검정색 구멍 외곽선
      4. RETR_TREE: 계층적 트리 구조로 모든 외곽선 검출

- Method
    1. CHAIN_APPROX_NONE: 근사화 없음
    2. CHAIN_APPROX_SIMPLE: 수직선, 수평선, 대각선에 대해 끝점만 사용하여 압축
    3. CHAIN_APPROX_TC89_L1: Teh & Chin L1 근사화
    4. CHAIN_APPROX_TC89_KCOS: Teh & Chin k cos 근사화
    

외곽선 관련 API

  • 면적 구하기
    cv2.contourArea(contour, oriented=None) -> retval
    • contour: 외곽선 좌표. numpy.ndarray. shape=(K, 1, 2)
    • oriented: True이면 외곽선 진행 방향에 따라 부호 있는 면적을 반환
    • retval: 외곽선으로 구성된 면적
  • 외곽선 길이 구하기
    cv2.arcLength(curve, closed) -> retval
    • curve: 외곽선 좌표. numpy.ndarray. shape=(K, 1, 2)
    • closed: True이면 폐곡선으로 간주
    • retval: 외곽선 길이
  • 바운딩 박스(외곽선을 외접하여 둘러싸는 가장 작은 사각형) 구하기
    cv2.boundingRect(array) -> retval
    • array: 외곽선 좌표. numpy.ndarray. shape=(K, 1, 2)
    • retval: 사각형. (x, y, w, h)
  • 바운딩 서클(외곽선을 외접하여 둘러싸는 가장 작은 원) 구하기
    cv2.minEnclosingCircle(points) -> center, radius
    • points: 외곽선 좌표. numpy.ndarray. shape=(K, 1, 2)
    • center: 바운딩 서클 중심 좌표 (x, y)
    • radius: 바운딩 서클 반지름
  • 외곽선 근사화
    cv2.approxPolyDP(curve, epsilon, closed, approxCurve=None) -> approxCurve
    • curve: 입력 곡선 좌표. numpy.ndarray. shape=(K, 1, 2).
    • epsilon: 근사화 정밀도 조절. 입력 곡선과 근사화 곡선 간의
      최대 거리. e.g) (외곽선 전체 길이) * 0.02
    • closed: True를 전달하면 폐곡선으로 간주
    • approxCurve: 근사화된 곡선 좌표. numpy.ndarray. shape=(K', 1, 2)

[https://thebook.io/006939/ch12/02/02-03/](https://thebook.io/006939/ch12/02/02-03/)
  • 컨벡스 검사
    cv2.isContourConvex(contour) -> retval
    • contour: 입력 곡선 좌표. numpy.ndarray. shape=(K, 1, 2).
    • retval: 컨벡스이면 True, 아니면 False.

영상의 기하학적 변환

  • 어파인 변환 3개의 점을 통해 나머지 1개의 점의 위치를 알 수 있음
  • 투시변환 일그러트려지기 때문에 4개의 점이 모두 필요

  • 투시 변환 행렬 구하기
    cv2.getPerspectiveTransform(src, dst, solveMethod=None) -> retval
    • src: 4개의 원본 좌표점. numpy.ndarray. shape=(4, 2)
    • dst: 4개의 결과 좌표점. numpy.ndarray. shape=(4, 2)
    • 반환값: 3x3 크기의 투시 변환 행렬
  • 영상의 투시변환
    cv2.warpPerspective(src, M, dsize, dst=None, flags=None,
    										borderMode=None, borderValue=None) -> dst
    • src: 입력 영상
    • M: 3x3 변환 행렬, 실수형
    • dsize: 결과 영상의 크기. (0, 0)을 지정하면 src와 같은 크기.
    • dst: 출력 영상
    • flags: 보간법. 기본값은 cv2.INTER_LINEAR
    • borderMode: 가장자리 픽셀 확장 방식.
    • borderValue: cv2.BORDER_CONSTANT일 때 사용할 상수 값. 기본값은 0.

코드

  • 코드
    • 이미지 불러오기, 이진화
      import sys
      import cv2
      import numpy as np
      
      src = cv2.imread('namecard1.jpg')
      
      if src is None:
          print('image load failed')
          sys.exit()
      
      # src = cv2.resize(src, (640, 480))
      #src = cv2.resize(src, (0, 0), fx=0.5, fy=0.5)
      
      # 흑백흑백
      src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
      
      # 이진화 => 임계값 함수 => otsu를 쓰니 알아서 하라고 0을 줌.
      _, src_bin = cv2.threshold(src_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    • 외곽선 편집
      contours, _ = cv2.findContours(src_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
      
      print(len(contours))
      
      for pts in contours:
          if cv2.contourArea(pts) < 1000:
              continue
              
          approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True)*0.02, True)
          
          if len(approx) != 4:
              continue
          
          w, h = 900, 500
          srcQuad = np.array([[approx[0, 0, :]], [approx[1, 0, :]], \
                              [approx[2, 0, :]], [approx[3, 0, :]]]).astype(np.float32)
          dstQuad = np.array([[0, 0], [0, h], [w, h], [w, 0]]).astype(np.float32)
          
          pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
          dst = cv2.warpPerspective(src, pers, (w, h)) 
          
          cv2.polylines(src, pts, True, (0, 0, 255))
    • 출력
      # cv2.imshow('src',src)
      # cv2.imshow('src_gray',src_gray)
      # cv2.imshow('src_bin',src_bin)
      cv2.imshow('dst', dst)
      cv2.waitKey()
      cv2.destroyAllWindows()
  • 결과

0개의 댓글