python - 将框分割成不同的图像

标签 python opencv image-processing computer-vision

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

a box array

我曾尝试使用 cv2.approxPolyDPcv2.HoughLines 函数,但两者都给出了 Not Acceptable 结果。 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

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

最佳答案

我花了一些时间,但我自己弄明白了。

实际图像:

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)

水平索贝尔滤波器后:

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)

垂直索贝尔滤波器后:

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)

膨胀、腐 eclipse 和反转之后:

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:

关于python - 将框分割成不同的图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56311965/

相关文章:

python - 查找多个值相同的元组的最大值

c++ - OpenCV Video Capture无法作为类成员(C++)

java - 使用java的直方图均衡

ruby - 即使一张图像的裁剪/比例略有不同,我如何检测到两张图像是 "the same"?

python - 在Python openCV中获取视频每一帧的耗时

opencv - 使用 opencv 和 c++ 同时搜索多种颜色

python - Django 1.4 静态文件问题并且不在我项目的其他 url 上呈现

python - Python 中向后兼容的输入调用

Python-将带有大写和小写字符串元素的遗传数据(不带空格)拆分为列表

.net - C++ 和 Windows 窗体中的数组