使用pytesseract和OpenCV进行楼层平面图截图OCR

4

我正在尝试编写一个函数,它可以使用OCR从房屋平面图的jpg图像中提取书写在图片上的平方英尺数值。

    import requests
    from PIL import Image
    import pytesseract
    import pandas as pd
    import numpy as np
    import cv2
    import io

    def floorplan_ocr(url):
    """ a row-wise function to use pytesseract to scrape the word data from the floorplan
    images, requires tesseract 
    to be installed https://github.com/tesseract-ocr/tesseract/wiki"""

    if pd.isna(url):
        return np.nan

    res = ''
    response = requests.get(url, stream=True)
    if response.status_code == 200:
        img = response.raw
        img = np.asarray(bytearray(img.read()), dtype="uint8")
        img = cv2.imdecode(img, cv2.CV_8UC1)
        img = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv2.THRESH_BINARY,11,2)
        #img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
        res = pytesseract.image_to_string(img, lang='eng', config='--remove-background')
        del response
        del img
    else:
        return np.nan

    #print(res)
    return res

在此输入图片描述

但是我没有取得太多成功,只有大约四分之一的图像实际上输出包含平方英尺的文本。

例如目前floorplan_ocr(https://i.imgur.com/9qwozIb.jpg) 输出 'K\'Fréfiéfimmimmuuéé\n2|; apprnxx 135 max\nGArhaPpmxd1m max\n\n \n\n \n\n \n\n \n\n \n\n \n\n \n\n总APPaux中午区域523平方英尺,U.S.50, M )\nav .Wzms他 "a! m... mi unwary mmnmrmm mma y“ mum“;\n‘ wmduw: reams m wuhrmmm mm“ .m nanspmmmmy 3 mm :51\nmm" m mmm m; wan wmumw- mm my and mm mm as m by any\nwfmw PM” rmwm mm m .pwmwm m. mum mud ms nu mum.\n(.5 n: ma undammmw an we Ewen\nM vagw‘m Mewpkeem'(而且需要很长时间才能完成)

floorplan_ocr(https://i.imgur.com/sjxMpVp.jpg)则输出' '

我觉得我面临的一些问题是:

  1. 文本可能是灰度的
  2. 图像低 DPI(似乎有一些争议,是否实际上很重要或者是否是总分辨率)
  3. 文本格式不一致

我被卡住了,而且我正在努力改善我的结果。 我想要提取的只是'XXX平方英尺'(以及可能写法的所有方式)

有更好的方法吗?

非常感谢。


也许更容易识别墙壁、比例和单位,然后自己进行计算,不是吗? ;) - Mad Physicist
我不知道为什么会有任何关于低DPI是否重要的争论。它是很重要的。如果你看一下二值化图像的质量,你会惊讶于tesseract能够识别出任何文本。如果可能的话,建议使用更高的DPI,并且最好使用无损格式(PNG通常是一个不错的选择)。对于这样的图像,无损压缩通常仍然可以得到较小的文件大小。 - bfris
你只是想提取“Approximate Gross Internal Area = 50.7 sq m / 546 sq ft”这一行吗? - nathancy
@bfris 这场辩论似乎是关于DPI和分辨率之间的区别,因为DPI只是一个显示指令。也就是说,分辨率很重要,但DPI不是。 - Harvs
@nathancy 是的,那就是那一行,更具体地说是“546平方英尺”。 - Harvs
2个回答

4
所有文本周围的像素化使得Tesseract难以完成其任务。 我使用了一个简单的亮度/对比度算法来消除这些点。我没有进行任何阈值处理/二值化。但是我确实必须缩放图像才能获得任何字符识别。
import pytesseract   
import numpy as np
import cv2

img = cv2.imread('floor_original.jpg', 0) # read as grayscale
img = cv2.resize(img, (0,0), fx=2, fy=2)  # scale image 2X

alpha = 1.2
beta = -20
img = cv2.addWeighted( img, alpha, img, 0, beta)
cv2.imwrite('output.png', img)  

res = pytesseract.image_to_string(img, lang='eng', config='--remove-background')
print(res)

编辑 以上代码可能会受到平台/版本的影响。它可以在我的Linux机器上运行,但无法在Windows机器上运行。为了让它在Windows上运行,我修改了最后两行代码:

res = pytesseract.image_to_string(img, lang='eng', config='remove-background')
print(res.encode())

tesseract输出结果(我加粗以突出面积):

TT xs?

IN

近似总体积 = 50.7平方米 / 546平方英尺

所有的尺寸只是估计,可能不是完全准确,因为测量计划可能会改变。草图、渲染、图形材料、熔岩、外观等全部由开发商、管理公司、业主和其他关联方酌情处理,而没有经过任何担保或保证。

面积仅供参考

处理后的图片:

enter image description here


更新了代码以写入输出图像。我正在使用您发布的图像。如果您的输出图像与我的不同,那么我们可能使用的是不同的源图像。而且我正在使用 tesseract 4.0.0-beta.1。 - bfris
1
@bfris 在 Tesseract 上使用 config='--remove-background' 有效吗?对我来说,当我删除前两个破折号时,它可以正常工作 config='remove-background' - singrium
1
@singrium,感谢你的指出。我已经更新了答案。除非我删除选项上的双破折号,否则我无法在Windows上运行tesseract。 - bfris

4
通过将这几行代码应用于您的第二张图片,裁剪图像底部四分之一之后,可以调整大小并改变对比度/亮度:
img = cv2.imread("download.jpg")

img = cv2.resize(img, (0, 0), fx=2, fy=2)

img = cv2.convertScaleAbs(img, alpha=1.2, beta=-40)

text = pytesseract.image_to_string(img, config='-l eng --oem 1 --psm 3')

我得到了如下结果:

总大约面积528平方英尺(49.0平方米)。

虽然已经尽力确保此处包含的平面图的准确性,但门窗、房间和任何其他物品的尺寸是近似值,并且不承担任何错误、遗漏或误报的责任。这个计划仅供说明目的,应该由任何潜在买家作为这样使用。所显示的服务、系统和电器未经测试,不能保证操作或效率。Made with Metropix©2019



我没有对图像进行处理,因为图像结构互不相同,且由于图像不仅包含文本,OTSU阈值处理无法找到正确的值。

总的来说:Tesseract最适合灰度图像(黑色文本白色背景)。

关于DPI/分辨率问题,确实存在一些争议,但也有一些实证真相: DPI值并不重要(因为文本大小可以变化),为使Tesseract OCR最佳,您的字符需要是30-33像素(高度),稍小几个像素就会使Tesseract几乎无用,而更大的字符实际上会降低准确性,尽管影响不大。(编辑:找到源- > https://groups.google.com/forum/#!msg/tesseract-ocr/Wdh_JJwnw94/24JHDYQbBQAJ

最后,文本格式不会真正改变(至少在您的示例中)。所以你在这里的主要问题是文本大小和解析整个页面的事实。如果您想要的文本行始终位于图像底部,请提取(切片)原始图像,以便只向Tesseract提供相关数据,这也将使其更快。


编辑:如果您还在寻找一种从OCR的文本中提取平方英尺的方法:

text = "some place holder text 5471 square feet some more text"
# store here all the possible way it can be written
sqft_list = ["sq ft", "square feet", "sqft"]
extracted_value = ""

for sqft in sqft_list:
    if sqft in text:
        start = text.index(sqft) - 1
        end = start + len(sqft) + 1
        while text[start - 1] != " ":
            start -= 1
        extracted_value = text[start:end]
        break

print(extracted_value)

5471平方英尺。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接