ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 이미지 스캐너
    프로젝트 2022. 3. 3. 20:32

    안면인식 프로그램에 이어 이미지 스캐너입니다.

    https://pyimagesearch.com/2014/09/01/build-kick-ass-mobile-document-scanner-just-5-minutes/

     

    How to Build a Kick-Ass Mobile Document Scanner in Just 5 Minutes - PyImageSearch

    Building a document scanner with OpenCV can be accomplished in just three simple steps: Step 1: Detect edges. Step 2: Use the edges in the image to find the contour (outline) representing the piece of paper being scanned. Step 3:…

    pyimagesearch.com

     

    이미지 스캐너란?

    하나의 이미지 속에서 따로 원하는 영역을 자르고 싶을 때,
    그 원하는 요소의 모서리를 클릭 함으로써 이미지가 쉽게 잘리도록 돕는 프로그램입니다.

     


     

    1. 라이브러리 호출과 이미지 띄우기

    import cv2
    import numpy as np
    
    img = cv2.imread('img.jpg')
    
    cv2.imshow('img', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    이 곳은 딱히 어려운 점이 없습니다.
    기본적인 OpenCV 이미지 호출 방법을 사용하기 때문입니다

     

     

    2. 마우스 클릭이 감지되면 그 감지된 위치 저장하기

    import cv2
    import numpy as np
    
    point_list = [] # 감지된 위치를 저장할 곳
    img = cv2.imread('img.jpg')
    
    # 마우스 이벤트 함수
    def mouse_handler(event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN: # 마우스 왼쪽 버튼 누름을 감지하는 이벤트
            point_list.append((x, y)) # 튜플형으로 삽입
            
    cv2.namedWindow('img') # img란 이름의 윈도우를 먼저 만들어두는 것, 여기에 마우스 이벤트를 처리하기 위한 핸들러 적용
    cv2.setMouseCallback('img', mouse_handler
    cv2.imshow('img', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    먼저 위치를 저장할 공간이 필요하기 때문에 point_list 라는 List를 생성해줍니다.

    그리고 마우스 클릭 기능을 구현해야하기 때문에 mouse_handler라는 함수(def) 를 만들어줍니다.
    이 함수의 소스코드가 굉장히 복잡해보이지만 코드를 말로 쉽게 풀어서, 만약 마우스 왼쪽 버튼 클릭이 감지 된다면, 아까 만들어주었던 point_list 안에 그 감지된 위치를 넣어준다. 라고 이해하면 될 것 같습니다.

    한마디로 mouse_handler 함수 == 클릭한 위치를 찾도록 돕는 역할,
    point_list == mouse_handler 함수를 통해 찾은 클릭한 위치를 담고있는 공간

     

    3. 클릭한 지점을 이미지 위에 표시

    import cv2
    import numpy as np
    
    point_list = [] # 감지된 위치를 저장할 곳
    img = cv2.imread('img.jpg')
    COLOR = (255, 0, 255) # 핑크색
    
    # 마우스 이벤트 함수
    def mouse_handler(event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN: # 마우스 왼쪽 버튼 누름을 감지하는 이벤트
            point_list.append((x, y)) # 튜플형으로 삽입
            
        for point in point_list:
            cv2.circle(img, point, 15, COLOR, cv2.FILLED)
            # 어떤 이미지에? 중심점? 원의 크기, 원의 색, 원의 모양 (꽉차게)
        
        cv2.imshow('img', img)
            
    cv2.namedWindow('img') # img란 이름의 윈도우를 먼저 만들어두는 것, 여기에 마우스 이벤트를 처리하기 위한 핸들러 적용
    cv2.setMouseCallback('img', mouse_handler
    cv2.imshow('img', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    마우스 이벤트 함수안에 for문 하나가 추가 됬는데, 이 for문의 역할을 살펴보겠습니다.
    아까 마우스를 클릭한 위치에 작은 원을 그려 어떤 곳에 클릭을 했는지 표시해주겠다 라는 뜻으로 해석하면 쉽습니다. 그렇다면 원의 색상도 지정해줘야겠죠? 코드 윗 부분에 COLOR 라는 변수를 두고 RGB 값으로 색상을 지정 주었습니다.

     


     

    그럼 이제 여기까지의 결괏값을 한번 확인해볼까요?

    지금까지의 결괏 값

    이렇게 이미지 위에 마우스 클릭을 하면, 핑크색의 원으로 클릭을 했던 곳 위에 표시가 됩니다.

    그럼 이제 모서리를 클릭한 요소를 잘라내는 기능을 구현을 할 차례입니다.

     

    4. 클릭한 요소 잘라내기 (참고)

    import cv2
    import numpy as np
    
    img = cv2.imread('newspaper.jpg')
    
    # 여기서 부터 기능구현 시작 
    width, height = 640, 240 # 가로 크기 640, 세로 크기 240 으로 결과물 출력
    
    # input 데이터 만들기
    src = np.float32(point_list) # 변환행렬을 하기 위한 준비코드
    dst = np.array([ [0, 0], [width, 0], [width, height], [0, height]], dtype=np.float32) # 결괏값의 창의 크기 설정
       
    matrix = cv2.getPerspectiveTransform(src, dst)
    result = cv2.warpPerspective(img, matrix, (width, height)) # 결괏값의 창의 크기 설정
    
    cv2.imshow('img', img)
    cv2.imshow('result', result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    클릭한 요소를 잘라내기 전에 알아야 할 몇가지 개념이 있어서 준비해봤습니다!

    윗줄 코드에 width와 height의 크기를 지정해놨는데, 이건 그냥 결과값의 크기(잘린이미지)를 지정해놓은 코드입니다.

    point_list안에 있는 클릭한 위치의 값들을 나중에 변환행렬하여 결괏값을 도출하기 위해 
    src라는 변수에 float32라는 함수를 활용해 point_list를 넣어줍니다.

    그리고, dst라는 변수의 속성 값들은 우리가 모서리를 클릭해서 자르면 또 하나의 결괏 값 창이 나오는데, 그 창의 크기를 지정하기 위한 속성이라고 생각하면 됩니다. ( 윗 코드에서 width, height를 지정해놓은 코드와 동일한 기능 )

     

    (밑 내용은 대충 이해한다는 느낌으로만 읽어주세요.)

    warpPerspective 함수를 사용하여 퍼스펙티브 변환(PerspectiveTransformation)을 구현합니다.

     

    퍼스펙티브 변환에서 원본 이미지의 모든 직선은 출력 이미지에서 직선으로 유지됩니다.

    (이미지가 아무리 삐뚤어져있어도 모서리를 클릭해서 잘라내면 쭉 펴서 볼 수 있음)

    퍼스펙티브 변환 행렬을 찾으려면 입력 이미지의 4점과 대응하는 출력 이미지의 4점(point_list)이 필요합니다.

     

    getPerspectiveTransform 함수를 사용하면 대응하는 4점 쌍에 대한  변환 행렬을 구할 수 있습니다.

    warpPerspective 함수를 사용하여 변환을 실행합니다.

     

    위 내용이 무슨 말인지 이해를 못하실 분들을 위해 아주 간단하게 정리해드리겠습니다.
    getPerspectiveTransform 함수로 인해 행렬변환이 이루어져 4개의 모서리대로 이미지가 잘리고, 
    warpPerspective를 활용해 잘린 이미지(결괏 값)를 도출 해 냅니다.

     

    4. 클릭한 요소 잘라내기 (final)

    import cv2
    import numpy as np
    
    img = cv2.imread('images/poker.jpg')
    width, height = 530, 710
    point_list = []
    COLOR = (255, 0, 255)
    
    def mouse_handler(event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN: # 마우스 왼쪽 버튼 누름을 감지하는 이벤트
            point_list.append((x, y))
    
        for point in point_list:
            cv2.circle(img, point, 15, COLOR, cv2.FILLED)
            
        if len(point_list) == 4:
            show_result() # 결과 출력함수
        
        
        cv2.imshow('img', img)
        
    def show_result():
        width, height = 530, 710
    
        # input 데이터 만들기
        src = np.float32(point_list)
        dst = np.array([ [0, 0], [width, 0], [width, height], [0, height]], dtype=np.float32) # Output 4개 지점
        # 좌상, 우상, 우하, 좌하 (시계 방향으로 4 지점을 정의)
    
        matrix = cv2.getPerspectiveTransform(src, dst) # Matrix 얻어옴
        result = cv2.warpPerspective(img, matrix, (width, height)) # matrix 대로 변환을 함
        
        cv2.imshow('result', result)
        
    cv2.namedWindow('img') # img란 이름의 윈도우를 먼저 만들어 둠 -> 여기에 마우스 이벤트를 처리하기 위한 핸들러 적용
    cv2.setMouseCallback('img', mouse_handler)
    cv2.imshow('img', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    그럼 이제 마지막입니다.
    위에서 행렬변환을 위해 사용했던 코드를 그대로 가져와 show_result 라는 함수안에 복사 붙여넣기 하면 끝입니다!

     

    그럼 최종적으로 우리는 한 프로젝트 안에서 두개의 기능을 구현했습니다.
    1. 마우스를 클릭하면 핑크색 원으로 클릭한 위치 표시.

    2. 마우스로 모서리를 클릭해 그 모서리 안에 담긴 요소를 잘라내 새로운 창에 표시.

     

    결괏 값

     

     

    참고문서

    https://deep-learning-study.tistory.com/200

    https://www.youtube.com/watch?v=XK3eU9egll8&t=8724s

    https://pyimagesearch.com/2014/09/01/build-kick-ass-mobile-document-scanner-just-5-minutes/

    https://stackoverflow.com/questions/17637730/android-opencv-getperspectivetransform-and-warpperspective

    '프로젝트' 카테고리의 다른 글

    Python - 안면인식(OpenCV)  (0) 2021.10.15
Designed by Tistory.