c++ - OpenCV warpperspective
image image-processing (6)
warpPerspective () funciona bien. No es necesario volver a escribirlo. Probablemente lo uses incorrectamente.
Recuerde los siguientes consejos:
- (0,0) píxeles no está en el centro, sino en la esquina superior izquierda. Entonces, si amplifica la imagen x2, perderá las partes inferior y derecha, no el borde (como en matlab).
- Si deforma la imagen dos veces, es mejor multiplicar las transformaciones y activar la función una vez.
- Creo que funciona solo en matrices char / int y no en float / double.
- Cuando tienes una transformación, primero se aplican zoom / skew / rotation / perspective y finalmente la traducción. Entonces, si falta parte de la imagen, simplemente cambie la transacción (dos filas superiores de la última columna) en la matriz.
Por alguna razón, cada vez que uso la función warpPerspective () de OpenCV, la imagen deformada final no contiene todo en la imagen original. La parte izquierda de la imagen parece cortada. Creo que la razón por la que esto está sucediendo es porque la imagen deformada se crea en la posición más a la izquierda del lienzo para el warpPerspective (). ¿Hay alguna forma de arreglar esto? Gracias
El secreto viene en dos partes: la matriz de transformación (homografía) y el tamaño de imagen resultante.
calcule una transformación correcta usando getPerspectiveTransform (). Tome 4 puntos de la imagen original, calcule su posición correcta en el destino, colóquelos en dos vectores en el mismo orden y utilícelos para calcular la matriz de transformación de perspectiva.
Asegúrese de que el tamaño de la imagen de destino (tercer parámetro para warpPerspective ()) sea exactamente lo que desea. Defínalo como Tamaño (myWidth, myHeight).
El problema se produce porque la homografía asigna parte de la imagen a valores x, y negativos que están fuera del área de la imagen, por lo que no se pueden trazar. lo que deseamos hacer es compensar la salida deformada por cierto número de píxeles para "derivar" toda la imagen deformada en coordenadas positivas (y, por lo tanto, dentro del área de la imagen).
Las homografías se pueden combinar mediante la multiplicación de matrices (por lo que son tan potentes). Si A y B son homografías, entonces AB representa la homografía que aplica B primero, y luego A.
Debido a esto, todo lo que tenemos que hacer para compensar el resultado es crear la matriz de homografía para una traducción mediante un desplazamiento, y luego pre-multiplicar eso por nuestra matriz de homografía original.
Una matriz de homografía 2D se ve así:
[R11,R12,T1]
[R21,R22,T2]
[ P , P , 1]
donde R representa una matriz de rotación, T representa una traducción y P representa una deformación en perspectiva. Y entonces una homografía puramente traduccional se ve así:
[ 1 , 0 , x_offset]
[ 0 , 1 , y_offset]
[ 0 , 0 , 1 ]
Así que solo premultiply su homografía por una matriz similar a la anterior, y su imagen de salida se compensará.
(¡Asegúrate de usar multiplicación de matrices, no multiplicación sabia de elementos!)
He hecho un método ... Está funcionando.
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;
}
Acabo de encontrar el máximo de los puntos x y y, respectivamente, y ponerlo en
warpPerspective( tmp, transformedImage, homography, Size( maxCols, maxRows ) );
esta es mi solución
dado que el tercer parámetro en "warpPerspective ()" es una matriz de transformación,
podemos hacer una matriz de transformación, que mueve la imagen hacia atrás primero, luego gira la imagen y finalmente mueve la imagen hacia adelante.
En mi caso, tengo una imagen con una altura de 160 px y un ancho de 160 px. Quiero rotar la imagen alrededor de [80,80] en lugar de alrededor de [0,0]
primero, mueve la imagen hacia atrás (eso significa T1)
luego gira la imagen (eso significa R)
finalmente mueve la imagen hacia adelante (eso significa 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);
}
Prueba el siguiente homography_warp
.
void homography_warp(const cv::Mat& src, const cv::Mat& H, cv::Mat& dst);
src
es la imagen de origen.
H
es tu homografía.
dst
es la imagen deformada.
homography_warp
ajusta tu homografía como se describe en https://.com/users/1060066/matt-freeman en su respuesta https://.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());
}