繁体   English   中英

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

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

我正在尝试在通过鼠标事件单击获得的坐标之间绘制一个多边形。

第一次点击应该定义多边形的起点。 每次额外的点击都应该从上一次点击中绘制一条线段。 当全部点击的点绘制多边形时,多边形内部应该被填充。

有人可以建议如何在我单击图像的点之间绘制多边形吗? 我正在考虑cv2.polylines函数,但是我不知道如何将它与SetMouseCallback函数集成。

为了使用户界面更直观(因为用户很难准确地单击与起点相同的位置),让我们使用以下操作:

  • 单击鼠标左键在单击的位置向多边形添加一个点
  • 鼠标右键单击完成数据输入,并使程序显示最终填充的多边形

我们将需要几个变量来跟踪我们的进度:

  • 定义多边形的点列表。 每个点都是一个元组(x, y)
  • 一个布尔标志,表示数据输入何时完成
  • 作为奖励,鼠标光标的最后已知位置,因此我们可以为当前输入的段(光标后的一行)设置动画。

当适当的鼠标事件发生时,我们将使用鼠标回调来定期更新这些变量:

  • EVENT_MOUSEMOVE -- 鼠标已经移动,更新当前位置
  • EVENT_LBUTTONDOWN -- 用户按下鼠标左键,将当前位置添加到点列表
  • EVENT_RBUTTONDOWN -- 用户按下鼠标右键,将数据输入标记为完成

最后,我们将有一个实现显示循环的函数。

此函数将首先创建一个命名窗口,绘制一个空白画布,并设置鼠标回调。 然后它将通过以下方式反复更新屏幕:

  • 创建一个新的画布图像(在其上绘制)
  • 输入点时,使用cv2.polyline绘制连接线段
  • 使用cv2.line以不同的颜色绘制从最后输入的点指向当前位置的当前线段。
  • 显示新图像。
  • 等待一段时间,同时发送窗口消息。

数据输入过程完成后,该函数将绘制最终填充的多边形和干净的画布图像,并显示它,并在用户按下某个键时返回最终图像。


代码示例

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)

截图

绘图正在进行中,我们输入了 5 个点,当前线段显示为指向鼠标当前位置的深色线:

绘图进行中

绘图完成,程序显示整个多边形已填充:

绘图完成


最终图像

保存的图像

控制台输出

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

与上面相同的代码,但使用 C++。 将图像作为输入而不是画布

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

我修改了DanMašek的代码,将多边形绘制成图像 - 与user2848052的C ++实现相对应的Python。 可以在这里找到: https//github.com/mluerig/iso_track

暂无
暂无

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

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