opencv로 여백을 삭제하는 두 가지 예제 코드를 살펴 보는 포스팅을 하고자 한다. 참고로 여백 삭제를 위해서는 openCV이외에 Pillow 라이브러리를 사용할 수 있지만, 두 개의 포스팅 모두 openCV를 사용할 것이다.
첫 번째 예제 코드의 포스팅에서 사용할 이미지는 다음의 회로도이다.
여백을 삭제해 다음과 같이 이미지를 회로도만 보이게 할 것이다.
여백 삭제의 코드 흐름
이미지 여백 삭제를 위해 다음과 같은 흐름으로 코드를 작성할 것이다.
1) 이미지를 읽어 들인다.
2) 색 공간을 이진화한다.
3) 윤곽을 추출한다.
4) 윤곽의 안에서 부터 좌표가 최소 그리고 최대가 되는 x, y 각각을 취득하고, 그 값을 이용해 직사각형인 이미지를 추출한다.
1번째에서 3번째까지는 물체 인식이나 문자 인식에 잘 사용되는 방법이라고 생각된다.
여백을 삭제하는 함수
먼저 여백을 삭제하는 함수를 다음과 같이 정의했다. 이것이 이번 코드의 핵심이 되는 부분이다.
# 이미지를 삭제하는 함수
def crop(image): # 인수는 이미지의 상대 경로
# 이미지를 읽어들인다.
img = cv2.imread(image)
# 주위 부분을 강제적으로 트리밍
h, w = img.shape[:2]
h1, h2 = int(h * 0.05), int(h * 0.95)
w1, w2 = int(w * 0.05), int(w * 0.95)
img = img[h1: h2, w1: w2]
# cv2.imshow('img', img)
# Grayscale으로 변환 (흑백 이미지로 변환)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# cv2.imshow('gray', gray)
# 색 공간을 이진화한다.
img2 = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1]
# cv2.imshow('img2', img2)
# 윤곽을 추출한다.
contours = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]
# 윤곽의 좌표를 리스트에 대입한다.
x1 = [] #x좌표의 최소값
y1 = [] #y좌표의 최소값
x2 = [] #x좌표의 최대값
y2 = [] #y좌표의 최대값
for i in range(1, len(contours)):# i = 1 는 이미지 전체의 외곽이되므로 카운트에 포함시키지 않는다.
ret = cv2.boundingRect(contours[i])
x1.append(ret[0])
y1.append(ret[1])
x2.append(ret[0] + ret[2])
y2.append(ret[1] + ret[3])
# 외곽의 첫 번째 외곽을 오려냄
x1_min = min(x1)
y1_min = min(y1)
x2_max = max(x2)
y2_max = max(y2)
cv2.rectangle(img, (x1_min, y1_min), (x2_max, y2_max), (0, 255, 0), 3)
crop_img = img2[y1_min:y2_max, x1_min:x2_max]
# cv2.imshow('crop_img', crop_img)
return img, crop_img
해설
위 코드에 대해 하나 하나 어떤 의미를 갖는지 살펴보도록 하자.
- 이미지를 읽어들인다.
img = cv2.imread(image)
- 이미지를 강제로 잘라낸다.
아래는 원래 필요하지 않지만, 이번의 이미지는 첫 번째 외곽쪽에 좌표와 같은 것이 들어가 있으므로 외곽 추출로 그곳을 추출하지 않도록 하기 위해 강제적으로 그 부분을 삭제한다.
shape[]는 행의 수, 열의 수, 채널 수를 반환하므로, 필요한 처음의 두 가지를 취득한다.
4번째 행에서 슬라이스하여 이미지의 주변을 강제적으로 삭제한다.
h, w = img.shape[:2]
h1, h2 = int(h * 0.05), int(h * 0.95)
w1, w2 = int(w * 0.05), int(w * 0.95)
img = img[h1: h2, w1: w2]
- grayscale화 (흑백 이미지화)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- 색 공간의 이진화
색 공간을 이진화한다. threshold는 임계값의 의미로 threshold()함수는 첫 번째 인수에 흑백 이미지를, 두 번째 이미지를 임계 값을, 세 번째 인수에는 임계값 이상의 값을 가진 값에 할당할 값을 지정하고, 네 번째 인수에는 임계값에 어떠한 처리를 할 것인가를 지정한다.
두 번째의 리턴 값이 임계 값 처리된 이진화 이미지이므로 [1]에서는 2번 째의 리턴값만 취득한다.
img2 = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1]
- 윤곽 추출
윤곽을 추출한다. contour은 "윤곽"이라는 의미이다. findContours() 함수는 첫 번째 인수에 이미지를, 두 번째에 인수에는 추출 모드를, 세 번째 인수에는 근사 방법을 지정한다.
추출 모드는 RETR_LIST, RETR_EXTERNAL, RETR_CCOMP, RETR_TREE 중 선택할 수 있다. 반환 값에 대해서는 각각 주의할 필요가 있다.
findContours()함수는 OpenCV의 버전에 따라 반환 값이 달라진다. OpenCV3까지는 반환값이 3개였지만, OpenCV4에서는 2개로 줄어 들었다.
OpenCV4에서는 첫 번째의 반환값으로 추출된 운곽 리스트가, 두 번째의 반환값에는 계층 구조의 리스트가 저장되어 있다. 따라서 인덱스[0]으로 하면, 윤곽의 리스트를 얻어낼 수 있다.
contours = cv2.findContours(img2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]
- 윤곽의 좌표를 리스트에 대입
윤곽의 좌표를 각각의 리스트에 추가한다. 여기서 i=1는 이미지 전체의 외곽이 되므로 제외한다.
boundingRect는 윤곽의 직사각형의, 왼쪽 위의 x,y 좌표를 첫 번째, 두 번째 반환값으로, 그리고 폭과 높이를 세 번째, 네 번째 인수에 반환하므로, 이것을 ret 리스트에 대입한다. ret은 return의 단축한 단어이다.
물체 인식이므로, 여기서 너무 작은 윤곽이나 너무 큰 윤곽을 제거하는 것도 있다.
x1 = [] #x좌표의 최소값
y1 = [] #y좌표의 최소값
x2 = [] #x좌표의 최대값
y2 = [] #y좌표의 최대값
for i in range(1, len(contours)):
ret = cv2.boundingRect(contours[i])
x1.append(ret[0])
y1.append(ret[1])
x2.append(ret[0] + ret[2])
y2.append(ret[1] + ret[3])
- 제일 바깥쪽의 직사각형으로 잘라낸다. 아까 전, 작성하고 싶은 리스트로부터 각각의 최소값, 최대값을 얻어낸다. 이러한 값을 두 점으로 하는 직사각형으로 이미지를 잘라내면, 여백을 삭제한 전체 부분을 얻어 낼 수 있다.
rectangle() 함수는 첫 번째 인수에 이미지를, 두 번째 그리고 세 번째 인수에는 얻어낸 직사각형의 왼쪽 위와 오른 쪽 아래의 좌표를 지정하고, 네 번째 인수에는 직사각형의 선의 색을 RGB의 8비트로 지정하고, 마지막으로 다섯 번째 인수에는 선의 굵기를 지정한다.
x1_min = min(x1)
y1_min = min(y1)
x2_max = max(x2)
y2_max = max(y2)
cv2.rectangle(img, (x1_min, y1_min), (x2_max, y2_max), (0, 255, 0), 3)
crop_img = img2[y1_min:y2_max, x1_min:x2_max]
참고자료
'IT > AI\ML' 카테고리의 다른 글
머신러닝에서의 IoU(Intersection over Union)란? (0) | 2021.07.19 |
---|---|
[python/Tensorflow2.x] TFRecord의 사용법 (0) | 2021.07.08 |
카테고리 변수를 다루는 네 가지 방법 (0) | 2021.06.03 |
설명 변수와 목적 변수 (0) | 2021.05.18 |
ipywidgets 사용법 (2) (2) | 2021.04.07 |