Skip to content

Instantly share code, notes, and snippets.

@zhanghuimeng
Created May 26, 2017 06:26
Show Gist options
  • Save zhanghuimeng/f2ff34b0c532ccf6ba1d7ca415db78b9 to your computer and use it in GitHub Desktop.
Save zhanghuimeng/f2ff34b0c532ccf6ba1d7ca415db78b9 to your computer and use it in GitHub Desktop.
opencv鼠标笔刷标记区域
// 1. 一些基本定义
typedef cv::Vec3b Color;
typedef cv::Mat_<Color> Image;
Image img; // 要处理的那张图
struct MouseArgs // 用于和回调函数交互,至于为什么要特意攒一个struct后面会讲~
{
Image &img; // 显示在窗口中的那张图
std::vector<std::vector<int>> &mask; // 用户绘制的选区(删除/保留)
Color color; // 用来高亮选区的颜色
MouseArgs(Image &img, std::vector<std::vector<int>> &mask, const Color color)
: img(img), mask(mask), color(color) {}
};
// 2. 创建一个可交互的窗口
Image showImg = img.clone(); // 拷贝一张图用于显示(因为需要在显示的图上面高亮标注,从而造成修改)
cv::namedWindow("Draw ROI", CV_WINDOW_AUTOSIZE); // 新建一个窗口
// 3. 定义一个回调函数,这里主要实现2个功能:
// 1) 用半透明颜色高亮选区
// 2) 将选区存到数组中
// 这个回调函数有一个固定的格式(函数名随意):
void onMouse(int event, int x, int y, int flags, void *param);
/**
* event: 触发回调函数的鼠标事件(如移动/点击/松开等)
* (x, y): 鼠标事件发生的位置
* flags: 貌似是用位运算的方式表示左/中/右三个键是否被按下
* param: 传给回调函数的参数,可以在回调函数内部被读/写【我们将从这里获取数据】
*
* 以上这堆参数只有param需要自行在外部准备,其余的opencv都搞定了,咱们不用管直接用就行了
*/
void onMouse(int event, int x, int y, int flags, void *param)
{
// C++没有类似Java的单根继承机制,为了支持多类型的交互数据这里只能传入void *再强制转换
// 为什么必须定义一个MouseArgs结构体:不然没法同时给回调函数传入多个数据
MouseArgs *args = (MouseArgs *)param;
// 按下鼠标左键拖动时
if ((event == CV_EVENT_MOUSEMOVE || event == CV_EVENT_LBUTTONDOWN)
&& (flags & CV_EVENT_FLAG_LBUTTON))
{
int brushRadius = 10; // 笔刷半径
int rows = args->img.rows, cols = args->img.cols;
// 以下的双重for循环遍历的是半径为10的圆形区域,实现笔刷效果
// 注意传回给回调函数的x, y是【窗口坐标】下的,所以y是行,x是列
for (int i = max(0, y - brushRadius); i < min(rows, y + brushRadius); i++)
{
int halfChord = sqrt(pow(brushRadius, 2) - pow(i - y, 2)); // 半弦长
for (int j = max(0, x - halfChord); j < min(cols, x + halfChord); j++)
if (args->mask[i][j] == 0)
{
// 高亮这一笔
args->img(i, j) = args->img(i, j) * 0.7 + args->color * 0.3;
// 将这一笔添加到选区
args->mask[i][j] = 1;
}
}
}
}
vector<vector<int>> maskRemove(rows, vector<int>(cols, 0)); // 希望获取的待删除选区
MouseArgs *args = new MouseArgs(showImg, maskRemove, Color(0, 0, 255)); // 攒一个MouseArgs结构体用于交互
cv::setMouseCallback("Draw ROI", onMouse, (void*)args); // 给窗口设置回调函数
// 拖动鼠标作画
while (1)
{
cv::imshow("Draw ROI", args->img);
// 按 esc 键退出绘图模式,获得选区
if (cv::waitKey(100) == 27)
break;
}
// 4. 垃圾回收
cv::setMouseCallback("Draw ROI", NULL, NULL); // 取消回调函数
delete args; args = NULL; // 垃圾回收
@roastduck
Copy link

mask用一个cv::Mat1b就好了,不需要嵌套vector

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment