졸업프로젝트 🎓/OpenCV 🌉

[ OpenCV ] 가로 직선, 세로 직선 지우기 -> 문제 별로 크롭 && 문제 영역 확보해서 자르기

컴공생 C 2021. 2. 10. 16:04
반응형

초기 화면

원본이미지 -> 의도한 크롭 결과

결과화면







과정

1. 이미지 불러오기

2. 문제집 위의 헤더 (문제 영역이 아닌 부분) 자르기

3. 가운데 세로선 기준으로 왼쪽 오른쪽 나누기

4. 문제영역 contour잡기 (왼쪽-> 오른쪽 순서)

5. 각 contour의 위쪽 좌표를 이용해서 현재 contour~ 다음 contour까지 영역 잡아서 자르기 (이걸 반복)

6. 헤더에서 페이지 수 읽어오기  //아직 성공 못했다. 이건 다 멍청한 pytesseract 탓이다

코드

0. 드라이브 마운트

#일단 드라이브 마운트
from google.colab import drive
drive.mount('/content/drive')

1. 2. 이미지 불러오기 && 윗 부분 자르기

#이미지 윗 부분 자르기

def cropTop(image):
    src=cv2.imread(image) #이미지 불러오기
    #cv2_imshow(src)
    dst = src.copy()
    
    #흑백처리
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    
    #canny
    canny = cv2.Canny(gray, 5000, 1500, apertureSize = 5, L2gradient = True)
    
    #min_theta=0 max_theta=np.pi/2
 	#허프만 이용해서 가로 직선 찾기
    #220으로 되어있는 것이 임계값 : 높을수록 선명한? 직선을 검출
    #min_theta :최소각도 
    #max_theta: 최대 각도
    lines = cv2.HoughLines(canny, 0.8, np.pi / 180, 220, srn = 100, stn = 200, min_theta = 89, max_theta = 91)

	#직선이 여러개 나오는데 가장 위쪽에 있는 직선을 찾으려고 miny사용
    miny=700

    for i in lines:
        rho, theta = i[0][0], i[0][1]
        #print(theta)
        a, b = np.cos(theta), np.sin(theta)
        x0, y0 = a*rho, b*rho
        
        scale = src.shape[0] + src.shape[1]

        x1 = int(x0 + scale * -b)
        y1 = int(y0 + scale * a)
        x2 = int(x0 - scale * -b)
        y2 = int(y0 - scale * a)
        #print("y1",y1,"y2",y2)
        
        if (y2 < miny):
            miny=y2
        cv2.line(dst, (x1, y1), (x2, y2), (0, 0, 255), 2)
        #cv2.circle(dst, (x0, y0), 3, (255, 0, 0), 1, cv2.FILLED)
       
       #찾은 가로 직선 기준으로 위쪽은 header, 아래쪽은 body로 넘겨줌
        header = src[:miny, :]
        body=src[miny+10:,:]
        
    #print(miny)
    #cv2_imshow(body)
        
    return header,body
      
     
       

 

3. 가운데 세로선 기준으로 왼쪽 오른쪽 나누기

<모의고사 , 평가원>

이 문제집들은 정가운데에 세로 줄이 있어서 이미지의 가로길이의 절반을 이용했다.

def cropCenter(img):
    src=img
    dst=src.copy
    
    #가로길이
    w=int (img.shape[1]/2) 
    
    #왼쪽
    left=src[:,:w-5]
    #오른쪽
    right=src[:,w+5:]
    #cv2_imshow(left)
    #cv2_imshow(right)
    
    
    contour(left)
    contour(right)

<수능완성>

수능완성은 오른쪽 왼쪽 페이지가 있어서 세로선이 정 가운데에 있지 않다.

def cropCenter(img):
    src=img
    dst = src.copy()
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    canny = cv2.Canny(gray, 5000, 1500, apertureSize = 5, L2gradient = True)
    lines = cv2.HoughLines(canny, 0.8, np.pi / 180, 200, srn = 100, stn = 200, min_theta = 0, max_theta = np.pi/8)
    row=0
    col=0
    for i in lines:
        rho, theta = i[0][0], i[0][1]
        a, b = np.cos(theta), np.sin(theta)
        x0, y0 = a*rho, b*rho
        print("x0",x0,"y0",y0)
        scale = src.shape[0] + src.shape[1]

        x1 = int(x0 + scale * -b)
        y1 = int(y0 + scale * a)
        x2 = int(x0 - scale * -b)
        y2 = int(y0 - scale * a)
        row=x2
        col=y2
        cv2.line(dst, (x1, y1), (x2, y2), (0, 0, 255), 2)
    #cv2.circle(dst, (x0, y0), 3, (255, 0, 0), 1, cv2.FILLED)

    left = src[:, :row-15]
    right= src[:,row+15:]
   
    cv2_imshow(dst)
    #contour(left)
    #contour(right)

위의 cropTop에서 min_theta, max_theta를 수정한것이다.

이 방법보다는 가능하면 정가운데에 직선이 있는 애들이 좋다. 왜냐면 문제속에 박스나 표가 있으면 걔네도 임계값을 넘어서는 세로 직선으로 인식되었다.

임계값을 잘 조정하면 될 것 같은데 220은 작동 잘하고 250이면 아예 아무 직선도 통과를 못해서.. 필요할때 다시 조정해봐야겠다.

4. 문제 영역잡기 && 5. 문제영역 자르기

#반페이지를 입력받고 크롭하기 
def contour(page_rl):
    print("contour")

    #이미지 흑백화 
    imgray = cv2.cvtColor(page_rl, cv2.COLOR_BGR2GRAY) 
    img2=imgray.copy()
    #이미지 이진화 (스캔본 처럼)
    blur = cv2.GaussianBlur(imgray, (3,3), 0)
    thresh = cv2.threshold(blur, 70, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]


    # Morph operations
    edge = cv2.Canny(imgray, 100, 200)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(1000,200))
    closed = cv2.morphologyEx(edge, cv2.MORPH_CLOSE, kernel)

	#문제영역 윤곽 잡기
    #contours가 찾은 경계의 배열
    contours, hierarchy = cv2.findContours(closed.copy(),cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
    contours_xy = np.array(contours , dtype=object)
    contours_xy.shape
    
    #한페이지 내에서 문제 순서대로 불러오기
    contours=reversed(contours)


	#한페이지 내의 모든 폐곡선 범위에 대해 실행 
    top=[] #폐곡선의 맨 위 x값을 담아놓는 배열 
    
    
    for c in contours:
    
		#폐곡선 바운더리 
        x,y,w,h = cv2.boundingRect(c)
        top.append(y)

	
	total=len(top)-1

    for i in range(total):
    	#여백이 너무 좁아서 맨위에 있는 문제 제외하고 위쪽 여백을 추가했음
        #어차피_여백이 넉넉해서 괜찮
        
        if (i==0):
            img_trim=page_rl[top[i]:top[i+1]-5,:]
        else:
            img_trim=page_rl[top[i]-10:top[i+1]-5,:]
        #cv2.imwrite('/content/MyDrive/GRADIING_Study/kh/trim/'+str(qnum)+'.png',img_trim)
        #print(qnum)
        cv2_imshow(img_trim)

  6. 헤더에서 페이지 수 읽어오기

pytesseract를 사용해서

이 header의 페이지 수를 추출하려고 했으나 몽총이 pystesseract가 읽어 내지 못하고 참고로 google cloud vision은 잘 읽어냄.

욜로로 잘 하면 페이지를 읽을 필요가 없어서 일단은 이렇게 마무리 하도록 했다.

반응형