printf("ho_tari\n");

ep.36 OpenCV2 본문

두산 로보틱스 부트캠프 ROKEY/Computer Vision 교육

ep.36 OpenCV2

호타리 2024. 8. 27. 16:15

2024.8.27

 

4.3 스레시홀딩

스레시홀딩은 바이너리 이미지를 만드는 가장 대표적인 방법입니다.

바이너리 이미지(binary image)란 검은색과 흰색만으로 표현한 이미지를 의미합니다.

스레시홀딩이란 여러 값을 어떤 임계점을 기준으로 두 가지 부류로 나누는 방법을 의미합니다.

4.3.1 전역 스레시홀딩


 

어떤 임계값을 정한 뒤 픽셀 값이 임계값을 넘으면 255, 임계값을 넘지 않으면 0으로 지정하는 방식을 전역 스레시홀딩이라고 합니다.

이런 작업은 간단하게 numpy로 연산할 수 있지만, OpenCV에서 cv2.threshold() 함수로 구현할 수도 있습니다.

아래는 전역 스레시홀딩 작업을 numpy 연산과 cv2.threshold() 함수를 통해 수행하는 과정을 보여줍니다.

 

우선, 검은색에서 흰색으로 점점 변하는 그라데이션 이미지를 회색조로 읽습니다. 첫 번째로, numpy 연산을 통해 픽셀 값이 127보다 크면 255, 픽셀 값이 127보다 작거나 같으면 0으로 바꾸는 작업을 수행했습니다.

Numpy API가 그 결과입니다.

`

이 작업은 간단하게 cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) 함수를 호출하여 수행할 수도 있습니다.

cv2.threshold() 함수 사용법은 다음과 같습니다.

ret, out = cv2.threshold(img, threshold, value, type_flag)

  • img: 변환할 이미지
  • threshold : 스레시홀딩 임계값
  • value : 임계값 기준에 만족하는 픽셀에 적용할 값
  • type_flag : 스레시홀딩 적용 방법
  • type_flag 값은 다음과 같습니다.
    • cv2.THRESH_BINARY: 픽셀 값이 임계값을 넘으면 value로 지정하고, 넘지 못하면 0으로 지정
    • cv2.THRESH_BINARY_INV: cv.THRESH_BINARY의 반대
    • cv2.THRESH_TRUNC: 픽셀 값이 임계값을 넘으면 value로 지정하고, 넘지 못하면 원래 값 유지
    • cv2.THRESH_TOZERO: 픽셀 값이 임계값을 넘으면 원래 값 유지, 넘지 못하면 0으로 지정
    • cv2.THRESH_TOZERO_INV: cv2.THRESH_TOZERO의 반대

이 함수는 두 개의 결과를 반환하는데 첫 번째 결과인 ret은 스레시홀딩에 사용한 임계값이고, 두번째 결과인 out은 스레시홀딩이 적용된 바이너리 이미지입니다.

대부분 첫번째 결과인 ret은 threshold 파라미터로 전달한 값과 동일합니다.

아래 코드는 여러 type_flag를 활용한 예제입니다. 참고로 0은 검은색, 255는 흰색임에 유의하시기 바랍니다.

 

4.3.2 오츠의 알고리즘


 

바이너리 이미지를 만들 때 가장 중요한 점은 임계값을 얼마로 정하냐 하는 것입니다. 1979년 오츠 노부유키는 반복적인 시도 없이 한 번에 임계값을 찾을 수 있는 방법을 찾아냈습니다. 이것이 바로 오츠의 이진화 알고리즘(Otsu's binarization method)입니다. 오츠의 알고리즘은 임계값을 임의로 정해 픽셀을 두 부류로 나누고 두 부류의 명암 분포를 구하는 작업을 반복합니다. 모든 경우의 수 중에서 두 부류의 명암 분포가 가장 균일할 때의 임계값을 선택합니다. 아래 예제에서는 임계값이 120~140 사이일 때 이미지가 가장 선명하다는 것을 알 수 있습니다.

 

OpenCV 함수를 활용하면 오츠의 알고리즘을 적용할 수 있습니다. cv2.threshold() 함수의 마지막 파라미터로 cv2.THRESH_OTSU를 전달하기만 하면 됩니다. 오츠의 알고리즘은 최적의 임계값을 찾아주므로 cv2.threshold() 함수에 전달하는 threshold 파라미터는 아무 값이어도 상관없습니다. 어차피 무시되기 때문입니다.

 

원본 이미지의 글씨는 선명하지 않습니다. 하지만 바이너리 이미지로 변환하니 글씨가 좀 더 선명해졌습니다. 맨 왼쪽은 원본 이미지, 두 번째 이미지는 임계값을 130으로 지정해준 바이너리 이미지, 세 번째 이미지는 오츠의 알고리즘을 적용한 바이너리 이미지입니다. 오츠의 알고리즘에 따르면 최적의 임계값은 131 임을 알 수 있습니다. (세 번째 이미지 상단에 otsu: 131이라고 표시되어 있음) 아래 코드는 오츠의 알고리즘을 적용하는 코드입니다.

 

두 번째 파라미터인 -1은 threshold를 전달하는 값입니다. 이미 설명드린 대로 오츠의 알고리즘에서 이 값은 무시되므로 아무 값이나 넣어도 상관없습니다.

오츠의 알고리즘이 최적의 임계값을 자동으로 찾아준다는 장점이 있지만, 모든 경우의 수에 대해 조사해야 하므로 속도가 빠르지 않다는 단점도 있습니다.

 

4.3.3 적응형 스레시홀드


위에서 설명한 전역 스레시홀딩이 매번 좋은 성능을 내는 것은 아닙니다. 원본 이미지에서 조명이 일정하지 않거나 배경색이 여러 개인 경우에는 하나의 임계값으로 선명한 바이너리 이미지를 만들어내기 힘들 수도 있습니다. 이때는 이미지를 여러 영역으로 나눈 뒤, 그 주변 픽셀 값만 활용하여 임계값을 구해야 하는데, 이를 적응형 스레시홀딩(Adaptive Thresholding)이라고 합니다. 적응형 스레시홀딩은 다음 함수로 제공합니다.

 

 

cv2.adaptiveThreshold(img, value, method, type_flag, block_size, C)

  • img: 입력영상
  • value: 임계값을 만족하는 픽셀에 적용할 값
  • method: 임계값 결정 방법
    • cv2.ADAPTIVE_THRESH_MEAN_C: 이웃 픽셀의 평균으로 결정
    • cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 가우시안 분포에 따른 가중치의 합으로 결정
  • type_flag: 스레시홀딩 적용 방법 (cv2.threshod()와 동일)
  • block_size: 영역으로 나눌 이웃의 크기(n x n), 홀수
  • C: 계산된 임계값 결과에서 가감할 상수(음수 가능)

4.4 이미지 연산

4.4.1 영상과 영상의 연산


 

 

이미지 연산을 위해서 numpy 연산을 활용하는 방법이 있습니다. 하지만 OpenCV에서도 이미지 연산을 위한 함수를 제공합니다. OpenCV에서 굳이 연산을 위한 함수를 제공하는 이유는 값의 범위 때문입니다. 한 픽셀이 가질 수 있는 값의 범위는 0 ~ 255인데, 연산의 결과가 255보다 크거나 0보다 작을 수 있어서 결과 값을 0 ~ 255로 제한할 필요가 있습니다.

OpenCV에서 제공하는 사칙연산 함수는 다음과 같습니다.

cv2.add(src1, src2, dest, mask, dtype) : src1과 src2 더하기

  • src1: 첫 번째 입력 이미지
  • src2: 두 번째 입력 이미지
  • dest(optional): 출력 영상
  • mask(optional): mask 값이 0이 아닌 픽셀만 연산
  • dtype(optional): 출력 데이터 타입(dtype)

cv2.subtract(src1, src2, dest, mask, dtype) : src1에서 src2 빼기

  • 모든 파라미터는 cv2.add()와 동일

cv2.multiply(src1, src2, dest, scale, dtype) : src1과 src2 곱하기

  • scale(optional): 연산 결과에 추가 연산할 값

cv2.divide(src1, src2, dest, scale, dtype): src1을 src2로 나누기

  • 모든 파라미터는 cv2.multiply()와 동일

img1+img2 : 화소가 고르지 못하고 255를 초과한 영역에 이상한 색이 보임

cv.add : 전체적으로 하얀 픽셀이 많이 보임


각 픽셀의 합이 255가 되지 않게 가각의 영상에 가중치를 줘서 계산

  • alpha값을 각 영상에 가중치로 적용해 적절한 비중으로 배분
 

이미지 블렌딩(Image Blending)

cv2.addWeighted(img1, alpha, img2, gamma)

  • 인자
    • img1, img2 : 합성할 두 영상
    • alpha : img1에 지정할 가중치
    • beta : img2에 지정할 가중치, 주로 (1-alpha)
    • gamma : 연산 결과에 가감할 상수, 주로 0
  • 두 이미지를 blending 할 수 있음
  • blending 하려는 두 이미지의 사이즈가 같아야함
  • [Simple Formula]
    • f0(x) : 첫번째 이미지 픽셀 값
    • f1(x) : 두번째 이미지 픽셀 값
    • β=1α
    • α,β 의 값을 통해 어떤 이미지를 더 강하게 드러내고, 어떤 이미지를 더 약하게 드러낼지 결정
    • γ 추가 가능 (optional)
  • g(x)=(1α)f0(x)+αf1(x)
 

 

알파블렌딩은 흔히 fade-in/fade-out 기법으로 영상이 전환될 때 사용

영화 변신 장면에서 얼굴 모핑 기법 사용됨

 

4.4.3 비트와이즈 연산


 

각 픽셀 단위 연산

두 영상을 합성할 때 특정 영역만 선택하거나 특정 영역만 제외하는 경우에 사용됨

 

  • cv2.bitwise_and(img1, img2, mask=None) : 각 픽셀에 대해 AND 연산
  • cv2.bitwise_or(img1, img2, mask=None) : 각 픽셀에 대해 OR 연산
  • cv2.bitwise_xor(img1, img2, mask=None) : 각 픽셀에 대해 XOR 연산
  • cv2.bitwise_not(img1, img2, mask=None) : 각 픽셀에 대해 NOT 연산
    • img1, img2 : 동일한 shape의 두 영상
    • mask : 0이 아닌 픽셀만 연산, 바이너리 이미지

 

마스킹하기 위한 코드에서 원본 영상과 똑같은 3채널 배열을 만들지만

mask 인자를 사용하면 2차원 배열 만으로도 가능함

  • ② 3차원 -> 2차원
mask = np.zeros_like(img)
cv2.circle(mask, (150,140), 100, (255,255,255), -1)

mask = np.zeros(img.shape[:2], dtype=np.uint8)
cv2.circle(mask, (150,140), 100, (255), -1)
  • ③ 마스킹
masked = cv2.bitwise_and(img, mask)
masked = cv2.bitwise_and(img, img, mask=mask)

4.4.4 차영상


 

차영상(image differencing)을 하는 이유

  • 영상에서 영상을 빼기 연산하면 두 영상의 변화를 알 수 있음
  • 빼기 연산을 하게 되면 음수가 나올 수 있기 때문에 절대값을 구해야 함

픽셀 값의 차를 구하는 함수

diff = cv2.absdiff(img1, img2)

  • img1, img2: 입력 이미지
  • diff: 두 이미지의 차의 절대 값

 

4.4.5 이미지 합성과 마스킹


 

여러개의 영상에서 특정 영역끼리 합성하기 위해, 우선 영상에서 원하는 영역을 떼어낼 때 마스크를 사용함

 

 

크로마키 원리: 색상을 이용한 마스크를 이용

  • 크로마키 : 합성하기 위한 초록색 또는 파란색 배경
  • 크로마킹 : 일기예보나 영화 촬영할 때 초록색 또는 파란색 배경을 두고 찍어서 나중에 원하는 배경과 합성하는 것

 

알파블렌딩과 마스킹을 통한 영상합성

  • cv2.inRange() 함수를 사용하여 크로마키가 있는 영역에서 색생 값 중에 가장 큰 값과 작은 값을 범위 지정하여 배경만 제거
  • 블렌딩을 위한 적절한 알파 값 선택과 마스킹을 위한 모양의 좌표, 색상 값 선택에는 많은 노력과 시간이 필요함

두 영상의 특징을 합성하는 함수

 

 

dst = cv2.seamlessClone(src, dst, mask, coords, flags, output)

  • src : 입력 이미지, 일반적으로 전경
  • dst : 대상 이미지, 일반적으로 배경
  • mask : 마스크, src에서 합성하고자 하는 영역은 255, 나머지는 0
  • coords : src가 놓이기 원하는 dst의 좌표 (중앙)
  • flags : 합성 방식
    • cv2.NORMAL_CLONE : 입력 원본 유지
    • cv2.MIXED_CLONE : 입력과 대상의 혼합
  • output(optional) : 합성 결과
  • dst : 합성 결과

4.5 히스토그램

4.5.1 히스토그램 계산과 표시

 


히스토그램 :

  • 전체 영상에서 픽셀값이 1인 것이 몇개인지, 2인 픽셀이 몇개인지... 255인 픽셀이 몇개인지 세는 것을 말한다.
  • 세는 이유 : 전체 영상에서 픽셀들의 생상이나 명압의 분포를 파악하기 위함
 

 

cv2.calHist(img, channel, mask, histSize, ranges)

  • img: 이미지 영상, [img]처럼 리스트로 감싸서 전달
  • channel: 분석 처리할 채널, 리스트로 감싸서 전달 - 1 채널: [0], 2 채널: [0, 1], 3 채널: [0, 1, 2]
  • mask: 마스크에 지정한 픽셀만 히스토그램 계산, None이면 전체 영역
  • histSize: 계급(Bin)의 개수, 채널 개수에 맞게 리스트로 표현 - 1 채널: [256], 2 - 채널: [256, 256], 3 채널: [256, 256, 256]
  • ranges: 각 픽셀이 가질 수 있는 값의 범위, RGB인 경우 [0, 256]

회색조로 이미지를 읽어 1차원 히스토그램으로 표현해봤습니다. range가 [0, 256]인데 마지막 값은 범위에 포함되지 않으므로 실제 범위는 0부터 255입니다. cv2.calcHist()로 반환한 hist 객체는 plt.plot(hist)을 통해 그래프로 그려줄 수 있습니다.

 

cv2.split(img) 함수를 호출하면 R, G, B 채널이 각각 나뉩니다. 즉, img는 색상 이미지이므로 3 채널 이미지인데, cv2.split(img)을 해주면 빨강, 파랑, 초록의 각 1 채널 이미지로 나뉩니다. 히스토그램을 보면 파란색 분포가 큰 것을 알 수 있습니다. 이미지에서 파란 하늘의 영역이 많기 때문입니다.

 

4.5.2 노멀라이즈


픽셀 값들이 0~255에 골고루 분포하지 않고 특정 영역에 몰려 있는 경우 화질을 개선하고 영상 간의 어려움이 있기 때문에 서로 다른 조건을 같게 만들기 위한 노멀라이즈(정규화) 기능 제공

 

 

dst = cv2.normalize(src, dst, alpha, beta, type_flag)

  • src: 정규화 이전의 데이터
  • dst: 정규화 이후의 데이터
  • alpha: 정규화 구간 1
  • beta: 정규화 구간 2, 구간 정규화가 아닌 경우 사용 안 함
  • type_flag: 정규화 알고리즘 선택 플래그 상수
    • cv2.NORM_MINMAX : alpha와 beta 구간으로 정규화
    • cv2.NORM_L1 : 전체 합으로 나누기, alpha = 노멀라이즈 전체 합
    • cv2.NORM_L2 : 단위 벡터로 정규화
    • cv2.NORM_INF : 최댓값으로 나누기

4.5.3 이퀄라이즈


 

 

dst = cv2.equalizeHist(src, dst)

  • src: 대상 이미지, 8비트 1 채널
  • dst(optional): 결과 이미지

4.5.4 CLAHE


 

CLAHE는 Image Classification, Object Detection, Sementic Segmentation에서 Data augumention에 사용됨

 

4.5.5 2D 히스토그램


 

각 픽셀이 몇 개씩인지 세어서 그래프로 표현해 주는 1차 히스토그램과 비슷하게, 2차 히스토그램은 같은 축이 2개이고 가각의 푹이 만나는 지점의 개수를 표현함

 

4.5.6 역투영


 

2차원 히스토그램과 HSV 컬러 스페이스를 이용하면 색상으로 특정 물체난 사물의 일부분을 배경에서 분리할 수 있다.

이 방법의 원리는 물체가 있는 관심영역의 H와 V 값의 분포를 얻어낸 후에 전체 영상에서 해당 분포의 픽셀만 찾아내는 것임

 

4.5.7 히스토그램 비교

 
 

4.6 실전 워크숍


4.6.1 반해골 괴물 얼굴 합성

4.6.2 모션 감지 CCTV

opencv강좌_04.ipynb
12.98MB

5장 기하학적 변환

5.1 이동, 확대/축소, 회전

5.2 뒤틀기

5.2.1 어핀 변환

5.2.2 원근 변환

5.2.3 삼각형 어핀 변환

5.3 렌즈 왜곡

5.3.1 리매핑

5.3.2 오목 렌즈와 볼록 렌즈 왜곡

5.3.3 방사 왜곡

opencv강좌_05.ipynb
3.39MB
DR-01408_박성호_딥러닝개론_10차시.ipynb
0.51MB

'두산 로보틱스 부트캠프 ROKEY > Computer Vision 교육' 카테고리의 다른 글

ep.38 OpenCV4  (0) 2024.08.29
ep.37 OpenCV3  (0) 2024.08.28
ep.35 OpenCV1  (0) 2024.08.26
ep.34 딥러닝개론8  (0) 2024.08.23
ep.33 딥러닝개론7  (0) 2024.08.22