signed

QiShunwang

“诚信为本、客户至上”

旋转图片及标注lable的实现

2020/12/26 18:58:26   来源:

旋转图片相关内容

  • 背景
    • 旋转理论
    • OpenCV旋转操作及原理
    • 旋转图像及标注框

背景

1、做带角度的图像数据增强,一般使用rotate函数就可以,不过有时图片部分角就会旋转出去,如果不丢失信息,且补边呢?
2、如果标注的label是一个矩形,如果旋转后,想让label也跟着旋转,怎么计算

旋转理论

旋转图片一般用到了仿射变换,仿射变换的原理可以从几何理解,也可以从极坐标方式理解,具体计算方式就是矩阵相乘
在这里插入图片描述

OpenCV旋转操作及原理

OpenCV的方法是cv2.getRotationMatrix2D()方法,调用如下:

# 你是在
matRotate2 = cv2.getRotationMatrix2D((0, 0),30, 1.0)
print(matRotate2)

#output
[[ 0.8660254  0.5       -0.1830127]
 [-0.5        0.8660254  0.3169873]]

其中这个函数的源码如下:

cv::Mat cv::getRotationMatrix2D( Point2f center, double angle, double scale )
{
    angle *= CV_PI/180;
    double alpha = cos(angle)*scale;
    double beta = sin(angle)*scale;

    Mat M(2, 3, CV_64F);
    double* m = (double*)M.data;

    m[0] = alpha;
    m[1] = beta;
    m[2] = (1-alpha)*center.x - beta*center.y;
    m[3] = -beta;
    m[4] = alpha;
    m[5] = beta*center.x + (1-alpha)*center.y;
    return M;
}
}

代码参考的公式为1
在这里插入图片描述
那这个公式是怎么推导的呢,原理是将旋转轴心转移到坐标中心点,旋转后,再放回原来位置2
在这里插入图片描述

旋转图像及标注框

基本操作就是所有的像素点与之变换矩阵相乘,标签与之相乘,具体代码详解参考了图像增强3,新的宽高计算入下图4
在这里插入图片描述
图像旋转代码如下4

def rotate_box(bb, cx, cy, h, w):
    new_bb = list(bb)
    for i,coord in enumerate(bb):
        # opencv calculates standard transformation matrix
        M = cv2.getRotationMatrix2D((cx, cy), theta, 1.0)
        # Grab  the rotation components of the matrix)
        cos = np.abs(M[0, 0])
        sin = np.abs(M[0, 1])
        # compute the new bounding dimensions of the image
        nW = int((h * sin) + (w * cos))
        nH = int((h * cos) + (w * sin))
        # adjust the rotation matrix to take into account translation
        M[0, 2] += (nW / 2) - cx
        M[1, 2] += (nH / 2) - cy
        # Prepare the vector to be transformed
        v = [coord[0],coord[1],1]
        # Perform the actual rotation and return the image
        calculated = np.dot(M,v)
        new_bb[i] = (calculated[0],calculated[1])
    return new_bb

点的转换模型如下:

def rotate_box(bb, cx, cy, h, w):
    new_bb = list(bb)
    for i,coord in enumerate(bb):
        # opencv calculates standard transformation matrix
        M = cv2.getRotationMatrix2D((cx, cy), theta, 1.0)
        # Grab  the rotation components of the matrix)
        cos = np.abs(M[0, 0])
        sin = np.abs(M[0, 1])
        # compute the new bounding dimensions of the image
        nW = int((h * sin) + (w * cos))
        nH = int((h * cos) + (w * sin))
        # adjust the rotation matrix to take into account translation
        M[0, 2] += (nW / 2) - cx
        M[1, 2] += (nH / 2) - cy
        # Prepare the vector to be transformed
        v = [coord[0],coord[1],1]
        # Perform the actual rotation and return the image
        calculated = np.dot(M,v)
        new_bb[i] = (calculated[0],calculated[1])
    return new_bb

效果图如下
在这里插入图片描述

附代码,基本参考是Image rotation using OpenCV

# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     a6_rotate_with_box.py
   Description :
   Author :       lj_tal
   date:          2020/12/26
-------------------------------------------------
"""
import cv2
import numpy as np

def cocoBbox2Point4(coco_bbox):
    xmin = coco_bbox[0]
    ymin = coco_bbox[1]
    xmax = coco_bbox[2]
    ymax = coco_bbox[3]
    points = np.array([[xmin, ymax], [xmin, ymin], [xmax, ymin], [xmax, ymax]], dtype=np.int).reshape(4, 2)
    return points

def point42CocoBbox(bbox_numpy):
    bbox_temp = bbox_numpy.reshape(4,2)
    xmin = np.min(bbox_temp[:,0])
    xmax = np.max(bbox_temp[:,0])
    ymin = np.min(bbox_temp[:,1])
    ymax = np.max(bbox_temp[:,1])
    # print([xmin,ymin,xmax,ymax])
    return np.array([xmin,ymin,xmax,ymax]).astype(np.int)

# point2 = point42CocoBbox(points)
point4 = np.asarray([35 , 50, 175, 215])
print(point4)

def rotate_bound(image, angle):
    # grab the dimensions of the image and then determine the
    # center
    (h, w) = image.shape[:2]
    (cX, cY) = (w // 2, h // 2)
    # grab the rotation matrix (applying the negative of the
    # angle to rotate clockwise), then grab the sine and cosine
    # (i.e., the rotation components of the matrix)
    M = cv2.getRotationMatrix2D((cX, cY), angle, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])
    # compute the new bounding dimensions of the image
    nW = int((h * sin) + (w * cos))
    nH = int((h * cos) + (w * sin))
    # adjust the rotation matrix to take into account translation
    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY
    # perform the actual rotation and return the image
    return cv2.warpAffine(image, M, (nW, nH))


def rotate_box(bb, cx, cy, h, w,angle):
    new_bb = list(bb)
    for i,coord in enumerate(bb):
        # opencv calculates standard transformation matrix
        M = cv2.getRotationMatrix2D((cx, cy), angle, 1.0)
        # Grab  the rotation components of the matrix)
        cos = np.abs(M[0, 0])
        sin = np.abs(M[0, 1])
        # compute the new bounding dimensions of the image
        nW = int((h * sin) + (w * cos))
        nH = int((h * cos) + (w * sin))
        # adjust the rotation matrix to take into account translation
        M[0, 2] += (nW / 2) - cx
        M[1, 2] += (nH / 2) - cy
        # Prepare the vector to be transformed
        v = [coord[0],coord[1],1]
        # Perform the actual rotation and return the image
        calculated = np.dot(M,v)
        new_bb[i] = [calculated[0],calculated[1]]
    return new_bb

if __name__ == '__main__':

    img = cv2.imread('cat.jpg')
    print(img.shape)

    theta = 20

    point8_list = np.asarray([[[776.9486798180992, 383.9884930278827], [881.0203274611703, 380.39981552294927], [884.6090049661037, 498.8261731857542], [780.5373573230327, 498.8261731857542]],
    [[897.1693762333709, 534.712948235089], [999.4466851239752, 538.3016257400226], [1003.0353626289086, 653.139305897894], [900.7580537383044, 653.139305897894]],
    [[1279.3635305087869, 111.24900265293799], [1293.718240528521, 805.6580998575671], [624.4298858584261, 787.7147123328997], [628.0185633633596, 113.04334140540473]]])

    rotated_img = rotate_bound(img,theta)
    a_img = img.copy()
    for point8 in point8_list:
        point4 = point42CocoBbox(point8)
        cv2.rectangle(a_img, (point4[0], point4[1]), (point4[2], point4[3]), (255, 0, 0), 1)
    #
    cv2.imshow('aa',a_img)
    cv2.imwrite('cat_a.jpg',a_img)
    cv2.waitKey(0)


    # Calculate the shape of rotated images
    (heigth, width) = img.shape[:2]
    (cx, cy) = (width // 2, heigth // 2)
    (new_height, new_width) = rotated_img.shape[:2]
    (new_cx, new_cy) = (new_width // 2, new_height // 2)
    print(cx,cy,new_cx,new_cy)
    new_bb = {}

    for points8 in point8_list:

        points8_rotate= rotate_box(points8, cx, cy, heigth, width,theta)
        points8_rotate = np.asarray(points8_rotate).astype(np.int)

        print(points8_rotate)
        points8_rotate = points8_rotate.reshape((-1, 1, 2))
        new_image = cv2.polylines(rotated_img, [points8_rotate], True, (255, 0, 0), 1)
    cv2.imwrite('cat_b.jpg',new_image)

    cv2.imshow('bb',new_image)

    cv2.waitKey(0)

参考


  1. https://blog.csdn.net/u011681952/article/details/98942207 ↩︎

  2. https://math.stackexchange.com/questions/2093314/rotation-matrix-of-rotation-around-a-point-other-than-the-origin ↩︎

  3. https://blog.paperspace.com/data-augmentation-for-object-detection-rotation-and-shearing/ ↩︎

  4. https://cristianpb.github.io/blog/image-rotation-opencv ↩︎ ↩︎