Comment redresser un rectangle rotatif d'une image en utilisant opencv en python?

la photo suivante vous dira ce que je veux.

j'ai les informations des rectangles dans l'image, la largeur, la hauteur, le point central et le degré de rotation. Maintenant, je veux écrire un script pour les découper et les sauver en image, mais les redresser. Comme je veux aller de l'rectangle indiquée à l'intérieur de l'image dans le rectangle qui est montré à l'extérieur.

j'utilise OpenCV python,merci dites-moi un moyen pour effectuer ce.

gentiment montrer un peu de code, comme des exemples de OpenCV Python sont difficiles à trouver.

Example Image

17
demandé sur IcyFlame 2012-07-24 12:52:24

4 réponses

Vous pouvez utiliser le warpAffine fonction pour faire tourner l'image autour d'un point central défini. La matrice de rotation appropriée peut être générée en utilisant getRotationMatrix2D (où thetadegrés).

Start ImageAfter finding the desired rectangle

vous pouvez alors utiliser Numpy découpage pour couper l'image.

Rotated ImageResult

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[:2]

   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

Gardez à l'esprit que dsize est la forme du sortie image. Si le patch / angle est suffisamment grand, les bords sont coupés (comparer l'image ci-dessus) si vous utilisez la forme originale comme--pour des raisons de simplicité--fait ci-dessus. Dans ce cas, vous pouvez introduire un facteur d'échelle pour shape (pour agrandir l'image de sortie) et le point de référence pour le tranchage (ici center).

la fonction ci-dessus peut être utilisée comme suit:

image = cv2.imread('owl.jpg')
image = subimage(image, center=(110, 125), theta=30, width=100, height=200)
cv2.imwrite('patch.jpg', image)
34
répondu rroowwllaanndd 2018-02-01 14:56:16

j'ai eu des problèmes d'erreurs de compensation avec le ici et dans des questions similaires affiché des solutions. Donc j'ai fait le calcul et est venu avec la solution suivante qui fonctionne:

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)

Pour référence, voici une image qui explique les mathématiques derrière elle:

Notez que

w_dst = width-1
h_dst = height-1

c'est parce Que la dernière coordonner a la valeur width-1 et non width;height.

S'il y a des questions sur les mathématiques, demandez - leur des commentaires et j'essaierai d'y répondre.

12
répondu xaedes 2017-10-18 17:54:12

c'est ma version C++ qui exécute la même tâche. J'ai remarqué qu'il est un peu lent. Si quelqu'un voit quelque chose qui pourrait améliorer la performance de cette fonction, alors s'il vous plaît faites le moi savoir. :)

bool extractPatchFromOpenCVImage( cv::Mat& src, cv::Mat& dest, int x, int y, double angle, int width, int height) {

  // obtain the bounding box of the desired patch
  cv::RotatedRect patchROI(cv::Point2f(x,y), cv::Size2i(width,height), angle);
  cv::Rect boundingRect = patchROI.boundingRect();

  // check if the bounding box fits inside the image
  if ( boundingRect.x >= 0 && boundingRect.y >= 0 &&
       (boundingRect.x+boundingRect.width) < src.cols &&  
       (boundingRect.y+boundingRect.height) < src.rows ) { 

    // crop out the bounding rectangle from the source image
    cv::Mat preCropImg = src(boundingRect);

    // the rotational center relative tot he pre-cropped image
    int cropMidX, cropMidY;
    cropMidX = boundingRect.width/2;
    cropMidY = boundingRect.height/2;

    // obtain the affine transform that maps the patch ROI in the image to the
    // dest patch image. The dest image will be an upright version.
    cv::Mat map_mat = cv::getRotationMatrix2D(cv::Point2f(cropMidX, cropMidY), angle, 1.0f);
    map_mat.at<double>(0,2) += static_cast<double>(width/2 - cropMidX);
    map_mat.at<double>(1,2) += static_cast<double>(height/2 - cropMidY);

    // rotate the pre-cropped image. The destination image will be
    // allocated by warpAffine()
    cv::warpAffine(preCropImg, dest, map_mat, cv::Size2i(width,height)); 

    return true;
  } // if
  else {
    return false;
  } // else
} // extractPatch
3
répondu mcvz 2013-12-30 11:29:27

recette similaire pour openCV version 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)
0
répondu abreschi 2018-02-01 00:25:37