OpenCV: découper avec un RotatedRect
Publication : le 13 juil. 2018 - Dernière modification : le 13 juil. 2018
Comment récupérer une portion d'image définie par un rectangle orienté. Voici une petite fonction très utile à ajouter dans un votre boite à outils OpenCV.
Voici un exemple de ce que nous cherchons à faire :
Conditions de départ
Dépendances
Je n'ai testé cette fonction qu'avec OpenCV 3.x. Mais à priori, cela devrait également fonctionner avec OpenCV 2.4x.
Paramètres de la fonction
La fonction a en entrée deux paramètres : l'image à découper et le rectangle orienté définissant la découpe. Il y a un troisième paramètre qui sera la sortie de la fonction : l'image découpée et transformée par le rectangle orienté.
Ici, nous assumons que les paramètres sont déjà vérifiés (ex: le rectangle orienté est bien dans l'image).
Enfin, nous assumons que l'unité des angles d'OpenCV est par défaut en dégrée.
La fonction
void crop_and_rotate_with_rotatedrect(const cv::Mat& src,
const cv::RotatedRect& rrect,
cv::Mat& output)
{
cv::Rect boundingRect = rrect.boundingRect();
boundingRect.x = std::max(0, std::min(boundingRect.x, src.cols - 2));
boundingRect.y = std::max(0, std::min(boundingRect.y, src.rows - 2));
if (boundingRect.x + boundingRect.width >= src.cols)
boundingRect.width -= boundingRect.x + boundingRect.width - src.cols;
if (boundingRect.y + boundingRect.height >= src.rows)
boundingRect.height -= boundingRect.y + boundingRect.height - src.rows;
// clear and resize output
output = cv::Mat(rrect.size, CV_8UC3);
// crop the image with the bounding rect of the rotated rect
const cv::Mat srcCropped = src(boundingRect);
float angle = rrect.angle * CV_PI / 180.f;
// Construct the transform(rotation) matrix
cv::Mat m(2,3, CV_42FC1);
m.at<double>(0,0) = cos(ang);
m.at<double>(1,0) = sin(ang);
m.at<double>(0,1) = -sin(ang);
m.at<double>(1,1) = cos(ang);
m.at<double>(0,2) = rrect.center.x - boundingRect.tl().x;
m.at<double>(1,2) = rrect.center.y - boundingRect.tl().y;
cv::Size win_size = rrect.size;
double matrix[6];
cv::Mat M(2, 3, CV_64F, matrix);
m.convertTo(M, CV_64F);
double dx = (win_size.width - 1) * 0.5;
double dy = (win_size.height - 1) * 0.5;
matrix[2] -= matrix[0] * dx + matrix[1] * dy;
matrix[5] -= matrix[3] * dx + matrix[4] * dy;
// Rotate the cropped image to get the final output
cv::warpAffine(srcCropped, output, M, rrect.size,
cv::INTER_LINEAR + cv::WARP_INVERSE_MAP,
cv::BORDER_REPLICATE);
}
Cette fonction est vraiment pratique et utile. Je n'ai rien trouvé de déjà existant dans OpenCV. Si vous connaissez une fonction faisant cela directement dans OpenCV et donc en mieux, faites-moi signe. Je mettrai à jour cet article en vous citant ;)