-
Notifications
You must be signed in to change notification settings - Fork 0
/
grubcuts.cpp
203 lines (186 loc) · 5.24 KB
/
grubcuts.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#include "grubcuts.h"
/*给类的变量赋值*/
void GrubCuts::reset()
{
if (!mask.empty())
mask.setTo(Scalar::all(GC_BGD));
bgdPxls.clear(); fgdPxls.clear();
prBgdPxls.clear(); prFgdPxls.clear();
isInitialized = false;
rectState = NOT_SET; //NOT_SET == 0
lblsState = NOT_SET;
prLblsState = NOT_SET;
iterCount = 0;
}
/*给类的成员变量赋值*/
void GrubCuts::setImageAndWinName(const Mat& _image, const string& _winName)
{
if (_image.empty() || _winName.empty())
return;
image = &_image;
winName = &_winName;
mask.create(image->size(), CV_8UC1);
reset();
}
/*显示4个点,一个矩形和图像内容,因为后面的步骤很多地方都要用到这个函数,所以单独拿出来*/
void GrubCuts::showImage() const
{
if (image->empty() || winName->empty())
return;
Mat res;
Mat binMask;
if (!isInitialized)
image->copyTo(res);
else
{
getBinMask(mask, binMask);
image->copyTo(res, binMask); //按照最低位是0还是1来复制,只保留跟前景有关的图像,比如说可能的前景,可能的背景
}
vector<Point>::const_iterator it;
/*下面4句代码是将选中的4个点用不同的颜色显示出来*/
for (it = bgdPxls.begin(); it != bgdPxls.end(); ++it) //迭代器可以看成是一个指针
circle(res, *it, radius, BLUE, thickness);
for (it = fgdPxls.begin(); it != fgdPxls.end(); ++it) //确定的前景用红色表示
circle(res, *it, radius, RED, thickness);
for (it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it)
circle(res, *it, radius, LIGHTBLUE, thickness);
for (it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it)
circle(res, *it, radius, PINK, thickness);
/*画矩形*/
if (rectState == IN_PROCESS || rectState == SET)
rectangle(res, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height), GREEN, 2);
imshow(*winName, res);
}
/*该步骤完成后,mask图像中rect内部是3,外面全是0*/
void GrubCuts::setRectInMask()
{
assert(!mask.empty());
mask.setTo(GC_BGD); //GC_BGD == 0
rect.x = max(0, rect.x);
rect.y = max(0, rect.y);
rect.width = min(rect.width, image->cols - rect.x);
rect.height = min(rect.height, image->rows - rect.y);
(mask(rect)).setTo(Scalar(GC_PR_FGD)); //GC_PR_FGD == 3,矩形内部,为可能的前景点
}
void GrubCuts::setLblsInMask(int flags, Point p, bool isPr)
{
vector<Point> *bpxls, *fpxls;
uchar bvalue, fvalue;
if (!isPr) //确定的点
{
bpxls = &bgdPxls;
fpxls = &fgdPxls;
bvalue = GC_BGD; //0
fvalue = GC_FGD; //1
}
else //概率点
{
bpxls = &prBgdPxls;
fpxls = &prFgdPxls;
bvalue = GC_PR_BGD; //2
fvalue = GC_PR_FGD; //3
}
if (flags & BGD_KEY)
{
bpxls->push_back(p);
circle(mask, p, radius, bvalue, thickness); //该点处为2
}
if (flags & FGD_KEY)
{
fpxls->push_back(p);
circle(mask, p, radius, fvalue, thickness); //该点处为3
}
}
/*鼠标响应函数,参数flags为CV_EVENT_FLAG的组合*/
void GrubCuts::mouseClick(int event, int x, int y, int flags, void*)
{
// TODO add bad args check
switch (event)
{
case CV_EVENT_LBUTTONDOWN: // set rect or GC_BGD(GC_FGD) labels
{
bool isb = (flags & BGD_KEY) != 0,
isf = (flags & FGD_KEY) != 0;
if (rectState == NOT_SET && !isb && !isf)//只有左键按下时
{
rectState = IN_PROCESS; //表示正在画矩形
rect = Rect(x, y, 1, 1);
}
if ((isb || isf) && rectState == SET) //按下了alt键或者shift键,且画好了矩形,表示正在画前景背景点
lblsState = IN_PROCESS;
}
break;
case CV_EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels
{
bool isb = (flags & BGD_KEY) != 0,
isf = (flags & FGD_KEY) != 0;
if ((isb || isf) && rectState == SET) //正在画可能的前景背景点
prLblsState = IN_PROCESS;
}
break;
case CV_EVENT_LBUTTONUP:
if (rectState == IN_PROCESS)
{
rect = Rect(Point(rect.x, rect.y), Point(x, y)); //矩形结束
rectState = SET;
setRectInMask();
assert(bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty());
showImage();
}
if (lblsState == IN_PROCESS) //已画了前后景点
{
setLblsInMask(flags, Point(x, y), false); //画出前景点
lblsState = SET;
showImage();
}
break;
case CV_EVENT_RBUTTONUP:
if (prLblsState == IN_PROCESS)
{
setLblsInMask(flags, Point(x, y), true); //画出背景点
prLblsState = SET;
showImage();
}
break;
case CV_EVENT_MOUSEMOVE:
if (rectState == IN_PROCESS)
{
rect = Rect(Point(rect.x, rect.y), Point(x, y));
assert(bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty());
showImage(); //不断的显示图片
}
else if (lblsState == IN_PROCESS)
{
setLblsInMask(flags, Point(x, y), false);
showImage();
}
else if (prLblsState == IN_PROCESS)
{
setLblsInMask(flags, Point(x, y), true);
showImage();
}
break;
}
}
/*该函数进行grabcut算法,并且返回算法运行迭代的次数*/
int GrubCuts::nextIter()
{
if (isInitialized)
//使用grab算法进行一次迭代,参数2为mask,里面存的mask位是:矩形内部除掉那些可能是背景或者已经确定是背景后的所有的点,且mask同时也为输出
//保存的是分割后的前景图像
grabCut(*image, mask, rect, bgdModel, fgdModel, 1);
else
{
if (rectState != SET)
return iterCount;
if (lblsState == SET || prLblsState == SET)
grabCut(*image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK);
else
grabCut(*image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT);
isInitialized = true;
}
iterCount++;
bgdPxls.clear(); fgdPxls.clear();
prBgdPxls.clear(); prFgdPxls.clear();
return iterCount;
}