OpenCV warpperspective - PullRequest
       5

OpenCV warpperspective

15 голосов
/ 22 мая 2011

По какой-то причине всякий раз, когда я использую функцию OpenCV warpPerspective (), окончательное деформированное изображение не содержит всего исходного изображения. Кажется, что левая часть изображения обрезана. Я думаю, что причина этого заключается в том, что деформированное изображение создается в крайнем левом положении холста для warpPerspective (). Есть ли способ исправить это? Спасибо

Ответы [ 7 ]

31 голосов
/ 22 ноября 2011

Проблема возникает из-за того, что гомография отображает часть изображения на отрицательные значения x, y, которые находятся за пределами области изображения и поэтому не могут быть нанесены на график. то, что мы хотим сделать, это сместить деформированный вывод на некоторое количество пикселей, чтобы «шунтировать» все деформированное изображение в положительные координаты (и, следовательно, внутри области изображения).

Гомографии могут быть объединены с использованием умножения матриц (вот почему они такие мощные). Если A и B являются гомографиями, то AB представляет гомографию, которая сначала применяет B, а затем A.

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

Матрица 2D-гомографии выглядит следующим образом:

[R11,R12,T1]
[R21,R22,T2]
[ P , P , 1]

, где R представляет матрицу вращения, T представляет перевод, а P представляет перспективную деформацию. Итак, чисто поступательная гомография выглядит так:

[ 1 , 0 , x_offset]
[ 0 , 1 , y_offset]
[ 0 , 0 ,    1    ]

Так что просто умножьте вашу гомографию на матрицу, аналогичную приведенной выше, и ваше выходное изображение будет смещено.

(Убедитесь, что вы используете матричное умножение, а не поэлементное умножение!)

4 голосов
/ 12 июля 2013

Я сделал один метод ... Это работает.

  perspectiveTransform(obj_corners,scene_corners,H);
int maxCols(0),maxRows(0);

 for(int i=0;i<scene_corners.size();i++)
{
   if(maxRows < scene_corners.at(i).y)
        maxRows = scene_corners.at(i).y;
   if(maxCols < scene_corners.at(i).x)
        maxCols = scene_corners.at(i).x;
}

Я просто нахожу максимум точек x и y соответственно и помещаю его на

warpPerspective( tmp, transformedImage, homography, Size( maxCols, maxRows ) );
4 голосов
/ 25 октября 2011

Секрет состоит из двух частей: матрицы преобразования (гомография) и полученного размера изображения.

  • вычисляет правильное преобразование с помощью getPerspectiveTransform ().Возьмите 4 точки из исходного изображения, вычислите их правильное положение в пункте назначения, поместите их в два вектора в одинаковом порядке и используйте их для вычисления матрицы перспективного преобразования.

  • Убедитесь, чторазмер целевого изображения (третий параметр для warpPerspective ()) - это именно то, что вам нужно.Определите его как размер (myWidth, myHeight).

2 голосов
/ 17 мая 2016

Попробуйте следующее homography_warp.

void homography_warp(const cv::Mat& src, const cv::Mat& H, cv::Mat& dst);

src - исходное изображение.

H - ваша гомография.

dst -искаженное изображение.

homography_warp отрегулируйте свою гомографию, как описано https://stackoverflow.com/users/1060066/matt-freeman в его ответе https://stackoverflow.com/a/8229116/15485

// Convert a vector of non-homogeneous 2D points to a vector of homogenehous 2D points.
void to_homogeneous(const std::vector< cv::Point2f >& non_homogeneous, std::vector< cv::Point3f >& homogeneous)
{
    homogeneous.resize(non_homogeneous.size());
    for (size_t i = 0; i < non_homogeneous.size(); i++) {
        homogeneous[i].x = non_homogeneous[i].x;
        homogeneous[i].y = non_homogeneous[i].y;
        homogeneous[i].z = 1.0;
    }
}

// Convert a vector of homogeneous 2D points to a vector of non-homogenehous 2D points.
void from_homogeneous(const std::vector< cv::Point3f >& homogeneous, std::vector< cv::Point2f >& non_homogeneous)
{
    non_homogeneous.resize(homogeneous.size());
    for (size_t i = 0; i < non_homogeneous.size(); i++) {
        non_homogeneous[i].x = homogeneous[i].x / homogeneous[i].z;
        non_homogeneous[i].y = homogeneous[i].y / homogeneous[i].z;
    }
}

// Transform a vector of 2D non-homogeneous points via an homography.
std::vector<cv::Point2f> transform_via_homography(const std::vector<cv::Point2f>& points, const cv::Matx33f& homography)
{
    std::vector<cv::Point3f> ph;
    to_homogeneous(points, ph);
    for (size_t i = 0; i < ph.size(); i++) {
        ph[i] = homography*ph[i];
    }
    std::vector<cv::Point2f> r;
    from_homogeneous(ph, r);
    return r;
}

// Find the bounding box of a vector of 2D non-homogeneous points.
cv::Rect_<float> bounding_box(const std::vector<cv::Point2f>& p)
{
    cv::Rect_<float> r;
    float x_min = std::min_element(p.begin(), p.end(), [](const cv::Point2f& lhs, const cv::Point2f& rhs) {return lhs.x < rhs.x; })->x;
    float x_max = std::max_element(p.begin(), p.end(), [](const cv::Point2f& lhs, const cv::Point2f& rhs) {return lhs.x < rhs.x; })->x;
    float y_min = std::min_element(p.begin(), p.end(), [](const cv::Point2f& lhs, const cv::Point2f& rhs) {return lhs.y < rhs.y; })->y;
    float y_max = std::max_element(p.begin(), p.end(), [](const cv::Point2f& lhs, const cv::Point2f& rhs) {return lhs.y < rhs.y; })->y;
    return cv::Rect_<float>(x_min, y_min, x_max - x_min, y_max - y_min);
}

// Warp the image src into the image dst through the homography H.
// The resulting dst image contains the entire warped image, this
// behaviour is the same of Octave's imperspectivewarp (in the 'image'
// package) behaviour when the argument bbox is equal to 'loose'.
// See http://octave.sourceforge.net/image/function/imperspectivewarp.html
void homography_warp(const cv::Mat& src, const cv::Mat& H, cv::Mat& dst)
{
    std::vector< cv::Point2f > corners;
    corners.push_back(cv::Point2f(0, 0));
    corners.push_back(cv::Point2f(src.cols, 0));
    corners.push_back(cv::Point2f(0, src.rows));
    corners.push_back(cv::Point2f(src.cols, src.rows));

    std::vector< cv::Point2f > projected = transform_via_homography(corners, H);
    cv::Rect_<float> bb = bounding_box(projected);

    cv::Mat_<double> translation = (cv::Mat_<double>(3, 3) << 1, 0, -bb.tl().x, 0, 1, -bb.tl().y, 0, 0, 1);

    cv::warpPerspective(src, dst, translation*H, bb.size());
}
1 голос
/ 22 мая 2011

warpPerspective () работает нормально.Не нужно его переписывать.Возможно, вы используете его неправильно.

Запомните следующие советы:

  1. (0,0) пикселей не в центре, а в левом верхнем углу.Таким образом, если вы увеличите изображение x2, вы потеряете нижнюю и правую части, а не границу (как в matlab).
  2. Если вы деформируете изображение дважды, лучше умножить преобразования и активировать функцию один раз.
  3. Я думаю, что это работает только на матрицах char / int, а не на float / double.
  4. Когда у вас есть преобразование, сначала применяются масштабирование / наклон / поворот / перспектива и, наконец, перевод.Поэтому, если часть изображения отсутствует, просто измените переход (две верхние строки последнего столбца) в матрице.
0 голосов
/ 13 марта 2018

Вот решение opencv-python для вашей проблемы, я поместил его на github: https://github.com/Sanster/notes/blob/master/opencv/warpPerspective.md

Ключевой момент: user3094631 сказал, получить две матрицы перевода (T1, T2) и применить к матрице поворота (M) T2*M*T1

В коде, который я даю, T1 от центральной точки исходного изображения, а T2 от левой верхней точки преобразованного ограничивающего прямоугольника. Преобразованный ограничивающий прямоугольник исходит из исходных угловых точек:

height = img.shape[0]
width = img.shape[1]
#..get T1
#..get M
pnts = np.asarray([
    [0, 0],
    [width, 0],
    [width, height],
    [0, height]
    ], dtype=np.float32)
pnts = np.array([pnts])
dst_pnts = cv2.perspectiveTransform(pnts, M * T1)[0]
dst_pnts = np.asarray(dst_pnts, dtype=np.float32)
bbox = cv2.boundingRect(dst_pnts)
T2 = np.matrix([[1., 0., 0 - bbox[0]],
                [0., 1., 0 - bbox[1]],
                [0., 0., 1.]])
0 голосов
/ 23 мая 2016

это мое решение

, поскольку третий параметр в "warpPerspective ()" является матрицей преобразования,

мы можем создать матрицу преобразования, которая сначала перемещает изображение назад, а затем поворачиваетimage, наконец, перемещает изображение вперед.

В моем случае у меня есть изображение с высотой 160 пикселей и шириной 160 пикселей.Я хочу сначала повернуть изображение вокруг [80,80], а не вокруг [0,0]

, переместить изображение назад (что означает T1)

, а затем повернуть изображение (это означает, чтоR)

наконец перемещает изображение вперед (что означает T2)

void rotateImage(Mat &src_img,int degree)
{
float radian=(degree/180.0)*M_PI;
Mat R(3,3,CV_32FC1,Scalar(0));
R.at<float>(0,0)=cos(radian);R.at<float>(0,1)=-sin(radian);
R.at<float>(1,0)=sin(radian);R.at<float>(1,1)=cos(radian);
R.at<float>(2,2)=1;
Mat T1(3,3,CV_32FC1,Scalar(0));
T1.at<float>(0,2)=-80;
T1.at<float>(1,2)=-80;
T1.at<float>(0,0)=1;
T1.at<float>(1,1)=1;
T1.at<float>(2,2)=1;
Mat T2(3,3,CV_32FC1,Scalar(0));
T2.at<float>(0,2)=80;
T2.at<float>(1,2)=80;
T2.at<float>(0,0)=1;
T2.at<float>(1,1)=1;
T2.at<float>(2,2)=1;

std::cerr<<T1<<std::endl;
std::cerr<<R<<std::endl;
std::cerr<<T2<<std::endl;
std::cerr<<T2*R*T1<<"\n"<<std::endl;

cv::warpPerspective(src_img, src_img, T2*R*T1, src_img.size(), cv::INTER_LINEAR);
}
...