字符/数字的边界框检测

如何解决字符/数字的边界框检测

我有图像,如下所示:

enter image description here

我想找到 8 位数字的边界框。我的第一次尝试是使用带有以下代码的 cv2:

import cv2
import matplotlib.pyplot as plt
import cvlib as cv
from cvlib.object_detection import draw_bbox

im = cv2.imread('31197402.png')
bbox,label,conf = cv.detect_common_objects(im)
output_image = draw_bbox(im,bbox,conf)
plt.imshow(output_image)
plt.show()

不幸的是,这不起作用。有人有想法吗?

解决方法

您的解决方案中的问题很可能是输入图像的质量很差。人物和背景之间几乎没有任何对比。 cvlib 中的斑点检测算法可能无法区分字符斑点和背景,从而产生无用的二进制掩码。让我们尝试使用纯粹的 OpenCV 来解决这个问题。

我建议采取以下步骤:

  1. 应用自适应阈值以获得相当好的二元掩码。
  2. 使用区域过滤器从斑点噪声中清除二进制掩码。
  3. 使用形态学提高二值图像的质量。
  4. 获取每个字符的外部轮廓,并将边界矩形适合每个字符 blob。
  5. 裁剪每个字符使用之前计算的边界矩形。

让我们看看代码:

# importing cv2 & numpy:
import numpy as np
import cv2

# Set image path
path = "C:/opencvImages/"
fileName = "mrrm9.png"

# Read input image:
inputImage = cv2.imread(path+fileName)
inputCopy = inputImage.copy()

# Convert BGR to grayscale:
grayscaleImage = cv2.cvtColor(inputImage,cv2.COLOR_BGR2GRAY)

从这里开始没有太多要讨论的内容,只需读取 BGR 图像并将其转换为 grayscale。现在,让我们使用 adaptive threshold 方法应用 gaussian。这是棘手的部分,因为参数是根据输入的质量手动调整的。该方法的工作方式是将图像划分为 windowSize 的单元格网格,然后应用局部阈值来找到前景和背景之间的最佳分离。可以将 windowConstant 指示的附加常数添加到阈值以微调输出:

# Set the adaptive thresholding (gasussian) parameters:
windowSize = 31
windowConstant = -1
# Apply the threshold:
binaryImage = cv2.adaptiveThreshold(grayscaleImage,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,windowSize,windowConstant)

你得到这个漂亮的二进制图像:

现在,如您所见,图像有一些斑点噪声。让我们应用 area filter 来消除噪音。噪声比感兴趣的目标 blob 小,因此我们可以根据区域轻松过滤它们,如下所示:

# Perform an area filter on the binary blobs:
componentsNumber,labeledImage,componentStats,componentCentroids = \
cv2.connectedComponentsWithStats(binaryImage,connectivity=4)

# Set the minimum pixels for the area filter:
minArea = 20

# Get the indices/labels of the remaining components based on the area stat
# (skip the background component at index 0)
remainingComponentLabels = [i for i in range(1,componentsNumber) if componentStats[i][4] >= minArea]

# Filter the labeled pixels based on the remaining labels,# assign pixel intensity to 255 (uint8) for the remaining pixels
filteredImage = np.where(np.isin(labeledImage,remainingComponentLabels) == True,0).astype('uint8')

这是过滤后的图像:

我们可以通过一些形态学来提高这个图像的质量。某些字符似乎已损坏(查看第一个 3 - 它被分成两个单独的 blob)。我们可以加入他们应用关闭操作:

# Set kernel (structuring element) size:
kernelSize = 3

# Set operation iterations:
opIterations = 1

# Get the structuring element:
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize))

# Perform closing:
closingImage = cv2.morphologyEx(filteredImage,cv2.MORPH_CLOSE,maxKernel,None,opIterations,cv2.BORDER_REFLECT101)

这是“关闭”的图像:

现在,您想要获取每个字符的 bounding boxes。让我们检测每个 blob 的外轮廓并在其周围放置一个漂亮的矩形:

# Get each bounding box
# Find the big contours/blobs on the filtered image:
contours,hierarchy = cv2.findContours(closingImage,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

contours_poly = [None] * len(contours)
# The Bounding Rectangles will be stored here:
boundRect = []

# Alright,just look for the outer bounding boxes:
for i,c in enumerate(contours):

    if hierarchy[0][i][3] == -1:
        contours_poly[i] = cv2.approxPolyDP(c,3,True)
        boundRect.append(cv2.boundingRect(contours_poly[i]))


# Draw the bounding boxes on the (copied) input image:
for i in range(len(boundRect)):
    color = (0,0)
    cv2.rectangle(inputCopy,(int(boundRect[i][0]),int(boundRect[i][1])),\
              (int(boundRect[i][0] + boundRect[i][2]),int(boundRect[i][1] + boundRect[i][3])),color,2)

最后一个 for 循环几乎是可选的。它从列表中获取每个边界矩形并将其绘制在输入图像上,因此您可以看到每个单独的矩形,如下所示:

让我们在二值图像上想象一下:

另外,如果你想使用我们刚刚得到的边界框裁剪每个字符,你可以这样做:

# Crop the characters:
for i in range(len(boundRect)):
    # Get the roi for each bounding rectangle:
    x,y,w,h = boundRect[i]

    # Crop the roi:
    croppedImg = closingImage[y:y + h,x:x + w]
    cv2.imshow("Cropped Character: "+str(i),croppedImg)
    cv2.waitKey(0)

这是获取单个边界框的方法。现在,也许您正在尝试将这些图像传递给 OCR。我尝试将过滤后的二进制图像(在关闭操作之后)传递给 pyocr(这是我正在使用的 OCR),然后我将其作为输出字符串:31197402>

我用来获取关闭图像的 OCR 的代码是这样的:

# Set the OCR libraries:
from PIL import Image
import pyocr
import pyocr.builders

# Set pyocr tools:
tools = pyocr.get_available_tools()
# The tools are returned in the recommended order of usage
tool = tools[0]

# Set OCR language:
langs = tool.get_available_languages()
lang = langs[0]

# Get string from image:
txt = tool.image_to_string(
    Image.open(path + "closingImage.png"),lang=lang,builder=pyocr.builders.TextBuilder()
)

print("Text is:"+txt)

请注意,OCR 接收白色背景上的黑色字符,因此您必须先反转图像。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-