Image Processing/Cv2

(opencv) 두 이미지의 차이를 박스로 나타내기

jinmc 2021. 3. 10. 16:01
반응형

저번 포스트에서 walkaroundthedevelop.tistory.com/56 

어떻게 하면 두 이미지의 차이를 알 수 있는지에 대해서 알아보았습니다.

그럼 두 개의 이미지를 이용해서 차이를 표시해낼 수 있을까요?

일단 두 개의 이미지를 사용해서 얻어낸 diff를 디스플레이 해 봅시다.

 

두 개의 이미지에다가 diff를 구하면, 이렇게 됩니다.

 

이 diff를 보면, 포스트잇을 붙인 곳이 검은색으로 된 것을 볼 수 있습니다.

 

# 이전 포스트에서 diff 가져옴!

thresh = cv2.threshold(diff, 100, 255, cv2.THRESH_BINARY_INV)[1]

# get contours
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

for c in cnts:
    area = cv2.contourArea(c)
    if area > sens_int:
        x, y, w, h = cv2.boundingRect(c)
        cv2.rectangle(imageA, (x, y), (x + w, y + h), (51, 255, 102), 2)
        cv2.rectangle(imageB, (x, y), (x + w, y + h), (51, 255, 102), 2)

#imageA = cv2.resize(imageA, (550, 350))
#imageB = cv2.resize(imageB, (550, 350))
#cv2.imshow('Original', imageA)
#cv2.imshow('Modified', imageB)
#cv2.imshow('CroppedA', imageA)
#cv2.imshow('CroppedB', imageB)
#cv2.waitKey(0)
#cv2.destroyAllWindows()

threshold 에는 여러가지 활용법이 있는데,

크게는 simple threshold와 adaptive threshold로 나눌 수 있습니다.

 

threshold에 대해서는 python-opencv documentation 홈페이지에 잘 나와있습니다.

opencv-python.readthedocs.io/en/latest/doc/09.imageThresholding/imageThresholding.html

diyver.tistory.com/63 

등등 레퍼런스를 참고해도 좋을 것 같습니다.

 

simple threshold는 전체 이미지를 대상으로 하였고, 

adaptive threshold는 각각의 지역에 대해서 주변의 픽셀과 비교해서 threshold 값을 구하는 것을 말합니다.

 

simple threshold 관련 function의 documentation:

docs.opencv.org/master/d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57

Simple threshold를 사용할 때의 함수는

threshold(src, dst, threshold_value, Max_value, threshold_type)

threshold(src, dst, threshold_value, Max_value, threshold_type)

이렇게 구성되어 있는데, dst는 optional이고, 

 - threshold_value : 사용하는 임계값

- Max_value : THRESH_BINARY and THRESH_BINARY_INV를 사용할 때 사용하는 값. 임계 값 이상의 픽셀들에  적용할 값,

     0 또는 Max_value 의 두 값으로만 이미지를 표현하게 됨, 보통 255 를 사용합니다.

- threshold_type : 5가지의 int type으로 이루어져 있으며, 어떻게 binary를 처리하는지에 대한 방법에 대한 내용입니다.

현재는 저 검은색인 부분을 하얀색으로 만들고 나머지를 검은색으로 만들기 위해서, THRESH_BINARY_INV를 사용했고, 

다른 threshold type에 대해서는 깊게 다루지 않겠습니다.

  • cv2.THRESH_BINARY : 픽셀값 > min_value  max_value, 작으면 0
  • cv2.THRESH_BIANRY_INV : 픽셀값 > min_value  0, 작으면 max_value 
  • cv2.THRESH_TRUNC : 픽셀값 > min_value min_value , 작으면 픽셀값
  • cv2.THRESH_TOZERO : 픽셀값 > max_value 픽셀값 , 작으면 0
  • cv2.THRESH_TOZERO_INV : 픽셀값 > max_value 0 , 작으면 픽셀 

이렇게 있다고 알고 넘어가면 좋을 것 같습니다.

참고 : laheepapa.tistory.com/52

 

그럼 이렇게 나오게 됩니다.

여기서 저 흰색의 박스를 잡기위해서 쓰는 함수가 cv2.findContours 입니다.

cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

Contour는 한국어로 윤곽이란 뜻으로, 특히 opencv에서 윤곽이란 검은색 바탕에서 하얀색 물체를 찾는 걸 의미한다고 합니다.

참조 : blog.naver.com/samsjang/220516697251 

그러기 때문에 우리가 threshold함수를 사용해서 binary 이미지를 만든 게 딱 맞는다고 할 수 있겠죠.

findContours는 원본 이미지를 변형하기 때문에 copy를 사용하는 게 좋습니다.

findContours의 documentation을 보면, 다음과 같이 되어 있습니다.

contours, hierarchy = cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]])

- image : binary image

- mode :

  • cv2.RETR_EXTERNAL : contours line중 가장 바같쪽 Line만 찾음.
  • cv2.RETR_LIST : 모든 contours line을 찾지만, hierachy 관계를 구성하지 않음.
  • cv2.RETR_CCOMP : 모든 contours line을 찾으며, hieracy관계는 2-level로 구성함.
  • cv2.RETR_TREE : 모든 contours line을 찾으며, 모든 hieracy관계를 구성함.

- method : contours를 찾을 때 사용하는 근사치 방법

  • cv2.CHAIN_APPROX_NONE : 모든 contours point를 저장.
  • cv2.CHAIN_APPROX_SIMPLE : contours line을 그릴 수 있는 point 만 저장. (ex; 사각형이면 4개 point)
  • cv2.CHAIN_APPROX_TC89_L1 : contours point를 찾는 algorithm
  • cv2.CHAIN_APPROX_TC89_KCOS : contours point를 찾는 algorithm

Returns: image, contours, hierarchy

참조 : opencv-python.readthedocs.io/en/latest/doc/15.imageContours/imageContours.html#contours 

 

사실 이 findContours 함수는 opencv 버전 2, 4에서와 3에서의 활용이 다릅니다.

# check to see if we are using OpenCV 2.X or OpenCV 4
if imutils.is_cv2() or imutils.is_cv4():
	(cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
		cv2.CHAIN_APPROX_SIMPLE)
# check to see if we are using OpenCV 3
elif imutils.is_cv3():
	(_, cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
		cv2.CHAIN_APPROX_SIMPLE)

확인해 주도록 합니다.

또 하나 주의할 점은, findContours는 두 개의 값들을 list 형태로 return하기 때문에, (버전 2, 4일 경우) 

contour만 활용할 경우 cnts[1]를 사용해서 가져와야 한다는 점입니다.

 

contourArea 함수는 contour의 영역을 계산합니다.

그래서 count영역값이 sens_int 값보다 작으면 무시하고, sens_int값보다 클 경우만 박스를 쳐 주도록 합니다.

cv2.boundingRect() 는 그 영역에 맞는 박스값을 찾아주고,

cv2.rectangle() 은 실제 박스를 쳐 주는 역할을 합니다.

 

반응형