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.
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ù theta
degrés).
vous pouvez alors utiliser Numpy découpage pour couper l'image.
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)
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.
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
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)