如何从文档中删除图像的边框(如 MNIST 手写字符)?
How to remove borders from images taken from document (like MNIST handwritten chars)?
我想提取这样写在盒子里的手写字符。
我正在提取宽度为 29 像素的正方形,这会给我这样的图像。
要正确识别字符,单个字符图像需要非常干净。像这样,
我在做什么,
- 计算水平和垂直投影
每张图片。
遍历两个数组的每个元素。如果projection 的值大于某个阈值,则表示没有遇到边界。它删除了边框周围的空白。
然后在图像中找到轮廓。
- 如果轮廓的面积大于某个阈值。获取边界矩形并裁剪它。
但问题是,这个方法不是那么准确。在某些情况下,它工作正常,但在大多数情况下,如果失败得很惨。
它会生成像
这样的图像
投影值也非常特定于此图像(或更接近此图像的图像)。它不能很好地概括。
有没有其他方法可以很好地应对这种情况?
代码,
char = cv2.imread(image)
char_gray = cv2.cvtColor(char, cv2.COLOR_BGR2GRAY)
char_bw = cv2.adaptiveThreshold(char_gray, 255,
cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 9)
(rows, cols) = char_gray.shape
bit_not = cv2.bitwise_not(char_bw)
proj_h = cv2.reduce(bit_nv2.REDUCE_AVG)
proj_v = cv2.reduce(bit_not, 0, cv2.REDUCE_AVG)
thresh_h = 200
thresh_v = 100
start_x, start_y, end_x, end_y = 0, 0, cols - 1, rows - 1
#proj_h = proj_h[0]
proj_v = proj_v[0]
num_iter_h = cols // 8
num_iter_v = rows // 8
for _ in range(num_iter_h):
if proj_h[start_y][0] > 35:
start_y += 1
for _ in range(num_iter_h):
if proj_h[end_y][0] > 160:
end_y -= 1
for _ in range(num_iter_v):
if proj_v[start_x] > 15: #25:
start_x += 1
for _ in range(num_iter_v):
if proj_v[end_x] > 125:
end_x -= 1
print('processing.. %s.png' % idx)
output_char = char[start_y:end_y, start_x:end_x]
output_char = get_cropped_char(output_char)
return output_char
def get_cropped_char(img):
"""
Returns Grayscale cropped image
"""
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(img, (3,3), 0)
thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 75, 10)
im2, cnts, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour = None
for c in cnts:
area = cv2.contourArea(c)
if area > 100:
contour = c
if contour is None: return None
(x, y, w, h) = cv2.boundingRect(contour)
img = img[y:y+h, x:x+w]
return img
我认为在图像阈值后直接裁剪字符不是一个好方法。我相信 morphy-op 可以制造场景。
块元素排列整齐,所以尝试morphy-erode-op分离块(或去除块边界)。获得 clean
字符图像后,您可以轻松裁剪字符图像。
...
英语不好,哈哈哈
这是我得到的结果。
裁剪过的图片。
步骤:
我是 OpenCV 的新手(我正在从事类似的项目...),但这是我根据经验可以说的。提取干净的字符是可能的,至少对于最后两个。由于横跨数字的线,第一个有点困难。
你必须制作图像的灰色版本、阈值并尝试一些 opening/closing operations. After that you have to do 来删除每个方块的 horizontal/vertical 行。
我尝试了我的程序版本,它完成了 40% 的工作。我需要改进它...
之后,根据结果,您必须提取每个数字的边界框。这并不难。有些数字会失败,但大多数会被提取。
"Extremely clean" 很难达到这个水平。
做更多的研究。有很多关于如何执行大多数操作的示例..
编辑:你的图片一定和我的相似。做这样的事情更容易..
这就是我从我身上获得的成果:看看内部方块,每个方块都围绕着一个数字。它们可以很容易地提取并保存以供下一步处理。
我想提取这样写在盒子里的手写字符。
我正在提取宽度为 29 像素的正方形,这会给我这样的图像。
要正确识别字符,单个字符图像需要非常干净。像这样,
我在做什么,
- 计算水平和垂直投影 每张图片。
遍历两个数组的每个元素。如果projection 的值大于某个阈值,则表示没有遇到边界。它删除了边框周围的空白。
然后在图像中找到轮廓。
- 如果轮廓的面积大于某个阈值。获取边界矩形并裁剪它。
但问题是,这个方法不是那么准确。在某些情况下,它工作正常,但在大多数情况下,如果失败得很惨。 它会生成像
这样的图像投影值也非常特定于此图像(或更接近此图像的图像)。它不能很好地概括。
有没有其他方法可以很好地应对这种情况?
代码,
char = cv2.imread(image)
char_gray = cv2.cvtColor(char, cv2.COLOR_BGR2GRAY)
char_bw = cv2.adaptiveThreshold(char_gray, 255,
cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 9)
(rows, cols) = char_gray.shape
bit_not = cv2.bitwise_not(char_bw)
proj_h = cv2.reduce(bit_nv2.REDUCE_AVG)
proj_v = cv2.reduce(bit_not, 0, cv2.REDUCE_AVG)
thresh_h = 200
thresh_v = 100
start_x, start_y, end_x, end_y = 0, 0, cols - 1, rows - 1
#proj_h = proj_h[0]
proj_v = proj_v[0]
num_iter_h = cols // 8
num_iter_v = rows // 8
for _ in range(num_iter_h):
if proj_h[start_y][0] > 35:
start_y += 1
for _ in range(num_iter_h):
if proj_h[end_y][0] > 160:
end_y -= 1
for _ in range(num_iter_v):
if proj_v[start_x] > 15: #25:
start_x += 1
for _ in range(num_iter_v):
if proj_v[end_x] > 125:
end_x -= 1
print('processing.. %s.png' % idx)
output_char = char[start_y:end_y, start_x:end_x]
output_char = get_cropped_char(output_char)
return output_char
def get_cropped_char(img):
"""
Returns Grayscale cropped image
"""
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(img, (3,3), 0)
thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 75, 10)
im2, cnts, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour = None
for c in cnts:
area = cv2.contourArea(c)
if area > 100:
contour = c
if contour is None: return None
(x, y, w, h) = cv2.boundingRect(contour)
img = img[y:y+h, x:x+w]
return img
我认为在图像阈值后直接裁剪字符不是一个好方法。我相信 morphy-op 可以制造场景。
块元素排列整齐,所以尝试morphy-erode-op分离块(或去除块边界)。获得 clean
字符图像后,您可以轻松裁剪字符图像。
...
英语不好,哈哈哈
这是我得到的结果。
裁剪过的图片。
步骤:
我是 OpenCV 的新手(我正在从事类似的项目...),但这是我根据经验可以说的。提取干净的字符是可能的,至少对于最后两个。由于横跨数字的线,第一个有点困难。
你必须制作图像的灰色版本、阈值并尝试一些 opening/closing operations. After that you have to do
之后,根据结果,您必须提取每个数字的边界框。这并不难。有些数字会失败,但大多数会被提取。 "Extremely clean" 很难达到这个水平。
做更多的研究。有很多关于如何执行大多数操作的示例..
编辑:你的图片一定和我的相似。做这样的事情更容易..
这就是我从我身上获得的成果:看看内部方块,每个方块都围绕着一个数字。它们可以很容易地提取并保存以供下一步处理。