将框分割成不同的图像

时间:2019-05-26 08:38:24

标签: python opencv image-processing computer-vision

我正在尝试从文档中读取信息,在这些文档中,数据字段的类型只能在一个框中输入一个字母/数字。我设法为各个数据字段分割了框数组,但是在分割这些框数组中的各个框时遇到了问题。

a box array

我尝试使用cv2.approxPolyDPcv2.HoughLines函数,但是两者均给出不可接受的结果。 sudoku问题在某一点上使用了这样一个事实,即垂直/水平线的长度比单个数字大得多。就我而言,数字有时会从包装箱中溢出,并且几乎总是碰到包装箱。

此功能无法单独检测小盒子:

def detect_boxes(img):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    sharp_img = cv2.filter2D(np.asarray(gray), -1, kernel) 
    ret,thresh = cv2.threshold(sharp_img,180,255,1)
    edges = cv2.Canny(sharp_img,50,150,apertureSize = 3)
    _,contours,h = cv2.findContours(thresh,1,2)
    boxes = []
    for cnt in contours:
        approx = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True)
        temp = img
        if len(approx)==4:
            boxes.append(cnt)
            print(cnt.shape)
            print(max(cnt[0])-min(cnt[0]),max(cnt[1])-min(cnt[1]))
            cv2.drawContours(temp,[cnt],0,(0,0,255),-1)
            cv2_imshow(temp)
    return boxes

approxPolyDP的结果是:

approxPolyDP result

另一个功能是:

def det_box(img):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    sharp_img = cv2.filter2D(np.asarray(gray), -1, kernel)
    ret,thresh = cv2.threshold(sharp_img,180,255,1)
    edges = cv2.Canny(gray,50,150,apertureSize = 3)
    cv2_imshow(edges)
    lines = cv2.HoughLines(edges,1,np.pi/180,200)
    temp = img
    for rho,theta in lines[0]:
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a*rho
        y0 = b*rho
        x1 = int(x0 + 1000*(-b))
        y1 = int(y0 + 1000*(a))
        x2 = int(x0 - 1000*(-b))
        y2 = int(y0 - 1000*(a))

        cv2.line(temp,(x1,y1),(x2,y2),(0,0,255),2)
    cv2_imshow(temp)
    return lines

HoughLines的结果是:

HoughLines result

我试图按顺序获取每个小盒子的盒子点/轮廓。任何帮助将不胜感激。即使删除框中的水平和垂直线也将很有帮助。

1 个答案:

答案 0 :(得分:1)

花一些时间,但我自己想通了。

实际图片:

Actual image:

if len(img.shape) != 2:
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
else:
    gray = img

kernel = np.array([[-1,-1,-1],[-1,9,-1],[-1,-1,-1]])
sharp_img = cv2.filter2D(np.asarray(gray), -1, kernel)
gray = cv2.bitwise_not(gray)
ret,bw = cv2.threshold(sharp_img,200,255,1) 

#### HORIZONTAL TRANSFORMATIONS #######
hz_kernel = np.array([[1,2,1],[0,0,0],[-1,-2,-1]])
vert_kernel = np.array([[-1,0,1],[-2,0,2],[-1,0,1]])

hz_img = cv2.filter2D(np.asarray(bw),-1,hz_kernel)
dilated = cv2.dilate(hz_img, np.ones((1, 5)),iterations = 2)
hz_img = cv2.erode(dilated,np.ones((1,5)),iterations = 4)
#cv2_imshow(bw)

print('after hz sobel->')
cv2_imshow(hz_img)

在水平sobel滤波器之后:

After horizontal sobel filter:

_, contours, hierarchy = cv2.findContours(
        hz_img, 
        cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
mask = np.ones(img.shape[:2], dtype="uint8") * 255
for cnt in contours:
    x,y,w,h = cv2.boundingRect(cnt)
    if w < (img.shape[1] - 10):
        #print(w)
        cv2.drawContours(mask, [cnt], -1, 0, -1)

hz_lines = cv2.bitwise_and(hz_img, hz_img, mask=mask)
if i == 0:
    print("after removing noise")
    cv2_imshow(hz_lines)

消除水平杂讯后:

After horizontal noise removal:

######## VERTICAL TRANSFORMATIONS #########
vert_img = cv2.filter2D(np.asarray(bw),-1,vert_kernel)
dilated = cv2.dilate(vert_img, np.ones((3, 1)),iterations = 1)
vert_img = cv2.erode(dilated,np.ones((3,1)),iterations = 1)

print("after vertical soble->")
cv2_imshow(vert_img)

在垂直sobel滤波器之后:

After vertical sobel filter:

_, vert_contours, _ = cv2.findContours(
        vert_img, 
        cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
vert_mask = np.ones(img.shape[:2], dtype="uint8") * 255
for cnt in vert_contours:
    x,y,w,h = cv2.boundingRect(cnt)
    if h<vert_img.shape[0]-10 or w > 5:
        #print(w)
        cv2.drawContours(vert_mask, [cnt], -1, 0, -1)

vert_lines = cv2.bitwise_and(vert_img, vert_img, mask=vert_mask)


print('after removing noise ->')
cv2_imshow(vert_lines)

去除垂直噪声后:

After vertical noise removal:

####### COMBINATION ##########
boxes_array = cv2.bitwise_or(vert_lines,hz_lines)

print('box array')
cv2_imshow(boxes_array)

按位或结果:

Bitwise or of results:

dilated = cv2.dilate(boxes_array, np.ones((7, 7)),iterations = 3)
eroded = cv2.bitwise_not(cv2.erode(dilated,np.ones((7,7)),iterations = 3))


print('dilated and inverted->')
cv2_imshow(eroded)

膨胀,腐蚀和反转后:

After dilation, erosion and inversion:

# Finally find the contours and find the bounding boxes
imz,contours,_ = cv2.findContours(
        eroded, 
        cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[::-1]
boxes = []
for cnt in contours:
    rect = cv2.boundingRect(cnt)
    if rect[2]/rect[3] < 0.6 or rect[3]/rect[2] < 0.6:
        continue
    boxes.append(rect)
    num_img = img[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]]
    cv2_imshow(num)

裁剪后的盒子:

A box after cropping: