Python 将盒子分割成不同的图像

Python 将盒子分割成不同的图像,python,opencv,image-processing,computer-vision,Python,Opencv,Image Processing,Computer Vision,我试图从文档中读取信息,其中的数据字段类型为只能在框中输入一个字母/数字。我设法为各自的数据字段分割了框数组,但是在分割这些框数组中的各个框时遇到了问题 我曾尝试使用cv2.approxPolyDP和cv2.HoughLines函数,但这两个函数都给出了不可接受的结果。有一个问题利用了垂直/水平线的长度远大于单个数字这一事实。在我的例子中,数字有时会从盒子里溢出,几乎总是碰到盒子 此功能无法单独检测小盒子: def检测盒(img): 灰色=cv2.CVT颜色(img,cv2.COLOR\U B

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

我曾尝试使用
cv2.approxPolyDP
cv2.HoughLines
函数,但这两个函数都给出了不可接受的结果。有一个问题利用了垂直/水平线的长度远大于单个数字这一事实。在我的例子中,数字有时会从盒子里溢出,几乎总是碰到盒子

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

def检测盒(img):
灰色=cv2.CVT颜色(img,cv2.COLOR\U BGR2GRAY)
sharp\u img=cv2.filter2D(np.asarray(灰色),-1,内核)
ret,thresh=cv2.阈值(夏普•伊姆格,180255,1)
边缘=cv2.Canny(锋利,50150,孔径尺寸=3)
_,等高线,h=cv2。findContours(阈值,1,2)
框=[]
对于轮廓中的cnt:
近似=cv2.近似聚合度(cnt,0.01*cv2.弧长(cnt,真),真)
温度=img
如果len(近似值)=4:
框。追加(cnt)
打印(cnt.形状)
打印(最大值(cnt[0])-min(cnt[0]),最大值(cnt[1])-min(cnt[1]))
cv2.拉伸轮廓(温度[cnt],0,(0,0255),-1)
cv2_显示(温度)
返回框
approxPolyDP
结果是:

另一个功能是:

def检测盒(img):
灰色=cv2.CVT颜色(img,cv2.COLOR\U BGR2GRAY)
sharp\u img=cv2.filter2D(np.asarray(灰色),-1,内核)
ret,thresh=cv2.阈值(夏普•伊姆格,180255,1)
边缘=cv2.Canny(灰色,50150,光圈尺寸=3)
cv2_imshow(边缘)
线=cv2.霍夫线(边,1,np.pi/180200)
温度=img
对于ρ,θ在[0]行中:
a=np.cos(θ)
b=np.sin(θ)
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.线(温度,(x1,y1),(x2,y2),(0,0255),2)
cv2_显示(温度)
回程线
HoughLines
结果是:


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

花了我一些时间,但我自己解决了

实际图像:

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过滤器之后:

_, 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)
_, 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)

在垂直sobel过滤器之后:

_, 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)
_, 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)

结果的按位或按位排序:

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)

膨胀、侵蚀和反转后:

# 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)

#最后找到轮廓并找到边界框
imz,等高线,=cv2.findContours(
侵蚀,
cv2.RETR_树,cv2.CHAIN_近似值(简单)
等高线=等高线[:-1]
框=[]
对于轮廓中的cnt:
rect=cv2.boundingRect(cnt)
如果rect[2]/rect[3]<0.6或rect[3]/rect[2]<0.6:
持续
box.append(rect)
num_img=img[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]]
cv2_imshow(num)
裁剪后的长方体:


如果您事先知道带有未填充框的文档,您可以使用matchTemplate或筛选/浏览空文档作为模板,只需硬编码每个框相对于模板的位置,或者从文档中减去模板,只得到手绘的模板parts@HugoRune我认为那是最后的办法。然而,我设法找到了一个没有它的解决方案。