OpenCV/Python:cv2.minAreaRect不会返回一个旋转的矩形

问题描述:

我想使用歪斜图像。要做到这一点,我写(当然有很多的帮助)的程序:OpenCV/Python:cv2.minAreaRect不会返回一个旋转的矩形

  1. 变换图像是一个更容易计算(脱粒,扩张等)
  2. 周围绘制的所有对象
  3. 单位计算的轮廓周围的文字轮廓4个极值点(忽略了利润率的任何东西)
  4. 绘制使用cv2.minAreaRect

的想法是cv2.minAreaRect返回的角度为好,这是我能在那个区域周围的矩形用于歪斜图像。但是,在我的情况下,它是-90°。

您可以看到示例输入图像here。 你可以看到我得到的结果here

我测试了一个“干净”的图像(MS Word截图rotaten≈30°在GIMP)的程序,并给出了相同的结果。

我的代码:

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) 

为什么不是矩形偏向文本匹配吗?我没有看到任何可以证明这一点的文物。

编辑:添加干净的天生的数字文件:inputoutput

+1

你可以绘制(用一个红圈或其他)包含在'new_rect'中的点吗? – Miki

+0

你可以添加GIMP图像和结果吗? – Micka

+1

在get_min_max_values中返回一个轴对齐的矩形(角点),这就是minAreaRect优化的内容。 – Micka

TLDR:只使用凸包而不是4个点!

第1部分:您当前方法中的错误。

您的函数get_min_max_values计算axis-aligned bounding box of all contours的角点。但是你实际上想要在这里计算的是所有轮廓的最左边,最顶端,最右边和最底部的坐标。

除了“记住”最小y之外,还必须保留y最小点(最高点)的坐标。所有其他要点也是如此。

下面的代码显示了如何正确计算这些点。我决定保持代码片段简短易读,这就是为什么我只展示如何计算最左边和最上面的点。所有四点都是以相同的方式计算出来的......

正如你会注意到的,我不会比较(clamp)与循环的边界点;相反,我只在循环结束时这样做一次,因为这样做会产生相同的结果,但代码更简单。

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) 

现在让我们来看看结果:

enter image description here

你可以看到所有四个“极值”点确实是旋转矩形内,但许多其他的点仍然之外,因为“最小区域”限制。当您计算最小轮转边界矩形时,您需要考虑所有“边界”点以使其正确工作。

第2部分:该作品,并需要在你的代码

最小的更改之后,计算与findContours轮廓,你必须对所有这些轮廓点复制到同一个数组,然后最后你必须通过该解决方案该数组到convexHull function。该功能计算convex hull分。然后使用这些点作为minAreaRect功能输入,这是你获得什么:

enter image description here

进一步完善您的解决方案

我敢肯定你的算法,如果你运行得更快根本不计算轮廓。相反,只需使用阈值像素位置作为凸包函数的输入。

+0

感谢您的建议和示例代码。我试着推断并调整了你所建议的代码的其余部分,但它一直返回真正奇怪的坐标。我对如何应用您的最终建议有了一个想法,所以让我接受您的答案,尽管我遇到了运行此代码的问题。 – MrVocabulary

+0

我现在才注意到你在我回答之前编辑过帖子(没有刷新)。非常感谢您的帮助 - 您获得的输出正是我所需要的!但是,我很难再现您的结果 - 它不会绘制矩形。你能看看我的尝试,并告诉我我失踪了吗? http://pastebin.com/VEVHgtii – MrVocabulary

+1

你好,它看起来你只需要确保你把所有*找到的轮廓中的所有*点放到一个单一的数组中,然后你将它作为参数传递给了convexHull。顺便说一句,不要忘记我的建议,避免使用轮廓和直接使用阈值点;这将使工作更容易,结果将是相同的。 :) – Nejc