Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
O
opencv
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
opencv
Commits
fede94e9
Commit
fede94e9
authored
May 21, 2015
by
Vadim Pisarevsky
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
it's now true MSER algorithm that works well on binary images too
parent
b5a71228
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
200 additions
and
150 deletions
+200
-150
mser.cpp
modules/features2d/src/mser.cpp
+200
-150
No files found.
modules/features2d/src/mser.cpp
View file @
fede94e9
...
...
@@ -29,12 +29,12 @@
*
* OpenCV functions for MSER extraction
*
* 1. there are two different implementation of MSER, one for gr
e
y image, one for color image
* 2. the gr
e
y image algorithm is taken from: Linear Time Maximally Stable Extremal Regions;
* 1. there are two different implementation of MSER, one for gr
a
y image, one for color image
* 2. the gr
a
y image algorithm is taken from: Linear Time Maximally Stable Extremal Regions;
* the paper claims to be faster than union-find method;
* it actually get 1.5~2m/s on my centrino L7200 1.2GHz laptop.
* 3. the color image algorithm is taken from: Maximally Stable Colour Regions for Recognition and Match;
* it should be much slower than gr
e
y image method ( 3~4 times );
* it should be much slower than gr
a
y image method ( 3~4 times );
* the chi_table.h file is taken directly from paper's source code which is distributed under GPL.
* 4. though the name is *contours*, the result actually is a list of point set.
*/
...
...
@@ -121,15 +121,129 @@ public:
};
typedef
int
PPixel
;
struct
WParams
{
Params
p
;
vector
<
vector
<
Point
>
>*
msers
;
vector
<
Rect
>*
bboxvec
;
Pixel
*
pix0
;
int
step
;
};
// the history of region grown
struct
CompHistory
{
CompHistory
()
{
shortcut
=
child
=
0
;
stable
=
val
=
size
=
0
;
}
CompHistory
*
shortcut
;
CompHistory
*
child
;
int
stable
;
// when it ever stabled before, record the size
CompHistory
()
{
parent_
=
child_
=
next_
=
0
;
val
=
size
=
0
;
var
=
-
1.
f
;
head
=
0
;
checked
=
false
;
}
void
updateTree
(
WParams
&
wp
,
CompHistory
**
_h0
,
CompHistory
**
_h1
,
bool
final
)
{
if
(
var
>=
0.
f
)
return
;
int
delta
=
wp
.
p
.
delta
;
CompHistory
*
h0_
=
0
,
*
h1_
=
0
;
CompHistory
*
c
=
child_
;
if
(
size
>=
wp
.
p
.
minArea
)
{
for
(
;
c
!=
0
;
c
=
c
->
next_
)
{
if
(
c
->
var
<
0.
f
)
c
->
updateTree
(
wp
,
c
==
child_
?
&
h0_
:
0
,
c
==
child_
?
&
h1_
:
0
,
final
);
if
(
c
->
var
<
0.
f
)
return
;
}
}
// find h0 and h1 such that:
// h0->val >= h->val - delta and (h0->parent == 0 or h0->parent->val < h->val - delta)
// h1->val <= h->val + delta and (h1->child == 0 or h1->child->val < h->val + delta)
// then we will adjust h0 and h1 as h moves towards latest
CompHistory
*
h0
=
this
,
*
h1
=
h1_
&&
h1_
->
size
>
size
?
h1_
:
this
;
if
(
h0_
)
{
for
(
h0
=
h0_
;
h0
!=
this
&&
h0
->
val
<
val
-
delta
;
h0
=
h0
->
parent_
)
;
}
else
{
for
(
;
h0
->
child_
&&
h0
->
child_
->
val
>=
val
-
delta
;
h0
=
h0
->
child_
)
;
}
for
(
;
h1
->
parent_
&&
h1
->
parent_
->
val
<=
val
+
delta
;
h1
=
h1
->
parent_
)
;
if
(
_h0
)
*
_h0
=
h0
;
if
(
_h1
)
*
_h1
=
h1
;
// when we do not well-defined ER(h->val + delta), we stop
// the process of computing variances unless we are at the final step
if
(
!
final
&&
!
h1
->
parent_
&&
h1
->
val
<
val
+
delta
)
return
;
var
=
(
float
)(
h1
->
size
-
h0
->
size
)
/
size
;
c
=
child_
;
for
(
;
c
!=
0
;
c
=
c
->
next_
)
c
->
checkAndCapture
(
wp
);
if
(
final
&&
!
parent_
)
checkAndCapture
(
wp
);
}
void
checkAndCapture
(
WParams
&
wp
)
{
if
(
checked
)
return
;
checked
=
true
;
if
(
size
<
wp
.
p
.
minArea
||
size
>
wp
.
p
.
maxArea
||
var
<
0.
f
||
var
>
wp
.
p
.
maxVariation
)
return
;
if
(
child_
)
{
CompHistory
*
c
=
child_
;
for
(
;
c
!=
0
;
c
=
c
->
next_
)
{
if
(
c
->
var
>=
0.
f
&&
var
>
c
->
var
)
return
;
}
}
if
(
parent_
&&
parent_
->
var
>=
0.
f
&&
var
>=
parent_
->
var
)
return
;
int
xmin
=
INT_MAX
,
ymin
=
INT_MAX
,
xmax
=
INT_MIN
,
ymax
=
INT_MIN
,
j
=
0
;
wp
.
msers
->
push_back
(
vector
<
Point
>
());
vector
<
Point
>&
region
=
wp
.
msers
->
back
();
region
.
resize
(
size
);
const
Pixel
*
pix0
=
wp
.
pix0
;
int
step
=
wp
.
step
;
for
(
PPixel
pix
=
head
;
j
<
size
;
j
++
,
pix
=
pix0
[
pix
].
getNext
()
)
{
int
y
=
pix
/
step
;
int
x
=
pix
-
y
*
step
;
xmin
=
std
::
min
(
xmin
,
x
);
xmax
=
std
::
max
(
xmax
,
x
);
ymin
=
std
::
min
(
ymin
,
y
);
ymax
=
std
::
max
(
ymax
,
y
);
region
[
j
]
=
Point
(
x
,
y
);
}
wp
.
bboxvec
->
push_back
(
Rect
(
xmin
,
ymin
,
xmax
-
xmin
+
1
,
ymax
-
ymin
+
1
));
}
CompHistory
*
child_
;
CompHistory
*
parent_
;
CompHistory
*
next_
;
int
val
;
int
size
;
float
var
;
PPixel
head
;
bool
checked
;
};
struct
ConnectedComp
...
...
@@ -144,141 +258,87 @@ public:
head
=
tail
=
0
;
history
=
0
;
size
=
0
;
grey_level
=
gray
;
dvar
=
false
;
var
=
0
;
gray_level
=
gray
;
}
// add history chunk to a connected component
void
growHistory
(
CompHistory
*
h
)
{
h
->
child
=
h
;
if
(
!
history
)
void
growHistory
(
CompHistory
*&
hptr
,
WParams
&
wp
,
int
new_gray_level
,
bool
final
,
bool
force
=
false
)
{
bool
update
=
final
;
if
(
new_gray_level
<
0
)
new_gray_level
=
gray_level
;
if
(
!
history
||
(
history
->
size
!=
size
&&
size
>
0
&&
(
gray_level
!=
history
->
val
||
force
)))
{
CompHistory
*
h
=
hptr
++
;
h
->
parent_
=
0
;
h
->
child_
=
history
;
h
->
next_
=
0
;
if
(
history
)
history
->
parent_
=
h
;
h
->
val
=
gray_level
;
h
->
size
=
size
;
h
->
head
=
head
;
history
=
h
;
h
->
var
=
FLT_MAX
;
h
->
checked
=
true
;
if
(
h
->
size
>=
wp
.
p
.
minArea
)
{
h
->
shortcut
=
h
;
h
->
stable
=
0
;
h
->
var
=
-
1.
f
;
h
->
checked
=
false
;
update
=
true
;
}
else
{
history
->
child
=
h
;
h
->
shortcut
=
history
->
shortcut
;
h
->
stable
=
history
->
stable
;
}
h
->
val
=
gre
y_level
;
h
->
size
=
size
;
history
=
h
;
gray_level
=
new_gra
y_level
;
if
(
update
&&
history
)
history
->
updateTree
(
wp
,
0
,
0
,
final
)
;
}
// merging two connected components
static
void
merge
(
const
ConnectedComp
*
comp1
,
const
ConnectedComp
*
comp2
,
ConnectedComp
*
comp
,
CompHistory
*
h
,
Pixel
*
pix0
)
{
comp
->
grey_level
=
comp2
->
grey_level
;
h
->
child
=
h
;
// select the winner by size
if
(
comp1
->
size
<
comp2
->
size
)
void
merge
(
ConnectedComp
*
comp1
,
ConnectedComp
*
comp2
,
CompHistory
*&
hptr
,
WParams
&
wp
)
{
comp1
->
growHistory
(
hptr
,
wp
,
-
1
,
false
);
comp2
->
growHistory
(
hptr
,
wp
,
-
1
,
false
);
if
(
comp1
->
size
<
comp2
->
size
)
std
::
swap
(
comp1
,
comp2
);
if
(
!
comp1
->
history
)
{
h
->
shortcut
=
h
;
h
->
stable
=
0
;
}
else
if
(
comp2
->
size
==
0
)
{
comp1
->
history
->
child
=
h
;
h
->
shortcut
=
comp1
->
history
->
shortcut
;
h
->
stable
=
comp1
->
history
->
stable
;
}
if
(
comp2
->
history
&&
comp2
->
history
->
stable
>
h
->
stable
)
h
->
stable
=
comp2
->
history
->
stable
;
h
->
val
=
comp1
->
grey_level
;
h
->
size
=
comp1
->
size
;
// put comp1 to history
comp
->
var
=
comp1
->
var
;
comp
->
dvar
=
comp1
->
dvar
;
if
(
comp1
->
size
>
0
&&
comp2
->
size
>
0
)
pix0
[
comp1
->
tail
].
setNext
(
comp2
->
head
);
PPixel
head
=
comp1
->
size
>
0
?
comp1
->
head
:
comp2
->
head
;
PPixel
tail
=
comp2
->
size
>
0
?
comp2
->
tail
:
comp1
->
tail
;
// always made the newly added in the last of the pixel list (comp1 ... comp2)
comp
->
head
=
head
;
comp
->
tail
=
tail
;
comp
->
history
=
h
;
comp
->
size
=
comp1
->
size
+
comp2
->
size
;
}
float
calcVariation
(
int
delta
)
const
{
if
(
!
history
)
return
1.
f
;
int
val
=
grey_level
;
CompHistory
*
shortcut
=
history
->
shortcut
;
while
(
shortcut
!=
shortcut
->
shortcut
&&
shortcut
->
val
+
delta
>
val
)
shortcut
=
shortcut
->
shortcut
;
CompHistory
*
child
=
shortcut
->
child
;
while
(
child
!=
child
->
child
&&
child
->
val
+
delta
<=
val
)
{
shortcut
=
child
;
child
=
child
->
child
;
}
// get the position of history where the shortcut->val <= delta+val and shortcut->child->val >= delta+val
history
->
shortcut
=
shortcut
;
return
(
float
)(
size
-
shortcut
->
size
)
/
(
float
)
shortcut
->
size
;
// here is a small modification of MSER where cal ||R_{i}-R_{i-delta}||/||R_{i-delta}||
// in standard MSER, cal ||R_{i+delta}-R_{i-delta}||/||R_{i}||
// my calculation is simpler and much easier to implement
}
bool
isStable
(
const
Params
&
p
)
{
// tricky part: it actually check the stablity of one-step back
if
(
!
history
||
history
->
size
<=
p
.
minArea
||
history
->
size
>=
p
.
maxArea
)
return
false
;
float
div
=
(
float
)(
history
->
size
-
history
->
stable
)
/
(
float
)
history
->
size
;
float
_var
=
calcVariation
(
p
.
delta
);
bool
_dvar
=
(
var
<
_var
)
||
(
history
->
val
+
1
<
grey_level
);
bool
stable
=
_dvar
&&
!
dvar
&&
_var
<
p
.
maxVariation
&&
div
>
p
.
minDiversity
;
var
=
_var
;
dvar
=
_dvar
;
if
(
stable
)
history
->
stable
=
history
->
size
;
return
stable
;
gray_level
=
comp1
->
gray_level
;
head
=
comp1
->
head
;
tail
=
comp1
->
tail
;
size
=
comp1
->
size
;
history
=
comp1
->
history
;
return
;
}
// convert the point set to CvSeq
Rect
capture
(
const
Pixel
*
pix0
,
int
step
,
vector
<
Point
>&
region
)
const
{
int
xmin
=
INT_MAX
,
ymin
=
INT_MAX
,
xmax
=
INT_MIN
,
ymax
=
INT_MIN
;
region
.
clear
();
CompHistory
*
h1
=
comp1
->
history
;
CompHistory
*
h2
=
comp2
->
history
;
for
(
PPixel
pix
=
head
;
pix
!=
0
;
pix
=
pix0
[
pix
].
getNext
()
)
{
int
y
=
pix
/
step
;
int
x
=
pix
-
y
*
step
;
xmin
=
std
::
min
(
xmin
,
x
);
xmax
=
std
::
max
(
xmax
,
x
);
ymin
=
std
::
min
(
ymin
,
y
);
ymax
=
std
::
max
(
ymax
,
y
);
gray_level
=
std
::
max
(
comp1
->
gray_level
,
comp2
->
gray_level
);
history
=
comp1
->
history
;
wp
.
pix0
[
comp1
->
tail
].
setNext
(
comp2
->
head
);
region
.
push_back
(
Point
(
x
,
y
));
head
=
comp1
->
head
;
tail
=
comp2
->
tail
;
size
=
comp1
->
size
+
comp2
->
size
;
bool
keep_2nd
=
h2
->
size
>
wp
.
p
.
minArea
;
growHistory
(
hptr
,
wp
,
-
1
,
false
,
keep_2nd
);
if
(
keep_2nd
)
{
h1
->
next_
=
h2
;
h2
->
parent_
=
history
;
}
return
Rect
(
xmin
,
ymin
,
xmax
-
xmin
+
1
,
ymax
-
ymin
+
1
);
}
PPixel
head
;
PPixel
tail
;
CompHistory
*
history
;
int
gr
e
y_level
;
int
gr
a
y_level
;
int
size
;
float
var
;
// the current variation (most time is the variation of one-step back)
bool
dvar
;
// the derivative of last var
};
void
detectRegions
(
InputArray
image
,
...
...
@@ -296,7 +356,7 @@ public:
heapbuf
.
resize
(
cols
*
rows
+
256
);
histbuf
.
resize
(
cols
*
rows
);
Pixel
borderpix
;
borderpix
.
setDir
(
4
);
borderpix
.
setDir
(
5
);
for
(
j
=
0
;
j
<
step
;
j
++
)
{
...
...
@@ -349,6 +409,12 @@ public:
Pixel
**
heap
[
256
];
ConnectedComp
comp
[
257
];
ConnectedComp
*
comptr
=
&
comp
[
0
];
WParams
wp
;
wp
.
p
=
params
;
wp
.
msers
=
&
msers
;
wp
.
bboxvec
=
&
bboxvec
;
wp
.
pix0
=
ptr0
;
wp
.
step
=
step
;
heap
[
0
]
=
&
heapbuf
[
0
];
heap
[
0
][
0
]
=
0
;
...
...
@@ -359,9 +425,9 @@ public:
heap
[
i
][
0
]
=
0
;
}
comptr
->
gr
e
y_level
=
256
;
comptr
->
gr
a
y_level
=
256
;
comptr
++
;
comptr
->
gr
e
y_level
=
ptr
->
getGray
(
ptr0
,
imgptr0
,
mask
);
comptr
->
gr
a
y_level
=
ptr
->
getGray
(
ptr0
,
imgptr0
,
mask
);
ptr
->
setDir
(
1
);
int
dir
[]
=
{
0
,
1
,
step
,
-
1
,
-
step
};
for
(
;;
)
...
...
@@ -427,48 +493,32 @@ public:
ptr
=
*
heap
[
curr_gray
];
heap
[
curr_gray
]
--
;
if
(
curr_gray
<
comptr
[
-
1
].
grey_level
)
{
// check the stablity and push a new history, increase the grey level
if
(
comptr
->
isStable
(
params
)
)
{
msers
.
push_back
(
vector
<
Point
>
());
vector
<
Point
>&
mser
=
msers
.
back
();
Rect
box
=
comptr
->
capture
(
ptr0
,
step
,
mser
);
bboxvec
.
push_back
(
box
);
}
comptr
->
growHistory
(
histptr
++
);
comptr
[
0
].
grey_level
=
curr_gray
;
}
if
(
curr_gray
<
comptr
[
-
1
].
gray_level
)
comptr
->
growHistory
(
histptr
,
wp
,
curr_gray
,
false
);
else
{
// keep merging top two comp in stack until the gr
e
y level >= pixel_val
// keep merging top two comp in stack until the gr
a
y level >= pixel_val
for
(;;)
{
comptr
--
;
ConnectedComp
::
merge
(
comptr
+
1
,
comptr
,
comptr
,
histptr
++
,
ptr0
);
if
(
curr_gray
<=
comptr
[
0
].
gr
e
y_level
)
comptr
->
merge
(
comptr
,
comptr
+
1
,
histptr
,
wp
);
if
(
curr_gray
<=
comptr
[
0
].
gr
a
y_level
)
break
;
if
(
curr_gray
<
comptr
[
-
1
].
gr
e
y_level
)
if
(
curr_gray
<
comptr
[
-
1
].
gr
a
y_level
)
{
// check the stablity here otherwise it wouldn't be an ER
if
(
comptr
->
isStable
(
params
)
)
{
msers
.
push_back
(
vector
<
Point
>
());
vector
<
Point
>&
mser
=
msers
.
back
();
Rect
box
=
comptr
->
capture
(
ptr0
,
step
,
mser
);
bboxvec
.
push_back
(
box
);
}
comptr
->
growHistory
(
histptr
++
);
comptr
[
0
].
grey_level
=
curr_gray
;
comptr
->
growHistory
(
histptr
,
wp
,
curr_gray
,
false
);
break
;
}
}
}
}
}
for
(
;
comptr
->
gray_level
!=
256
;
comptr
--
)
{
comptr
->
growHistory
(
histptr
,
wp
,
256
,
true
,
true
);
}
}
Mat
tempsrc
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment