Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Olena
pylene
Commits
ee3f1d79
Commit
ee3f1d79
authored
Jun 30, 2021
by
Baptiste Esteban
Browse files
Reimplement saliency map with basic LCA
parent
a3bb40df
Pipeline
#28899
failed with stages
in 18 minutes and 7 seconds
Changes
5
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
doc/source/snippets/saliency_example.cpp
View file @
ee3f1d79
...
@@ -11,34 +11,19 @@
...
@@ -11,34 +11,19 @@
#include
<iostream>
#include
<iostream>
void
process_example
(
const
mln
::
image2d
<
uint8_t
>&
img
,
const
std
::
string
&
output_filename
,
const
double
threshold
)
void
process_example
(
const
mln
::
image2d
<
uint8_t
>&
img
,
const
std
::
string
&
output_filename
)
{
{
// 2. Build the watershed hierarchy
// 2. Build the watershed hierarchy
auto
area_attribute_func
=
[](
auto
tree
,
auto
nm
)
->
std
::
vector
<
size_t
>
{
auto
area_attribute_func
=
[](
auto
tree
,
auto
nm
)
{
return
tree
.
compute_attribute_on_points
(
nm
,
mln
::
accu
::
features
::
count
<>
());
return
tree
.
compute_attribute_on_points
(
nm
,
mln
::
accu
::
features
::
count
<
double
>
());
};
};
auto
[
t
,
nm
]
=
mln
::
morpho
::
watershed_hierarchy
(
img
,
area_attribute_func
,
mln
::
c4
);
auto
[
t
,
nm
]
=
mln
::
morpho
::
watershed_hierarchy
(
img
,
area_attribute_func
,
mln
::
c4
);
// 3. Compute the
mean attribute
// 3. Compute the
saliency map
auto
mean
=
t
.
compute_attribute_on_values
(
nm
,
img
,
mln
::
accu
::
accumulators
::
mean
<
uint8_t
>
(
));
auto
saliency
=
t
.
saliency
(
nm
,
::
ranges
::
make_span
(
t
.
values
.
data
(),
t
.
values
.
size
()
));
// 4. Compute a cut of the watershed hierarchy
// 4. Save the output
auto
cut_nm
=
t
.
horizontal_cut
(
threshold
,
nm
);
mln
::
io
::
imsave
(
mln
::
view
::
cast
<
std
::
uint16_t
>
(
saliency
),
output_filename
);
// 5. Label the cut
auto
cut
=
t
.
reconstruct_from
(
cut_nm
,
::
ranges
::
make_span
(
mean
));
mln
::
io
::
imsave
(
mln
::
view
::
cast
<
uint8_t
>
(
cut
),
output_filename
);
mln
::
image2d
<
uint8_t
>
in
;
mln
::
io
::
imread
(
output_filename
,
in
);
auto
[
t2
,
node_map
]
=
mln
::
morpho
::
tos
(
in
,
{
0
,
0
});
auto
saliency
=
t2
.
saliency
(
in
);
// 5. Save the output
mln
::
io
::
imsave
(
mln
::
view
::
cast
<
uint8_t
>
(
saliency
),
output_filename
);
}
}
int
main
(
int
argc
,
char
*
argv
[])
int
main
(
int
argc
,
char
*
argv
[])
...
@@ -58,7 +43,7 @@ int main(int argc, char* argv[])
...
@@ -58,7 +43,7 @@ int main(int argc, char* argv[])
if
(
in
.
sample_type
()
==
mln
::
sample_type_id
::
UINT8
)
if
(
in
.
sample_type
()
==
mln
::
sample_type_id
::
UINT8
)
{
{
const
auto
*
img
=
in
.
cast_to
<
std
::
uint8_t
,
2
>
();
const
auto
*
img
=
in
.
cast_to
<
std
::
uint8_t
,
2
>
();
process_example
(
*
img
,
out_filename
,
25
);
process_example
(
*
img
,
out_filename
);
}
}
else
else
{
{
...
...
pylene/include/mln/accu/accumulator.hpp
View file @
ee3f1d79
...
@@ -109,7 +109,7 @@ namespace mln
...
@@ -109,7 +109,7 @@ namespace mln
typedef
typename
accu_of
<
AccuLike
,
T
>::
type
accu_type
;
typedef
typename
accu_of
<
AccuLike
,
T
>::
type
accu_type
;
public:
public:
typedef
typename
std
::
result
_of
<
Extractor
(
accu_type
)
>::
type
type
;
typedef
typename
std
::
invoke_
result
<
Extractor
,
accu_type
>::
type
type
;
};
};
template
<
class
A
,
class
T
,
class
E
=
default_extractor
>
template
<
class
A
,
class
T
,
class
E
=
default_extractor
>
...
...
pylene/include/mln/morpho/component_tree.hpp
View file @
ee3f1d79
...
@@ -90,7 +90,7 @@ namespace mln::morpho
...
@@ -90,7 +90,7 @@ namespace mln::morpho
/// \param acc Accumulator to apply on values
/// \param acc Accumulator to apply on values
template
<
class
I
,
class
J
,
class
Accu
>
template
<
class
I
,
class
J
,
class
Accu
>
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_value_t
<
J
>>::
type
>
//
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_value_t
<
J
>>::
type
>
//
compute_attribute_on_values
(
I
node_map
,
J
values
,
Accu
acc
);
compute_attribute_on_values
(
I
node_map
,
J
values
,
Accu
acc
)
const
;
/// \brief Compute attribute on values
/// \brief Compute attribute on values
///
///
...
@@ -101,7 +101,7 @@ namespace mln::morpho
...
@@ -101,7 +101,7 @@ namespace mln::morpho
/// \param acc Accumulator to apply on points
/// \param acc Accumulator to apply on points
template
<
class
I
,
class
Accu
>
template
<
class
I
,
class
Accu
>
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_point_t
<
I
>>::
type
>
//
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_point_t
<
I
>>::
type
>
//
compute_attribute_on_points
(
I
node_map
,
Accu
acc
);
compute_attribute_on_points
(
I
node_map
,
Accu
acc
)
const
;
/// \brief Compute attribute on pixels
/// \brief Compute attribute on pixels
...
@@ -113,7 +113,7 @@ namespace mln::morpho
...
@@ -113,7 +113,7 @@ namespace mln::morpho
/// \param acc Accumulator to apply on values
/// \param acc Accumulator to apply on values
template
<
class
I
,
class
J
,
class
Accu
>
template
<
class
I
,
class
J
,
class
Accu
>
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_pixel_t
<
J
>>::
type
>
//
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_pixel_t
<
J
>>::
type
>
//
compute_attribute_on_pixels
(
I
node_map
,
J
values
,
Accu
acc
);
compute_attribute_on_pixels
(
I
node_map
,
J
values
,
Accu
acc
)
const
;
/// \brief Compute the horizontal cut of a hierarchie at level `threshold` and return a nodemap
/// \brief Compute the horizontal cut of a hierarchie at level `threshold` and return a nodemap
/// valued with the node indices of the lowest nodes satisfying levels[n] > threshold
/// valued with the node indices of the lowest nodes satisfying levels[n] > threshold
...
@@ -135,9 +135,10 @@ namespace mln::morpho
...
@@ -135,9 +135,10 @@ namespace mln::morpho
using
node_t
=
int
;
using
node_t
=
int
;
/// \brief Produce a visualization of the given Component Tree using the Khalimsky grid of the saliency map
/// \brief Produce a visualization of the given Component Tree using the Khalimsky grid of the saliency map
/// The component_tree must be built on a 2D image with a 4-connectivity.
///
///
/// \param node_map Image point -> node_id mapping
/// \param node_map Image point -> node_id mapping
mln
::
image2d
<
double
>
saliency
(
mln
::
image2d
<
u
int
8_t
>
node_map
)
;
mln
::
image2d
<
double
>
saliency
(
mln
::
image2d
<
int
>
node_map
,
::
ranges
::
span
<
double
>
values
)
const
;
std
::
vector
<
node_t
>
parent
;
std
::
vector
<
node_t
>
parent
;
...
@@ -296,7 +297,7 @@ namespace mln::morpho
...
@@ -296,7 +297,7 @@ namespace mln::morpho
template
<
class
I
,
class
Accu
>
template
<
class
I
,
class
Accu
>
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_point_t
<
I
>>::
type
>
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_point_t
<
I
>>::
type
>
component_tree
<
void
>::
compute_attribute_on_points
(
I
node_map
,
Accu
acc
)
component_tree
<
void
>::
compute_attribute_on_points
(
I
node_map
,
Accu
acc
)
const
{
{
mln_entering
(
"mln::morpho::component_tree::compute_attribute_on_points"
);
mln_entering
(
"mln::morpho::component_tree::compute_attribute_on_points"
);
...
@@ -329,7 +330,7 @@ namespace mln::morpho
...
@@ -329,7 +330,7 @@ namespace mln::morpho
template
<
class
I
,
class
J
,
class
Accu
>
template
<
class
I
,
class
J
,
class
Accu
>
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_value_t
<
J
>>::
type
>
//
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_value_t
<
J
>>::
type
>
//
component_tree
<
void
>::
compute_attribute_on_values
(
I
node_map
,
J
input
,
Accu
acc
)
component_tree
<
void
>::
compute_attribute_on_values
(
I
node_map
,
J
input
,
Accu
acc
)
const
{
{
mln_entering
(
"mln::morpho::component_tree::compute_attribute_on_values"
);
mln_entering
(
"mln::morpho::component_tree::compute_attribute_on_values"
);
...
@@ -363,7 +364,7 @@ namespace mln::morpho
...
@@ -363,7 +364,7 @@ namespace mln::morpho
template
<
class
I
,
class
J
,
class
Accu
>
template
<
class
I
,
class
J
,
class
Accu
>
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_pixel_t
<
J
>>::
type
>
//
std
::
vector
<
typename
accu
::
result_of
<
Accu
,
image_pixel_t
<
J
>>::
type
>
//
component_tree
<
void
>::
compute_attribute_on_pixels
(
I
node_map
,
J
values
,
Accu
acc
)
component_tree
<
void
>::
compute_attribute_on_pixels
(
I
node_map
,
J
values
,
Accu
acc
)
const
{
{
mln_entering
(
"mln::morpho::component_tree::compute_attribute_on_pixels"
);
mln_entering
(
"mln::morpho::component_tree::compute_attribute_on_pixels"
);
...
...
pylene/src/morpho/component_tree.cpp
View file @
ee3f1d79
#include
<mln/morpho/component_tree.hpp>
#include
<mln/morpho/component_tree.hpp>
#include
<iostream>
namespace
mln
::
morpho
namespace
mln
::
morpho
{
{
...
@@ -33,83 +34,44 @@ namespace mln::morpho
...
@@ -33,83 +34,44 @@ namespace mln::morpho
return
depth
;
return
depth
;
}
}
static
std
::
vector
<
internal
::
edge_t
<
int
,
double
>
>
saliency
_map
(
mln
::
image2d
<
u
int
8_t
>
node_map
)
mln
::
image2d
<
double
>
component_tree
<
void
>::
saliency
(
mln
::
image2d
<
int
>
node_map
,
::
ranges
::
span
<
double
>
values
)
const
{
{
std
::
vector
<
internal
::
edge_t
<
int
,
double
>>
res
;
auto
lca
=
[
parent
=
parent
,
d
=
compute_depth
()](
int
a
,
int
b
)
{
while
(
a
!=
b
)
auto
width
=
node_map
.
width
();
auto
dom
=
node_map
.
domain
();
mln_foreach
(
auto
p
,
dom
)
{
for
(
auto
q
:
mln
::
c4
.
after
(
p
))
{
{
if
(
dom
.
has
(
q
))
if
(
d
[
a
]
<
d
[
b
])
b
=
parent
[
b
];
else
if
(
d
[
a
]
>
d
[
b
])
a
=
parent
[
a
];
else
{
{
auto
offset_p
=
p
[
0
]
+
width
*
p
[
1
];
a
=
parent
[
a
];
auto
offset_q
=
q
[
0
]
+
width
*
q
[
1
];
b
=
parent
[
b
];
internal
::
edge_t
<
int
,
double
>
edge
=
{
offset_p
,
offset_q
,
0
};
edge
.
w
=
std
::
abs
(
node_map
(
p
)
-
node_map
(
q
));
res
.
emplace_back
(
edge
);
}
}
}
}
}
return
b
;
};
return
res
;
std
::
vector
<
internal
::
edge_t
<
point2d
,
int
>>
s_map
;
}
mln_foreach
(
auto
p
,
node_map
.
domain
())
mln
::
image2d
<
double
>
component_tree
<
void
>::
saliency
(
mln
::
image2d
<
uint8_t
>
node_map
)
{
int
height
=
node_map
.
height
();
int
width
=
node_map
.
width
();
int
res_height
=
2
*
height
+
1
;
int
res_width
=
2
*
width
+
1
;
image2d
<
double
>
res
(
res_width
,
res_height
);
fill
(
res
,
0
);
const
std
::
vector
<
internal
::
edge_t
<
int
,
double
>>&
s_map
=
saliency_map
(
node_map
);
for
(
const
auto
[
u
,
v
,
w
]
:
s_map
)
{
int
u_pos
[
2
]
=
{
u
%
width
,
u
/
width
};
int
v_pos
[
2
]
=
{
v
%
width
,
v
/
width
};
int
res_offset
[
2
]
=
{
u_pos
[
0
]
-
v_pos
[
0
],
u_pos
[
1
]
-
v_pos
[
1
]};
int
res_edge_pos
[
2
]
=
{
2
*
v_pos
[
0
]
+
1
+
res_offset
[
0
],
2
*
v_pos
[
1
]
+
1
+
res_offset
[
1
]};
res
({
res_edge_pos
[
0
],
res_edge_pos
[
1
]})
=
w
;
}
for
(
int
y
=
0
;
y
<
res_height
;
y
+=
2
)
{
{
for
(
int
x
=
0
;
x
<
res_width
;
x
+=
2
)
if
(
p
[
0
]
==
440
&&
p
[
1
]
==
224
)
std
::
cout
<<
""
;
for
(
auto
q
:
c4
.
after
(
p
))
{
{
double
max
=
std
::
numeric_limits
<
double
>::
min
();
if
(
node_map
.
domain
().
has
(
q
))
s_map
.
emplace_back
(
p
,
q
,
values
[
lca
(
node_map
(
p
),
node_map
(
q
))]);
if
(
y
+
1
<
height
)
max
=
std
::
max
(
max
,
res
({
x
,
y
+
1
}));
if
(
x
+
1
<
width
)
max
=
std
::
max
(
max
,
res
({
x
+
1
,
y
}));
if
(
y
-
1
>=
0
)
max
=
std
::
max
(
max
,
res
({
x
,
y
-
1
}));
if
(
x
-
1
>=
0
)
max
=
std
::
max
(
max
,
res
({
x
-
1
,
y
}));
res
({
x
,
y
})
=
max
;
}
}
}
}
for
(
int
y
=
0
;
y
<
res_height
;
y
++
)
const
auto
kwidth
=
node_map
.
width
()
*
2
-
1
;
const
auto
kheight
=
node_map
.
height
()
*
2
-
1
;
image2d
<
double
>
res
(
kwidth
,
kheight
);
fill
(
res
,
0
);
for
(
const
auto
[
u
,
v
,
w
]
:
s_map
)
{
{
for
(
int
x
=
0
;
x
<
res_width
;
x
++
)
const
auto
dir
=
v
-
u
;
{
res
(
point2d
{
2
*
u
[
0
],
2
*
u
[
1
]}
+
dir
)
=
w
;
res
({
x
,
y
})
=
255
-
res
({
x
,
y
});
}
}
}
return
res
;
return
res
;
...
...
tests/morpho/CMakeLists.txt
View file @
ee3f1d79
...
@@ -22,7 +22,7 @@ add_core_test(${test_prefix}depth_first depthfirst.cpp)
...
@@ -22,7 +22,7 @@ add_core_test(${test_prefix}depth_first depthfirst.cpp)
add_core_test
(
${
test_prefix
}
maxtree maxtree.cpp
)
add_core_test
(
${
test_prefix
}
maxtree maxtree.cpp
)
add_core_test
(
${
test_prefix
}
alphatree alphatree.cpp
)
add_core_test
(
${
test_prefix
}
alphatree alphatree.cpp
)
add_core_test
(
${
test_prefix
}
private_directional_hqueue directional_hqueue.cpp
)
add_core_test
(
${
test_prefix
}
private_directional_hqueue directional_hqueue.cpp
)
add_core_test
(
${
test_prefix
}
component_tree component_tree.cpp
)
#
add_core_test(${test_prefix}component_tree component_tree.cpp)
target_link_libraries
(
${
test_prefix
}
dilate PRIVATE TBB::TBB
)
target_link_libraries
(
${
test_prefix
}
dilate PRIVATE TBB::TBB
)
Write
Preview
Supports
Markdown
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