Python과 OpenCV – 20 : 히스토그램(Histogram) 1/4

이 글의 원문은 https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_histograms/py_histogram_begins/py_histogram_begins.html#histograms-getting-started 입니다.

아래 그림을 보면..

이미지에 대해 어떤 그래프를 표시하고 있는데, 화소의 강도(Gray 값 또는 하나의 채널에 대한 값)를 갖는 화소의 개수를 각각 X축과 Y축으로 표시하고 있습니다. 대부분의 경우 화소의 강도는 0-255입니다.

히스토그램을 얻는 방법은 Numpy를 이용하는 것과 OpenCV를 이용하는 것이 있습니다. 먼저 OpenCV를 이용한 예제를 보면..

import cv2

img = cv2.imread('./data/home.jpg',0)
hist = cv2.calcHist([img],[0],None,[256],[0,256])

4번째 cv2.calcHist 함수가 이미지에 대한 히스토그램 정보를 얻는데, 이 함수의 첫번째는 입력 이미지의 배열이며 두번째는 히스토그램을 얻을 채널 인덱스, 세번째는 Mask 이미지, 네번째는 X 축 요소(BIN)의 개수이고 다섯번째는 Y 축 요소값의 범위로 하나의 채널에 대한 화소 강도가 0~255이므로 대부분의 경우 [0,256]이 됩니다. 이 함수의 반환값은 256개의 요소를 갖는 배열입니다.

Numpy를 이용하여 히스토그램을 얻는 코드는 다음과 같습니다.

import cv2
import numpy as np

img = cv2.imread('./data/home.jpg',0)
hist,bins = np.histogram(img.ravel(),256,[0,256])

5번째 코드의 np.histogram 함수가 히스토그램을 얻는 함수인데 반환값은 반환값은 256개의 요소를 갖는 배열인 hist와 X축 요소의 값을 나타내는 배열인 bins입니다. 이 함수 이외에도 hist = np.bincount(img.ravel(),minlength=256) 와 같은 더 빠른 함수가 가능합니다.

속도면에서 Numpy보다 OpenCV 방식이 훨씬 빠르므로 OpenCV를 사용하는 것이 좋습니다.

히스토그램 값을 그래프로 표시하기 위한 예제는 다음과 같습니다.

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('./data/home.jpg',0)
plt.hist(img.ravel(), 256, [0,256]); 
plt.show()

결과는 다음과 같습니다.

BGR 형태와 같이 3개의 채널로 구성된 이미지에 대한 각 채널의 히스토그램도 시각화가 가능한데, 관련된 예제는 다음과 같습니다.

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('./data/home.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
    histr = cv2.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color = col)
    plt.xlim([0,256])
plt.show()

결과는 다음처럼 3개 채널 각각의 히스토그램 결과가 표시됩니다.

지금까지는 히스토그램 분석을 이미지 전체에 대해서 수행했는데, 필요할 경우 이미지의 원하는 영역에 대한 마스크를 지정해 해당 영역에 대한 히스토그램만을 분석할 수 있습니다. 아래의 코드가 예입니다.

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('./data/home.jpg',0)

# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img,img,mask = mask)

# Calculate histogram with mask and without mask
# Check third argument for mask
hist_full = cv2.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256])

plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask,'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])

plt.show()

결과는 다음과 같습니다.

Python과 OpenCV – 19 : 이미지의 등치선(Contours) – 5/5

이 글은 https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contours_hierarchy/py_contours_hierarchy.html#contours-hierarchy 를 참조로 하였습니다.

먼저 다음과 같은 코드가 있습니다.

import numpy as np
import cv2

img = cv2.imread('./data/opencv_contour_h.png')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255,0)

_, contours,hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

print(hierarchy)
cv2.imshow('img', img)

for i in range(len(contours)):
    cv2.waitKey()
    img = cv2.drawContours(img, contours, i, (0,0,255), 2)
    cv2.imshow('img', img)

print('END')
cv2.waitKey()
cv2.destroyAllWindows()

8번 코드의 cv2.findContours 함수의 2번째 인자에 따라 그 결과가 달라지는데, 특히 반환값 중 3번째인 hierarchy 값이 크게 달라집니다. hierarchy 값은 추출된 등치선 간의 계층 정보를 나타냅니다. cv2.findContours 함수의 2번째 인자에 따라 어떻게 변경되는지 요약 그림을 언급하는 선에서 정리합니다.

먼저 cv2.RETR_LIST 일 경우, 추출된 등치선의 인덱스 번호에 대한 그림입니다.

그리고 반환된 계층 정보는 다음과 같습니다.

[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [ 3  1 -1 -1]
  [ 4  2 -1 -1]
  [ 5  3 -1 -1]
  [ 6  4 -1 -1]
  [ 7  5 -1 -1]
  [ 8  6 -1 -1]
  [-1  7 -1 -1]]]

총 9개의 등치선이 추출되었으므로 위와 같이 총 9개의 계층 정보가 반환되는데 위의 각 9개 요소의 순서는 앞선 그림에서 표시된 등치선의 인덱스 순서와 동일합니다. 0부터 시작하고요. 그리고 각 요소는 다시 4개로 구성되는데.. [Next, Previous, First_Child, Parent]와 같습니다. 즉, [다음 등치선의 인덱스, 이전 등치선의 인덱스, 첫번째 자식 등치선의 인덱스, 부모 등치선의 인덱스] 입니다. 위의 계층 정보에 대한 내용은 다음 그림으로 표시될 수 있습니다.

다음은 cv2.RETR_EXTERNAL 인자에 대한 등치선의 인덱스 번호에 대한 그림입니다.

계층 정보는 다음과 같습니다.

[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [-1  1 -1 -1]]]

위 계층정보의 의미는 다음과 같습니다.

다음은 cv2.RETR_CCOMP 인자에 대한 등치선의 인덱스 번호에 대한 그림입니다.

계층 정보는 다음과 같습니다.

[[[ 3 -1  1 -1]
  [ 2 -1 -1  0]
  [-1  1 -1  0]
  [ 5  0  4 -1]
  [-1 -1 -1  3]
  [ 7  3  6 -1]
  [-1 -1 -1  5]
  [ 8  5 -1 -1]
  [-1  7 -1 -1]]]

위 계층정보의 의미는 다음과 같고요.

마지막으로 cv2.RETR_TREE 인자에 대한 등치선의 인덱스 번호에 대한 그림입니다.

계층 정보는 다음과 같습니다.

[[[ 7 -1  1 -1]
  [-1 -1  2  0]
  [-1 -1  3  1]
  [-1 -1  4  2]
  [-1 -1  5  3]
  [ 6 -1 -1  4]
  [-1  5 -1  4]
  [ 8  0 -1 -1]
  [-1  7 -1 -1]]]

위의 계층정보의 의미를 도식화하면 다음과 같습니다.