Поверните cv :: Mat, используя cv :: warpAffine, смещает конечное изображение - PullRequest
20 голосов
/ 19 октября 2011

Я пытаюсь повернуть a 1296x968 изображение на 90 градусов , используя C ++ API OpenCV, и я сталкиваюсь несколько проблем.

Input : input

повернутый : output

Как видите, у повернутого изображения есть несколько проблем. Во-первых, он имеет тот же размер оригинала, хотя я специально создаю пункт назначения Mat с инвертированным размером оригинала. В результате целевое изображение обрезается.

Я подозреваю, что это происходит, потому что я звоню warpAffine() и передаю размер оригинала Mat вместо размера пункта назначения Mat. Но я делаю это, потому что Я следовал за этим ответом , но теперь я подозреваю, что ответ может быть неправильным. Так что это мое первое сомнение / проблема.

Во-вторых, warpAffine() - это запись в пункт назначения с определенным смещением (возможно, для копирования повернутых данных в середину изображения), и эта операция оставляет ужасное и большая черная рамка вокруг изображения.

Как мне исправить эти проблемы?

Я делюсь исходным кодом ниже:

#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;
}

Ответы [ 5 ]

27 голосов
/ 19 октября 2011

Я нашел решение , которое не включает warpAffine().

Но перед этим я должен заявить (для будущих ссылок), что мое подозрение было правильным, вам нужно было передать размер пункта назначения при вызове warpAffine():

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

Насколько я могу судить, черная граница (вызванная записью со смещением), нарисованная этой функцией, кажется, является ее стандартным поведением. Я заметил это с интерфейсом C, а также с интерфейсом C ++ OpenCV, работающим на Mac и Linux, используя версии 2.3.1a и 2.3.0.

Решение, которое я в итоге использовал, намного проще , чем все это деформация . Вы можете использовать cv::transpose() и cv::flip(), чтобы повернуть изображение на 90 градусов. Вот оно:

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

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

imwrite("rotated90.jpg", dst);

image ---- I> image

10 голосов
/ 23 апреля 2013

У многих людей были проблемы с вращением изображений или фрагментов изображения из-за смещений и т. Д. Итак, я публикую решение, позволяющее вам вращать область (или целое) изображения и вставлять его в другое изображение есть функция вычислить изображение, где все будет просто соответствовать.

// 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);
}
4 голосов
/ 10 марта 2014

Может быть, это может кому-то помочь.
переменные
img: исходное изображение
угол: градусы
масштаб
dst: конечное изображение

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 голосов
/ 26 января 2012

Я понимаю, что вы нашли другие более быстрые решения (поворот на 90 градусов должен быть очень быстрым и не требует всего механизма warpAffine), но я хочу решить проблему черной границы для всех, кто сталкивается с этим.

Что еще может сделать WarpAffine?Целевое изображение было указано шире, чем оно было высоким, а аффинное преобразование указывало только указанное вращение (вокруг центра изображения), а не масштабирование.Это именно то, что он сделал.Нигде нет информации, чтобы сообщить warpAffine, что следует рисовать на этих черных границах, поэтому они оставили их черными.

Прямой физический эксперимент: положите лист на стол.Нарисуйте контур вокруг него (это то, что вы сделали, когда указали, что хотите, чтобы результат был той же формы / размера, что и оригинал).Теперь поверните этот лист на 90 градусов вокруг его центра.Посмотрите на область, ограниченную контуром на столе.Если бы это был черный стол, он бы выглядел точно так же, как ваш результат.

2 голосов
/ 19 октября 2011

Одна проблема, которую я обнаружил, заключается в том, что размер целевого изображения для warpAffine установлен на image.size() вместо rotated_img.size().Однако после деформации он все еще слишком далеко переводится в x и y ... Я попробовал точно такой же деформацию

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

из getCotationMatrix2D в OpenCVв Matlab, и это сработало отлично.Я начинаю чувствовать запах возможной ошибки с warpAffine ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...