Rotation cv:: Mat en utilisant cv:: warpaffine offsets destination image

j'essaie de tourner a 1296x968 image par 90 degrés en utilisant le API C++ D'OpenCV et je suis confronté à quelques problèmes.

Entrée : input

Tourné : output

Comme vous pouvez le voir, l'image pivotée a quelques problèmes. Tout d'abord, il a la même taille de l'original, même si je crée spécifiquement la destination Mat avec la taille inversée de l'original. En conséquence, l'image de destination est coupée.

je soupçonne que cela se produit parce que j'appelle warpAffine() et je passe la taille de l'original Mat au lieu de la taille de la destination Mat . Mais je fais cela parce que j'ai suivi cette réponse , mais je soupçonne maintenant que la réponse est erronée. C'est donc mon premier doute du problème.

la seconde, c'est que warpAffine() est écrire à la destination à un certain décalage (probablement pour copier les données tournées au milieu de l'image) et cette opération laisse une horrible et grand bord noir autour de l'image.

comment résoudre ces problèmes?

je suis partager le code source ci-dessous:

#include <cv.h>
#include <highgui.h>
#include <iostream>

using namespace cv;
using namespace std;

void rotate(Mat& image, double angle)
{
    Point2f src_center(image.cols/2.0F, image.rows/2.0F);

    Mat rot_matrix = getRotationMatrix2D(src_center, angle, 1.0);

    Mat rotated_img(Size(image.size().height, image.size().width), image.type());

    warpAffine(image, rotated_img, rot_matrix, image.size());
    imwrite("rotated.jpg", rotated_img);
}

int main(int argc, char* argv[])
{
    Mat orig_image = imread(argv[1], 1);
    if (orig_image.empty())
    {
        cout << "!!! Couldn't load " << argv[1] << endl;
        return -1;
    }

    rotate(orig_image, 90);

    return 0;
}
20
demandé sur Community 2011-10-19 00:36:01

5 réponses

j'ai trouvé une solution qui n'implique pas warpAffine() .

mais avant cela, je dois dire (pour les références futures) que mes soupçons étaient fondés, vous avez dû passer la taille de la destination lors de l'appel warpAffine() :

warpAffine(image, rotated_img, rot_matrix, rotated_img.size());

pour autant que je sache, la bordure noire (causée par l'écriture à un décalage) dessinée par cette fonction semble être un comportement standard. J'ai remarqué cela avec l'interface C et aussi avec L'interface C++ D'OpenCV tournant sur Mac et Linux, en utilisant les versions 2.3.1 a et 2.3.0.

la solution que j'ai fini par utiliser est beaucoup plus simple que tout ce chose de la chaîne . Vous pouvez utiliser cv::transpose() et cv::flip() pour faire tourner une image de 90 degrés. Le voici:

Mat src = imread(argv[1], 1);

cv::Mat dst;
cv::transpose(src, dst);
cv::flip(dst, dst, 1);

imwrite("rotated90.jpg", dst);

----j'>

27
répondu karlphillip 2011-10-19 17:21:12

beaucoup de gens ont eu des problèmes avec les images tournantes ou des morceaux d'image en raison des décalages, etc. Donc, je poste une solution pour vous permettre de faire tourner une région (ou un ensemble) d'une image et de la coller dans une autre image ou avoir la fonction Calculer une image où tout ira bien.

// ROTATE p by R
/**
 * Rotate p according to rotation matrix (from getRotationMatrix2D()) R
 * @param R     Rotation matrix from getRotationMatrix2D()
 * @param p     Point2f to rotate
 * @return      Returns rotated coordinates in a Point2f
 */
Point2f rotPoint(const Mat &R, const Point2f &p)
{
    Point2f rp;
    rp.x = (float)(R.at<double>(0,0)*p.x + R.at<double>(0,1)*p.y + R.at<double>(0,2));
    rp.y = (float)(R.at<double>(1,0)*p.x + R.at<double>(1,1)*p.y + R.at<double>(1,2));
    return rp;
}

//COMPUTE THE SIZE NEEDED TO LOSSLESSLY STORE A ROTATED IMAGE
/**
 * Return the size needed to contain bounding box bb when rotated by R
 * @param R     Rotation matrix from getRotationMatrix2D()
 * @param bb    bounding box rectangle to be rotated by R
 * @return      Size of image(width,height) that will compleley contain bb when rotated by R
 */
Size rotatedImageBB(const Mat &R, const Rect &bb)
{
    //Rotate the rectangle coordinates
    vector<Point2f> rp;
    rp.push_back(rotPoint(R,Point2f(bb.x,bb.y)));
    rp.push_back(rotPoint(R,Point2f(bb.x + bb.width,bb.y)));
    rp.push_back(rotPoint(R,Point2f(bb.x + bb.width,bb.y+bb.height)));
    rp.push_back(rotPoint(R,Point2f(bb.x,bb.y+bb.height)));
    //Find float bounding box r
    float x = rp[0].x;
    float y = rp[0].y;
    float left = x, right = x, up = y, down = y;
    for(int i = 1; i<4; ++i)
    {
        x = rp[i].x;
        y = rp[i].y;
        if(left > x) left = x;
        if(right < x) right = x;
        if(up > y) up = y;
        if(down < y) down = y;
    }
    int w = (int)(right - left + 0.5);
    int h = (int)(down - up + 0.5);
    return Size(w,h);
}

/**
 * Rotate region "fromroi" in image "fromI" a total of "angle" degrees and put it in "toI" if toI exists.
 * If toI doesn't exist, create it such that it will hold the entire rotated region. Return toI, rotated imge
 *   This will put the rotated fromroi piece of fromI into the toI image
 *
 * @param fromI     Input image to be rotated
 * @param toI       Output image if provided, (else if &toI = 0, it will create a Mat fill it with the rotated image roi, and return it).
 * @param fromroi   roi region in fromI to be rotated.
 * @param angle     Angle in degrees to rotate
 * @return          Rotated image (you can ignore if you passed in toI
 */
Mat rotateImage(const Mat &fromI, Mat *toI, const Rect &fromroi, double angle)
{
    //CHECK STUFF
    // you should protect against bad parameters here ... omitted ...

    //MAKE OR GET THE "toI" MATRIX
    Point2f cx((float)fromroi.x + (float)fromroi.width/2.0,fromroi.y +
               (float)fromroi.height/2.0);
    Mat R = getRotationMatrix2D(cx,angle,1);
    Mat rotI;
    if(toI)
        rotI = *toI;
    else
    {
        Size rs = rotatedImageBB(R, fromroi);
        rotI.create(rs,fromI.type());
    }

    //ADJUST FOR SHIFTS
    double wdiff = (double)((cx.x - rotI.cols/2.0));
    double hdiff = (double)((cx.y - rotI.rows/2.0));
    R.at<double>(0,2) -= wdiff; //Adjust the rotation point to the middle of the dst image
    R.at<double>(1,2) -= hdiff;

    //ROTATE
    warpAffine(fromI, rotI, R, rotI.size(), INTER_CUBIC, BORDER_CONSTANT, Scalar::all(0)); 

    //& OUT
    return(rotI);
}
10
répondu user1270710 2013-04-23 02:02:54

je me rends compte que vous avez trouvé d'autres solutions plus rapides (la rotation de 90 degrés devrait être très rapide, et n'a pas besoin de toutes les machines de warpAffine), mais je veux aborder le problème de la frontière noire pour toute autre personne qui court à travers cela.

que pouvait faire warpAffine? L'image de destination a été spécifiée pour être plus large qu'elle n'était haute, et la transformation affine a seulement spécifié la rotation (autour du centre de l'image), pas l'échelle. C'est exactement ce qu'il fait. Il est aucune information ne dit à warpAffine ce qui doit être dessiné dans ces frontières noires, donc il les a laissés noirs.

physique Directe de l'expérience: Mettre une feuille sur la table. Dessinez un contour autour (c'est ce que vous avez fait quand vous avez spécifié que vous vouliez que le résultat soit la même forme/taille que l'original). Maintenant, faites pivoter cette feuille de 90 degrés autour de son centre. Regardez la région délimitée par le contour sur la table. Si c'était un noir tableau, il ressemblerait exactement comme votre résultat.

4
répondu DigitalMonk 2012-01-26 00:08:58

peut-être que ça peut aider quelqu'un.

les variables sont

img: image originale

angle: degrés

échelle

dst: image de la destination

int width = img.size().width, 
    height = img.size().height;
Mat rot = getRotationMatrix2D(Point2f(0,0), angle, scale)/scale; //scale later
double sinv = rot.at<double>(0,1),
       cosv = rot.at<double>(0,0);
rot.at<double>(1,2) = width*sinv;  //adjust row offset
Size dstSize(width*cosv + height*sinv, width*sinv + height*cosv);
Mat dst;
warpAffine(img, dst, rot, dstSize);
resize(dst, dst, Size(), scale, scale);  //scale now
4
répondu plhn 2014-03-10 10:11:47

un problème que j'ai trouvé, c'est que la taille de l'image de destination pour warpAffine est définie à image.size() au lieu de rotated_img.size() . Cependant, après la chaîne, il est encore traduit trop loin dans x et y ...J'ai essayé la même distorsion

[ 6.123031769111886e-17 1                     163.9999999999999;
 -1                     6.123031769111886e-17 1132;
  0                     0                     1]

d'Opencv's getroationmatrix2d dans Matlab, et ça a fonctionné parfaitement. Je commence à sentir un bug possible avec warpAffine ...

2
répondu mevatron 2011-10-19 13:21:56