OpenCV C++如何使用mouseclickleft存储坐标和画圆

Posted

技术标签:

【中文标题】OpenCV C++如何使用mouseclickleft存储坐标和画圆【英文标题】:OpenCV C++ How to use mouseclickleft to store coordinates and draw circles 【发布时间】:2021-07-24 18:39:26 【问题描述】:

目前我正在开发一个能够基于四个点绘制样条曲线的程序。如下图所示,我目前有,所以需要手动将这些点输入到代码中。我希望使用鼠标点击来改变它。我打算做的是让用户能够点击图像,立即画一个点。这将能够根据用户的需要进行多次,但它会在每绘制四个点时绘制一条样条曲线。我遇到了两个问题。首先,当尝试实现一个简单的鼠标单击以在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;
    
    

【问题讨论】:

【参考方案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::Pointx, 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::Scalar255, 255, 255;

  while (true) 
    for (const auto& point : points) 
      cv::circle(image, point, 5, cv::Scalar0, 200, 0, -1);
    
    cv::imshow(mainWindow, image);
    cv::waitKey(25);
  
  cv::waitKey();
  cv::destroyAllWindows();
  return 0;

编辑: 请注意,此解决方案将重新渲染旧点。您可以通过仅绘制新点来改善这一点。 当您单击时,将该点添加到临时vector,当您从临时vector 绘制所有点时,清除该vector。 如需更多改进,您可以检查vector 是否为空,然后调用cv::imshow

【讨论】:

如何在我当前的程序中实现这一点?我看到我需要插入一个图像才能在尝试快速测试它时工作(我没有太多时间来测试它,因为我要离开几个小时)。 @Elvis Oric @Hdog,我刚刚编辑了答案,你不需要加载图像。 感谢您为我这样做,我现在可以正确运行代码了。我正在努力弄清楚如何在我的代码中完成这项工作,因为它的格式与我所见过的不同。我的程序没有让这个功能正常工作怎么办? @Hdog,我会再次更新答案,检查一下。 哇,这太完美了,谢谢。我没有意识到在 opencv 中包含在哪个窗口中显示函数的概念,因此非常感谢您的帮助。另一方面,对于我问题的第二部分,我应该编写代码将坐标存储在主函数中还是在它之外?如果我这样做了,你认为我应该如何存储它们,以便我可以循环将每四组坐标插入公式?某种数组?

以上是关于OpenCV C++如何使用mouseclickleft存储坐标和画圆的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MacOS 上使用 OpenCV 链接 C++ 程序 [重复]

如何使用 C++ 在 OpenCV 4.2.0 中使用 SIFT? [关闭]

如何在 OSX 中使用 C++ opencv highgui 将活动窗口设置为 opencv 图像

OpenCV / C++ - 如何在向量上使用adaptiveThreshold而不是Mat?

Opencv4Android:如何与 C++ 一起使用

OpenCV,C++:如何使用 cv::Meanshift