如何在python中使用opencv来拉直图像的旋转矩形区域?

hak*_*ami 27 python opencv image-processing

下图将告诉你我想要什么.

我有图像中的矩形信息,宽度,高度,中心点和旋转度.现在,我想编写一个脚本来剪切它们并将它们保存为图像,但要理顺它们.因为我想从图像内部显示的矩形转到外面显示的矩形.

我正在使用OpenCV python,告诉我一种方法来实现这一目标.

显示一些代码作为OpenCV Python的例子很难找到.

示例图像

rro*_*ndd 44

您可以使用该warpAffine功能围绕定义的中心点旋转图像.可以使用getRotationMatrix2D(theta度为单位)生成合适的旋转矩阵.

开始图片 找到所需的矩形后

然后,您可以使用Numpy切片来剪切图像.

旋转图像 结果

import cv2
import numpy as np

def subimage(image, center, theta, width, height):

   ''' 
   Rotates OpenCV image around center with angle theta (in deg)
   then crops the image according to width and height.
   '''

   # Uncomment for theta in radians
   #theta *= 180/np.pi

   shape = ( image.shape[1], image.shape[0] ) # cv2.warpAffine expects shape in (length, height)

   matrix = cv2.getRotationMatrix2D( center=center, angle=theta, scale=1 )
   image = cv2.warpAffine( src=image, M=matrix, dsize=shape )

   x = int( center[0] - width/2  )
   y = int( center[1] - height/2 )

   image = image[ y:y+height, x:x+width ]

   return image
Run Code Online (Sandbox Code Playgroud)

请记住,这dsize输出图像的形状.如果贴片/角度足够大,如果使用原始形状,则边缘会被切断(比较上面的图像) - 为简单起见 - 在上面完成.在这种情况下,您可以引入缩放因子shape(以放大输出图像)和切片参考点(此处center).

以上功能可以使用如下:

image = cv2.imread('owl.jpg')
image = subimage(image, center=(110, 125), theta=30, width=100, height=200)
cv2.imwrite('patch.jpg', image)
Run Code Online (Sandbox Code Playgroud)

  • 如何计算“theta”? (2认同)

jdh*_*hao 14

其他方法只有当矩形的内容在旋转后的旋转图像中时才有效,在其他情况下会严重失败。如果部分丢失了怎么办?请参阅下面的示例:

在此处输入图片说明

如果您要使用上述方法裁剪旋转后的矩形文本区域,

import cv2
import numpy as np


def main():
    img = cv2.imread("big_vertical_text.jpg")
    cnt = np.array([
            [[64, 49]],
            [[122, 11]],
            [[391, 326]],
            [[308, 373]]
        ])
    print("shape of cnt: {}".format(cnt.shape))
    rect = cv2.minAreaRect(cnt)
    print("rect: {}".format(rect))

    box = cv2.boxPoints(rect)
    box = np.int0(box)

    print("bounding box: {}".format(box))
    cv2.drawContours(img, [box], 0, (0, 0, 255), 2)

    img_crop, img_rot = crop_rect(img, rect)

    print("size of original img: {}".format(img.shape))
    print("size of rotated img: {}".format(img_rot.shape))
    print("size of cropped img: {}".format(img_crop.shape))

    new_size = (int(img_rot.shape[1]/2), int(img_rot.shape[0]/2))
    img_rot_resized = cv2.resize(img_rot, new_size)
    new_size = (int(img.shape[1]/2)), int(img.shape[0]/2)
    img_resized = cv2.resize(img, new_size)

    cv2.imshow("original contour", img_resized)
    cv2.imshow("rotated image", img_rot_resized)
    cv2.imshow("cropped_box", img_crop)

    # cv2.imwrite("crop_img1.jpg", img_crop)
    cv2.waitKey(0)


def crop_rect(img, rect):
    # get the parameter of the small rectangle
    center = rect[0]
    size = rect[1]
    angle = rect[2]
    center, size = tuple(map(int, center)), tuple(map(int, size))

    # get row and col num in img
    height, width = img.shape[0], img.shape[1]
    print("width: {}, height: {}".format(width, height))

    M = cv2.getRotationMatrix2D(center, angle, 1)
    img_rot = cv2.warpAffine(img, M, (width, height))

    img_crop = cv2.getRectSubPix(img_rot, size, center)

    return img_crop, img_rot


if __name__ == "__main__":
    main()
Run Code Online (Sandbox Code Playgroud)

这就是你会得到的:

在此处输入图片说明

显然,有些部分被剪掉了!既然我们可以用cv.boxPoints()方法得到它的四个角点,为什么不直接扭曲旋转的矩形呢?

import cv2
import numpy as np


def main():
    img = cv2.imread("big_vertical_text.jpg")
    cnt = np.array([
            [[64, 49]],
            [[122, 11]],
            [[391, 326]],
            [[308, 373]]
        ])
    print("shape of cnt: {}".format(cnt.shape))
    rect = cv2.minAreaRect(cnt)
    print("rect: {}".format(rect))

    box = cv2.boxPoints(rect)
    box = np.int0(box)
    width = int(rect[1][0])
    height = int(rect[1][1])

    src_pts = box.astype("float32")
    dst_pts = np.array([[0, height-1],
                        [0, 0],
                        [width-1, 0],
                        [width-1, height-1]], dtype="float32")
    M = cv2.getPerspectiveTransform(src_pts, dst_pts)
    warped = cv2.warpPerspective(img, M, (width, height))
Run Code Online (Sandbox Code Playgroud)

现在裁剪的图像变成

在此处输入图片说明

好多了,不是吗?如果仔细检查,您会注意到裁剪后的图像中有一些黑色区域。那是因为检测到的矩形的一小部分超出了图像的边界。为了解决这个问题,您可以稍微填充图像并在此之后进行裁剪。这个答案中有一个例子。

现在,我们比较从图像中裁剪旋转矩形的两种方法。这种方法不需要旋转图像,可以用更少的代码更优雅地处理这个问题。


xae*_*des 12

我在这里遇到了错误的偏移问题,并在类似的问题中发布了解决方案.所以我做了数学计算并提出了以下有效的解决方案:

def subimage(self,image, center, theta, width, height):
    theta *= 3.14159 / 180 # convert to rad

    v_x = (cos(theta), sin(theta))
    v_y = (-sin(theta), cos(theta))
    s_x = center[0] - v_x[0] * ((width-1) / 2) - v_y[0] * ((height-1) / 2)
    s_y = center[1] - v_x[1] * ((width-1) / 2) - v_y[1] * ((height-1) / 2)

    mapping = np.array([[v_x[0],v_y[0], s_x],
                        [v_x[1],v_y[1], s_y]])

    return cv2.warpAffine(image,mapping,(width, height),flags=cv2.WARP_INVERSE_MAP,borderMode=cv2.BORDER_REPLICATE)
Run Code Online (Sandbox Code Playgroud)

这里参考的是一个解释它背后的数学的图像:

注意

w_dst = width-1
h_dst = height-1
Run Code Online (Sandbox Code Playgroud)

那是因为最后一个坐标有值width-1而不是width; 或height.

如果有关于数学的问题,请将它们作为评论,我将尝试回答它们.


abr*_*chi 6

openCV版本3.4.0的相似配方。

from cv2 import cv
import numpy as np

def getSubImage(rect, src):
    # Get center, size, and angle from rect
    center, size, theta = rect
    # Convert to int 
    center, size = tuple(map(int, center)), tuple(map(int, size))
    # Get rotation matrix for rectangle
    M = cv2.getRotationMatrix2D( center, theta, 1)
    # Perform rotation on src image
    dst = cv2.warpAffine(src, M, src.shape[:2])
    out = cv2.getRectSubPix(dst, size, center)
    return out

img = cv2.imread('img.jpg')
# Find some contours
thresh2, contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Get rotated bounding box
rect = cv2.minAreaRect(contours[0])
# Extract subregion
out = getSubImage(rect, img)
# Save image
cv2.imwrite('out.jpg', out)
Run Code Online (Sandbox Code Playgroud)

  • @vamsidharmuthireddy 感谢您的评论。但是,我相信您犯了一个拼写错误:它应该是 (src.shape[1] ,src.shape[0]) :) (5认同)
  • 对代码进行了一次更改。在 cv2.warpAffine() 处,代替 src.shape[:2] 给出 (src.shape[0],src.shape[1])。因为 src.shape[:2] 给出 (rows,cols)。但是 opencv 期望 (cols,rows) (2认同)