파이썬(Python)/opencv

[Python]OpenCV 3차원 이미지 스캔화면(2차원)처럼 바꾸기

끄적끄적아무거나 2022. 4. 5. 19:04
반응형

 

목차

     

     

    [Python]OpenCV 기하학적 변형하기(Geometry formation)

     

    이번 포스트는 이름을 어떻게 작성할까 고민 끝에 일반 사진(Picture) 파일을 2차원의 스캔(Scan)한 것과 같은 효과로 만드는 것 같아서 스캔하기로 적었습니다. 

     

    이전 장에서는 2차원 평면(Plane)의 그림(Image)을 원근(Perspective)을 줘서 3차원처럼 보이게 만들었는데 이번에는 반대로 3차원의 원근 거리감이 있는 이미지 파일을 거리감이 없어지게 변형하는 코드를 작성해 보았습니다. 

     

    설명에 앞서 아래 예제의 결과를 먼저 공유 해서 이번 포스트의 진행 방향을 쉽게 이해가도록 하겠습니다.

     

    아래 예제 결과>>

     

    최근에 위와 같은 예제를 명함이나 스마트폰의 사진 기술에서 많이 볼 수 있습니다. 명함을 비뚫게 촬영해도 앱(App)에서 자동으로 스캔(Scan) 화면처럼 변경해 줍니다.

     

    위 예제를 구현하기 위해서는 2가지의 단계가 필요 합니다.

     

    1. 종이 끝점의 좌표 구하기

    2. 좌표를 적용해서 2차원으로 변형하기

     

    한번에 구현하면 이해하기 힘들 수 있으므로 우선 1. 종이 끝 점의 좌표(Coordination)을 구하는 코드부터 구현 및 설명하고 다음으로 좌표를 사용해서 평면(Plane) 화면으로 변형하는 예제를 구현해 보겠습니다.

     

     

     

     

    파이썬 OpenCV 종이 끝점의 좌표 구하기

     

    코드 설명>>

     

    아래 코드는 종이의 끝점을 마우스(Mouse) 왼쪽 클릭 하면 빨간색 원이 생기고 해당 좌표 값은 리스트에 기록됩니다.  4개의 포인트를 기록하고 5번 마우스를 클릭하거나 마우스 오른쪽 클릭하면 기존의 빨간색 원이 사라지고 리셋됩니다. 

     

    예제 코드>>

    import cv2
    import numpy as np
    
    def mouse_click(event, x, y, flags, param):
        global points, p_cnt, marked_img     
            
        if (event == cv2.EVENT_FLAG_RBUTTON) or (p_cnt == 5):   
            points = []
            p_cnt = 0
            marked_img = cv2.imread("hello.jpg")
            cv2.imshow("Original", marked_img)
        elif (event == cv2.EVENT_FLAG_LBUTTON):    
            p_cnt = p_cnt + 1
            print(p_cnt)
            cv2.circle(marked_img, (x, y), 5, (0, 0, 255), 2)
            cv2.imshow("Original", marked_img)
            points.append([x,y])    
    
    ###Initialization###
    points = []
    p_cnt = 0
    
    marked_img = cv2.imread("hello.jpg")
    
    cv2.imshow("Original", marked_img)
    cv2.setMouseCallback("Original", mouse_click)
    
    while True:           
        if cv2.waitKey(0):
            break
    
    print(p_cnt)
    print(points)
    print(marked_img.shape)
    x_pos = marked_img.shape[0]
    y_pos = marked_img.shape[0]
    
    cv2.destroyAllWindows()

    4~17번 라인: 마우스 이벤트(Event) 동작시 발생하는 함수 입니다.

    5번 라인: global로 설정해야 함수 외부에서도 해당 변수들을 사용할 수 있습니다.

    7~11번 라인: 오른쪽 버튼 클릭하거나 클릭을 5번 할 경우 기존의 원 그림을 지우고 리셋합니다.

    12~17번 라인: 마우스 오른쪽 버튼을 클릭하면 클릭한 위치에 빨강 원을 그립니다. 그리고 해당 좌표는 points 리스트(list)에 추가 합니다.

    20~26번 라인: 초기값을 세팅합니다. 마우스 이벤트에 대한 세팅도 진행합니다. 

    28~30번 라인: 마우스로 좌표를 클릭하는 동안 이미지 유지를 위해 while 문을 사용합니다. 완료 후 키보드(Keyboard) 아무값이나 입력하면 좌표값 입력이 종료됩니다.

    32~34번 라인: 좌표값 결과를 확인 합니다.

     

    마우스 이벤트 관련해서 상세한 내용을 확인하고 싶다면 아래 포스트를 참조하시면 됩니다. 

     

    https://scribblinganything.tistory.com/500

     

    [Python]OpenCV 마우스 이벤트/클릭(Mouse Event, Click) 콜백함수

    목차 파이썬 마우스 클릭시 함수 구현 파이썬 OpenCV에서 이미지를 띄워 놓고 마우스 클릭 시 특정 함수가 동작되게 하기 위해서는 콜백 함수를 사용해야 합니다. 콜백 함수는 이벤트(Event)가 발생

    scribblinganything.tistory.com

     

     

    결과>>

    [[175, 48], [46, 207], [449, 127], [377, 330]]
    (367, 515, 3)

     

     

     

    파이썬 OpenCV 좌표를 적용해서 2차원 그림으로 변형

     

    코드 설명>>

    이번 코드는 앞의 예제 코드에서 39~49번 라인만 더 추가된 코드 입니다. 2차원 / 스캔 화면으로 변경하는 코드만 추가되었습니다. 아래 예제 코드의 주석을 참조하시면 됩니다.

     

    주의 할 점은 좌표를 찍을 때 왼쪽 상단, 왼쪽 하단, 오른쪽 상단, 오른쪽 하단 순서대로 찍어야 합니다. 그래야 matrix에서 처리할 때 이상없이 처리 가능 합니다. 

     

     

    예제 코드>>

    import cv2
    import numpy as np
    
    def mouse_click(event, x, y, flags, param):
        global points, p_cnt, marked_img     
            
        if (event == cv2.EVENT_FLAG_RBUTTON) or (p_cnt == 5):   
            points = []
            p_cnt = 0
            marked_img = cv2.imread("hello.jpg")
            cv2.imshow("Original", marked_img)
        elif (event == cv2.EVENT_FLAG_LBUTTON):    
            p_cnt = p_cnt + 1
            print(p_cnt)
            cv2.circle(marked_img, (x, y), 5, (0, 0, 255), 2)
            cv2.imshow("Original", marked_img)
            points.append([x,y])    
    
    ###Initialization###
    points = []
    p_cnt = 0
    
    marked_img = cv2.imread("hello.jpg")
    
    cv2.imshow("Original", marked_img)
    cv2.setMouseCallback("Original", mouse_click)
    
    while True:           
        if cv2.waitKey(0):
            break
    
    print(p_cnt)
    print(points)
    print(marked_img.shape)
    x_pos = marked_img.shape[0]
    y_pos = marked_img.shape[1]
    
    
    ori_coordinate = np.float32(points)
    warped_coordinate = np.float32([[0,0], [0,x_pos], [y_pos, 0], [y_pos,x_pos]])
    Matrix = cv2.getPerspectiveTransform(ori_coordinate, warped_coordinate)
    print(ori_coordinate)
    marked_img = cv2.imread("hello.jpg")
    warped_img = cv2.warpPerspective(marked_img, Matrix, (y_pos, x_pos))
    cv2.imshow("Flattened", warped_img)
    
    while True:           
        if cv2.waitKey(0):
            break
    
    cv2.destroyAllWindows()

    39번 라인: points 리스트에 입력된 x, y 좌표 값을 넘파이(numpy) float32을 써서 실수 형태의 array로 넘겨 줍니다.

    40번 라인: 기울어진 이미지를 2차원으로 폈을때의 포인트 점을 numpy array로 만듭니다.

    41번 라인: getPerspectiveTransform 함수를 사용해서 3차원 -> 2차원의 좌표 값을 입력해서 return으로 matrix 값을 받습니다. 

    44번 라인: warpPerspective 함수를 사용해서 이미지를 펼쳐 줍니다. 

    47~49번 라인: 키보드 입력 값이 있을 때까지 이미지를 띄워 줍니다.

     

    참고로 이미지를 펼쳐주는 방식은 이미지에 원근법을 주는 방식과 동일합니다. 좌표만 반대로 사용하면 됩니다. 해당 내용은 아래 포스트를 참조 하시면 이해하기 쉬우실 겁니다.

     

    https://scribblinganything.tistory.com/505

     

    [Python]OpenCV 이미지 기울어지게 만들기, 원근감 주기(getPerspectiveTransform, warpPerspective)

    목차 파이썬 OpenCV 이미지 파일 원근감 주기(Perspective) 이번 포스트에서는 파이썬의 OpenCV를 사용해서 사진 파일(Picture file)을 불러와서 2차원 평면을 기울려서 3차원과 같은 형태로 원근감을 줘보

    scribblinganything.tistory.com

     

     

    결과>>

     

     

    반응형