Commit 2ec5bb44 authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Add type of attribute computation on g2 interface.

	*  apps/g2/CMakeLists.txt: Remove old apps.
	*  apps/g2/compute_ctos-cli.cpp,
	*  apps/g2/compute_ctos.cpp,
	*  apps/g2/compute_ctos.hpp: Use new interface.
parent 4e8c2734
......@@ -2,14 +2,15 @@ link_libraries(${FreeImage_LIBRARIES})
add_library(g2-tools OBJECT satmaxtree.cpp compute_ctos.cpp routines.cpp compute_g2.cpp)
add_executable(g2-maxdepth g2-maxdepth.cpp $<TARGET_OBJECTS:g2-tools>)
add_executable(g2-maxtree g2-maxtree.cpp $<TARGET_OBJECTS:g2-tools>)
add_executable(compute_ctos-cli compute_ctos-cli.cpp $<TARGET_OBJECTS:g2-tools>)
target_link_libraries(compute_ctos-cli ${Boost_PROGRAM_OPTIONS_LIBRARY})
#add_library(g2-tools-16 OBJECT routines.cpp compute_g2.cpp)
add_executable(compute_ctos_16-cli compute_ctos-cli.cpp $<TARGET_OBJECTS:g2-tools>)
set_target_properties(compute_ctos_16-cli
PROPERTIES COMPILE_DEFINITIONS MLN_INPUT_VALUE_TYPE=mln::rgb16)
target_link_libraries(compute_ctos_16-cli ${Boost_PROGRAM_OPTIONS_LIBRARY})
add_library(g2-tools-16 OBJECT routines.cpp compute_g2.cpp)
add_executable(g2-maxdepth-16 g2-maxdepth.cpp $<TARGET_OBJECTS:g2-tools-16>)
add_executable(g2-maxtree-16 g2-maxtree.cpp $<TARGET_OBJECTS:g2-tools-16>)
set_target_properties(g2-tools-16 g2-maxdepth-16 g2-maxtree-16 PROPERTIES COMPILE_DEFINITIONS MLN_INPUT_VALUE_TYPE=mln::rgb16)
add_executable(satmaxtree-cli satmaxtree-cli.cpp $<TARGET_OBJECTS:g2-tools>)
......@@ -5,11 +5,22 @@
#include <mln/morpho/component_tree/reconstruction.hpp>
#include "compute_ctos.hpp"
#include <apps/tos/croutines.hpp>
#include <boost/program_options.hpp>
#ifndef MLN_INPUT_VALUE_TYPE
# define MLN_INPUT_VALUE_TYPE rgb8
#endif
int main(int argc, char** argv)
{
using namespace mln;
namespace po = boost::program_options;
const char* usage =
"Compute the cToS of an image. It ouputs the tree (out.tree)"
"and an optional depth image (out.tiff). The tree is computed on "
"K1 (doubled-sized image + border) and so is the depth image\n"
"If a grain is given, a grain filter is applied.\n";
if (argc < 3)
{
......@@ -21,21 +32,94 @@ int main(int argc, char** argv)
std::exit(1);
}
image2d<rgb8> f;
io::imread(argv[1], f);
po::options_description hidden("Allowed options");
hidden.add_options()
("input_path", po::value<std::string>()->required(), "Input file (rgb8)")
("tree_path", po::value<std::string>()->required(), "Output tree")
("depth_path", po::value<std::string>()->required(), "Output depth map (uint16)")
;
po::options_description visible("Allowed options");
visible.add_options()
("help", "Help message")
("grain,g", po::value<int>(), "Grain filter")
("attr,a", po::value<std::string>()->default_value("depth"),
"The type of attribute used to compute the inclusion map:\n"
"depth: Length of the path to A\n"
"count: Number of nodes including A\n"
"pwcount: Number of nodes including x\n")
("export-mdepth", po::value<std::string>(), "Export marginal depth to stem-{0,1...}.tiff")
;
po::positional_options_description pd;
pd.add("input_path", 1)
.add("tree_path", 1)
.add("depth_path", 1)
;
po::options_description all("Allowed options");
all.add(hidden).add(visible);
po::variables_map vm;
bool err = false;
try {
po::store(po::command_line_parser(argc, argv)
.options(all)
.positional(pd).run(), vm);
po::notify(vm);
if (vm.count("help"))
err = true;
} catch (...) {
err = true;
}
e_ctos_attribute attr;
std::string str_attr = vm["attr"].as<std::string>();
if (str_attr == "depth")
attr = CTOS_DEPTH;
else if (str_attr == "count")
attr = CTOS_COUNT;
else if (str_attr == "pwcount")
attr = CTOS_PW_COUNT;
else
err = true;
auto tree = compute_ctos(f, NULL);
if (err)
{
std::cerr << "Usage: " << argv[0] << " [options] input.tiff out.tree [depthmap.tiff]\n"
<< usage
<< visible;
std::exit(1);
}
/***********************************/
/*** REAL STUFF STARTS HERE ***/
/***********************************/
typedef MLN_INPUT_VALUE_TYPE V;
image2d<V> f;
io::imread(vm["input_path"].as<std::string>(), f);
ctos_extra_params_t params;
if (vm.count("export-mdepth")) {
params.export_marginal_depth = true;
params.export_marginal_depth_path = vm["export-mdepth"].as<std::string>();
}
if (argc > 4)
grain_filter_inplace(tree, std::atoi(argv[4]));
auto tree = compute_ctos(f, NULL, attr, params);
if (vm.count("grain"))
grain_filter_inplace(tree, vm["grain"].as<int>());
morpho::save(tree, argv[2]);
morpho::save(tree, vm["tree_path"].as<std::string>());
if (argc > 3) {
if (vm.count("depth_path")) {
image2d<uint16> depth;
depth.resize(tree._get_data()->m_pmap.domain());
auto dmap = morpho::compute_depth(tree);
morpho::reconstruction(tree, dmap, depth);
io::imsave(depth, argv[3]);
io::imsave(depth, vm["depth_path"].as<std::string>());
}
}
#include <mln/core/image/morphers/casted_image.hpp>
#include <mln/core/algorithm/accumulate.hpp>
#include <mln/core/neighb2d.hpp>
#include <mln/core/dontcare.hpp>
#include <mln/accu/accumulators/max.hpp>
#include <mln/morpho/tos/ctos.hpp>
#include <mln/morpho/component_tree/reconstruction.hpp>
#include <mln/morpho/component_tree/compute_depth.hpp>
#include <mln/io/imsave.hpp>
#include <apps/tos/addborder.hpp>
#include <apps/tos/Kinterpolate.hpp>
#include <apps/tos/topology.hpp>
#include "compute_ctos.hpp"
#include "compute_g2.hpp"
#include "satmaxtree.hpp"
......@@ -14,6 +19,8 @@
#include <boost/graph/transpose_graph.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <tbb/parallel_for.h>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
namespace mln
{
......@@ -46,6 +53,115 @@ namespace mln
return depth;
}
boost::vector_property_map<unsigned>
compute_graph_count(const MyGraph& g)
{
struct viz_t : public boost::default_dfs_visitor
{
viz_t(boost::vector_property_map<unsigned>& common)
: m_common (common)
{
}
void examine_edge(MyGraph::edge_descriptor e, const MyGraph& g)
{
MyGraph::vertex_descriptor s = boost::source(e, g);
MyGraph::vertex_descriptor t = boost::target(e, g);
m_common[t] += m_common[s];
}
boost::vector_property_map<unsigned>& m_common;
};
mln_entering("Compute graph count");
// 1. Set for each node its redondancy (the number of tree it belongs to minus 1)
boost::vector_property_map<unsigned> common(boost::num_vertices(g));
{
auto tlinks = boost::get(&my_graph_content::tlinks, g);
BOOST_FOREACH(MyGraph::vertex_descriptor v, boost::vertices(g))
{
common[v] = 0;
for (int i = 0; i < NTREE; ++i)
if (tlinks[v][i].id() != tree_t::npos())
common[v] += 1;
common[v] -= 1;
}
MyGraph::vertex_descriptor root = 0;
common[root] = 0;
}
// 2. Propagate i.e. for each node X, compute the number of redondancy in X↑
MyGraph gT;
boost::transpose_graph(g, gT);
boost::depth_first_search(gT, boost::visitor(viz_t(common)));
// 3. Set for each node, the number of node it is included in.
boost::vector_property_map<unsigned>& count = common; // do not need another vector, inplace
{
auto depth = boost::get(&my_graph_content::depth, g);
BOOST_FOREACH(MyGraph::vertex_descriptor v, boost::vertices(g))
count[v] = sum(depth[v]) - common[v];
}
mln_exiting();
return count;
}
/// \brief Compute for each point the number of shapes it belongs to.
/// \[ ω(x) = { X ∈ S, x ∈ X } \]
image2d<unsigned>
compute_pw_count(const MyGraph& g,
const tree_t* t,
const tlink_t* tlink)
{
// 1. Compute the weight of each node to remove the redondancy
// e.g., if a node appears twice, its weight is 1/2
// and we sum up the weights throurgh the paths
// We first store in redondancy_map[X] the number of trees X belongs to.
boost::vector_property_map<char> redondancy_map(boost::num_vertices(g));
auto glinks = boost::get(&my_graph_content::tlinks, g);
BOOST_FOREACH(MyGraph::vertex_descriptor v, boost::vertices(g))
{
redondancy_map[v] = 0;
for (int i = 0; i < NTREE; ++i)
redondancy_map[v] += (glinks[v][i].id() != t[i].npos());
}
property_map<tree_t, float> w[NTREE];
for (int i = 0; i < NTREE; ++i)
{
w[i] = property_map<tree_t, float> (t[i]);
w[i][t[i].get_root()] = 0;
mln_foreach(auto x, t[i].nodes_without_root()) // downward
{
MyGraph::vertex_descriptor v = tlink[i][x];
w[i][x] = w[i][x.parent()] + 1.0 / redondancy_map[v];
}
}
// 2. We set the weights value point-wise
image2d<unsigned> dmap;
resize(dmap, t[0]._get_data()->m_pmap).init(0);
mln_foreach(auto px, dmap.pixels())
{
float r = 0;
for (int i = 0; i < NTREE; ++i)
{
tree_t::node_type x = t[i].get_node_at(px.index());
r += w[i][x];
}
px.val() = std::lround(r);
}
return dmap;
}
template <class MyGraph, class ValueMap, class BinaryFunction, class ValueType>
void
write_vmap_to_image(const MyGraph& g, const tree_t* t, const tlink_t* tlink,
......@@ -69,11 +185,29 @@ namespace mln
}
static
void
export_marginal_depth(const tree_t* t, int sz, std::string stem)
{
image2d<uint16> f;
resize(f, t[0]._get_data()->m_pmap);
for (int i = 0; i < sz; ++i)
{
auto depth = morpho::compute_depth(t[i]);
morpho::reconstruction(t[i], depth, f);
io::imsave(f, (boost::format("%s-%i.tiff") % stem % i).str());
}
}
template <class V>
tree_t
compute_ctos(const image2d<V>& input,
image2d<uint16>* imdepth)
image2d<uint16>* imdepth,
e_ctos_attribute attribute,
ctos_extra_params_t params
)
{
typedef V value_t;
enum { NTREE = value_t::ndim };
......@@ -83,6 +217,7 @@ namespace mln
image2d<value_t> F = immerse_k1(f);
image2d<uint16> maxdepth;
point2d pmin = {0,0};
resize(maxdepth, F);
{
/// Compute the marginal tree
......@@ -92,22 +227,52 @@ namespace mln
return x[i]; }), c4, pmin);
});
if (params.export_marginal_depth)
export_marginal_depth(t, NTREE, params.export_marginal_depth_path);
/// Compute the graph
Graph<NTREE> g2;
std::array<property_map<tree_t, typename MyGraph::vertex_descriptor>, NTREE> tlink;
std::tie(g2, tlink) = compute_g2<NTREE>(t);
if (attribute == CTOS_DEPTH)
{
/// Compute depth
boost::vector_property_map<unsigned> gdepth;
gdepth = compute_graph_depth(g2);
/// Compute depth
boost::vector_property_map<unsigned> gdepth;
gdepth = compute_graph_depth(g2);
/// Compute the pw depth image from graph
write_vmap_to_image(g2, t, &tlink[0], gdepth,
functional::max_t<unsigned> (), uint16(0), maxdepth);
}
else if (attribute == CTOS_COUNT)
{
/// Compute depth
boost::vector_property_map<unsigned> gdepth;
gdepth = compute_graph_count(g2);
/// Compute the pw depth image from graph
write_vmap_to_image(g2, t, &tlink[0], gdepth,
functional::max_t<unsigned> (), uint16(0), maxdepth);
}
else if (attribute == CTOS_PW_COUNT)
{
auto maxdepth_ = compute_pw_count(g2, t, &tlink[0]);
unsigned maxv = accumulate(maxdepth_, accu::features::max<>());
if (maxv > value_traits<uint16>::max())
std::cerr << "Warning: too many values in the depth (depth image output is wrong)\n";
/// Compute the pw depth image from graph
resize(maxdepth, F);
write_vmap_to_image(g2, t, &tlink[0], gdepth,
functional::max_t<unsigned> (), uint16(0), maxdepth);
maxdepth = eval(imcast<uint16>(maxdepth_));
}
else
{
std::cerr << "Unimplemented." << std::endl;
std::abort();
}
}
/// Compute the saturated maxtree
......@@ -180,12 +345,18 @@ namespace mln
template
tree_t
compute_ctos<rgb8>(const image2d<rgb8>& input,
image2d<uint16>* imdepth);
image2d<uint16>* imdepth,
e_ctos_attribute attribute,
ctos_extra_params_t params
);
// Explicit instanciation.
template
tree_t
compute_ctos<rgb16>(const image2d<rgb16>& input,
image2d<uint16>* imdepth);
image2d<uint16>* imdepth,
e_ctos_attribute attribute,
ctos_extra_params_t params
);
}
......@@ -9,6 +9,18 @@
namespace mln
{
enum e_ctos_attribute {
CTOS_DEPTH, // Depth of longest path to A
CTOS_COUNT, // Number of nodes including A
CTOS_PW_COUNT, // Number of nodes including x
};
struct ctos_extra_params_t
{
bool export_marginal_depth = false;
std::string export_marginal_depth_path;
};
/// \brief A convenient wrapper around:
/// * The Graph of shapes computation + depth attribute valuation
......@@ -19,7 +31,10 @@ namespace mln
template <class V>
morpho::component_tree<unsigned, image2d<unsigned> >
compute_ctos(const image2d<V>& input,
image2d<uint16>* depth = nullptr);
image2d<uint16>* depth = nullptr,
e_ctos_attribute attribute = CTOS_DEPTH,
ctos_extra_params_t params = ctos_extra_params_t ()
);
morpho::component_tree<unsigned, image2d<unsigned> >
......@@ -30,12 +45,18 @@ namespace mln
extern template
morpho::component_tree<unsigned, image2d<unsigned> >
compute_ctos<rgb8>(const image2d<rgb8>& input,
image2d<uint16>* depth = nullptr);
image2d<uint16>* depth,
e_ctos_attribute attribute,
ctos_extra_params_t params
);
extern template
morpho::component_tree<unsigned, image2d<unsigned> >
compute_ctos<rgb16>(const image2d<rgb16>& input,
image2d<uint16>* depth = nullptr);
image2d<uint16>* depth,
e_ctos_attribute attribute,
ctos_extra_params_t params
);
}
......
Markdown is supported
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