opencv 如何使用鼠标事件不规则地选择图像区域?C/C++

2021-12-10 00:00:00 opencv c++

我最近在学习 opencv.有没有办法使用鼠标事件选择图像区域?我已经尝试了三角形一.如果我想选择特定区域而不是三角形怎么办?谢谢!

I'm learning opencv recently. Is there a way to select a region of image using mouse event? I already try out the triangle one. What if I want to choose a specific region but not a triangle shape? Thank you!

推荐答案

我在这方面做了一点尝试――它可能不是最干净的代码,但应该能给你一些想法.

I had a little attempt at this - it is probably not the cleanest code, but should give you some ideas.

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

// Globals
bool finished=false;
Mat img,ROI;
vector<Point> vertices;

void
CallBackFunc(int event,int x,int y,int flags,void* userdata)
{
   if(event==EVENT_RBUTTONDOWN){
      cout << "Right mouse button clicked at (" << x << ", " << y << ")" << endl;
      if(vertices.size()<2){
         cout << "You need a minimum of three points!" << endl;
         return;
      }
      // Close polygon
      line(img,vertices[vertices.size()-1],vertices[0],Scalar(0,0,0));

      // Mask is black with white where our ROI is
      Mat mask= Mat::zeros(img.rows,img.cols,CV_8UC1);
      vector<vector<Point>> pts{vertices};
      fillPoly(mask,pts,Scalar(255,255,255));
      img.copyTo(ROI,mask);
      finished=true;

      return;
   }
   if(event==EVENT_LBUTTONDOWN){
      cout << "Left mouse button clicked at (" << x << ", " << y << ")" << endl;
      if(vertices.size()==0){
         // First click - just draw point
         img.at<Vec3b>(x,y)=Vec3b(255,0,0);
      } else {
         // Second, or later click, draw line to previous vertex
         line(img,Point(x,y),vertices[vertices.size()-1],Scalar(0,0,0));
      }
      vertices.push_back(Point(x,y));
      return;
   }
}

int main()
{
   // Read image from file 
   img=imread("demo.jpg");

   // Check it loaded
   if(img.empty()) 
   { 
      cout << "Error loading the image" << endl;
      exit(1);
   }

   //Create a window
   namedWindow("ImageDisplay",1);

   // Register a mouse callback
   setMouseCallback("ImageDisplay",CallBackFunc,nullptr);

   // Main loop
   while(!finished){
      imshow("ImageDisplay",img);
      waitKey(50);
   }

   // Show results
   namedWindow("Result",1);
   imshow("Result",ROI);
   waitKey(5000);
}

main() 的开头我这样做:

At the beginning of main() I do this:

setMouseCallback("ImageDisplay",CallBackFunc,nullptr);

这告诉 OpenCV 在鼠标移动或点击时为我们调用函数 CallBackFunc().

and that tells OpenCV to call the function CallBackFunc() for us whenever the mouse moves or is clicked.

CallBackFunc() 与任何其他函数一样,只是一个普通函数.但是,我们自己从不调用它 - OpenCV 为我们异步调用它(当我们不期望它时).因此,我们看不到该函数的任何返回值――因为我们从未调用过它――这就是为什么它被声明为:

CallBackFunc() is just a normal function like any other function. However, we don't ever call it ourselves - OpenCV calls it for us asynchronously (when we aren't expecting it). Because of that, we can't see any returned value from the function - since we never called it - and that is why it is declared as:

void CallBackFunc(...)

因为它什么都不返回,或者一个虚无,或者一个肮脏的虚无的巨大虚空.

because it returns nothing, or a void, or a dirty great void of nothingness.

好的,让我们继续讨论调用它的参数.基本上,当 OpenCV 的设计者编写 setMouseCallback() 函数时,他们不知道我想将什么作为参数传递给它 - 也许我想传递一个文件名,也许我想传递一个文本字符串以在单击鼠标时在图像上绘制,也许我想传递一个我想用鼠标位置更新的变量.它可以是任何东西.所以,因为他们不知道,他们决定说它是一个指向任何东西的指针",我可以用它来做我喜欢的任何事情.好吧,什么都不是,所以指向 void 的指针可以指向任何东西.因此,他们说只需传递一个指向 void 的指针即可,您知道其含义.然后,在 CallBackFunc() 中,您可以将指针转换为您传递的任何内容,因为您知道它是什么.

Ok, let's move on to the parameters it is called with. Basically, when the designers of OpenCV wrote the setMouseCallback() function, they couldn't know what I would want to pass to it as a parameter - maybe I would want to pass a filename, maybe I would want to pass a text string to draw on the image when the mouse is clicked, maybe I would want to pass a variable that I wanted updated with the mouse position. It could be anything. So, as they didn't know, they decided to say it is a "pointer to anything" and I can use it for whatever I like. Well, nothing is anything, so a pointer to void can point to anything. So, they say just pass a pointer to void that you know the meaning of. Then, inside CallBackFunc() you can just cast the pointer to whatever it was you passed since you know what it is.

因此,总而言之,指向 void 的指针只是指向某事物的指针的位置标记,您可以决定该事物是什么,而 OpenCV 设计人员无需知道.

So, in summary a pointer to void is just a place-marker for a pointer to something and you can decide what that something is without the OpenCV designers needing to know.

希望有帮助!

相关文章