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:
Necesitaremos varias variables para realizar un seguimiento de nuestro progreso:
(x, y)
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:
cv2.polyline
cv2.line
. 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.
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)
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:
El dibujo está completo y el progtwig muestra el polígono completo lleno:
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