ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CHAPTER 13] OpenCV
    Deep Learning/인공지능 입문 코딩 2021. 6. 6. 16:28
    728x90

    이미지를 다루는 모듈, OpenCV에 대해 살펴보자


    이미지 읽기

    cv2.imread() 함수를 사용해 이미지를 읽고

    cv2.imshow() 함수를 사용해 이미지를 그릴 수 있다.

     

    import cv2
    
    img_gray = cv2.imread('../data/mandrill.png', cv2.IMREAD_GRAYSCALE)
    img_color = cv2.imread('../data/mandrill.png', cv2.IMREAD_COLOR)
    
    cv2.imshow('grayscale', img_gray)
    cv2.imshow('color', img_color)
    
    cv2.waitKey(0)  # 사용자 입력을 기다림
    cv2.destroyAllWindows()  # 모든 창을 없애고 프로그램 종료


    합성사진

    img1, im2를 합성해보자.

    addWeighted() 함수를 이용해서 각 이미지의 weigth를 조절한다.

    createTrackbar() 함수는 트랙바(trackbar)를 만들 수 있는데 이 트랙바를 조절하면서 이미지의 weight 를 조절하도록 만들었다.

    주의할 점은 합성할 이미지의 사이즈가 같아야한다는 것이다.

    import cv2
    
    global img1, img2
    
    
    def on_change_weight(x):
        weight = x / 100
        img_merged = cv2.addWeighted(img1, 1 - weight, img2, weight, 0)
        cv2.imshow('Display', img_merged)
    
    
    cv2.namedWindow('Display')
    cv2.createTrackbar('weight', 'Display', 0, 100, on_change_weight)
    
    img1 = cv2.imread('../data/green_back.png')
    img2 = cv2.imread('../data/iceberg.png')
    img1 = cv2.resize(img1, (300, 400))
    img2 = cv2.resize(img2, (300, 400))
    
    cv2.imshow('Display', img1)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


    이미지 마스크

    이미지에 and, or, xor 연산을 해보자.

    mask 이미지의 검정부분은 1, 흰색 배경부분은 0이다.

    import cv2
    
    mask_image = cv2.imread('../data/mask_circle.png')
    back_image = cv2.imread('../data/iceberg.png')
    mask_image = cv2.resize(mask_image, (300, 400))
    back_image = cv2.resize(back_image, (300, 400))
    
    mask_ANDed = cv2.bitwise_and(mask_image, back_image)
    mask_ORed = cv2.bitwise_or(mask_image, back_image)
    mask_XORed = cv2.bitwise_xor(mask_image, back_image)
    mask_Inverted = cv2.bitwise_and(cv2.bitwise_not(mask_image), back_image)
    
    cv2.imshow('mask', mask_image)
    cv2.imshow('back', back_image)
    cv2.imshow('mask and', mask_ANDed)
    cv2.imshow('mask or', mask_ORed)
    cv2.imshow('maskxor', mask_XORed)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()

     

     

    bitwise_and() 비트단위로 AND연산을 하는 함수이다.

    마스크의 검은 부분은 0이니까 0 AND x = 0 이므로

    따라서 왼쪽의 그림과 같은 결과가 나온다.

     

     

     

     

    bitwise_or() 비트단위로 OR연산을 하는 함수이다.

    마스크의 배경 흰색 부분은 1이다. 1 OR x = 1 이고 

    마스크의 검은 부분은 0이다. 0 OR x = x 이므로 오른쪽의 그림과 같은 결과가 나온다.

     

     

     

     

    1 XOR 1 = 0, 1 XOR 0 = 1

    1과 XOR 연산을 하면 반전이 되는 결과가 나온다. 

    따라서 배경은 색이 반전이 되었고

    0 XOR 1 = 1, 0 XOR 0 = 0

    0과 XOR 연산을 하면 그대로 나온다. 따라서 왼쪽의 그림과 같은 결과가 나온다.

     

     

     


    원하는 색 추출

    mandrill.png

    madrill.png 에서 푸른색만 추출해 보려고한다.

    푸른색 픽셀만 추출할 때에는 RGB나 BGR 방식의 색상 모드 보다는 HSV 방식이 더 적합하다.

    HSV는 색상의 범위를 0도에서 360도로 표현한다.

    0에서 360사이의 정수로 표현한다면 한 바이트 이상이 필요하다.

    그래서 이미지 파일에서는 이 값을 반으로 줄여 0도에서 180도로 표현한다.

     

    구글에서 color picker를 검색하면 HSV의 색상을 얻을 수 있다.

    cvtColor() 함수는 OpenCV의 기본 이미지모드는 BGR 모드에서 원하는 모드로 바꿔주는 함수이다.

    HSV로 변경할 것이다.

    푸른색의 범위는 160 ~ 260이다. 반으로 나눈 값 80 ~ 130 을 범위로 쓸 것이다.

    명도 채도는 0~255로 모든 범위를 지정해 최소값을 blue_low 최대값을 blue_high에 저장했다.

    inRange() 함수를 이용해 특정이미지 image_hsv에서 blue_low와 blue_high 사이의 픽셀들을 찾아낸다.

    import numpy as np
    import cv2
    
    image = cv2.imread('../data/mandrill.png')
    image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    
    blue_low = np.array([80, 0, 0])
    blue_high = np.array([130, 255, 255])
    
    my_mask = cv2.inRange(image_hsv, blue_low, blue_high)
    extracted = cv2.bitwise_and(image, image, mask=my_mask)
    
    cv2.imshow('extracted', extracted)
    cv2.imshow('mask', my_mask)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    my_mask(image, my_mask)라고 하지 않은 이유는 채널 수가 다르기 때문이다.

    이러한 경우 같은 이미지로 and연산을한 뒤 mask인자로 my_mask를 전달하면 된다.

     


    이미지 필터

    커널(kernel)을 이용한 필터링을 알아보자.

    커널이 아래와 같은 행렬 이라면

    자신을 포함한 주위 8개의 픽셀의 합을 구해 평균을 취한다.

     

    커널 필터링은 한 픽셀의 주위의 값을 고려하여 평균을 취하는 것이다. 그러면 전체적으로 부드러운 이미지가 된다.

    이러한 처리를 이미지 흐림(blur) 이라고한다.

     

    코드로는 3 x 3 행렬의 커널, 9 x 9 행렬의 커널을 만들어서 적용 시켜보자.

    filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]]) 함수를 사용하면 된다.

    ddepth에 -1을 주면 주어진 입력 이미지와 동일한 값이 돤다.

    import numpy as np
    import cv2
    
    org = cv2.imread('../data/mandrill.png', 1)
    
    kernel1 = np.ones((3, 3), np.float32) / 9
    kernel2 = np.ones((9, 9), np.float32) / 81
    
    averaged33 = cv2.filter2D(org, -1, kernel1)
    averaged99 = cv2.filter2D(org, -1, kernel2)
    
    cv2.imshow('original', org)
    cv2.imshow('filtered1', averaged33)
    cv2.imshow('filtered2', averaged99)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    커널이 클 수록 더 많이 흐려지는 것을 볼 수 있다. 적당한 값을 적용 시키는게 중요한 것 같다.

     

    가우스 필터

    가우스 필터는 평균치를 적용하는게 아닌 중앙값에 더 높은 가중치를 주는 필터이다.

    cv2.GaussianBlur(src, ksize, sigmaX[, dst[, simgmaY[, borderType]]]) 함수를 사용하면 된다.

    ksize는 커널의 크기, sigmaX는 x축 방향으로의 정규분포 표준편차이다. 네 번째 인자부터는 선택사항이다.

    dst는 결과가 출력될 이미지, sigmaY는  y축 표준편차이다. 

    import cv2
    
    org = cv2.imread('../data/mandrill.png', 1)
    
    averaged33 = cv2.GaussianBlur(org, (3,3), 1)
    averaged99 = cv2.GaussianBlur(org, (9,9), 1)
    
    cv2.imshow('original', org)
    cv2.imshow('Gaussian 33', averaged33)
    cv2.imshow('Gaussian 99', averaged99)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

     

    중앙값 흐림(median blur)

    medan blur는 이미지에서 주위에 비해 튀는 작은 점들을 지우는 데에 효과적이다.

    첫 번째 인자는 원본 이미지, 두번 번째 인자는 필터의 크기이다.

    import cv2
    
    original_image = cv2.imread('../data/salt_pepper.png', 0)
    result_image = cv2.medianBlur(original_image,  5)
    
    cv2.imshow('original_image', original_image)
    cv2.imshow('result', result_image)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    양방향 필터(bilateral filter)

    커널의 중심에서 얼마나 떨어져 있는가, 픽셀의 색상값이 얼마나 차이가 나는가 두가지를 고려한 것이 양방향 필터(bilateral filter)이다.

    원본  이미지, 필터의 크기 가 첫 번째, 두 번째 인자로 전달되고

    커널 함수의 퍼진 정도가 세 번째, 네 번째 인자로 전달된다.

     

    3개의 필터를 비교해 보자.

    import numpy as np
    import cv2
    
    original_image = cv2.imread('../data/mandrill.png', 1)
    
    result_image1 = cv2.GaussianBlur(original_image,  (9,9), 1)
    result_image2 = cv2.medianBlur(original_image,  9)
    result_image3 = cv2.bilateralFilter(original_image,  9, 50, 50)
    
    cv2.imshow('original_image', original_image)
    cv2.imshow('GaussianBlur', result_image1)
    cv2.imshow('medianBlur', result_image2)
    cv2.imshow('bilateralFilter', result_image3)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()


    관심 있는 곳만 남겨보자.

    candles.jpg

    특정한 임계값 이상인 값은 유효한 값, 그렇지 않은 값은 유ㅜ효하지 않은 값으로 구분하여 작업이 필요할 때가 있다. 이러한 작업을 이진화(binarization)라고한다.

    threshold() 함수를 이용하여 이진화를 할 수있다.

    ret, result_image = threshold(src_image, thresh_value, maxValue, thresh_option)

    ret , result :  (사용된 임계값, 임계값을 적용하여 얻은 새로운 이미지)

    src_image : 원본 이미지

    thresh_value : 임계값,

    maxValue : 이진화를 하여 픽셀을 최댓값으로 바꿀 때 이 값이 사용됨

    thresh_option :  아래 6가지 옵션을 선택

    • THRESH_BINARY : 조건 만족하는 픽셀 값은 최대, 만족하지 않는 픽셀값은 0
    • THRESH_BINARY_INV : 조건 만족하는 픽셀 값은 0, 만족하지 않는 픽셀값은 최대
    • THRESH_TRUNC : 조건 만족하는 픽셀 값은 최대, 만족하지 않는 픽셀값은 원본
    • THRESH_TOZERO : 조건 만족하는 픽셀 값은 원본, 만족하지 않는 픽셀값은 0
    • THRESH_TOZERO_INV : 조건 만족하는 픽셀 값은 0, 만족하지 않는 픽셀값은 원본

    THRESH_OPTION

     

    import cv2
    
    global color_image
    
    
    # 트랙바가 변경되면 그 값을 임계치로 이진화
    def on_change_threshold(x):
        _, th_image = cv2.threshold(color_image, x, 255, cv2.THRESH_BINARY)
        cv2.imshow('Thresholding', th_image)
    
    
    # 윈도를 생성함
    cv2.namedWindow('Thresholding')
    cv2.createTrackbar('threshold', 'Thresholding', 0, 255, on_change_threshold)
    
    # 촛불 이미지를 읽음
    color_image = cv2.imread('../data/candles.jpg', cv2.IMREAD_COLOR)
    
    # 처음에는 원본 이미지를 그림 (트랙바를 변경하면 임계치에 따라 이진화 결과 출력)
    cv2.imshow('Thresholding', color_image)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()


    윤곽선 뽑아내기

    green_back.jpg

    윤곽선은 주위에 비해 튀는 값을 가진 픽셀이다.

    adaptiveThreshold() 함수를 사용하면 된다.

    cv2.adaptiveThreshold(img, value, method, thresholdType, blocksize, C)

    img : 원본 이미지

    value : 조건을 만족하는 픽셀에 적용할 최대 값

    method : 임계치를 결정하는 방법

    ADAPTIVE_THRESH_MEAN_C : blockSize 영역의 모든 픽셀에 평균 가중치를 적용

    ADAPTIVE_THRESH_GAUSSOAN_C : blockSize 영역의 모든 픽셀에 중심점으로부터의 거리에 대한 가우시안 가중치 적용

     

    green_back.jpg에서 윤곽선을 뽑아내보자.

    import cv2
    
    global color_image
    
    img_gray = cv2.imread('../data/green_back.png', cv2.IMREAD_GRAYSCALE)
    
    
    # 5가 임계치가 되고, 이보다 크면 255 아니면 0
    img_edge = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
                                    cv2.THRESH_BINARY,   blockSize=9, C=0)
    
    cv2.imshow('result', img_edge)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

     


    지금까지 배운 것들로

    책에서 글자 추출하기

    아래 book.png에 있는 글자만들 추출해보자.

    book.png

    먼저 book.png에 잡음을 제거하기 위해 bilateralFilter를 적용한 후,

    adaptiveThreshold() 를 사용해 윤곽선 추출 즉, 글자를 추출한다.

    import cv2
    
    img = cv2.imread('../data/book.png')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    
    # 잡음은 제거하면서 특징은 유지할 수 있는 양방향 필터 적용
    blur_bilateral = cv2.bilateralFilter(img, 11, 75, 75)
    
    # 회색조로 변환
    gray = cv2.cvtColor(blur_bilateral, cv2.COLOR_BGR2GRAY)
    
    
    # 인근 픽셀과 비교한 이진화
    thresh = cv2.adaptiveThreshold(gray, 255,
             cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, 7)
    
    gray = cv2.cvtColor(gray, cv2.COLOR_BGR2RGB)
    thresh = cv2.cvtColor(thresh, cv2.COLOR_BGR2RGB)
    
    
    cv2.imshow('result',thresh)
    
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

     

     

    728x90

    댓글

Designed by Tistory.