问题描述
我想使用图像去歪斜。 为此,我编写了一个程序(必须有很多帮助):
- 将图像转换为更易于计算(脱粒,膨胀等)
- 在所有对象周围绘制轮廓
- 计算文字轮廓周围的四个极限点(忽略任何带有边距的东西)
- 使用cv2.minAreaRect在该区域周围绘制一个矩形
这个想法是cv2.minAreaRect也返回角度,我可以使用它来校正图像。 但是,在我的情况下是–90°。
您可以看到一个示例输入图像 。 你可以看到我得到的结果 。
我在“干净”的图像上测试了该程序(MS Word屏幕截图在Gimp中旋转了≈30°),并且给出了相同的结果。
我的代码:
import numpy as np
import cv2
import itertools
img = cv2.imread('zuo.png')
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,64,255,0)
############
kernel = np.ones((2,2),np.uint8)
img_e = cv2.dilate(thresh,kernel,iterations = 1)
# cv2.imwrite("out_eroded.png", img_e)
# http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html
# img_e = thresh
############
imgbw, contours, hierarchy = cv2.findContours(img_e,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# imgbw, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
margin_distance = 25
def flatten(arr, n = 1):
# print(arr)
ret = list(itertools.chain.from_iterable(arr))
# print(ret)
if n != 1:
return flatten(ret, n - 1)
else:
return ret
# print(list(flatten([[1,2,3],[4,5,6], [7], [8,9]])))
def get_min_max_values(cs, im_y, im_x):
# print(flatten(cs), 1)
# print(im_y, im_x)
min_y = im_y - margin_distance
min_x = im_x - margin_distance
max_y = margin_distance
max_x = margin_distance
for lvl1 in cs:
for lvl2 in lvl1:
x, y = lvl2[0]
# x = im_x - x
# y = im_y - y
max_y = max(y, max_y) if y + margin_distance < im_y else max_y
max_x = max(x, max_x) if x + margin_distance < im_x else max_x
min_y = min(y, min_y) if y > margin_distance else min_y
min_x = min(x, min_x) if x > margin_distance else min_x
return ((min_y, min_x), (min_y, max_x), (max_y, min_x), (max_y, max_x))
new_rect = get_min_max_values(contours, len(img), len(img[0]))
new_rect = list(map(lambda x: list(x)[::-1], list(new_rect)))
print(new_rect)
rect = cv2.minAreaRect(np.int0(new_rect))
# print(rect)
print(rect)
box = cv2.boxPoints(rect)
box = np.int0(box)
img_out = cv2.drawContours(img, [box], -1, (0,0,255), 5) # -1 = wszystkie kontury
img_out = cv2.drawContours(img, contours, -1, (0,255,0), 3)
cv2.imwrite("out.png", img_out)
为什么矩形不倾斜以匹配文本? 我看不到任何可以证明这一点的文物。
编辑:添加了干净的出生数字文件: 和 。
1楼
TLDR :仅使用凸包而不是四个点!
第1部分:您当前方法中的错误。
您的函数get_min_max_values计算的的角点。 但是,您实际要在此处计算的是所有轮廓的最左,最顶,最右和最底点的坐标。
不仅要“记住”最小的y,还必须保留y最小的点(最高点)的两个坐标。 所有其他要点也一样。
下面的代码显示了如何正确计算这些点。 我决定使代码段简短易读,这就是为什么我仅在此处显示如何计算最左和最高点的原因。 无论如何,所有四个点都以相同的方式计算...
如您所见,我不会随循环将这些点与边距进行比较( ); 相反,我在循环结束时只执行一次,因为这样做会产生相同的结果,但是代码更简单。
def get_min_max_values(cs, im_height, im_width):
min_y = im_height - margin_distance
min_x = im_width - margin_distance
left_point = (min_y, min_x)
top_point = (min_y, min_x)
for lvl1 in cs:
for lvl2 in lvl1:
x, y = lvl2[0]
left_point = left_point if x > left_point[1] else (y, x)
top_point = top_point if y > top_point[0] else (y, x)
left_point[0] = left_point[0] if left_point[0] > margin_distance else margin_distance + 1
left_point[1] = left_point[1] if left_point[1] > margin_distance else margin_distance + 1
top_point[0] = top_point[0] if top_point[0] > margin_distance else margin_distance + 1
top_point[1] = top_point[1] if top_point[1] > margin_distance else margin_distance + 1
return (top_point, left_point)
现在让我们看一下结果:
您可以看到所有四个“极值”点确实在旋转的矩形内,但由于“最小面积”约束,许多其他点仍在外面。 计算最小旋转边界矩形时,需要考虑所有“边界”点,以使其正常工作。
第2部分:该解决方案有效并且需要对代码进行最少的更改
用findContours计算轮廓后,必须将所有轮廓点复制到同一数组中,然后最后将该数组传递给 。 此函数计算点。 然后,将这些点用作minAreaRect函数的输入,这是您获得的结果:
进一步改善您的解决方案
如果您完全不计算轮廓,我很确定您的算法可以运行得更快。 相反,仅将阈值像素位置用作凸包函数的输入。