Commit 3d41c3f4 authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Add automatic markers detection for segmentation.

	*  apps/supervised-gui/CMakeLists.txt,
	*  apps/supervised-gui/automatic.cpp,
	*  apps/supervised-gui/supervised-cli-multi.cpp: New.
parent c8462f0a
if(Qt4_FOUND)
if(QT4_FOUND)
INCLUDE(${QT_USE_FILE})
......@@ -12,15 +12,22 @@ if(Qt4_FOUND)
QT4_GENERATE_MOC(${CMAKE_HOME_DIRECTORY}/mln/qt/imageviewer.hxx
${CMAKE_CURRENT_BINARY_DIR}/imageviewer.moc.cpp)
add_executable(supervised-cli supervised-cli.cpp)
add_executable(supervised-cli-multi supervised-cli-multi.cpp)
add_executable(automatic automatic.cpp)
add_executable(supervised-gui-3c supervised-gui-3c.cpp brush.cpp
${supervised_MOC}
${CMAKE_HOME_DIRECTORY}/mln/qt/qtimage.cpp
${CMAKE_CURRENT_BINARY_DIR}/imageviewer.moc.cpp)
target_link_libraries(supervised-gui-3c ${QT_QTMAIN_LIBRARY} ${QT_LIBRARIES})
target_link_libraries(supervised-cli ${QT_QTMAIN_LIBRARY} ${QT_LIBRARIES})
endif(Qt4_FOUND)
set_property(TARGET supervised-cli-multi APPEND PROPERTY INCLUDE_DIRECTORIES "/work/carlinet/src/eigen/")
endif(QT4_FOUND)
#include <mln/core/image/image2d.hpp>
#include <mln/core/neighb2d.hpp>
#include <mln/core/win2d.hpp>
#include <mln/core/colors.hpp>
#include <mln/colors/literal.hpp>
#include <mln/io/imread.hpp>
#include <mln/io/imsave.hpp>
#include <mln/morpho/alphatree/alphatree.hpp>
#include <mln/morpho/component_tree/accumulate.hpp>
#include <mln/morpho/component_tree/graphviz.hpp>
#include <mln/morpho/component_tree/cuts.hpp>
#include <mln/morpho/component_tree/reconstruction.hpp>
#include <mln/accu/accumulators/infsup.hpp>
#include <mln/accu/accumulators/mean.hpp>
#include <mln/transform/chamfer_distance_transform.hpp>
#include <mln/morpho/algebraic_filter.hpp>
int main(int argc, char** argv)
{
if (argc != 6) {
std::cerr << "Usage: " << argv[0] << " input.ppm BV grain #markers markers.pgm" << std::endl;
std::exit(1);
}
using namespace mln;
typedef morpho::component_tree<unsigned, image2d<unsigned> > tree_t;
typedef rgb8 V;
std::string outname = argv[5];
std::string stem = outname.substr(0, outname.rfind('.'));
image2d<V> f;
io::imread(argv[1], f);
tree_t tree;
property_map<tree_t, double> vmap;
std::tie(tree, vmap) = morpho::alphatree_indexes(f, c4);
auto infsup = morpho::vaccumulate(tree, f, accu::accumulators::infsup<V> ());
auto bvmap = make_functional_property_map<tree_t::node_type>
([&infsup](const tree_t::node_type& x) { return l2dist(infsup[x].first, infsup[x].second); });
auto bvmap2 = make_functional_property_map<unsigned>
([&infsup](unsigned x) { return l2dist(infsup[x].first, infsup[x].second); });
// Filter the alpha tree
int maxbv = std::atoi(argv[2]);
morpho::cut_inplace(tree,
make_functional_property_map<tree_t::node_type>
([bvmap, maxbv](tree_t::node_type x) {
return bvmap[x] < maxbv;
}));
image2d<V> out;
resize(out, f);
auto avmap = morpho::vaccumulate(tree, f, accu::features::mean<>());
auto mc = morpho::paccumulate(tree, f, accu::accumulators::mean<point2df>());
auto areamap = morpho::accumulate(tree, accu::features::count<>());
morpho::reconstruction(tree, avmap, out);
// Get the seeds.
unsigned area_threshold = std::atoi(argv[3]);
property_map<tree_t, uint8> active(tree, true);
std::vector<unsigned> seeds;
// Backward
mln_reverse_foreach(auto x, tree.nodes())
{
if (not active[x])
active[x.parent()] = false;
else if (areamap[x] > area_threshold)
{
active[x.parent()] = false;
seeds.push_back(x.id());
}
else
{
active[x] = false;
}
}
auto energy = [&areamap, &bvmap2](unsigned x) -> int { return areamap[x]; }; // - 50 * bvmap2[x]; };
std::sort(seeds.begin(), seeds.end(), [&energy](unsigned x, unsigned y) { return energy(x) > energy(y); });
// Only retain the # first seeds.
int NMarkers = std::min<int>(std::atoi(argv[4]), seeds.size());
for (int i = 0; i < seeds.size(); ++i)
active[seeds[i]] = (i < NMarkers) ? i+1 : 0;
// Forward
mln_foreach(auto x, tree.nodes())
if (active[x.parent()])
active[x] = active[x.parent()]; // label propagation
// Output the seeds
image2d<uint8> ske, imlabel;
resize(ske, f).init(0);
resize(imlabel, f).init(0);
{
// Distance transform
mln_foreach(auto px, ske.pixels())
{
tree_t::node_type x = tree.get_node_at(px.index());
if (active[x])
px.val() = 1;
}
// Separate the regions
{
mln_pixter(px, ske);
mln_iter(q, c4(px));
mln_forall(px) {
unsigned id1 = active[tree.get_node_at(px->index())];
mln_forall(q) {
unsigned id2 = active[tree.get_node_at(q->index())];
if (id1 != id2 and id2 != 0) {
px->val() = 0;
break;
}
}
}
}
ske = morpho::area_closing(ske, c8, 20);
transform::chamfer_distance_transform(ske, c4, ske);
io::imsave(ske, stem + ".ske.pgm");
// Mark the pixels which are far enough from the borders
auto avg_dist = morpho::vaccumulate(tree, ske, accu::features::mean<float>());
mln_pixter(px1, px2, ske, imlabel);
mln_forall(px1, px2)
{
tree_t::node_type x = tree.get_node_at(px1->index());
if (active[x] and px1->val() > avg_dist[x])
px2->val() = active[x];
}
}
{
rect2d mywin = make_rectangle2d(11, 11);
point2d p;
mln_iter(q, mywin(p));
for (int i = 0; i < NMarkers; ++i)
{
unsigned id = seeds[i];
std::cout << "Seed #" << i << " : " << energy(id) << std::endl;
p = mc[id];
mln_forall(q) {
if (out.domain().has(*q)) {
unsigned node_id = tree.get_node_at(f.index_of_point(*q)).id();
if (node_id == id) {
out(*q) = colors::literal::blue;
//imlabel(*q) = i+1;
}
}
}
}
}
io::imsave(imlabel, argv[5]);
io::imsave(out, stem + ".markers.tiff");
}
#include <mln/core/image/image2d.hpp>
#include <mln/io/imread.hpp>
#include <mln/core/colors.hpp>
#include <mln/core/algorithm/fill.hpp>
#include <mln/core/algorithm/accumulate.hpp>
#include <mln/core/algorithm/transform.hpp>
#include <mln/colors/literal.hpp>
#include <mln/morpho/component_tree/component_tree.hpp>
#include <mln/morpho/component_tree/io.hpp>
#include <mln/morpho/component_tree/reconstruction.hpp>
#include <mln/morpho/component_tree/accumulate.hpp>
#include <mln/accu/accumulators/mean.hpp>
#include <mln/accu/accumulators/max.hpp>
#include <mln/morpho/algebraic_filter.hpp>
#include <apps/tos/Kinterpolate.hpp>
#include <apps/tos/croutines.hpp>
#include "constants.hpp"
#include "compute_distance.hpp"
#include <Eigen/Dense>
using namespace mln;
property_map<tree_t, uint8>
labelize_tree(const tree_t& tree,
const image2d<uint8>& markers,
int nlabels)
{
typedef Eigen::VectorXi MyVec;
property_map<tree_t, uint8> colormap(tree, NONE);
property_map<tree_t, MyVec > count(tree, MyVec::Zero(nlabels+1) );
mln_foreach(auto px, markers.pixels())
{
tree_t::node_type node = tree.get_node_at(px.index());
if (px.val() != 0)
count[node][px.val()]++;
}
mln_foreach(auto x, tree.nodes()) {
MyVec::Index maxpos;
int maxv = count[x].maxCoeff(&maxpos);
if (maxv != 0)
colormap[x] = maxpos;
}
return colormap;
}
image2d<uint8>
segmentation(const tree_t& tree,
const property_map<tree_t,rgb<int> >& vmap,
const image2d<uint8>& markers_,
int nlabel,
float reject = 0.5)
{
image2d<uint8> markers = Kadjust_to(markers_, tree._get_data()->m_pmap.domain(), "zero");
// 1. Labelize the tree
property_map<tree_t, uint8> colormap = labelize_tree(tree, markers, nlabel);
// 2. Compute distances
property_map<tree_t, float> dmap[nlabel+1];
for (int i = 1; i <= nlabel; ++i)
dmap[i] = compute_distance(tree, colormap, vmap, i);
// 3. Classify
property_map<tree_t, uint8> cmap(tree, 0);
mln_foreach(auto x, tree.nodes())
{
int imin = 1;
for (int i = 1; i <= nlabel; ++i)
if (dmap[i][x] < dmap[imin][x])
imin = i;
cmap[x] = imin;
}
image2d<uint8> out;
resize(out, tree._get_data()->m_pmap);
morpho::reconstruction(tree, cmap, out);
auto output = Kadjust_to(out, markers_.domain());
return output;
}
image2d<rgb8>
recons(const image2d<uint8>& lbl,
const image2d<rgb8>& ori,
int nlabel)
{
// 4. Reconstruction
accu::accumulators::mean<rgb8> m[nlabel+1];
mln_pixter(pxm, pxv, lbl, ori);
mln_forall(pxm, pxv)
m[pxm->val()].take(pxv->val());
m[0].init();
rgb8 vmean[nlabel+1];
for (int i = 0; i <= nlabel; ++i)
vmean[i] = m[i].to_result();
image2d<rgb8> rec = transform(lbl, [&vmean](uint8 x){ return vmean[x]; });
return rec;
}
int main(int argc, char** argv)
{
if (argc < 6) {
std::cout << "Usage: " << argv[0] << " tree input.ppm markers.pgm grain output.pgm [output.ppm]\n"
"Perform the classification on the tree and outputs a label map"
"and a map with the average color.\n";
std::exit(1);
}
tree_t tree;
{
std::ifstream fs(argv[1]);
morpho::load(fs, tree);
}
image2d<rgb8> ima;
io::imread(argv[2], ima);
image2d<uint8> markers;
io::imread(argv[3], markers);
int grain = std::atoi(argv[4]);
if (grain != 0) {
grain_filter_inplace(tree, grain);
}
image2d<rgb8> F = Kadjust_to(ima, tree._get_data()->m_pmap.domain());
property_map<tree_t, rgb<int> > vmap = morpho::vaccumulate_proper(tree, F, accu::features::mean<>());
image2d<uint8> res;
image2d<rgb8> seg;
int nlabel = accumulate(markers, accu::features::max<> ());
res = segmentation(tree, vmap, markers, nlabel);
for (int i = 1; i <= nlabel; ++i) {
auto clo = morpho::area_closing(res == i, c4, grain);
fill(res | clo, i);
}
seg = recons(res, ima, nlabel);
io::imsave(res, argv[5]);
if (argc >= 7) {
io::imsave(seg, argv[6]);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment