cv::warpPerspective показывает только часть искаженного изображения

Я меняю изображение с фронтальной перспективы на вид глазами, используя getHomography и warpPerspective.

Это работает в том, что изображение искажается до желаемой перспективы, но кадрирование отключено. Он перемещает искаженное изображение в значительной степени за пределы поля изображения. Я предполагаю, что причина в том, что операция приводит к отрицательным координатам.

Я рассчитал точки для расчета матрицы перевода вручную, а не с помощью каких-либо функций opencv: s для этого, поскольку, например, функции шахматной доски не смогли определить правильные точки.

Я думаю, это можно исправить, внеся дополнительные изменения в матрицу преобразования. Но как это делается? Кроме того, есть ли способ убедиться, что преобразованное изображение центрировано по оси X, а затем позволить оси Y отрегулировать желаемое положение?

Фрагмент кода, который выполняет эту работу сейчас:

cv::Mat image; // image is loaded with the original image

cv::Mat warpPers; // The container for the resulting image
cv::Mat H;

std::vector<cv::Point2f> src;
std::vector<cv::Point2f> dst;

// In reality several more points.
src.push_back(cv::Point2f(264,301));
src.push_back(cv::Point2f(434,301));
src.push_back(cv::Point2f(243,356));
src.push_back(cv::Point2f(476,356));

dst.push_back(cv::Point2f(243,123));
dst.push_back(cv::Point2f(476,123));
dst.push_back(cv::Point2f(243,356));
dst.push_back(cv::Point2f(476,356));

H = cv::findHomography(src, dst, CV_RANSAC);

cv::warpPerspective(image, 
newPers,
H,
cv::Size(3000,3000),
cv::INTER_NEAREST | CV_WARP_FILL_OUTLIERS
);

cv::namedWindow("Warped persp", cv::WINDOW_AUTOSIZE );
cv::imshow( "Warped persp", newPers);

person Einar Sundgren    schedule 06.03.2014    source источник
comment
вы можете трансформировать граничные точки вашего изображения cv::Point2f(0,0)``cv::Point2f(image.cols, 0)``cv::Point2f(image.cols, image.rows) cv::Point2f(0, image.rows) вручную (множественно играть с вашей гомографией) и проверить, соответствуют ли они вашему dst-изображению размером 3000,3000. Вычислите их минимальное/максимальное местоположение и соответствующим образом измените часть перевода вашей гомографии и/или масштаб (или размер изображения dst). Однако я не проверял, в порядке ли остальная часть вашего кода;)   -  person Micka    schedule 06.03.2014
comment
@Einar Если вы хотите, чтобы функция findHomography выполняла свою работу, вы должны тщательно определить точки назначения, чтобы искаженное изображение соответствовало тому, что вам нужно. Однако априори нет ничего плохого в составлении омографии с пользовательским переводом (полученным, как сказал Мика) перед деформацией изображения.   -  person BConic    schedule 06.03.2014


Ответы (3)


Opencv дает очень удобный способ сделать перспективное преобразование. Единственное, что вам нужно сделать, это позаботиться о возврате гомографии с помощью findHomography. Действительно, возможно, некоторые точки изображения, которое вы предоставляете, находятся в отрицательной части оси x или y. Таким образом, вы должны сделать некоторую проверку, прежде чем деформировать изображение.

шаг 1: найдите гомографию H с помощью findHomography, вы получите классическую структуру для гомографии

H = [ h00, h01, h02;
      h10, h11, h12;
      h20, h21,   1];

шаг 2: поиск положения углов изображения после деформации

Итак, позвольте мне определить порядок для угла:

(0,0) ________ (0, w)
     |        |
     |________|
(h,0)          (h,w)

Для этого просто создайте такую ​​матрицу:

P = [0, w, w, 0;
     0, 0, h, h;
     1, 1, 1, 1]

Сделайте продукт с H и получите деформированные координаты:

P' = H * P

шаг 3: проверьте минимум в x и y с этими новыми 4 точками и получите размер искаженного изображения. После того, как вы сделали продукт, вы получите что-то вроде этого:

P' = [s1*x1, s2*x2, s3*x3, s4*x4;
      s1*y1, s2*y2, s3*y3, s4*y4;
      s1   , s2   , s3   , s4]

Итак, чтобы получить новую действительную координату, просто разделите строки 1 и 2 на строку 3.

После этого проверьте минимум для столбца в первой строке и минимум для строки во второй строке (используйте cvReduce)

чтобы найти ограничивающую рамку, которая будет содержать изображение (т.е. размер матрицы dst для функции warpPerspective), просто найдите с помощью cvReduce максимум по каждой строке

пусть minx будет минимумом в первой строке (т.е. для столбца), maxx (максимум в 1-й строке), min и maxy во второй строке.

Таким образом, размер деформированного изображения должен быть cvSize(maxx-minx, maxy-miny)

шаг 4: добавить поправку к гомографии Проверить, являются ли minx и/или miny отрицательными, если minx ‹ 0, то добавить -minx к h02 и если miny ‹ 0, то добавить -miny к h12

поэтому H должно быть:

H = [ h00, h01, h02-minx; //if minx <0
      h10, h11, h12-miny; //if miny <0
      h20, h21,   1];

шаг 5: деформируйте изображение

person user3529407    schedule 13.04.2014
comment
@Einar Эйнар: Вы пробовали это решение? Это сработало для вас? - person navderm; 15.12.2014
comment
Просто комментарий. Если вы используете H.inv() для преобразования, вы должны изменить перевод в инвертированной гомографии, а не в основной матрице гомографии. Я сделал эту ошибку. Я надеюсь, что никто больше не тратит больше времени на такие глупые усилия :) - person navderm; 18.12.2014
comment
Это правильно до шага 4. Добавление смещения к матрице преобразования гомографии не работает. Вместо этого создайте матрицу идентичности 3x3 (я назову ее T), поместите значения перевода в t02 и t12, затем создайте новую матрицу гомографии H' = T * H. Используйте H' в вызове warpPerspective. - person SSteve; 24.02.2016
comment
Можете ли вы дать больше информации после расчета P'. Вставьте слова в пример кода, пожалуйста - person Martin86; 13.11.2020

Я думаю, что этот вопрос OpenCV warpperspective похож на текущий вопрос cv::warpPerspective показывает только часть искаженного изображения

Итак, я даю вам свой ответ https://stackoverflow.com/a/37275961/15485 также здесь:

Попробуйте 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());
}
person Alessandro Jacopson    schedule 20.05.2016
comment
Эй, спасибо за этот код. Я преобразовал его в java opencv, если кому-то понадобится такой процесс в будущем. gitlab.com/snippets/1917893 - person Lucas Montenegro Carvalhaes; 28.11.2019

Если я правильно понял, в основном вопрос требует метода вычисления правильного смещения для перевода искаженного изображения. Я объясню, как получить правильное смещение для перевода. Идея состоит в том, что совпадающие объекты на двух изображениях должны иметь одинаковую координату в конечном объединенном изображении.

Допустим, мы ссылаемся на изображения следующим образом:

  • «исходное изображение» (si): изображение, которое необходимо деформировать.
  • «целевое изображение» (di): изображение, в перспективу которого «исходное изображение» будет деформировано.
  • 'исходное искаженное изображение' (wsi): исходное изображение после его деформации в перспективу целевого изображения.

Вот что вам нужно сделать, чтобы рассчитать смещение для перевода:

  1. После того, как вы отобрали хорошие совпадения и нашли маску из гомографии, сохраните ключевую точку наилучшего совпадения (одну с минимальным расстоянием и являющуюся вставкой (должна получить значение 1 в маске, полученной из вычисления гомографии)) в si и di. Скажем, ключевая точка наилучшего соответствия в si and diisbm_siandbm_di` соответственно..

    bm_si = [x1, y1,1]

    bm_di = [x2, y2, 1]

  2. Найдите положение bm_si в wsi, просто умножив его на матрицу гомографии (H). bm_wsi = np.dot(H,bm_si)

    bm_wsi = [x/bm_wsi[2] for x in bm_wsi]

  3. В зависимости от того, где вы будете размещать di на выходе si деформации (=wsi), отрегулируйте bm_di

    Допустим, если вы выполняете деформацию от левого изображения к правому (например, левое изображение si, а правое изображение di), то вы поместите di справа от wsi и, следовательно, bm_di[0] += si.shape[0]

  4. Теперь после вышеуказанных шагов

    x_offset = bm_di[0] - bm_si[0]

    y_offset = bm_di[1] - bm_si[1]

  5. Используя рассчитанное смещение, найдите новую матрицу гомографии и деформируйте si.

    T = np.array([[1, 0, x_offset], [0, 1, y_offset], [0, 0, 1]])

    translated_H = np.dot(T.H)

    wsi_frame_size = tuple(2*x for x in si.shape)

    stitched = cv2.warpPerspective(si, translated_H, wsi_frame_size)

    stitched[0:si.shape[0],si.shape[1]:] = di

person Vijendra1125    schedule 25.08.2020
comment
Спасибо за ответ. Мой вопрос был задан несколько лет назад, и сегодня у меня нет разумного способа подтвердить ваше предложение. Мое фактическое решение состояло в том, чтобы вручную настроить его на известный размер. В конце концов, это было только для PoC. - person Einar Sundgren; 27.08.2020