c++ - OpenCV C++如何使用mouseclickleft存储坐标和画圆
问题描述
目前我正在开发一个能够基于四个点绘制样条曲线的程序。如下图所示,我目前有,所以需要手动将这些点输入到代码中。我希望使用鼠标点击来改变它。我打算做的是让用户能够点击图像,立即画一个点。这将能够根据用户的需要进行多次,但它会在每绘制四个点时绘制一条样条曲线。我遇到了两个问题。首先,当试图实现一个简单的鼠标点击来画一个圆圈时onMouse
,它不工作。其次,我想知道如何在每次鼠标点击发生时存储它们的坐标,因此我可以创建一个循环,将这些坐标插入到下面的矩阵方程中,以在程序运行时绘制样条曲线。我知道如何存储第一个坐标,但我不确定如何处理多个坐标以及如何为每个可以放入方程式的明确变量名称。很抱歉,尽管经验有限,但我对这个项目有很大的抱负,所以我希望能得到一些指导。
#include <iostream>
#include<opencv2/core/core.hpp> //Mat is defined there
#include<opencv2/imgproc/imgproc.hpp> //resize an image
#include<opencv2/highgui/highgui.hpp> //input or output: imread(), imshow()
using namespace std;
using namespace cv;
Mat img;
void onMouse(int event, int x, int y, int flags, void* param)
{
if (event == EVENT_LBUTTONDOWN)
{
printf("(%d, %d)\n", x, y);
int r = 1;
circle(img, Point(x, y), r, Scalar(0, 255, 100), 5);
}
}
int main(int argc, char** argv)
{
img.create(600, 800, CV_8UC3);
img = Scalar(255, 255, 255);
double a_values[3][2] = { { 2.0, 1 }, { 0.0, 3.0 }, { -2.0, -4.0 } };
//A.create(3, 2, CV_64FC1); //create a matrix 3x2 with double value
Mat A = Mat(3, 2, CV_64FC1, a_values); //Constructor: pass the values directly using a 2D array.
printf("matrix A:\n");
cout << A << endl;
Mat B;
B.create(2, 2, CV_64FC1); //2x2 matrix
B.ptr<double>(0)[0] = 1.0;
B.ptr<double>(0)[1] = 2.0;
B.ptr<double>(1)[0] = 0.0;
B.ptr<double>(1)[1] = -2.0;
printf("matrix B:\n");
cout << B << endl;
Mat C = A * B; //Matrix product
printf("matrix C:\n");
cout << C << endl;
Mat B_inv = B.inv();
printf("matrix B inverse:\n");
cout << B_inv << endl;
double m_values[4][4] = { { 0, 0, 0, 1 }, { 1, 1, 1, 1 }, { 0, 0, 1, 0 }, { 3, 2, 1, 0 } };
Mat M = Mat(4, 4, CV_64FC1, m_values);
Mat M_inv = M.inv();
printf("matrix M inverse:\n");
cout << M_inv << endl;
double h_values[4][4] = { { 2, -2, 1, 1 }, { -3, 3, -2, -1 }, { 0, 0, 1, 0 }, { 1, 0, 0, 0 } };
Mat Hermite = Mat(4, 4, CV_64FC1, h_values);
double point_values[4][2] = { { 200, 350 }, { 220, 400 }, { 600, 300 }, { 390, 300 } };
Mat Points = Mat(4, 2, CV_64FC1, point_values);
Mat Final = Hermite * Points;
printf("Final matrix:\n");
cout << Final << endl;
/* If there are two points P1(30, 50) and P2(80, 120), I want to draw a spline between
them and also make sure the speed of the spline at point P1 equals(500, 2) and at P2 equals(10, 1000). */
//Draw 1st point
circle(img, Point(200, 350), 1, Scalar(0, 0, 255), 3);
//Draw 2nd point
circle(img, Point(220, 400), 1, Scalar(0, 0, 255), 3);
circle(img, Point(400, 450), 1, Scalar(0, 0, 255), 3);
circle(img, Point(350, 500), 1, Scalar(0, 0, 255), 3);
//Draw the spline between 1st and 2nd points
//Use a loop on t [0, 1], for different t values, compute x(t), y(t); then use circle() to draw it
// x(t) = axt3 + bxt2 + cxt + dx
// y(t) = ayt3 + byt2 + cyt + dy
double ax = (int)(Final.at<double>(0, 0));
double ay = (int)(Final.at<double>(0, 1));
double bx = (int)(Final.at<double>(1, 0));
double by = (int)(Final.at<double>(1, 1));
double cx = (int)(Final.at<double>(2, 0));
double cy = (int)(Final.at<double>(2, 1));
double dx = (int)(Final.at<double>(3, 0));
double dy = (int)(Final.at<double>(3, 1));
printf("ax:\n");
cout << ax << endl;
printf("dx:\n");
cout << dx << endl;
for (double t = 0.0; t <= 1.0; t += 0.001)
{
int x = ax * t * t * t + bx * t * t + cx * t + dx;
int y = ay * t * t * t + by * t * t + cy * t + dy;
circle(img, Point(x, y), 1, Scalar(0, 0, 0), 1);
}
while (1)
{
imshow("Spline", img);
char c = waitKey(1);
if (c == 27)
break;
}
return 1;
}
解决方案
您定义onMouse
了函数,但没有将其注册到任何窗口。您需要使用创建窗口,cv::namedWindow
然后使用 注册鼠标回调cv::setMouseCallback
。在您的情况下,只需在main
函数中的任何内容之前添加它:
cv::namedWindow("Spline");
cv::setMouseCallback("Spline", onMouse);
这是绘制圆/点到提供的图像的简单程序。它将存储您单击的点,并将所有点绘制到图像上。
#include <opencv2/opencv.hpp>
std::vector<cv::Point> points;
void onMouse(int action, int x, int y, int, void*) {
if (action == cv::EVENT_LBUTTONDOWN) {
points.push_back(cv::Point{x, y});
}
}
int main(int argc, char** argv) {
const auto mainWindow = "Main Window";
cv::namedWindow(mainWindow);
cv::setMouseCallback(mainWindow, onMouse);
cv::Mat image {600, 800, CV_8UC3, cv::Scalar{255, 255, 255}};
while (true) {
for (const auto& point : points) {
cv::circle(image, point, 5, cv::Scalar{0, 200, 0}, -1);
}
cv::imshow(mainWindow, image);
cv::waitKey(25);
}
cv::waitKey();
cv::destroyAllWindows();
return 0;
}
编辑:请注意,此解决方案将重新渲染旧点。您可以通过仅绘制新点来改善这一点。当您单击时,将该点添加到临时vector
,当您从临时绘制所有点时vector
,清除该vector
。要获得更多改进,您可以检查 if vector
is not empty 和 call cv::imshow
。
推荐阅读
- java - 数字日期格式
- angular - 当使用异步管道在 NGXS 存储中更改状态时,Angular 组件不会重新评估
- sql - 过滤视图时如何利用基础索引?
- c# - 我需要使用 xpath 在我的 HTML 中获取没有标签的文本
- javascript - 当我尝试在我的网站上构建键盘快捷键时,为什么 jQuery keyup 不起作用?
- karate - 如果一个 json 包含另一个带有空手道的 json,则匹配
- pine-script - 寻找一个代码,找到两种颜色中的所有颜色,并将它们应用到一个范围内的脚本中
- java - 定义了一个 Int 但需要一个数组?
- express - 带有 traefik 的 docker swarm 自定义容器
- reactjs - 有没有办法在打字稿的界面中覆盖类型?