Поверните cv::Mat, используя cv::warpAffine, смещает конечное изображение

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

Вход:вход

Повернуто:выход

Как видите, у повернутого изображения есть несколько проблем. Во-первых, он имеет тот же размер оригинала, хотя я специально создаю пункт назначения 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 ответов

Решение

Я нашел решение, которое не связано с 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);

---- I>

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

// 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);
}

Может быть, это может кому-то помочь.
переменные
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

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

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

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

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

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

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

Другие вопросы по тегам