【问题标题】:How to extract white region in an image如何提取图像中的白色区域
【发布时间】:2016-01-01 16:50:22
【问题描述】:

我有一张这样的示例图片

我正在寻找一种方法来消除图像中的噪点,以便最终得到一张只有白色背景上的黑色文本的图像,以便我可以将其发送到 tesseract。

我尝试过使用

进行变形
kernel = np.ones((4,4),np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
cv2.imshow("opening", opening)

但它似乎不起作用。

我也尝试过寻找轮廓

img = cv2.cvtColor(rotated, cv2.COLOR_BGR2GRAY)
(cnts, _) = cv2.findContours(img, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    roi=rotated[y:y+h,x:x+w].copy()
    cv2.imwrite("roi.png", roi)

使用上面的代码,我得到以下轮廓:

裁剪时会导致此图像:

这仍然不够好。我想要白色背景上的黑色文本,以便我可以将其发送到 tesseract OCR 并获得良好的成功率。

还有什么我可以尝试的吗?

更新

这是另外一张类似的图片。这个比较简单,因为里面有一个平滑的矩形

【问题讨论】:

  • 致那些说问题太宽泛的选民。请让我知道如何进一步缩小范围。我不认为这个问题很广泛..
  • 你需要做一些自适应阈值,以及打开和关闭操作。 docs.opencv.org/doc/tutorials/imgproc/opening_closing_hats/…您还可以进行自定义腐蚀或膨胀(这基本上是打开和关闭操作所做的)。
  • 这是输入图像吗?或者是某种阈值的结果?
  • 它总是一些矩形背景吗?
  • @ZdaR 这不是原始图像。这是我在对原始图像进行预处理以提取我认为有文本的部分后得到的图像。我通过使用阈值、变形、houghlinesp、去偏斜和调整大小来做到这一点。

标签: python opencv image-processing computer-vision


【解决方案1】:

这个答案的基本思想是在文本周围使用边框。

1) 侵蚀 水平 具有非常大的内核,例如 100 像素大小或单个预期字符大小的 8 倍,类似这样。它应该按行进行。极端纵坐标将给出文本周围边界的 y 位置。

2) 垂直以相同的方式处理以获取文本周围边界的 x 位置。然后使用这些位置裁剪出你想要的图像。

-- 这种方法的一个好处是您可以单独分割每个句子/单词,我认为这对 OCR 很有用。

快乐编码:)

由 Mark Setchell 编辑

这是 1) 的演示

这里是 2) 的演示

【讨论】:

  • 我希望你不要介意我添加的动画 - 如果你介意,请随时删除。
  • 我会尝试这种方法,因为它可能更适用于其他类似的图像。我会在这里更新我的进度。我希望opencv有办法做水平和垂直侵蚀
  • 是的,您需要制作所需的内核并正确应用它。正如动画中的马克兄弟,你可以使用比 200 更大的内核.. 实验一下,你会发现:)
【解决方案2】:

我明白了: Result

源代码:

if __name__ == '__main__':
  SrcImg = cv2.imread('./Yahi9.png', cv2.CV_LOAD_IMAGE_GRAYSCALE)
  _, BinImg = cv2.threshold(SrcImg, 80, 255, cv2.THRESH_OTSU)

  Contours, Hierarchy = cv2.findContours(image=copy.deepcopy(SrcImg),
                                         mode=cv2.cv.CV_RETR_EXTERNAL,
                                         method=cv2.cv.CV_CHAIN_APPROX_NONE)
  MaxContour, _ = getMaxContour(Contours)
  Canvas = np.ones(SrcImg.shape, np.uint8)
  cv2.drawContours(image=Canvas, contours=[MaxContour], contourIdx=0, color=(255), thickness=-1)
  mask = (Canvas != 255)
  RoiImg = copy.deepcopy(BinImg)
  RoiImg[mask] = 255
  RoiImg = cv2.morphologyEx(src=RoiImg, op=cv2.MORPH_CLOSE, kernel=np.ones((3,3)), iterations=4)
  cv2.imshow('RoiImg', RoiImg)
  cv2.waitKey(0)

功能:

def getMaxContour(contours):
  MaxArea = 0
  Location = 0
  for idx in range(0, len(contours)):
      Area = cv2.contourArea(contours[idx])
      if Area > MaxArea:
          MaxArea = Area
          Location = idx
  MaxContour = np.array(contours[Location])
  return MaxContour, MaxArea

嗯,这是python代码。 仅当白色区域为最大轮廓时才有效。

【讨论】:

  • 如果可能,请您解释一下您的方法。感谢您的回答!
  • 对不起,我太累了,一到家就睡着了。
【解决方案3】:

以下适用于您给定的示例,尽管它可能需要针对更广泛的图像进行调整。

import numpy as np
import cv2

image_src = cv2.imread("input.png")
gray = cv2.cvtColor(image_src, cv2.COLOR_BGR2GRAY)
ret, gray = cv2.threshold(gray, 250,255,0)

image, contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
largest_area = sorted(contours, key=cv2.contourArea)[-1]
mask = np.zeros(image_src.shape, np.uint8)
cv2.drawContours(mask, [largest_area], 0, (255,255,255,255), -1)
dst = cv2.bitwise_and(image_src, mask)
mask = 255 - mask
roi = cv2.add(dst, mask)

roi_gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
ret, gray = cv2.threshold(roi_gray, 250,255,0)
image, contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

max_x = 0
max_y = 0
min_x = image_src.shape[1]
min_y = image_src.shape[0]

for c in contours:
    if 150 < cv2.contourArea(c) < 100000:
        x, y, w, h = cv2.boundingRect(c)
        min_x = min(x, min_x)
        min_y = min(y, min_y)
        max_x = max(x+w, max_x)
        max_y = max(y+h, max_y)

roi = roi[min_y:max_y, min_x:max_x]
cv2.imwrite("roi.png", roi)

为您提供以下类型的输出图像:

还有……

代码首先定位最大的轮廓区域。由此创建了一个蒙版,用于首先仅选择内部区域,即文本。然后将遮罩的反转添加到图像中,以将遮罩之外的区域转换为白色。

最后再次为这张新图像找到轮廓。任何超出合适大小范围的轮廓区域都将被丢弃(这用于忽略任何小的噪声区域),并为每个区域找到一个边界矩形。对于这些矩形中的每一个,都会为所有剩余的轮廓计算一个outer 边界矩形,并使用这些值进行裁剪以提供最终图像。

更新 - 要获取图像的其余部分,即移除上述区域,可以使用以下内容:

image_src = cv2.imread("input.png")
gray = cv2.cvtColor(image_src, cv2.COLOR_BGR2GRAY)
ret, gray = cv2.threshold(gray, 10, 255,0)
image, contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
largest_area = sorted(contours, key=cv2.contourArea)[-1]
mask = np.zeros(image_src.shape, np.uint8)
cv2.drawContours(mask, [largest_area], 0, (255,255,255,255), -1)
image_remainder = cv2.bitwise_and(image_src, 255 - mask)

cv2.imwrite("remainder.png", image_remainder)

【讨论】:

  • 如果可能,请您解释一下您的方法。感谢您的回答!
  • 谢谢。嗯,我在第 8 行遇到错误 image, contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) ValueError: need more than 2 values to unpack
  • 它是使用最新的 OpenCV 3 标准编码的,理论上你可以去掉第一个参数。一些常量也可能需要更改。
  • 有时我有一个边缘情况,图像可能像这样 s11.postimg.org/kkzld020j/image.png 。有一个白色矩形部分,然后是一些其他文本。有什么想法/方法可以把它变成两个图像吗?上和下。 Upper 可以由您的答案处理,我可以按原样发送到 OCR 的按钮
  • 我已更新答案以提供可能的起始解决方案。它使用不同的阈值并屏蔽掉原始部分。不过,它可能需要进一步处理。
猜你喜欢
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 2021-06-25
  • 1970-01-01
  • 1970-01-01
  • 2012-11-21
  • 2014-04-28
  • 1970-01-01
相关资源
最近更新 更多