목표) 문제집의 특정 페이지를 넣으면 문제만 크롭해서 저장하기
input : 수능완성 페이지 (png)
output)
24~29번까지 문제 별개로 크롭된 이미지
이미지를 크롭하기 위해서는 이미지 단순화, 제거, 보정을 통해 형태를 파악해야하기 때문에
OpenCV에서 제공하는 Morphological Transformations 기능을 이용해야한다
아래글을 먼저 참고해서 숙지해야함
1. 전처리
이미지 흑백화 + 이진화 (이전글 참고
2021/01/11 - [졸업프로젝트/OpenCV] - [OpenCV] 이미지 이진화
def contour():
#원본 이미지
image = cv2.imread('/gdrive/MyDrive/cropStudy/suwan_ga/suwan_ga_030.png')
#흑백 이미지
imgray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #원본 이미지와 흑백이미지가 따로 존재
#이미지 이진화
blur = cv2.GaussianBlur(imgray, ksize=(3,3), sigmaX=0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
cv2_imshow(thresh)
결과)
2. 이미지 외곽 추출
이진화 한 이미지에 대하여 외곽을 추출한다
Canny : edge를 찾아내는 알고리즘
cv2.Canny(image : edge를 찾아낼 이미지, threshold1 : min임계치, threshold2 : max임계치 )
Canny알고리즘은 모든 픽셀들의 edge를 검출한다.
검출하는 기준은 색이 급격하게 변하는 구간을 기준으로 한다.
색이 변하는 정도에 따라서 가지고있는 값이 있는데 해당 값이
min 임계치보다 작으면 edge가 아니고,
max 임계치보다 크면 edge이다.
min임계치와 max임계치 사이에 존재하는 경우 연결성에 초점을 두어 고려한다.
즉, 다음 그림의 경우 min과 max의 사이에 존재하는 것은 C,B이다.
B의 경우 전체 line이 min과 max사이에 존재하므로 edge가 아닌 것으로 판단한다.
하지만, C의 경우 line의 일부가 max임게치를 넘어선 범위에 존재하므로 edge라고 판단한다.
결국 min과 max임계치의 값이 모두 작을 수록 더 많은 edge를 검출해 낸다.
그렇기 때문에 threshold1,2의 값을 실험적으로 이미지에서 원하는 부분의 edge만 검출하도록 적절하게 찾아내야 한다.
min = 10 ~ max = 200
edge = cv2.Canny(imgray, 10, 200)
min값을 10으로 한 경우 원하지 않는 배경 로고 까지 edge로 판단된 것을 확인할 수 있다.
이 경우 min값의 크기를 높인다
min = 100 ~ max = 200
edge = cv2.Canny(imgray, 100, 200)
적절하게 원하는 문제부분들만 edge로 검출되었음을 확인할 수 있다
3.이미지 일반화
edge로 검출된 페이지에서 "번호와 문제와 보기"를 엮어서 하나의 범위로 지정해야한다.
서로 떨어진 edge들을 하나의 덩어리로 만들어야 하므로 Morphological Transformation중
- Closing : 전체적인 윤곽 파악에 적합
- Dilation(확장) : 경계가 부드러워지고, 구멍이 메워지는 효과
두 가지중 하나를 사용하는 것이 적합할 것이다
Closing
# Morph operations
edge = cv2.Canny(imgray, 100, 200)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(40,80))
closed = cv2.morphologyEx(edge, cv2.MORPH_CLOSE, kernel)
확인용으로 윤곽을 그려 확인해보면
# 확인용
contours, hierarchy = cv2.findContours(closed.copy(),cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours_image = cv2.drawContours(image, contours, -1, (0,255,0), 3)
cv2_imshow(contours_image)
findCountours
외곽을 찾는 함수
파라미터로 받는 이미지는 흑과 백으로만 이루어진 이미지(이진화된 이미지)여야 하며 0,1로만 구성되기 때문에
검은색배경에서 흰색 물체를 찾아내는 원리와 유사하다 -> Canny알고리즘으로 추출한 이미지 사용
cv2.findContours
cv2.findContours(image : 대상이되는 이미지url, mode : 관계를 정의하는 방법 , method : contours정보를 반환하는방법)
contours(외곽을 저장한 배열)
hierarchy : contours들 간의 계층구조를 나타내는 배열
mode
둘의 관계를 어떤식으로 정의할 것인지 RETR_TREE : TREE관계로 배열을 리턴RETR_LIST : 계층관계를 고려하지 않음RETR_EXTERNAL : 가장 바깥쪽의 와곽만 추출해냄
method
contours정보를 꼭지점만을 저장할 것인지 모든 좌표를 반환할 것인지에 대한 내용을 결정
결과)
Dilation
# Morph operations
edge = cv2.Canny(imgray, 100, 200)
cv2_imshow(edge)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(40,80))
closed = cv2.morphologyEx(edge, cv2.MORPH_DILATE, kernel)
결과)
결과에서 확인할 수 있듯이 문제의 길이가 충분히 긴것들은 충분한 응집성이 생기지만
문제의 길이가 아주 짧은 것들 끼리는 응집성을 가지기 어렵다. ex) 04번문제
그렇다고 kernel의 크기를 키우면 오른쪽 부분에 있는 문제와 왼쪽 부분에 있는 문제가 하나의 문제로 인식된다
또한 중앙에 있는 선과 위쪽에 있는 선(단원명 부분)으로 인하여
문제끼리 적절한 연관성을 가지기 어렵다
즉, 중앙선과 단원명을 제거한 후에 시도하는 것이 좋아보인다.
하지만 중앙선과 단원명 부분을 검출해내 제거하는 것 보다는 윗단과 중앙선을 애초에 이미지에서 잘라 내는 것이 효율적이라고 생각된다
코드)
#OpenCV
#라이브러리 임포트
import numpy as np
import cv2 #openCV package
from google.colab.patches import cv2_imshow
#구글 드라이브와 연동
from google.colab import drive
drive.mount('/gdrive')
def contour():
#원본 이미지
image = cv2.imread('/gdrive/MyDrive/cropStudy/suwan_ga/suwan_ga_008.png')
#흑백 이미지
imgray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #원본 이미지와 흑백이미지가 따로 존재
#이미지 임계화
blur = cv2.GaussianBlur(imgray, ksize=(3,3), sigmaX=0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# Morph operations
edge = cv2.Canny(imgray, 100, 200)
#RECT Shape, Dilation이용
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(150,80))
closed = cv2.morphologyEx(edge, cv2.MORPH_DILATE, kernel)
#출력
cv2_imshow(closed)
contours, hierarchy = cv2.findContours(closed.copy(),cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#원본 이미지 위에 윤곽 그리기
contours_image = cv2.drawContours(image, contours, -1, (0,255,0), 3)
#출력
cv2_imshow(contours_image)
#함수실행
if __name__ == "__main__":
contour()
#OpenCV - 문제 크롭하기 (미완)
#라이브러리 임포트
import numpy as np
import cv2 #openCV package
from google.colab.patches import cv2_imshow
#구글 드라이브와 연동
from google.colab import drive
drive.mount('/gdrive')
#수능완성 page trim
#페이지에서 왼쪽,오른쪽 반페이지 분리
def imtrim(page):
x=300
w=1120
left = page[465:, x:x+w]
x=1480
right = page[465:,x:x+w]
return right,left
#반페이지를 입력받고 크롭하기
def contour(page_rl):
#이미지 흑백화
imgray = cv2.cvtColor(page_rl, cv2.COLOR_BGR2GRAY)
#이미지 이진화 (스캔본 처럼)
blur = cv2.GaussianBlur(imgray, ksize=(3,3), sigmaX=0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# Morph operations
edge = cv2.Canny(imgray, 100, 200)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(1000,100))
closed = cv2.morphologyEx(edge, cv2.MORPH_CLOSE, kernel)
contours, hierarchy = cv2.findContours(closed.copy(),cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours_xy = np.array(contours)
contours_xy.shape
#한페이지 내의 모든 폐곡선 범위에 대해 실행
for c in contours:
#폐곡선 바운더리
x,y,w,h = cv2.boundingRect(c)
#바운더리를 이용해서 문제 크롭 후 보여주기
img_trim = page_rl[y:y+h , x:x+w]
cv2_imshow(img_trim)
#페이지 url을 입력하면
def problem_crop(image_url):
#이미지url을 open CV에 읽어들이기
imgfile = image_url
image = cv2.imread(imgfile)
#한 페이지를 반페이지로 나누기
right,left = imtrim(image)
#각 반페이지에 대해 크롭 진행
contour(right)
contour(left)
#함수실행
if __name__ == "__main__":
problem_crop('/gdrive/MyDrive/cropStudy/suwan_ga/suwan_ga_030.png')
참고 사이트
leembedded.tistory.com/16?category=698391
'졸업프로젝트 > OpenCV' 카테고리의 다른 글
[OpenCV] 이미지에서 특정 이미지 찾아내기 (2) | 2021.01.18 |
---|---|
[OpenCV]문제지에서 문제 크롭(2) (0) | 2021.01.12 |
[OpenCV]Morphological Transformation (0) | 2021.01.12 |
[OpenCV] 이미지 이진화 (0) | 2021.01.11 |
[OpenCV]이미지 읽기,출력,저장 (2) | 2021.01.11 |