我试图在坐标之间绘制一个多边形,通过单击鼠标事件获得。
第一次单击应定义多边形的起点。每次额外点击都应绘制上一次点击的线段。当整个点击点绘制多边形时,应填充多边形内部。
有人可以建议如何在点击图像的点之间绘制多边形吗?我正在考虑cv2.polylines
函数,但我不知道如何将其与SetMouseCallback
函数集成。
答案 0 :(得分:11)
为了使用户界面更直观(因为用户很难完全点击与起点相同的位置),让我们使用以下操作:
我们需要几个变量来跟踪我们的进展:
(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)]
答案 1 :(得分:1)
与上面相同的代码,但使用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)
}
答案 2 :(得分:0)
我修改了DanMašek的代码以将多边形绘制成图像-user2848052的C ++实现的Python对应物。可以在这里找到:https://github.com/mluerig/iso_track