2 votes

Comment faire pivoter une image sans perdre les bords ?

Je veux faire pivoter des images avec un angle variable (comme 22°) dans JavaCV/OpenCV. Pour l'instant, j'utilise cvWarpAffine() .

Mon problème est que je perds les bords de l'image après la rotation, je dois donc agrandir l'image dst.et déplacer le point central. Sur cette page J'ai trouvé un code AS pour calculer la nouvelle taille de l'image. Mais je ne sais pas comment le réaliser avec JavaCV/OpenCV.

Dans le temps, j'ai le code suivant :

public CvMat rotateImage(int angle) {
    CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,
            input.rows() / 2.0F);

    CvMat rotMat = cvCreateMat(2, 3, CV_32F);
    cv2DRotationMatrix(center, angle, 1, rotMat);
    CvMat dst = cvCreateMat(input.rows(), input.cols(), input.type());
    cvWarpAffine(input, dst, rotMat);
    return dst;

}

Quelqu'un a une idée ?

Salutations

//Mise à jour

Je ne sais pas... quelque chose ne va pas. Si je calcule l'image tournée, mon résultat a la bonne dimension, mais il est surtout noir (0 et 360° fonctionnent)... Voici le code :

public CvMat rotateImage(float angle) {
    CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,
            input.rows() / 2.0F);
    CvBox2D box = new CvBox2D(center, cvSize2D32f(input.cols() - 1,
            input.rows() - 1), angle);
    CvPoint2D32f points = new CvPoint2D32f(4);
    cvBoxPoints(box, points);
    CvMat pointMat = cvCreateMat(1, 4, CV_32FC2);
    pointMat.put(0, 0, 0, points.position(0).x());
    pointMat.put(0, 0, 1, points.position(0).y());
    pointMat.put(0, 1, 0, points.position(1).x());
    pointMat.put(0, 1, 1, points.position(1).y());
    pointMat.put(0, 2, 0, points.position(2).x());
    pointMat.put(0, 2, 1, points.position(2).y());
    pointMat.put(0, 3, 0, points.position(3).x());
    pointMat.put(0, 3, 1, points.position(3).y());
    CvRect boundingRect = cvBoundingRect(pointMat, 0);

    CvMat dst = cvCreateMat(boundingRect.height(), boundingRect.width(),
            input.type());

    CvPoint2D32f centerDst = new CvPoint2D32f(center.x()
            + (dst.cols() / 2.0F), center.y() + (dst.rows() / 2.0F));

    CvMat rotMat = cvCreateMat(2, 3, CV_32F);
    cv2DRotationMatrix(centerDst, angle, 1, rotMat);
    CvMat trans = cvCreateMat(3, 3, CV_32F);
    cvZero(trans);
    trans.put(0, 2, dst.cols() / 2.0F);
    trans.put(1, 2, dst.rows() / 2.0F);

    trans.put(0, 0, 1);
    trans.put(1, 1, 1);
    trans.put(2, 2, 1);

    CvMat newRot = cvCreateMat(3, 3, CV_32F);
    cvZero(newRot);
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            newRot.put(i, j, rotMat.get(i, j));
        }
    }

    newRot.put(2, 2, 1);
    cvMul(trans, newRot, newRot, 1);

    cvWarpPerspective(input, dst, newRot);
    // cvWarpAffine(input, dst, dstRotMat);

    return dst;

}

En rotMat ressemble :

[ 0.9396926, 0.34202015, -311.1334
 -0.34202015, 0.9396926, 601.47485 ]

En trans (l'image d'org. a une taille de 1428x928px) :

[ 1.0, 0.0, 836.0
0.0, 1.0, 699.0
0.0, 0.0, 1.0 ]

et le newRot

[ 0.9396926, 0.0, -260107.52
-0.0, 0.9396926, 420430.94
0.0, 0.0, 1.0 ]

Je n'arrive pas à trouver l'erreur

//Mise à jour 2

public CvMat rotateImage(float angle) {
    CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,
            input.rows() / 2.0F);
    CvBox2D box = new CvBox2D(center, cvSize2D32f(input.cols() - 1,
            input.rows() - 1), angle);
    CvPoint2D32f points = new CvPoint2D32f(4);
    cvBoxPoints(box, points);
    CvMat pointMat = cvCreateMat(1, 4, CV_32FC2);
    pointMat.put(0, 0, 0, points.position(0).x());
    pointMat.put(0, 0, 1, points.position(0).y());
    pointMat.put(0, 1, 0, points.position(1).x());
    pointMat.put(0, 1, 1, points.position(1).y());
    pointMat.put(0, 2, 0, points.position(2).x());
    pointMat.put(0, 2, 1, points.position(2).y());
    pointMat.put(0, 3, 0, points.position(3).x());
    pointMat.put(0, 3, 1, points.position(3).y());
    CvRect boundingRect = cvBoundingRect(pointMat, 0);

    CvMat dst = cvCreateMat(boundingRect.height(), boundingRect.width(),
            input.type());

    // CvPoint2D32f centerDst = new CvPoint2D32f(((dst.cols()-input.cols()
    // )/ 2.0F),(( dst.rows()-input.rows()) / 2.0F));

    CvMat rotMat = cvCreateMat(2, 3, CV_32F);
    cv2DRotationMatrix(center, angle, 1, rotMat);
    CvMat trans = cvCreateMat(3, 3, CV_32F);
    cvZero(trans);
    trans.put(0, 2, (dst.cols() - input.cols()) / 2.0F);
    trans.put(1, 2, (dst.rows() - input.rows()) / 2.0F);

    trans.put(0, 0, 1);
    trans.put(1, 1, 1);
    trans.put(2, 2, 1);

    CvMat newRot = cvCreateMat(3, 3, CV_32F);
    cvZero(newRot);
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            newRot.put(i, j, rotMat.get(i, j));
        }
    }

    newRot.put(2, 2, 1);
    cvMul(trans, newRot, newRot, 1);

    cvWarpPerspective(input, dst, newRot);
    // cvWarpAffine(input, dst, rotMat);

    System.out.println(rotMat);
    System.out.println(trans);
    System.out.println(newRot);

    return dst;

A 1 degré, la matrice ressemble à :

rotMat :

 [ 0.9998477, 0.017452406, -8.338219
 -0.017452406, 0.9998477, 12.534734 ]

trans

 [ 1.0, 0.0, -8.0
  0.0, 1.0, -12.0
  0.0, 0.0, 1.0 ]

newRot

 [ 0.9998477, 0.0, 66.70575
   -0.0, 0.9998477, -150.41681
   0.0, 0.0, 1.0 ]

FINAL

JavaCode de travail de l'instruction de Hammer :

public CvMat rotateImage(float angle) {
    CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,
            input.rows() / 2.0F);
    CvBox2D box = new CvBox2D(center, cvSize2D32f(input.cols() - 1,
            input.rows() - 1), angle);
    CvPoint2D32f points = new CvPoint2D32f(4);
    cvBoxPoints(box, points);
    CvMat pointMat = cvCreateMat(1, 4, CV_32FC2);
    pointMat.put(0, 0, 0, points.position(0).x());
    pointMat.put(0, 0, 1, points.position(0).y());
    pointMat.put(0, 1, 0, points.position(1).x());
    pointMat.put(0, 1, 1, points.position(1).y());
    pointMat.put(0, 2, 0, points.position(2).x());
    pointMat.put(0, 2, 1, points.position(2).y());
    pointMat.put(0, 3, 0, points.position(3).x());
    pointMat.put(0, 3, 1, points.position(3).y());
    CvRect boundingRect = cvBoundingRect(pointMat, 0);

    CvMat dst = cvCreateMat(boundingRect.height(), boundingRect.width(),
            input.type());

    CvMat rotMat = cvCreateMat(2, 3, CV_32FC1);
    cv2DRotationMatrix(center, angle, 1, rotMat);

    double y_1 = ((boundingRect.width() - input.cols()) / 2.0F)
            + rotMat.get(0, 2);
    double y_2 = ((boundingRect.height() - input.rows()) / 2.0F + rotMat
            .get(1, 2));

    rotMat.put(0, 2, y_1);
    rotMat.put(1, 2, y_2);

    cvWarpAffine(input, dst, rotMat);

    return dst;

}

6voto

Hammer Points 4435

Opencv a déjà une fonction pour trouver un rectangle de délimitation qui est ce que le code que vous avez lié fait. Voici un exemple d'utilisation de cette fonction

std::vector<cv::Point2f> points;
points.push_back(cv::Point2f(0,0));
points.push_back(cv::Point2f(13,0));
points.push_back(cv::Point2f(0,11));
points.push_back(cv::Point2f(10,10));

cv::Rect rectangle = cv::boundingRect(points);

Le rectangle renvoyé aura son coin supérieur gauche à (0,0), une largeur de 14 et une hauteur de 12, les plus petites dimensions qui contiendront chaque point. Les étapes nécessaires sont les suivantes

  1. Obtenez les coordonnées des 4 coins de votre image.
  2. Transformez chacune de ces coordonnées par votre transformée affine.
  3. Passez ces points dans cv::boundingRect pour obtenir le rectangle de délimitation.

Les dimensions du rectangle renvoyé par boundingRect sont les dimensions dont votre image de destination a besoin. Ne tenez pas compte de l'emplacement du rectangle de délimitation, mais uniquement de ses dimensions.

Modifier

Le deuxième problème consiste à aligner le centre de votre ancienne image avec le centre de la nouvelle image. Si les dimensions d'un rectangle changent par dw et dh, alors

centerNew.x = centerOld.x+dw/2;
centerNew.y = centerOld.y+dh/2;

Il faut donc que l'ancienne image entière se déplace vers la droite de dw/2 et vers le bas (y positif) dh/2. Ce mouvement peut être intégré dans votre transformée affine. Vous voulez que chaque pixel soit déformé par la transformation et ensuite translaté par la matrice

tran_mat = [1,0,dw/2,
            0,1,dh/2]

Pour combiner les deux, il faut les multiplier ensemble.

NewWarp = tran_mat*old_warp ;

Malheureusement, vous ne pouvez pas simplement les multiplier car leurs dimensions ne correspondent pas. Dans ce cas, convertissez-les toutes deux en matrices 3x3 en ajoutant [0,0,1] comme nouvelle troisième ligne. Vous pourrez alors les multiplier ensemble. Vous pouvez ensuite passer cette matrice dans cv::warpPerspective ou la reconvertir en une transformation affine en supprimant la ligne du bas (qui devrait toujours être [0,0,1]).

Edit 2 Je pense que vous avez deux problèmes. D'abord

cv2DRotationMatrix(centerDst, angle, 1, rotMat);

centerDst doit représenter le centre de l'image d'entrée, qui doit être

CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,input.rows() / 2.0F);

alors changez votre appel à

cv2DRotationMatrix(center, angle, 1, rotMat);

De plus, les variables placées dans la matrice de translation doivent être la distance entre le centre de l'image 1 et le centre de l'image 2. Il existe plusieurs façons de calculer cette distance, mais

trans.put(0, 2, dst.cols() / 2.0F);
trans.put(1, 2, dst.rows() / 2.0F);

n'en fait pas partie. Cela vous donnera le mouvement du centre

trans.put(0, 2, (dst.cols()-input.cols()) / 2.0F);
trans.put(1, 2, (dst.rows()-input.rows()) / 2.0F);

J'imagine que la majeure partie de l'écran était noire parce que vous traduisiez l'image entière de l'écran.

Edit3

Je viens de l'écrire moi-même et de le tester. Voici mon code. Il y a probablement une façon beaucoup plus élégante de faire cela sans toutes les conversions de Matx à pt et vice-versa, mais cela fonctionne pour moi. Il s'agit d'un code C++ mais il montre clairement le processus. Déterminez simplement où vous vous écartez de cette méthode.

cv::Mat im; //your image
cv::Matx23d rot = getRotationMatrix2D(cv::Point2f(im.cols/2,im.rows/2),45,1);
cv::Matx31d tl(0,0,1);
cv::Matx31d tr(im.cols,0,1);
cv::Matx31d bl(0,im.rows,1);
cv::Matx31d br(im.cols,im.rows,1);

std::vector<cv::Point2f> pts;
cv::Matx21d tl2 = rot*tl;
cv::Matx21d tr2 = rot*tr;
cv::Matx21d bl2 = rot*bl;
cv::Matx21d br2 = rot*br;
pts.push_back(cv::Point2f(tl2(0),tl2(1)));
pts.push_back(cv::Point2f(tr2(0),tr2(1)));
pts.push_back(cv::Point2f(bl2(0),bl2(1)));
pts.push_back(cv::Point2f(br2(0),br2(1)));

cv::Rect bounds = cv::boundingRect(pts);

cv::Matx33d tran(1,0,(bounds.width-im.cols)/2,
                 0,1,(bounds.height-im.rows)/2,
                 0,0,1);
cv::Matx33d rot33;
for(int i = 0; i < 6; i++)
    rot33(i) = rot(i);
rot33(2,0) = 0;
rot33(2,1) = 0;
rot33(2,2) = 1;
cv::Matx33d combined = tran*rot33;
cv::Matx23d final;
for(int i = 0; i < 6; i++)
    final(i) = combined(i);

cv::Size im_size(bounds.width,bounds.height);
cv::warpAffine(im, drawing_image,final, im_size);

Edit4

rotation = 45 deg;
width = 300 height = 287

rotation matrix
[0.7071067811865476, 0.7071067811865475, -57.18228688765842;
 -0.7071067811865475, 0.7071067811865476, 147.9497474683058]

translation matrix
[1, 0, 58;
 0, 1, 64;
 0, 0, 1]

combined matrix
[0.7071067811865476, 0.7071067811865475, 0.8177131123415791;
-0.7071067811865475, 0.7071067811865476, 211.9497474683058]

Voici l'image originale

Original Image

et ici il est tourné de 45 degrés en utilisant le code que j'ai posté tel quel.

Image at 45 deg

0voto

nv1620 Points 1

La réponse de Hammer a fait l'affaire pour moi. Elle a cependant donné une erreur. Il fallait juste modifier un peu la syntaxe. Voici la fonction qui a fonctionné pour moi.

Mat rotateImage(Mat im, double angle){
cv::Matx23d rot = getRotationMatrix2D(cv::Point2f(im.cols/2,im.rows/2),angle,1);
cv::Matx31d tl(0,0,1);
cv::Matx31d tr(im.cols,0,1);
cv::Matx31d bl(0,im.rows,1);
cv::Matx31d br(im.cols,im.rows,1);

std::vector<cv::Point2f> pts;
cv::Matx21d tl2 = rot*tl;
cv::Matx21d tr2 = rot*tr;
cv::Matx21d bl2 = rot*bl;
cv::Matx21d br2 = rot*br;
pts.push_back(cv::Point2f(tl2(0),tl2(1)));
pts.push_back(cv::Point2f(tr2(0),tr2(1)));
pts.push_back(cv::Point2f(bl2(0),bl2(1)));
pts.push_back(cv::Point2f(br2(0),br2(1)));

cv::Rect bounds = cv::boundingRect(pts);

cv::Matx33d tran(1,0,(bounds.width-im.cols)/2,0,1,(bounds.height-im.rows)/2,0,0,1);
cv::Matx33d rot33;
for(int i = 0; i < 2; i++)
    for(int j=0; j<3; j++)
        rot33(i,j) = rot(i,j);

rot33(2,0) = 0;
rot33(2,1) = 0;
rot33(2,2) = 1;
cv::Matx33d combined = tran*rot33;
cv::Matx23d finall;
for(int i = 0; i < 2; i++)
    for(int j=0; j<3; j++)
        finall(i,j) = combined(i,j);

cv::Size im_size(bounds.width,bounds.height);
Mat drawing_image;
cv::warpAffine(im, drawing_image,finall, im_size);

return drawing_image;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X