Estoy tratando de hacer un la transformación de perspectiva de un conjunto de puntos con el fin de lograr un eliminación de efecto:

http://nuigroup.com/?ACT=28&fid=27&aid=1892_H6eNAaign4Mrnn30Au8d

Estoy usando la imagen de abajo para las pruebas, y la verde rectángulo de visualización de la zona de interés.

La ejecución de cv::warpPerspective de un falso eliminación de un conjunto de cv::Punto de

Me preguntaba si es posible lograr el efecto estoy esperando para el uso de una simple combinación de cv::getPerspectiveTransform y cv::warpPerspective. Voy a compartir el código fuente que he escrito hasta ahora, pero no funciona. Esta es la imagen resultante:

La ejecución de cv::warpPerspective de un falso eliminación de un conjunto de cv::Punto de

Así que hay un vector<cv::Point> que define la región de interés, pero los puntos son no se almacenan en ningún orden en particular dentro del vector, y eso es algo que no se puede cambiar en el procedimiento de la detección. De todos modos, más tarde, los puntos en el vector se utiliza para definir un RotatedRect, que a su vez es utilizado para el montaje de cv::Point2f src_vertices[4];, una de las variables requeridas por cv::getPerspectiveTransform().

Mi entendimiento sobre vértices y cómo se organizan podría ser uno de los temas. Yo también creo que el uso de un RotatedRect no es la mejor idea para almacenar los puntos originales de la rentabilidad de la inversión, ya que el coordenadas cambio un poco para que quepa en el rectángulo girado, y que no es muy cool.

#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
cv::Mat src = cv::imread(argv[1], 1);
//After some magical procedure, these are points detect that represent 
//the corners of the paper in the picture: 
//[408, 69] [72, 2186] [1584, 2426] [1912, 291]
vector<Point> not_a_rect_shape;
not_a_rect_shape.push_back(Point(408, 69));
not_a_rect_shape.push_back(Point(72, 2186));
not_a_rect_shape.push_back(Point(1584, 2426));
not_a_rect_shape.push_back(Point(1912, 291));
//For debugging purposes, draw green lines connecting those points 
//and save it on disk
const Point* point = &not_a_rect_shape[0];
int n = (int)not_a_rect_shape.size();
Mat draw = src.clone();
polylines(draw, &point, &n, 1, true, Scalar(0, 255, 0), 3, CV_AA);
imwrite("draw.jpg", draw);
//Assemble a rotated rectangle out of that info
RotatedRect box = minAreaRect(cv::Mat(not_a_rect_shape));
std::cout << "Rotated box set to (" << box.boundingRect().x << "," << box.boundingRect().y << ") " << box.size.width << "x" << box.size.height << std::endl;
//Does the order of the points matter? I assume they do NOT.
//But if it does, is there an easy way to identify and order 
//them as topLeft, topRight, bottomRight, bottomLeft?
cv::Point2f src_vertices[4];
src_vertices[0] = not_a_rect_shape[0];
src_vertices[1] = not_a_rect_shape[1];
src_vertices[2] = not_a_rect_shape[2];
src_vertices[3] = not_a_rect_shape[3];
Point2f dst_vertices[4];
dst_vertices[0] = Point(0, 0);
dst_vertices[1] = Point(0, box.boundingRect().width-1);
dst_vertices[2] = Point(0, box.boundingRect().height-1);
dst_vertices[3] = Point(box.boundingRect().width-1, box.boundingRect().height-1);
Mat warpMatrix = getPerspectiveTransform(src_vertices, dst_vertices);
cv::Mat rotated;
warpPerspective(src, rotated, warpMatrix, rotated.size(), INTER_LINEAR, BORDER_CONSTANT);
imwrite("rotated.jpg", rotated);
return 0;
}

Alguien me puede ayudar a arreglar este problema?

  • puede compartir su mágico procedimiento… para detectar puntos que representan las esquinas del papel en la foto, Puede ser de ayuda a mí o a los demás?
  • he hecho aquí, no te olvides de votar hasta.
  • Hola puede u plz tll mí ¿cómo puedo mke ur mágico procedimiento viable para el libro blanco, que es de color blanco escritorio …(me refiero a cómo detectar objeto que tiene algo similar fondo no es exacta bt sus nt contraste ….es wud b plena si u compartir smething para este problema …
  • Hola, no sé. Tiene que haber un cierto contraste entre el papel & fondo para la detección de trabajar. Pero usted puede investigar la detección de sudoku plazas y a ver si trae alguna idea. Buena suerte!
  • bien thnks por el gr8 ayuda 🙂
  • debe en dst_vertices[2] = Punto(0, cuadro.boundingRect().altura-1); la altura ancho? (abajo a la derecha)

InformationsquelleAutor karlphillip | 2011-10-20

6 Comentarios

  1. 41

    Así, el primer problema es el de la esquina de la orden. Deben estar en el mismo orden en ambos vectores.
    Así, si en el primer vector tu pedido es:(arriba-izquierda, abajo-izquierda, abajo-derecha, arriba-derecha) , DEBEN estar en el mismo orden en otro vector.

    Segundo, tener la imagen resultante contiene sólo el objeto de interés, se debe establecer la anchura y la altura de ser el mismo rectángulo resultante de anchura y altura. No te preocupes, el src y dst imágenes en warpPerspective pueden ser de diferentes tamaños.

    Tercero, un rendimiento de preocupación. Mientras que su método es absolutamente exacta, porque usted está haciendo solamente afín transforma (rotar, cambiar el tamaño, alineación), matemáticamente, puede utilizar el afín corespondent de sus funciones. Son mucho más rápido.

    • getAffineTransform()

    • warpAffine().

    Nota importante: getAffine transformar necesita y espera SÓLO 3 puntos, y el resultado de la matriz es 2-por-3, en lugar de 3-por-3.

    Cómo hacer que el resultado de la imagen tiene un tamaño diferente al de la entrada:

    cv::warpPerspective(src, dst, dst.size(), ... );

    uso

    cv::Mat rotated;
    cv::Size size(box.boundingRect().width, box.boundingRect().height);
    cv::warpPerspective(src, dst, size, ... );

    Así que aquí están, y la programación de asignación es más.

    void main()
    {
    cv::Mat src = cv::imread("r8fmh.jpg", 1);
    //After some magical procedure, these are points detect that represent 
    //the corners of the paper in the picture: 
    //[408, 69] [72, 2186] [1584, 2426] [1912, 291]
    vector<Point> not_a_rect_shape;
    not_a_rect_shape.push_back(Point(408, 69));
    not_a_rect_shape.push_back(Point(72, 2186));
    not_a_rect_shape.push_back(Point(1584, 2426));
    not_a_rect_shape.push_back(Point(1912, 291));
    //For debugging purposes, draw green lines connecting those points 
    //and save it on disk
    const Point* point = &not_a_rect_shape[0];
    int n = (int)not_a_rect_shape.size();
    Mat draw = src.clone();
    polylines(draw, &point, &n, 1, true, Scalar(0, 255, 0), 3, CV_AA);
    imwrite("draw.jpg", draw);
    //Assemble a rotated rectangle out of that info
    RotatedRect box = minAreaRect(cv::Mat(not_a_rect_shape));
    std::cout << "Rotated box set to (" << box.boundingRect().x << "," << box.boundingRect().y << ") " << box.size.width << "x" << box.size.height << std::endl;
    Point2f pts[4];
    box.points(pts);
    //Does the order of the points matter? I assume they do NOT.
    //But if it does, is there an easy way to identify and order 
    //them as topLeft, topRight, bottomRight, bottomLeft?
    cv::Point2f src_vertices[3];
    src_vertices[0] = pts[0];
    src_vertices[1] = pts[1];
    src_vertices[2] = pts[3];
    //src_vertices[3] = not_a_rect_shape[3];
    Point2f dst_vertices[3];
    dst_vertices[0] = Point(0, 0);
    dst_vertices[1] = Point(box.boundingRect().width-1, 0); 
    dst_vertices[2] = Point(0, box.boundingRect().height-1);
    /* Mat warpMatrix = getPerspectiveTransform(src_vertices, dst_vertices);
    cv::Mat rotated;
    cv::Size size(box.boundingRect().width, box.boundingRect().height);
    warpPerspective(src, rotated, warpMatrix, size, INTER_LINEAR, BORDER_CONSTANT);*/
    Mat warpAffineMatrix = getAffineTransform(src_vertices, dst_vertices);
    cv::Mat rotated;
    cv::Size size(box.boundingRect().width, box.boundingRect().height);
    warpAffine(src, rotated, warpAffineMatrix, size, INTER_LINEAR, BORDER_CONSTANT);
    imwrite("rotated.jpg", rotated);
    }
    • El código automáticamente pedidos de los puntos, que es un mush -, sino que se vea la imagen está al revés. La solución es la correcta combinación de los puntos aquí » src_vertices[0] = pts[0]; src_vertices[1] = pts[1]; src_vertices[2] = pts[3];`
    • Una propiedad de una transformación afín es: las líneas paralelas permanecen paralelos. En este caso esto no se sostiene. AD es paralelo a BC en el final sólo. Así que esto no es una transformación afín, ¿no?
    • Es la perspectiva de la transformación, no afín. Afín transforma son un subconjunto de la perspectiva transforma, y tienen una serie de propiedades especiales, uno de ellos es el que usted ha mencionado
    • seguro, pero ¿por qué proponer getAffineTransform() para la velocidad, entonces???
    • No es esta propenso a no mantener la relación de aspecto del objeto aislado cuando se corrige? Veo que el uso de la anchura/altura de la caja de contorno como el destino de la transformación. Así que una más de sesgar la imagen del objeto aislado daría una relación de aspecto de las distorsiones en la imagen resultante, sí?
  2. 17

    El problema era el orden en el que los puntos fueron declarados dentro del vector, y luego también había otro problema relacionado con esto en la definición de dst_vertices.

    El orden de los puntos materia a getPerspectiveTransform() y se debe especificar en el siguiente orden:

    1st-------2nd
    |         |
    |         |
    |         |
    3rd-------4th

    Por lo tanto, los puntos de origen necesarios para ser re-ordenó a esto:

    vector<Point> not_a_rect_shape;
    not_a_rect_shape.push_back(Point(408, 69));
    not_a_rect_shape.push_back(Point(1912, 291));
    not_a_rect_shape.push_back(Point(72, 2186));
    not_a_rect_shape.push_back(Point(1584, 2426));

    y el destino:

    Point2f dst_vertices[4];
    dst_vertices[0] = Point(0, 0);
    dst_vertices[1] = Point(box.boundingRect().width-1, 0); //Bug was: had mistakenly switched these 2 parameters
    dst_vertices[2] = Point(0, box.boundingRect().height-1);
    dst_vertices[3] = Point(box.boundingRect().width-1, box.boundingRect().height-1);

    Después de esto, algunos de cultivo necesario para hacer debido a que la imagen resultante no es sólo el área dentro del rectángulo verde como pensé que sería:

    La ejecución de cv::warpPerspective de un falso eliminación de un conjunto de cv::Punto de

    No sé si esto es un bug de OpenCV o si me falta algo, pero el principal problema ha sido resuelto.

    • cómo puedo encontrar la esquina de papel? ( Ya me dibujar líneas alrededor de rectángulo.. ya después de su respuesta en el desbordamiento de pila)estoy luchando con encontrar la esquina del papel y recortar. Ahora tengo las coordenadas del rectángulo x:507 y:418 1776×1372 cuando puedo usar cout<< de Acuerdo a su respuesta. Estoy haciendo lo correcto? Así que por favor proporcionar la manera de cómo determinar las esquinas de papel.
    • Me gustaría poder upvote esto más de una vez! Después de horas de dolor de mi documento explora muy bien. Gracias
    • Siento su dolor! Gracias por el voto.
    • Llegué a casa y tenía que volver a utilizar este para un nuevo Xamarin proyecto!
    • Me alegro de escuchar!
  3. 5

    Cuando se trabaja con un cuadrángulo, OpenCV no es realmente su amigo. RotatedRect va a dar resultados incorrectos. También tendrá una proyección en perspectiva, en lugar de una afín a la proyección como otros mencionados aquí..

    Básicamente lo que se debe hacer es:

    • Bucle a través de todos los polígonos y los segmentos de conectar a aquellos que son casi equel.
    • Ordenarlos para que usted tenga las 4 más grandes segmentos de línea.
    • Se cruzan las líneas y tienes los 4 más probable que los puntos de esquina.
    • Transformar la matriz sobre la perspectiva recopilada a partir de los puntos de esquina y la relación de aspecto del objeto conocido.

    He implementado una clase Quadrangle que tenga a su cuidado de contorno de la cuadrángulo de conversión y se transformará también sobre la perspectiva correcta.

    Ver un trabajo de aplicación aquí:
    Java OpenCV eliminación de un contorno

  4. 4

    ACTUALIZACIÓN: SE RESUELVE

    Casi tengo este trabajo. Tan cerca de ser utilizable. Se corrige su sesgo si correctamente, pero me parece que tienen una escala o traducir el problema. He puesto en el punto de anclaje a cero y también experimentó con el cambio en el modo de escala (aspectFill, la escala de ajuste, etc…).

    Configuración de la alineación de los puntos (rojo hace que sean difíciles de ver):
    La ejecución de cv::warpPerspective de un falso eliminación de un conjunto de cv::Punto de

    Aplicar la transformación calcula:
    La ejecución de cv::warpPerspective de un falso eliminación de un conjunto de cv::Punto de

    Ahora se corrige su sesgo si. Esto se ve muy bien, excepto que no centrada en la pantalla. Mediante la adición de un gesto de desplazamiento a la vista de la imagen puedo arrastrar y verificar que las líneas:
    La ejecución de cv::warpPerspective de un falso eliminación de un conjunto de cv::Punto de

    Esto no es tan simple como traducir por -0.5, -0.5, porque la imagen original se convierten en un polígono que se extiende muy muy lejos (potencialmente), por lo que es rect delimitador es mucho más grande que el marco de la pantalla.

    ¿Alguien vea lo que puedo hacer para obtener esta envuelto? Me gustaría conseguir es comprometido y compartirlo aquí. Este es un tema popular, pero no he encontrado una solución que es tan sencillo como copiar/pegar.

    Código fuente completo está aquí:

    git clone https://github.com/zakkhoyt/Quadrilateral.git

    git checkout demo

    Sin embargo, voy a pegar las partes pertinentes aquí. Este primer método es el mío y es donde puedo conseguir la alineación de los puntos.

    - (IBAction)buttonAction:(id)sender {
    Quadrilateral quadFrom;
    float scale = 1.0;
    quadFrom.topLeft.x = self.topLeftView.center.x / scale;
    quadFrom.topLeft.y = self.topLeftView.center.y / scale;
    quadFrom.topRight.x = self.topRightView.center.x / scale;
    quadFrom.topRight.y = self.topRightView.center.y / scale;
    quadFrom.bottomLeft.x = self.bottomLeftView.center.x / scale;
    quadFrom.bottomLeft.y = self.bottomLeftView.center.y / scale;
    quadFrom.bottomRight.x = self.bottomRightView.center.x / scale;
    quadFrom.bottomRight.y = self.bottomRightView.center.y / scale;
    Quadrilateral quadTo;
    quadTo.topLeft.x = self.view.bounds.origin.x;
    quadTo.topLeft.y = self.view.bounds.origin.y;
    quadTo.topRight.x = self.view.bounds.origin.x + self.view.bounds.size.width;
    quadTo.topRight.y = self.view.bounds.origin.y;
    quadTo.bottomLeft.x = self.view.bounds.origin.x;
    quadTo.bottomLeft.y = self.view.bounds.origin.y + self.view.bounds.size.height;
    quadTo.bottomRight.x = self.view.bounds.origin.x + self.view.bounds.size.width;
    quadTo.bottomRight.y = self.view.bounds.origin.y + self.view.bounds.size.height;
    CATransform3D t = [self transformQuadrilateral:quadFrom toQuadrilateral:quadTo];
    //   t = CATransform3DScale(t, 0.5, 0.5, 1.0);
    self.imageView.layer.anchorPoint = CGPointZero;
    [UIView animateWithDuration:1.0 animations:^{
    self.imageView.layer.transform = t;
    }];
    }
    #pragma mark OpenCV stuff...
    -(CATransform3D)transformQuadrilateral:(Quadrilateral)origin toQuadrilateral:(Quadrilateral)destination {
    CvPoint2D32f *cvsrc = [self openCVMatrixWithQuadrilateral:origin];
    CvMat *src_mat = cvCreateMat( 4, 2, CV_32FC1 );
    cvSetData(src_mat, cvsrc, sizeof(CvPoint2D32f));
    CvPoint2D32f *cvdst = [self openCVMatrixWithQuadrilateral:destination];
    CvMat *dst_mat = cvCreateMat( 4, 2, CV_32FC1 );
    cvSetData(dst_mat, cvdst, sizeof(CvPoint2D32f));
    CvMat *H = cvCreateMat(3,3,CV_32FC1);
    cvFindHomography(src_mat, dst_mat, H);
    cvReleaseMat(&src_mat);
    cvReleaseMat(&dst_mat);
    CATransform3D transform = [self transform3DWithCMatrix:H->data.fl];
    cvReleaseMat(&H);
    return transform;
    }
    - (CvPoint2D32f*)openCVMatrixWithQuadrilateral:(Quadrilateral)origin {
    CvPoint2D32f *cvsrc = (CvPoint2D32f *)malloc(4*sizeof(CvPoint2D32f));
    cvsrc[0].x = origin.topLeft.x;
    cvsrc[0].y = origin.topLeft.y;
    cvsrc[1].x = origin.topRight.x;
    cvsrc[1].y = origin.topRight.y;
    cvsrc[2].x = origin.bottomRight.x;
    cvsrc[2].y = origin.bottomRight.y;
    cvsrc[3].x = origin.bottomLeft.x;
    cvsrc[3].y = origin.bottomLeft.y;
    return cvsrc;
    }
    -(CATransform3D)transform3DWithCMatrix:(float *)matrix {
    CATransform3D transform = CATransform3DIdentity;
    transform.m11 = matrix[0];
    transform.m21 = matrix[1];
    transform.m41 = matrix[2];
    transform.m12 = matrix[3];
    transform.m22 = matrix[4];
    transform.m42 = matrix[5];
    transform.m14 = matrix[6];
    transform.m24 = matrix[7];
    transform.m44 = matrix[8];
    return transform; 
    }

    Actualización: yo tengo trabajo correctamente. Las coordenadas necesarias para ser el origen en el centro, en la superior izquierda. He aplicado xOffset y yOffset y la viola. Código de demostración en el sitio mencionado anteriormente («demo» de la rama)

    • Gracias por el código de ejemplo! Me ayudó a resolver un problema similar. Terminé usando getPerspectiveTransform en lugar de FindHomography y TopLeft en lugar de ScaleToFit para el modo de contenido.
    • Me alegro de que te pueda ayudar. Me encantaría ver su código. Si es abierta, por favor compartir.
    • Por favor, encontrar simplificado código de ejemplo de respuestas separada.
  5. 1

    Muy inspirado por @VaporwareWolf la respuesta, implementado en C# usando Xamarin MonoTouch para iOS. La principal diferencia es que yo estoy usando GetPerspectiveTransform en lugar de FindHomography y TopLeft en lugar de ScaleToFit para el modo de contenido:

        void SetupWarpedImage(UIImage sourceImage, Quad sourceQuad, RectangleF destRectangle)
    {
    var imageContainerView = new UIView(destRectangle)
    {
    ClipsToBounds = true,
    ContentMode = UIViewContentMode.TopLeft
    };
    InsertSubview(imageContainerView, 0);
    var imageView = new UIImageView(imageContainerView.Bounds)
    {
    ContentMode = UIViewContentMode.TopLeft,
    Image = sourceImage
    };
    var offset = new PointF(-imageView.Bounds.Width / 2, -imageView.Bounds.Height / 2);
    var dest = imageView.Bounds;
    dest.Offset(offset);
    var destQuad = dest.ToQuad();
    var transformMatrix = Quad.GeneratePerspectiveTransformMatrixFromQuad(sourceQuad, destQuad);
    CATransform3D transform = transformMatrix.ToCATransform3D();
    imageView.Layer.AnchorPoint = new PointF(0f, 0f);
    imageView.Layer.Transform = transform;
    imageContainerView.Add(imageView);
    }
    • Donde se ToCATransform3D definido?
    • es sólo a partir de mi aplicación: public static CATransform3D ToCATransform3D( este DoubleMatrix matriz) { CATransform3D transformar = CATransform3D.La identidad, a la transformación.m11 = (float)de la matriz[0,0]; transformar.m21 = (float)de la matriz[0,1]; transformar.m41 = (float)de la matriz[0,2]; transformar.m12 = (float)de la matriz[1,0]; transformar.m22 = (float)de la matriz[1,1]; transformar.m42 = (float)de la matriz[1,2]; transformar.m14 = (float)de la matriz[2,0]; transformar.m24 = (float)de la matriz[2,1]; transformar.m44 = (float)de la matriz[2,2]; return transformar; } // lo Siento por los pobres formato como comentario

Dejar respuesta

Please enter your comment!
Please enter your name here