Dibujando polígonos rellenos usando eventos del mouse en cv abierto usando python

Estoy tratando de dibujar un polígono entre las coordenadas que se obtendrían haciendo clic con los eventos del mouse.

El primer clic debe definir el punto de inicio del polígono. Cada clic adicional debe dibujar un segmento de línea del clic anterior. Cuando todos los puntos en los que se haga clic dibujen el polígono, se debe rellenar el interior del polígono.

¿Alguien puede sugerir cómo dibujar polígonos entre los puntos donde hago clic en la imagen? Estoy considerando la función cv2.polylines , sin embargo, no tengo idea de cómo integrarla con la función SetMouseCallback .

Para hacer que la interfaz de usuario sea más intuitiva (ya que sería muy difícil para el usuario hacer clic exactamente en el mismo lugar que el punto de inicio), usemos las siguientes acciones:

  • Haga clic con el botón izquierdo del ratón para agregar un punto al polígono en la posición en la que se hizo clic
  • El clic derecho del ratón completa la entrada de datos, y hace que el progtwig muestre el polígono final completo

Necesitaremos varias variables para realizar un seguimiento de nuestro progreso:

  • Una lista de los puntos que definen nuestro polígono. Cada punto será una tupla (x, y)
  • Una bandera booleana que significará cuando se complete la entrada de datos
  • Como beneficio adicional, la última posición conocida del cursor del mouse, por lo que podemos animar el segmento que se está ingresando actualmente (una línea que sigue al cursor).

Usaremos una callback del mouse para actualizar esas variables periódicamente cuando ocurran los eventos de mouse apropiados:

  • EVENT_MOUSEMOVE – el mouse se ha movido, actualiza la posición actual
  • EVENT_LBUTTONDOWN : el usuario presionó el botón izquierdo del mouse, agrega la posición actual a la lista de puntos
  • EVENT_RBUTTONDOWN : el usuario presionó el botón derecho del mouse y marcó la entrada de datos como completa

Finalmente, tendremos una función implementando el bucle de visualización.

Esta función primero creará una ventana con nombre, dibujará un canvas en blanco y configurará la callback del mouse. Luego, repetidamente mantendrá la actualización de la pantalla:

  • Creando una nueva imagen de canvas (sobre la cual dibujar)
  • Cuando haya puntos ingresados, dibuje los segmentos conectados usando cv2.polyline
  • Dibuje el segmento actual apuntando desde el último punto ingresado a la posición actual con un color diferente usando cv2.line .
  • Mostrar la nueva imagen.
  • Espera un poco, bombea los mensajes de la ventana mientras lo haces.

Una vez que se completa el proceso de ingreso de datos, la función dibujará el polígono final lleno y una imagen de canvas limpio, la mostrará y, cuando el usuario presione una tecla, devolverá la imagen final.


Ejemplo de código

 import numpy as np import cv2 # ============================================================================ CANVAS_SIZE = (600,800) FINAL_LINE_COLOR = (255, 255, 255) WORKING_LINE_COLOR = (127, 127, 127) # ============================================================================ class PolygonDrawer(object): def __init__(self, window_name): self.window_name = window_name # Name for our window self.done = False # Flag signalling we're done self.current = (0, 0) # Current position, so we can draw the line-in-progress self.points = [] # List of points defining our polygon def on_mouse(self, event, x, y, buttons, user_param): # Mouse callback that gets called for every mouse event (ie moving, clicking, etc.) if self.done: # Nothing more to do return if event == cv2.EVENT_MOUSEMOVE: # We want to be able to draw the line-in-progress, so update current mouse position self.current = (x, y) elif event == cv2.EVENT_LBUTTONDOWN: # Left click means adding a point at current position to the list of points print("Adding point #%d with position(%d,%d)" % (len(self.points), x, y)) self.points.append((x, y)) elif event == cv2.EVENT_RBUTTONDOWN: # Right click means we're done print("Completing polygon with %d points." % len(self.points)) self.done = True def run(self): # Let's create our working window and set a mouse callback to handle events cv2.namedWindow(self.window_name, flags=cv2.CV_WINDOW_AUTOSIZE) cv2.imshow(self.window_name, np.zeros(CANVAS_SIZE, np.uint8)) cv2.waitKey(1) cv2.cv.SetMouseCallback(self.window_name, self.on_mouse) while(not self.done): # This is our drawing loop, we just continuously draw new images # and show them in the named window canvas = np.zeros(CANVAS_SIZE, np.uint8) if (len(self.points) > 0): # Draw all the current polygon segments cv2.polylines(canvas, np.array([self.points]), False, FINAL_LINE_COLOR, 1) # And also show what the current segment would look like cv2.line(canvas, self.points[-1], self.current, WORKING_LINE_COLOR) # Update the window cv2.imshow(self.window_name, canvas) # And wait 50ms before next iteration (this will pump window messages meanwhile) if cv2.waitKey(50) == 27: # ESC hit self.done = True # User finised entering the polygon points, so let's make the final drawing canvas = np.zeros(CANVAS_SIZE, np.uint8) # of a filled polygon if (len(self.points) > 0): cv2.fillPoly(canvas, np.array([self.points]), FINAL_LINE_COLOR) # And show it cv2.imshow(self.window_name, canvas) # Waiting for the user to press any key cv2.waitKey() cv2.destroyWindow(self.window_name) return canvas # ============================================================================ if __name__ == "__main__": pd = PolygonDrawer("Polygon") image = pd.run() cv2.imwrite("polygon.png", image) print("Polygon = %s" % pd.points) 

Capturas de pantalla

Dibujo en progreso, hemos ingresado 5 puntos y el segmento actual se muestra como una línea más oscura que apunta a la posición actual del mouse:

Dibujo en progreso

El dibujo está completo y el progtwig muestra el polígono completo lleno:

Dibujo completo


Imagen final

Imagen guardada

Salida de consola

 Adding point #0 with position(257,144) Adding point #1 with position(657,263) Adding point #2 with position(519,478) Adding point #3 with position(283,383) Adding point #4 with position(399,126) Adding point #5 with position(142,286) Adding point #6 with position(165,38) Completing polygon with 7 points. Polygon = [(257, 144), (657, 263), (519, 478), (283, 383), (399, 126), (142, 286), (165, 38)] 

Mismo código que el anterior pero con C ++. Toma una imagen como entrada en lugar de canvas

 #include  #include  cv::Scalar FINAL_LINE_COLOR (255, 255, 255); cv::Scalar WORKING_LINE_COLOR(127, 127, 127); class PolygonDrawer { public: std::string window_name_; bool done_; cv::Point current_; std::vector points_; boost::shared_ptr imgPtr; PolygonDrawer(const std::string window_name, std::string imgName){ window_name_ = window_name; done_ = false; current_ = cv::Point(0, 0); // Current position, so we can draw the line-in-progress imgPtr.reset(new cv::Mat(cv::imread(imgName))); } static void onMouse( int event, int x, int y, int f, void* data ) { PolygonDrawer *curobj = reinterpret_cast(data); if (curobj->done_) // Nothing more to do return; if(event == cv::EVENT_MOUSEMOVE) // We want to be able to draw the line-in-progress, so update current mouse position curobj->current_ = cv::Point(x, y); else if(event == cv::EVENT_LBUTTONDOWN) { // Left click means adding a point at current position to the list of points printf("Adding point #%zu with position(%d,%d) \n", curobj->points_.size(), x, y); curobj->points_.push_back(cv::Point(x, y)); } else if(event == cv::EVENT_RBUTTONDOWN) { // Right click means we're done printf("Completing polygon with %zu points \n", curobj->points_.size()); curobj->done_ = true; } } void run() { // Let's create our working window and set a mouse callback to handle events cv::namedWindow(window_name_, cv::WINDOW_KEEPRATIO); cv::imshow(window_name_, *imgPtr); cv::waitKey(1); cv::setMouseCallback(window_name_, onMouse, this); while(!done_) { cv::Mat img; imgPtr->copyTo(img); if (points_.size() > 0){ // Draw all the current polygon segments const cv::Point *pts = (const cv::Point*) cv::Mat(points_).data; int npts = cv::Mat(points_).rows; cv::polylines(img, &pts, &npts, 1, false, FINAL_LINE_COLOR); // And also show what the current segment would look like cv::line(img, points_[points_.size()-1], current_, WORKING_LINE_COLOR, 1.0); // Update the window } cv::imshow(window_name_, img); // And wait 50ms before next iteration (this will pump window messages meanwhile) if(cv::waitKey(50) == 27) done_ = true; } const cv::Point *pts = (const cv::Point*) cv::Mat(points_).data; int npts = cv::Mat(points_).rows; // user finished entering the polygon points if (points_.size() > 0) { cv::fillPoly(*imgPtr, &pts, &npts, 1, FINAL_LINE_COLOR); cv::imshow(window_name_, *imgPtr); //Waiting for the user to press any key cv::waitKey(); cv::destroyWindow(window_name_); } } }; int main(int argc, char** argv) { PolygonDrawer pd("Polygon", argv[1]); pd.run(); // cv2.imwrite("polygon.png", image) // print("Polygon = %s" % pd.points) } 

Modifiqué el código de Dan Mašek para dibujar polígonos en imágenes: la contraparte de Python para la implementación de C ++ del usuario 2848052. Se puede encontrar aquí: https://github.com/mluerig/iso_track