在OpenCV中,实现了grabcut分割算法,该算法可以方便的分割出前景图像,操作简单,而且分割的效果很好。
grabCut( InputArray img, InputOutputArray mask, Rect rect,
InputOutputArray bgdModel, InputOutputArray fgdModel,
int iterCount, int mode = GC_EVAL );
img:输入原图像;
mask:输出掩码;
rect:用户选择的前景矩形区域;
bgModel:输出背景图像;
fgModel:输出前景图像;
iterCount:迭代次数;
grabCut函数的第一个参数为我们要处理的图像,图像的类型必须为:CV_8UC3
第二个参数是mask图像,它的大小和image一样,但是它的格式为CV_8UC1,只能是单通道的,grabcut算法的结果就保存在该图像中。
前面的代码中,我们并没有对mask图像(result)进行初始化设置,因为第6个参数为cv::GC_INIT_WITH_RECT,它表示算法会根据rectangle的范围,来生成一个初始化的mask图像。
mask图像的值只能为下面下面4个值(PR,probably表示可能的):
GC_BGD = 0, //背景
GC_FGD = 1, //前景
GC_PR_BGD = 2, //可能背景
GC_PR_FGD = 3 //可能前景
根据rectangle生成的mask图像规则为:四边形外面的部分一定是背景,所以在mask图中对应的像素值为GC_BGD,而四边形内部的的值可能为前景,所以对应的像素值为GC_PR_FGD。
效果图:
原图
分割图
步骤:
(1) 读入并显示图像
(2) 设置鼠标事件,获得画出的矩形框
(3) 初始化mask矩阵
(4) 等待输入字母,运行GrabCut函数
代码:
//GrabCut程序
#include <iostream>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
void onMouse(int event, int x, int y, int flags, void* userdata);
Rect rect;
Mat src_img, roi_img, dst_img;
void showImg();
int main(int arc, char** argv)
{
src_img = imread("3.jpg");
namedWindow("src 1210", CV_WINDOW_AUTOSIZE);
imshow("src 1210", src_img);
setMouseCallback("src 1210", onMouse);
Mat dst_img = Mat::zeros(src_img.size(), CV_8UC1);
// GrabCut 抠图
//两个临时矩阵变量,作为算法的中间变量使用
Mat bgModel, fgModel;
char c = waitKey(0);
if (c == 'o') {
grabCut(src_img, dst_img, rect, bgModel, fgModel, 1, GC_INIT_WITH_RECT);
//比较dst_img的值为可能的前景像素才输出到dst_img中
compare(dst_img, GC_PR_FGD, dst_img, CMP_EQ);
// 产生输出图像
Mat foreground(src_img.size(), CV_8UC3, Scalar(255, 255, 255));
//将原图像src_img中的dst_img区域拷贝到foreground中
src_img.copyTo(foreground, dst_img);
imshow("dst 1210", foreground);
}
waitKey(0);
return 0;
}
void showImg() {
src_img.copyTo(roi_img);
rectangle(roi_img, rect, Scalar(0, 0, 255), 2);
imshow("src 1210", roi_img);
}
//鼠标选择矩形框
void onMouse(int event, int x, int y, int flags, void* userdata) {
switch (event)
{
case CV_EVENT_LBUTTONDOWN://鼠标左键按下事件
rect.x = x;
rect.y = y;
rect.width = 1;
rect.height = 1;
break;
case CV_EVENT_MOUSEMOVE://鼠标移动事件
if (flags && CV_EVENT_FLAG_LBUTTON) {
rect = Rect(Point(rect.x, rect.y), Point(x, y));
showImg();
}
break;
case EVENT_LBUTTONUP://鼠标弹起事件
if (rect.width > 1 && rect.height > 1) {
showImg();
}
break;
default:
break;
}
}
欢迎大家批评指正!