聚类边界框并在其上画线(OpenCV,Python)

Posted

技术标签:

【中文标题】聚类边界框并在其上画线(OpenCV,Python)【英文标题】:Cluster bounding boxes and draw line on them (OpenCV, Python) 【发布时间】:2018-04-25 21:30:21 【问题描述】:

使用这段代码,我在下图中的字符周围创建了一些边界框:

import csv
import cv2
from pytesseract import pytesseract as pt

pt.run_tesseract('bb.png', 'output', lang=None, boxes=True, config="hocr")

# To read the coordinates
boxes = []
with open('output.box', 'rt') as f:
    reader = csv.reader(f, delimiter=' ')
    for row in reader:
        if len(row) == 6:
            boxes.append(row)

# Draw the bounding box
img = cv2.imread('bb.png')
h, w, _ = img.shape
for b in boxes:
    img = cv2.rectangle(img, (int(b[1]), h-int(b[2])), (int(b[3]), h-int(b[4])), (0, 255, 0), 2)

cv2.imshow('output', img)
cv2.waitKey(0)

输出

我想要的是这样的:

程序应该在边界框的X轴上画一条垂直线(仅适用于第一个和第三个文本区域。中间的那个不能对这个过程感兴趣)。

目标是这样的(还有另一种方法可以实现,请解释一下):一旦我有了这两条线(或者,更好的是一组坐标),使用蒙版覆盖这两个区域。

有可能吗?

源图片:

按要求提供 CSV: 打印(框)

[['l', '56', '328', '63', '365', '0'], ['i', '69', '328', '76', '365', '0'], ['n', '81', '328', '104', '354', '0'], ['e', '108', '328', '130', '354', '0'], ['1', '147', '328', '161', '362', '0'], ['m', '102', '193', '151', '227', '0'], ['i', '158', '193', '167', '242', '0'], ['d', '173', '192', '204', '242', '0'], ['d', '209', '192', '240', '242', '0'], ['l', '247', '193', '256', '242', '0'], ['e', '262', '192', '292', '227', '0'], ['t', '310', '192', '331', '235', '0'], ['e', '334', '192', '364', '227', '0'], ['x', '367', '193', '398', '227', '0'], ['t', '399', '192', '420', '235', '0'], ['-', '440', '209', '458', '216', '0'], ['n', '481', '193', '511', '227', '0'], ['o', '516', '192', '548', '227', '0'], ['n', '553', '193', '583', '227', '0'], ['t', '602', '192', '623', '235', '0'], ['o', '626', '192', '658', '227', '0'], ['t', '676', '192', '697', '235', '0'], ['o', '700', '192', '732', '227', '0'], ['u', '737', '192', '767', '227', '0'], ['c', '772', '192', '802', '227', '0'], ['h', '806', '193', '836', '242', '0'], ['l', '597', '49', '604', '86', '0'], ['i', '610', '49', '617', '86', '0'], ['n', '622', '49', '645', '75', '0'], ['e', '649', '49', '671', '75', '0'], ['2', '686', '49', '710', '83', '0']]

编辑:

要使用zindarod 答案,您需要 tesserocr。通过pip install tesserocr 安装会出现各种错误。 我找到了它的***版本(经过数小时尝试安装和解决错误,请参阅我在答案下方的评论...):here you can find/download it。

希望这会有所帮助..

【问题讨论】:

我建议你对边界框进行聚类,然后在第 1 行聚类中获取最大 y,然后在第 2 行聚类中获取最小 y,并使用 2 y 和所有遮罩的宽度。 看来是对的。你知道怎么做吗?此外,我还为这项研究找到了另一个关键词:“Connected-component labeling”。 连接的组件不行。如果所有这些都以某种方式连接,则此方法有效。但是您可以将 k-means 与它们的 y 值和 k = 3 一起使用。然后根据它们的 y 值,您将拥有 3 个字母簇。 kmeans 在 opencv 中实现 我现在正在阅读它。但我不知道如何(或在哪里)在我的代码中实现这一点......从文档中看起来很简单但是......如何? 找到这些框后,每个框(顶部和底部)都有 2 个 y 坐标,您可以对它们进行平均,以获得每个字母 1 个 y 值。这将是一个您传递给 kmeans 的数组,然后 kmeans 会将每个值(每个字母中的每个 y)标记为 1,2,3(虽然不确定它是否是 0,1,2)不,您可以将每组一个盒子里的字母。从那里您可以获得创建掩码所需的值......我可以写一个完整的答案,但在几个小时内。您可以发布 csv 和初始图像吗?能够测试它 【参考方案1】:

Google 的 tesseract-ocr 已经在 page segmentation method(psm) 中提供了此功能。您只需要使用更好的 python 包装器,它比 pytesseract 公开了更多 tesseract 的功能。比较好的之一是tesserocr。

图片的简单示例:

  import cv2
  import numpy as np
  import tesserocr as tr
  from PIL import Image

  cv_img = cv2.imread('text.png', cv2.IMREAD_UNCHANGED)

  # since tesserocr accepts PIL images, converting opencv image to pil
  pil_img = Image.fromarray(cv2.cvtColor(cv_img,cv2.COLOR_BGR2RGB))

  #initialize api
  api = tr.PyTessBaseAPI()
  try:
    # set pil image for ocr
    api.SetImage(pil_img)
    # Google tesseract-ocr has a page segmentation methos(psm) option for specifying ocr types
    # psm values can be: block of text, single text line, single word, single character etc.
    # api.GetComponentImages method exposes this functionality
    # function returns:
    # image (:class:`PIL.Image`): Image object.
    # bounding box (dict): dict with x, y, w, h keys.
    # block id (int): textline block id (if blockids is ``True``). ``None`` otherwise.
    # paragraph id (int): textline paragraph id within its block (if paraids is True).
    # ``None`` otherwise.
    boxes = api.GetComponentImages(tr.RIL.TEXTLINE,True)
    # get text
    text = api.GetUTF8Text()
    # iterate over returned list, draw rectangles
    for (im,box,_,_) in boxes:
      x,y,w,h = box['x'],box['y'],box['w'],box['h']
      cv2.rectangle(cv_img, (x,y), (x+w,y+h), color=(0,0,255))
  finally:
    api.End()

  cv2.imshow('output', cv_img)
  cv2.waitKey(0)
  cv2.destroyAllWindows()

【讨论】:

这看起来比我提议的还要好 :) 是的,但是在尝试安装 tesserocr 时它给了我错误。实际上我也在使用 pyocr 作为包装器。不能用这个来完成吗?谢谢。看起来很适合我现在想做的事情..错误:python setup.py egg_info" failed with error code 1..正在寻找解决方案.. @api55 opencv 已经有一个基于 google 的 tesseract-ocr 的 text 模块,不幸的是,python API 没有公开 psm 功能。 @Link Pytesseract 只是将您的函数参数转换为 tesseract 的命令行参数。我无法通过命令行弄清楚 psm 方法,但也许你可以。 @zindarod 试图用this image 完成这项工作在检测文本块时不起作用。编辑:文本块太近了。如果我将它们分开,脚本就可以工作..有没有办法按原样处理图像?【参考方案2】:

我来晚了,正在寻找别的东西。我从未使用过 tesser 包装器,它们似乎只是妨碍了它并没有真正的好处。他们所做的只是抽象出对子流程的调用?

这就是我通过传递给子进程的 args 访问 psm 配置的方式。为了完整起见,我还包含了 oem、pdf 和 hocr 参数,但这不是必需的,您只需传递 psm 参数即可。请在终端拨打帮助电话,因为有 13 个 psm 选项和 4 个用于 oem。根据您所做的工作,质量可能高度依赖于 psm。

可以使用 subprocess.Popen() 进行管道输入和输出,或者如果您喜欢冒险,您可以使用 asyncio.create_subprocess_exec() 以几乎相同的方式异步进行。

import subprocess

# args 
# 'tesseract' - the executable name
# path to the image file
# output file name - no extension tesser will add .txt .pdf .hocr etc etc
# optional params
# -psm x to set the page segmentation mode see more with tesseract --help-psm at the cli
# -oem x to set ocr engine mode see more with tesseract --help-osm
# can add a mode parameter to the end of the args list to get output in :
# searchable pdf - just add a parameter 'pdf' as below
# hOCR output (html) - just add 'hocr' as below

args = ['tesseract', 'Im1.tiff', 'Im1', '-psm 1', '-oem 2']

# args = ['tesseract', 'Im1.tiff', 'Im1', '-psm 1', '-oem 2', 'pdf']
# args = ['tesseract', 'Im1.tiff', 'Im1', '-psm 1', '-oem 2', 'hocr']

try:
    proc = subprocess.check_call(args)
    print('subprocess retcode r'.format(r=proc))
except subprocess.CalledProcessError as exp:
    print('subprocess.CalledProcessError : ', exp)

【讨论】:

以上是关于聚类边界框并在其上画线(OpenCV,Python)的主要内容,如果未能解决你的问题,请参考以下文章

Tensorflow - 通过跟踪边界框并输出它们从左到右显示排序的预测

用于 Visual C++ 的简单面向对象的 2D 图形框架?

怎么在ARCGIS中国地图上画线

在 CImage 对象上使用设备上下文绘制线条

我们可以在陀螺仪给出的两点之间在android画布上画线吗?

(Android) 使用地图标记创建边界框并在 Google Map V2 中获取其宽度