简体   繁体   English

使用python在打开的cv中使用鼠标事件绘制填充的多边形

[英]Drawing filled polygon using mouse events in open cv using python

I am trying to draw a polygon between the coordinates which would be obtained by clicking by mouse events.我正在尝试在通过鼠标事件单击获得的坐标之间绘制一个多边形。

The first click should define the starting point of the polygon.第一次点击应该定义多边形的起点。 Each additional click should sketch a line segment from the previous click.每次额外的点击都应该从上一次点击中绘制一条线段。 When whole clicked points sketch the polygon, inside of the polygon should be filled.当全部点击的点绘制多边形时,多边形内部应该被填充。

Can someone please suggest how to draw polygons between the points I click on the image?有人可以建议如何在我单击图像的点之间绘制多边形吗? I am considering cv2.polylines function, however I have no idea how to integrate that with SetMouseCallback function.我正在考虑cv2.polylines函数,但是我不知道如何将它与SetMouseCallback函数集成。

To make the user interface more intuitive (since it would be very difficult for the user to click exactly in the same spot as the starting point), let's use the following actions:为了使用户界面更直观(因为用户很难准确地单击与起点相同的位置),让我们使用以下操作:

  • Left mouse click add a point to the polygon at the clicked position单击鼠标左键在单击的位置向多边形添加一个点
  • Right mouse click completes the data entry, and causes the program to display the final filled polygon鼠标右键单击完成数据输入,并使程序显示最终填充的多边形

We will need several variables to keep track of our progress:我们将需要几个变量来跟踪我们的进度:

  • A list of the points defining our polygon.定义多边形的点列表。 Each point will be a tuple (x, y)每个点都是一个元组(x, y)
  • A boolean flag that will signify when data entry is complete一个布尔标志,表示数据输入何时完成
  • As a bonus, the last known position of the mouse cursor, so we can animate the segment currently being entered (a line that follows the cursor).作为奖励,鼠标光标的最后已知位置,因此我们可以为当前输入的段(光标后的一行)设置动画。

We will use a mouse callback to update those variables periodically when the appropriate mouse events occur:当适当的鼠标事件发生时,我们将使用鼠标回调来定期更新这些变量:

  • EVENT_MOUSEMOVE -- mouse has moved, update the current position EVENT_MOUSEMOVE -- 鼠标已经移动,更新当前位置
  • EVENT_LBUTTONDOWN -- user pressed the left mouse button, add current position to list of points EVENT_LBUTTONDOWN -- 用户按下鼠标左键,将当前位置添加到点列表
  • EVENT_RBUTTONDOWN -- user pressed the right mouse button, mark data entry as complete EVENT_RBUTTONDOWN -- 用户按下鼠标右键,将数据输入标记为完成

Finally, we will have a function implementing the display loop.最后,我们将有一个实现显示循环的函数。

This function will first create a named window, draw a blank canvas, and set up the mouse callback.此函数将首先创建一个命名窗口,绘制一个空白画布,并设置鼠标回调。 Then it will repeatedly keep updating the screen by:然后它将通过以下方式反复更新屏幕:

  • Creating a new canvas image (upon which to draw)创建一个新的画布图像(在其上绘制)
  • When there are points entered, draw the connected segments using cv2.polyline输入点时,使用cv2.polyline绘制连接线段
  • Draw the current segment pointing from last entered point to current position with a different colour using cv2.line .使用cv2.line以不同的颜色绘制从最后输入的点指向当前位置的当前线段。
  • Show the new image.显示新图像。
  • Wait some time, pumping the window messages while doing so.等待一段时间,同时发送窗口消息。

Once the data entry process is complete, the function will draw the final filled polygon and a clean canvas image, show it, and when the user presses a key return the final image.数据输入过程完成后,该函数将绘制最终填充的多边形和干净的画布图像,并显示它,并在用户按下某个键时返回最终图像。


Code Sample代码示例

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)

Screenshots截图

Drawing in progress, we have 5 points entered, and the current segment is being shown as a darker line pointing to the current position of the mouse:绘图正在进行中,我们输入了 5 个点,当前线段显示为指向鼠标当前位置的深色线:

绘图进行中

Drawing is complete, and the program is showing the whole polygon filled:绘图完成,程序显示整个多边形已填充:

绘图完成


Final Image最终图像

保存的图像

Console Output控制台输出

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)]

Same Code as above but with C++.与上面相同的代码,但使用 C++。 Take an image as input rather than canvas将图像作为输入而不是画布

#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)
}

I modified Dan Mašek's code to draw polygons into images - the Python counterpart to user2848052's C++ implementation. 我修改了DanMašek的代码,将多边形绘制成图像 - 与user2848052的C ++实现相对应的Python。 Can be found here: https://github.com/mluerig/iso_track 可以在这里找到: https//github.com/mluerig/iso_track

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM