Saltar al contenido

Dibujar polígono relleno usando eventos de mouse en cv abierto usando python

Solución:

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

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

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 indicará 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 devolución de llamada del mouse para actualizar esas variables periódicamente cuando ocurran los eventos de mouse apropiados:

  • EVENT_MOUSEMOVE – el mouse se ha movido, actualice la posición actual
  • EVENT_LBUTTONDOWN – el usuario presionó el botón izquierdo del mouse, agregar la posición actual a la lista de puntos
  • EVENT_RBUTTONDOWN – el usuario presionó el botón derecho del mouse, marque la entrada de datos como completa

Finalmente, tendremos una función que implementa el ciclo de visualización.

Esta función primero creará una ventana con nombre, dibujará un lienzo en blanco y configurará la devolución de llamada del mouse. Luego, seguirá actualizando repetidamente la pantalla de la siguiente manera:

  • Crear una nueva imagen de lienzo (sobre la que 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.
  • Muestre la nueva imagen.
  • Espere un tiempo, bombeando los mensajes de la ventana mientras lo hace.

Una vez que se completa el proceso de entrada de datos, la función dibujará el polígono relleno final y una imagen de lienzo limpia, la mostrará y, cuando el usuario presione una tecla, devolverá la imagen final.


Muestra 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 (i.e. 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

Dibujando en progreso, tenemos 5 puntos ingresados ​​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 programa 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 ++. Tomar una imagen como entrada en lugar de lienzo

#include <boost/shared_ptr.hpp> 
#include <opencv2/opencv.hpp>


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<cv::Point> points_;
  boost::shared_ptr<cv::Mat> 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<PolygonDrawer*>(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)
}
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *