使用 OpenCV 检测纸板箱及其上的文本

Detecting cardboard Box and Text on it using OpenCV

我想使用 OpenCV 和 Python 数纸板箱并读取传送带上仅包含 3 个白色背景单词的特定标签。附件是我用于实验的图像。到目前为止的问题是,由于噪音,我无法检测到完整的盒子,如果我尝试检查 x, y, w, h = cv2.boundingRect(cnt) 中的 w 和 h,那么它只会过滤掉文本.在这种情况下,ABC 写在盒子上。此外,盒子检测到顶部和底部都有尖峰,我不确定如何过滤。

下面是我正在使用的代码

import cv2

# reading image
image = cv2.imread('img002.jpg')

# convert the image to grayscale format
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# apply binary thresholding
ret, thresh = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)

# visualize the binary image
cv2.imshow('Binary image', thresh)

# collectiong contours
contours,h = cv2.findContours(thresh, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

# looping through contours
for cnt in contours:

    x, y, w, h = cv2.boundingRect(cnt)
        
    cv2.rectangle(image,(x,y),(x+w,y+h),(0,215,255),2)
           

cv2.imshow('img', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

另外请建议如何裁剪文本 ABC,然后对其应用 OCR 以阅读文本。

非常感谢。

编辑 2: 非常感谢您的回答,根据您的建议,我更改了代码,以便它可以检查视频中的框。它的工作就像一个魅力期望它很长一段时间都无法识别一个盒子。下面是我的代码和我使用过的视频的 link。我有几个关于这个的问题,因为我是 OpenCV 的新手,如果你能抽出时间来回答的话。

import cv2
import numpy as np
from time import time as timer

 
def get_region(image):
    contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    c = max(contours, key = cv2.contourArea)
    black = np.zeros((image.shape[0], image.shape[1]), np.uint8)
    mask = cv2.drawContours(black,[c],0,255, -1)
    return mask

cap = cv2.VideoCapture("Resources/box.mp4")
ret, frame = cap.read()

fps = 60
fps /= 1000
framerate = timer()
elapsed = int()

while(1):

   start = timer()
   ret, frame = cap.read()

   # convert the image to grayscale format
   hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

   # Performing threshold on the hue channel `hsv[:,:,0]`
   thresh = cv2.threshold(hsv[:,:,0],127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]

   mask = get_region(thresh)
   masked_img = cv2.bitwise_and(frame, frame, mask = mask)

   newImg = cv2.cvtColor(masked_img, cv2.COLOR_BGR2GRAY)
   # collectiong contours

   c,h = cv2.findContours(newImg, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
   cont_sorted = sorted(c, key=cv2.contourArea, reverse=True)[:5]
   x,y,w,h = cv2.boundingRect(cont_sorted[0])

   cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),5)

   #cv2.imshow('frame',masked_img)
   cv2.imshow('Out',frame)

   if cv2.waitKey(1) & 0xFF == ord('q') or ret==False :
      break

   diff = timer() - start

   while  diff < fps:
       diff = timer() - start


cap.release()
cv2.destroyAllWindows()

Link 到视频: https://www.storyblocks.com/video/stock/boxes-and-packages-move-along-a-conveyor-belt-in-a-shipment-factory-a-few-blank-boxes-for-your-custom-graphics-lmgxtwq

问题:

  1. 我们如何才能 100% 确定绘制的矩形是否确实在盒子的顶部而不是在皮带或其他地方。
  2. 你能告诉我如何使用你在原始答案中提供的功能来使用这个新视频代码中的其他框吗?
  3. 再次将蒙版框转为灰色,再次找到轮廓绘制矩形是否正确?或者有没有更有效的方法。
  4. 此代码的最终版本旨在 运行 raspberry pi。那么我们可以做些什么来优化代码的性能。

再次感谢您的宝贵时间。

需要执行两个步骤:

1.框分割

我们可以假设由于传送带的存在,背景不会发生变化。我们可以使用不同的颜色 space 来分割框。在下面我使用了 HSV 颜色 space:

img = cv2.imread('box.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Performing threshold on the hue channel `hsv[:,:,0]`
th = cv2.threshold(hsv[:,:,0],127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]

屏蔽二值图像中的最大轮廓:

def get_region(image):
    contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    c = max(contours, key = cv2.contourArea)
    black = np.zeros((image.shape[0], image.shape[1]), np.uint8)
    mask = cv2.drawContours(black,[c],0,255, -1)
    return mask
mask = get_region(th)

在原始图像上应用蒙版:

masked_img = cv2.bitwise_and(img, img, mask = mask)

2。文本检测:

文本区域被白色包围,可以通过应用合适的阈值再次隔离。 (您可能希望应用一些统计措施来计算阈值

# Applying threshold at 220 on green channel of 'masked_img'
result = cv2.threshold(masked_img[:,:,1],220,255,cv2.THRESH_BINARY)[1]

Note:

  1. The code is written for the shared image. For boxes of different sizes you can filter contours with approximately 4 vertices/sides.
# Function to extract rectangular contours above a certain area
def extract_rect(contours, area_threshold):                           
    rect_contours = []
    for c in contours:
        if  cv2.contourArea(c) > area_threshold:
            perimeter = cv2.arcLength(c, True)  
            approx = cv2.approxPolyDP(c, 0.02*perimeter, True)    
            if len(approx) == 4:
                cv2.drawContours(image, [approx], 0, (0,255,0),2)
                rect_contours.append(c)
    return rect_contours
  1. Experiment using a statistical value (mean, median, etc.) to find optimal threshold to detect text region.

您的其他问题需要单独回答:

1.我们如何才能 100% 确定绘制的矩形是否真的在盒子的顶部而不是在皮带或其他地方?

  • PRO: 为此,我选择了 HSV 颜色 space 的 Hue 通道。灰色、白色和黑色(在传送带上)的阴影在此通道中是中性的。盒子的棕色对比鲜明,可以使用 Otsu 阈值轻松分割。 Otsu 的算法在没有用户输入的情况下找到最佳阈值。
  • CON 当盒子和传送带的颜色相同时,你可能会遇到问题

2。你能告诉我如何使用你在原始答案中提供的功能来使用这个新视频代码中的其他框吗?

  • PRO: 如果您想使用边缘检测而不使用颜色信息来查找框;很有可能得到许多不需要的边缘。通过使用 extract_rect() 函数,您可以过滤轮廓:

    1. 大约有 4 条边(四边形)
    2. 在一定区域以上
  • CON 如果您的 parcels/packages/bags 有超过 4 个面,您可能需要更改此设置。

3。再次将蒙版框转换为灰色是否正确,再次找到轮廓以绘制矩形。或者有没有更有效的方法。

我觉得这是最好的方法,因为剩下的就是用白色包围的文本区域。应用高值阈值是我脑海中最简单的想法。可能有更好的方法:)

(我不能回答第四个问题:))