01.
vector<
double
> create_sigma(
double
start,
double
step,
double
end)
02.
{
03.
vector<
double
> sigma;
04.
while
(start <= end+1e-
8
){
05.
double
s = exp(start);
06.
sigma.push_back(s);
07.
start += step;
08.
}
09.
return
sigma;
10.
}
1.
lap_gauss = fspecial(
'log'
,
3
,
1.0
)
01.
Mat create_LOG(
int
size,
double
sigma)
02.
{
03.
Mat H(size, size, CV_64F);
04.
int
cx = (size-
1
)/
2
;
05.
int
cy = (size-
1
)/
2
;
06.
double
sum =
0
;
07.
for
(
int
i =
0
; i < H.rows; i++)
08.
{
09.
for
(
int
j =
0
; j < H.cols; j++)
10.
{
11.
int
nx = i - cx;
12.
int
ny = j - cy;
13.
double
s = -
1
/(
3.1415926
* sigma*sigma*sigma*sigma);
14.
s = s* (
1
- (nx*nx + ny*ny)/(
2
*sigma*sigma));
15.
s = s*exp(-(nx*nx+ny*ny)/(
2
*sigma*sigma));
16.
sum += s;
17.
H.at<
double
>(i,j) = s;
18.
}
19.
}
20.
21.
double
mean = sum/(size*size);
22.
23.
for
(
int
i =
0
; i < H.rows; i++)
24.
{
25.
for
(
int
j =
0
; j < H.cols; j++)
26.
{
27.
H.at<
double
>(i,j) -= mean;
28.
}
29.
}
30.
31.
return
H;
32.
}
The response of a derivative of Gaussianfilter to a perfect step edge decreases asσincreases
To keep response the same (scale-invariant),must multiply Gaussian derivative byσ
Laplacian is the second Gaussian derivative,so it must be multiplied byσ2
为了使得response一致,我们对做卷积的图片要先乘上sigma^2,这一步叫做scale normalize,我们简单看下做不做normalize的对比。01.
class
SimpleBlobDetector :
public
FeatureDetector
02.
{
03.
public
:
04.
struct Params
05.
{
06.
Params();
07.
float
thresholdStep;
08.
float
minThreshold;
09.
float
maxThreshold;
10.
size_t minRepeatability;
11.
float
minDistBetweenBlobs;
12.
13.
bool filterByColor;
14.
uchar blobColor;
15.
16.
bool filterByArea;
17.
float
minArea, maxArea;
18.
19.
bool filterByCircularity;
20.
float
minCircularity, maxCircularity;
21.
22.
bool filterByInertia;
23.
float
minInertiaRatio, maxInertiaRatio;
24.
25.
bool filterByConvexity;
26.
float
minConvexity, maxConvexity;
27.
};
28.
29.
SimpleBlobDetector(
const
SimpleBlobDetector::Params ¶meters = SimpleBlobDetector::Params());
30.
31.
protected
:
32.
...
33.
};
OpenCV实现的算法如下:
- By color. This filter compares the intensity of a binary image at the center of a blob to blobColor. If they differ, the blob is filtered out. UseblobColor = 0 to extract dark blobs and blobColor = 255 to extract light blobs.
- By area. Extracted blobs have an area between minArea (inclusive) and maxArea (exclusive).
- By circularity. Extracted blobs have circularity () between minCircularity (inclusive) and maxCircularity(exclusive).
- By ratio of the minimum inertia to maximum inertia. Extracted blobs have this ratio between minInertiaRatio (inclusive) andmaxInertiaRatio (exclusive).
- By convexity. Extracted blobs have convexity (area / area of blob convex hull) between minConvexity (inclusive) and maxConvexity(exclusive).
你可以设置提取特征的方法,有上面5个选项。默认提取黑色圆形的blob特征。下面是一个示例用法。
01.
#include
"opencv2/core/core.hpp"
02.
#include
"opencv2/features2d/features2d.hpp"
03.
#include
"opencv2/highgui/highgui.hpp"
04.
#include
"opencv2/nonfree/features2d.hpp"
05.
#include
06.
#include
07.
using namespace cv;
08.
using namespace std;
09.
10.
int
main(
int
,
char
** argv )
11.
{
12.
Mat image = imread( argv[
1
] );
13.
vector keypoints;
14.
SimpleBlobDetector::Params params;
15.
params.filterByArea =
true
;
16.
params.minArea =
10
;
17.
params.maxArea =
10000
;
18.
19.
SimpleBlobDetector blobDetector( params );
20.
blobDetector.create(
"SimpleBlob"
);
21.
blobDetector.detect( image, keypoints );
22.
drawKeypoints(image, keypoints, image, Scalar(
255
,
0
,
0
));
23.
24.
namedWindow(
"result"
,
1
);
25.
imshow(
"result"
, image);
26.
waitKey();
27.
return
0
;
28.
}
01.
vector<
double
> create_sigma(
double
start,
double
step,
double
end)
02.
{
03.
vector<
double
> sigma;
04.
while
(start <= end+1e-
8
){
05.
double
s = exp(start);
06.
sigma.push_back(s);
07.
start += step;
08.
}
09.
return
sigma;
10.
}
01.
Mat create_LOG(
int
size,
double
sigma)
02.
{
03.
Mat H(size, size, CV_64F);
04.
int
cx = (size-
1
)/
2
;
05.
int
cy = (size-
1
)/
2
;
06.
double
sum =
0
;
07.
for
(
int
i =
0
; i < H.rows; i++)
08.
{
09.
for
(
int
j =
0
; j < H.cols; j++)
10.
{
11.
int
nx = i - cx;
12.
int
ny = j - cy;
13.
double
s = -
1
/(
3.1415926
* sigma*sigma*sigma*sigma);
14.
s = s* (
1
- (nx*nx + ny*ny)/(
2
*sigma*sigma));
15.
s = s*exp(-(nx*nx+ny*ny)/(
2
*sigma*sigma));
16.
sum += s;
17.
H.at<
double
>(i,j) = s;
18.
}
19.
}
20.
21.
double
mean = sum/(size*size);
22.
23.
for
(
int
i =
0
; i < H.rows; i++)
24.
{
25.
for
(
int
j =
0
; j < H.cols; j++)
26.
{
27.
H.at<
double
>(i,j) -= mean;
28.
}
29.
}
30.
31.
return
H;
32.
}
1.
struct blob
2.
{
3.
int
x;
4.
int
y;
5.
double
sigma;
6.
int
intensity;
7.
};
01.
vector create_scale_space(Mat &im_in, vector<
double
> &sigma)
02.
{
03.
vector scale_space;
04.
05.
for
(
int
i =
0
;i < sigma.size();i++)
06.
{
07.
int
n = ceil(sigma[i]*
3
)*
2
+
1
;
08.
Mat LOG = create_LOG(n, sigma[i]);
09.
Mat dst;
10.
11.
filter2D(im_in.mul(sigma[i]*sigma[i]), dst, -
1
, LOG, Point(-
1
,-
1
));
12.
scale_space.push_back(dst);
13.
}
14.
15.
return
scale_space;
16.
}
01.
vector detect_blobs(Mat &im_in,
double
thresh, vector<
double
> et,
int
padding =
10
)
02.
{
03.
vector blobs;
04.
Mat addborder,norm;
05.
copyMakeBorder(im_in, addborder, padding, padding, padding, padding, BORDER_CONSTANT, Scalar(
255
));
06.
normalize(addborder, norm,
1
,
0
, NORM_MINMAX, CV_64F);
07.
vector all_ims = create_scale_space(norm, et);
08.
09.
for
(
int
i =
0
; i < et.size();i++)
10.
{
11.
Mat grayscale,mx;
12.
normalize( all_ims[i], grayscale,
0
,
255
, NORM_MINMAX, CV_8U, Mat() );
13.
Mat structedElement(
3
,
3
, CV_8U, Scalar(
1
));
14.
dilate(grayscale, mx, structedElement);
15.
grayscale = ( grayscale == mx) & (all_ims[i] > thresh);
//取大于threshold并且是周围最大
16.
for
(
int
j =
0
; j < norm.rows; j++)
17.
{
18.
for
(
int
k =
0
; k < norm.cols; k++)
19.
{
20.
if
(grayscale.atchar
>(j,k) >
0
)
21.
{
22.
struct blob b;
23.
b.x = j - padding;
24.
b.y = k - padding;
25.
b.sigma = et[i];
26.
b.intensity = all_ims[i].at<
double
>(j,k);
27.
blobs.push_back(b);
28.
}
29.
}
30.
}
31.
}
32.
33.
return
blobs;
34.
}
01.
vector prune_blobs(vector blobs_in)
02.
{
03.
vectorkeep(blobs_in.size(),
true
);
04.
05.
for
(
int
i =
0
;i < blobs_in.size();i++)
06.
{
07.
for
(
int
j =
0
;j < blobs_in.size();j++)
08.
{
09.
if
( (i==j) || (keep[i] ==
false
) || (keep[j] ==
false
) )
10.
continue
;
11.
12.
struct blob blobi = blobs_in[i];
13.
struct blob blobj = blobs_in[j];
14.
15.
int
xi = blobi.x;
16.
int
yi = blobi.y;
17.
double
ri = blobi.sigma;
18.
int
xj = blobj.x;
19.
int
yj = blobj.y;
20.
double
rj = blobj.sigma;
21.
22.
double
d = sqrt((xj-xi)*(xj-xi) + (yj-yi)*(yj-yi));
23.
double
rirj = ri+rj;
24.
double
overlap_percent = rirj/d;
25.
if
(overlap_percent >
2.0
)
26.
{
27.
if
(blobi.intensity > blobj.intensity)
28.
{
29.
keep[i] =
true
;
30.
keep[j] =
false
;
31.
}
else
{
32.
keep[i] =
false
;
33.
keep[j] =
true
;
34.
}
35.
}
36.
}
37.
}
38.
39.
40.
vector blobs_out;
41.
42.
for
(
int
i =
0
;i < blobs_in.size();i++)
43.
if
(keep[i])
44.
blobs_out.push_back(blobs_in[i]);
45.
46.
return
blobs_out;
47.
}
001.
#include
"opencv2/highgui/highgui.hpp"
002.
#include
"opencv2/imgproc/imgproc.hpp"
003.
#include
004.
#include
005.
#include
006.
using namespace cv;
007.
using namespace std;
008.
009.
const
double
pi =
3
.1415926f;
010.
011.
struct blob
012.
{
013.
int
x;
014.
int
y;
015.
double
sigma;
016.
int
intensity;
017.
};
018.
019.
Mat create_LOG(
int
size,
double
sigma)
020.
{
021.
Mat H(size, size, CV_64F);
022.
int
cx = (size-
1
)/
2
;
023.
int
cy = (size-
1
)/
2
;
024.
double
sum =
0
;
025.
for
(
int
i =
0
; i < H.rows; i++)
026.
{
027.
for
(
int
j =
0
; j < H.cols; j++)
028.
{
029.
int
nx = i - cx;
030.
int
ny = j - cy;
031.
double
s = -
1
/(
3.1415926
* sigma*sigma*sigma*sigma);
032.
s = s* (
1
- (nx*nx + ny*ny)/(
2
*sigma*sigma));
033.
s = s*exp(-(nx*nx+ny*ny)/(
2
*sigma*sigma));
034.
sum += s;
035.
H.at<
double
>(i,j) = s;
036.
}
037.
}
038.
039.
double
mean = sum/(size*size);
040.
041.
for
(
int
i =
0
; i < H.rows; i++)
042.
{
043.
for
(
int
j =
0
; j < H.cols; j++)
044.
{
045.
H.at<
double
>(i,j) -= mean;
046.
}
047.
}
048.
049.
return
H;
050.
}
051.
052.
vector create_scale_space(Mat &im_in, vector<
double
> &sigma)
053.
{
054.
vector scale_space;
055.
056.
for
(
int
i =
0
;i < sigma.size();i++)
057.
{
058.
int
n = ceil(sigma[i]*
3
)*
2
+
1
;
059.
Mat LOG = create_LOG(n, sigma[i]);
060.
Mat dst;
061.
062.
filter2D(im_in.mul(sigma[i]*sigma[i]), dst, -
1
, LOG, Point(-
1
,-
1
));
063.
scale_space.push_back(dst);
064.
}
065.
066.
return
scale_space;
067.
}
068.
069.
vector<
double
> create_sigma(
double
start,
double
step,
double
end)
070.
{
071.
vector<
double
> sigma;
072.
while
(start <= end+1e-
8
){
073.
double
s = exp(start);
074.
sigma.push_back(s);
075.
start += step;
076.
}
077.
return
sigma;
078.
}
079.
080.
vector detect_blobs(Mat &im_in,
double
thresh, vector<
double
> et,
int
padding =
10
)
081.
{
082.
vector blobs;
083.
Mat addborder,norm;
084.
copyMakeBorder(im_in, addborder, padding, padding, padding, padding, BORDER_CONSTANT, Scalar(
255
));
085.
normalize(addborder, norm,
1
,
0
, NORM_MINMAX, CV_64F);
086.
vector all_ims = create_scale_space(norm, et);
087.
088.
for
(
int
i =
0
; i < et.size();i++)
089.
{
090.
Mat grayscale,mx;
091.
normalize( all_ims[i], grayscale,
0
,
255
, NORM_MINMAX, CV_8U, Mat() );
092.
Mat structedElement(
3
,
3
, CV_8U, Scalar(
1
));
093.
dilate(grayscale, mx, structedElement);
094.
grayscale = ( grayscale == mx) & (all_ims[i] > thresh);
095.
for
(
int
j =
0
; j < norm.rows; j++)
096.
{
097.
for
(
int
k =
0
; k < norm.cols; k++)
098.
{
099.
if
(grayscale.atchar
>(j,k) >
0
)
100.
{
101.
struct blob b;
102.
b.x = j - padding;
103.
b.y = k - padding;
104.
b.sigma = et[i];
105.
b.intensity = all_ims[i].at<
double
>(j,k);
106.
blobs.push_back(b);
107.
}
108.
}
109.
}
110.
}
111.
112.
return
blobs;
113.
}
114.
115.
vector prune_blobs(vector blobs_in)
116.
{
117.
vectorkeep(blobs_in.size(),
true
);
118.
119.
for
(
int
i =
0
;i < blobs_in.size();i++)
120.
{
121.
for
(
int
j =
0
;j < blobs_in.size();j++)
122.
{
123.
if
( (i==j) || (keep[i] ==
false
) || (keep[j] ==
false
) )
124.
continue
;
125.
126.
struct blob blobi = blobs_in[i];
127.
struct blob blobj = blobs_in[j];
128.
129.
int
xi = blobi.x;
130.
int
yi = blobi.y;
131.
double
ri = blobi.sigma;
132.
int
xj = blobj.x;
133.
int
yj = blobj.y;
134.
double
rj = blobj.sigma;
135.
136.
double
d = sqrt((xj-xi)*(xj-xi) + (yj-yi)*(yj-yi));
137.
double
rirj = ri+rj;
138.
double
overlap_percent = rirj/d;
139.
if
(overlap_percent >
2.0
)
140.
{
141.
if
(blobi.intensity > blobj.intensity)
142.
{
143.
keep[i] =
true
;
144.
keep[j] =
false
;
145.
}
else
{
146.
keep[i] =
false
;
147.
keep[j] =
true
;
148.
}
149.
}
150.
}
151.
}
152.
153.
154.
vector blobs_out;
155.
156.
for
(
int
i =
0
;i < blobs_in.size();i++)
157.
if
(keep[i])
158.
blobs_out.push_back(blobs_in[i]);
159.
160.
return
blobs_out;
161.
}
162.
163.
int
main(
int
,
char
** argv )
164.
{
165.
Mat src,gray;
166.
167.
src = imread( argv[
1
] );
168.
cvtColor( src, gray, CV_RGB2GRAY );
169.
vector<
double
> sigma = create_sigma(
1.0
,
0.2
,
3.0
);
170.
vector blobs = detect_blobs(gray,
0.2
, sigma);
171.
vector blobs_trimmed = prune_blobs(blobs);
172.
173.
for
(
int
i =
0
;i < blobs_trimmed.size();i++)
174.
{
175.
struct blob b = blobs_trimmed[i];
176.
circle( src, Point( b.y, b.x ),
1.5
*b.sigma, Scalar(
0
),
2
,
8
,
0
);
177.
}
178.
179.
namedWindow(
"result"
,
1
);
180.
imshow(
"result"
, src);
181.
waitKey();
182.
return
0
;
183.
}