Commit cc4d105c authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Proper interface for ToS attribute computation.

	*  apps/attributes/CMakeLists.txt,
	*  apps/attributes/attributes.cpp,
	*  apps/attributes/attributes.hpp: Attribute computation CLI interface.
	*  apps/attributes/MSER-cli.cpp,
	*  apps/attributes/meaningfullness-cli.cpp: Use new interface.
	*  apps/attributes/MSER-patternspec-cli.cpp,
	*  apps/attributes/meaningfullness-patternspec-cli.cpp: Removed.
parent 2ec5bb44
link_libraries(${FreeImage_LIBRARIES})
add_executable(MSER-patternspec-cli MSER-patternspec-cli.cpp)
add_executable(meaningfullness-patternspec-cli meaningfullness-patternspec-cli.cpp curvature.cpp)
add_executable(meaningfullness-cli meaningfullness-cli.cpp curvature.cpp)
add_executable(stat stat.cpp)
add_executable(MSER-cli MSER-cli.cpp attributes.cpp)
add_executable(meaningfullness-cli meaningfullness-cli.cpp curvature.cpp attributes.cpp)
target_link_libraries(MSER-cli ${Boost_PROGRAM_OPTIONS_LIBRARY})
target_link_libraries(meaningfullness-cli ${Boost_PROGRAM_OPTIONS_LIBRARY})
\ No newline at end of file
#include <mln/io/imread.hpp>
#include <mln/morpho/component_tree/accumulate.hpp>
#include <mln/accu/accumulators/accu_if.hpp>
#include <mln/accu/accumulators/count.hpp>
#include <apps/tos/Kinterpolate.hpp>
#include <apps/tos/topology.hpp>
#include "attributes.hpp"
#include "cMSER.hpp"
int main(int argc, char** argv)
{
using namespace mln;
po::options_description desc("MSER Options");
desc.add_options()
("eps,h", po::value<float>()->required(), "The height for neighbor lookup (e.g. 10).");
po::variables_map vm = process_cmdline(argc, argv, desc);
tree_t tree = preprocess(vm);
image2d<float> f;
io::imread(vm["input_path"].as<std::string>(), f, true);
image2d<float> F = Kadjust_to(f, tree._get_data()->m_pmap.domain());
auto vmap = morpho::make_attribute_map_from_image(tree, F);
accu::accumulators::accu_if< accu::accumulators::count<>,
K1::is_face_2_t,
point2d > counter;
auto amap = morpho::paccumulate(tree, F, counter);
auto amser = compute_MSER(tree, vmap, amap, vm["eps"].as<float>(), MSER_NORM);
auto smap = postprocess(vm, tree, amser);
const char* names[] = {"MSER", "Extinction"};
std::function<float(tree_t::node_type)> attrs[] = { _as_fun(amser), _as_fun(smap) };
export_(vm, tree, smap, names, attrs, 2);
}
#include <mln/core/image/image2d.hpp>
#include <mln/core/algorithm/accumulate.hpp>
#include <mln/accu/accumulators/max.hpp>
#include <mln/morpho/component_tree/io.hpp>
#include <mln/morpho/component_tree/accumulate.hpp>
#include <mln/morpho/component_tree/reconstruction.hpp>
#include <mln/morpho/component_tree/filtering.hpp>
#include <mln/morpho/algebraic_filter.hpp>
#include <mln/accu/accumulators/accu_if.hpp>
#include <mln/accu/accumulators/count.hpp>
#include <apps/tos/topology.hpp>
#include "cMSER.hpp"
#include <mln/morpho/extinction.hpp>
#include <mln/morpho/datastruct/checktree.hpp>
int main(int argc, char** argv)
{
if (argc < 9)
{
std::cerr << "Usage: " << argv[0] << "tree image.tiff λ ε γ t₁ t₂ spectra.csv\n"
<< " λ:\t Grain filter before anything else (number of 2F) (consider: 20-50, *20*) \n"
<< " ε:\t The delta level considered when fetching the neighborhood of a node in MSER (consider 5-20, *10*)\n"
<< " γ:\t Not used anymore.\n"
<< " t₁:\t Threshold above which node having a MSER value greater that t₁ are removed (consider 0.7-2.0, *1.0*)\n"
<< " t₂:\t Threshold below which node having an extinction value lesser than t₂ are removed (consider 0-t₁, *0.2*).\n";
std::exit(1);
}
using namespace mln;
const char* tree_path = argv[1];
const char* img_path = argv[2];
unsigned grain = std::atoi(argv[3]);
int eps = std::atoi(argv[4]);
int areaAS = std::atoi(argv[5]);
float threshold1 = std::atof(argv[6]);
float threshold2 = std::atof(argv[7]);
const char* out_path = argv[8];
typedef morpho::component_tree<unsigned, image2d<unsigned> > tree_t;
tree_t tree;
{
std::ifstream f(tree_path, std::ios::binary);
morpho::load(f, tree);
}
image2d<uint16> ima;
io::imread(img_path, ima);
if (ima.domain() != tree._get_data()->m_pmap.domain())
{
std::cerr << "Domain between image differ.\n"
<< ima.domain() << " vs "
<< tree._get_data()->m_pmap.domain() << std::endl;
}
auto vmap = morpho::make_attribute_map_from_image(tree, ima);
accu::accumulators::accu_if< accu::accumulators::count<>,
K1::is_face_2_t,
point2d > counter;
auto amap = morpho::paccumulate(tree, ima, counter);
auto pred = make_functional_property_map<tree_t::vertex_id_t>([&amap, grain](tree_t::vertex_id_t x) {
return amap[x] > grain;
});
morpho::filter_direct_inplace(tree, pred);
morpho::internal::checktree(tree);
auto amser = compute_MSER(tree, vmap, amap, eps, MSER_NORM);
// convert to image and filter
auto imser = morpho::make_image(tree, amser);
{
//float maxval = accumulate(imser, accu::features::max<> ());
mln_foreach(float& v, imser.values())
if (v > threshold1)
v = threshold1;
}
auto nmser = morpho::extinction(imser, morpho::tree_neighb_t());
//auto nmser = eval(nmser_ / imser);
//auto nmser = morpho::area_closing(imser, morpho::tree_neighb_t(), areaAS);
{
std::ofstream fout(out_path);
fout << "area,mser,extinction\n";
mln_foreach(auto x, tree.nodes())
fout << amap[x] << "," << amser[x] << "," << nmser(x) << "\n";
fout.close();
}
{
image2d<uint16> tmp;
resize(tmp, ima);
auto& attr = nmser.get_vmap();
auto pred = [&attr, threshold2](const tree_t::node_type& x) { return threshold2 < attr[x]; };
morpho::filter_direct_and_reconstruct(tree, make_functional_property_map<tree_t::node_type>(pred),
vmap, tmp);
io::imsave(tmp, "selection.tiff");
}
// image2d<float> out(200,200);
// morpho::pattern_spectra(tree, amser, amap,
// make_functional_property_map([&](const tree_t::node_type& x) -> float {
// return amap[x];
// }), out, true, true);
// io::imsave(out, argv[4]);
}
#include "attributes.hpp"
#include <apps/tos/croutines.hpp>
#include <mln/morpho/extinction.hpp>
#include <mln/morpho/component_tree/io.hpp>
#include <mln/morpho/component_tree/filtering.hpp>
#include <mln/morpho/component_tree/reconstruction.hpp>
#include <mln/morpho/component_tree/compute_depth.hpp>
#include <mln/io/imsave.hpp>
namespace mln
{
po::variables_map
process_cmdline(int argc, char** argv, po::options_description desc_, const char* usage)
{
po::options_description hidden("Allowed options");
hidden.add_options()
("tree_path", po::value<std::string>()->required(), "Input tree")
("input_path", po::value<std::string>()->required(), "Input file (gray level)")
;
po::options_description desc("General options");
desc.add_options()
("help", "Help message")
("grain,g", po::value<int>(), "Perform a grain filter before anything else");
po::options_description desc2("Extinction Options");
desc2.add_options()
("clip", po::value<float>(), "Clip the energy map to [0, t₁]");
po::options_description desc3("Output Options");
desc3.add_options()
("output-tree", po::value<std::string>(), "Path to save the tree.")
("output-saliency", po::value<std::string>(), "Path to save the saliency map")
("output-csv", po::value<std::string>(), "Path to the csv file to save the attributes")
("output-simp-tree", po::value<std::string>(), "Path to save the simplified tree.")
("output-simp-depth", po::value<std::string>(), "Path to save the simplified tree depth image.")
;
po::positional_options_description pd;
pd.add("tree_path", 1)
.add("input_path", 1)
;
po::options_description all("Allowed options");
po::options_description visible("Allowed options");
all.add(desc_).add(hidden).add(desc).add(desc2).add(desc3);
visible.add(desc_).add(desc2).add(desc3);
po::variables_map vm;
try {
po::store(po::command_line_parser(argc, argv)
.options(all)
.positional(pd).run(), vm);
po::notify(vm);
} catch (...) {
std::cerr << "Usage: " << argv[0] << " input.tree input.tiff [options]\n"
<< usage
<< visible;
std::exit(1);
}
if (vm.count("help"))
{
std::cerr << "Usage: " << argv[0] << " input.tree input.tiff [options]\n"
<< usage
<< visible;
std::exit(1);
}
return vm;
}
tree_t
preprocess(const po::variables_map& vm)
{
tree_t tree;
morpho::load(vm["tree_path"].as<std::string>(), tree);
if (vm.count("grain"))
grain_filter_inplace(tree, vm["grain"].as<int>());
if (vm.count("output-tree"))
morpho::save(tree, vm["output-tree"].as<std::string>());
return tree;
}
property_map<tree_t, float>
postprocess(const po::variables_map& vm,
const tree_t& tree,
const property_map<tree_t, float>& energy)
{
// convert to image and filter
auto ienergy = morpho::make_image(tree, energy);
if (vm.count("clip"))
{
float threshold1 = vm["clip"].as<float>();
mln_foreach(float& v, ienergy.values())
if (v > threshold1)
v = threshold1;
}
auto extincted = morpho::extinction(ienergy, morpho::tree_neighb_t());
property_map<tree_t, float> vmap = std::move(extincted.get_vmap());
return vmap;
}
void export_(const po::variables_map& vm,
tree_t& tree,
const property_map<tree_t, float>& saliency_map,
const char* names[],
std::function<float(tree_t::node_type)> vmaps[],
int sz)
{
mln_entering("export_");
if (vm.count("output-saliency"))
{
image2d<float> sal = set_value_on_contour(tree, saliency_map);
io::imsave(sal, vm["output-saliency"].as<std::string>());
}
if (vm.count("output-csv"))
{
std::ofstream f(vm["output-csv"].as<std::string>());
for (int i = 0; i < sz; ++i)
f << names[i] << ((i+1 < sz) ? ',' : '\n');
mln_foreach(auto x, tree.nodes())
for (int i = 0; i < sz; ++i)
f << vmaps[i](x) << ((i+1 < sz) ? ',' : '\n');
}
if (vm.count("output-simp-tree") or vm.count("output-simp-depth"))
{
auto binmap = make_functional_property_map<tree_t::vertex_id_t>
([&saliency_map](tree_t::vertex_id_t x) -> bool {
return saliency_map[x] > 0;
});
morpho::filter_direct_inplace(tree, binmap);
if (vm.count("output-simp-tree"))
morpho::save(tree, vm["output-simp-tree"].as<std::string>());
else
{
auto depth = morpho::compute_depth(tree);
image2d<uint16> d;
d.resize(tree._get_data()->m_pmap.domain());
morpho::reconstruction(tree, depth, d);
io::imsave(d, vm["output-simp-depth"].as<std::string>());
}
}
mln_exiting();
}
}
#ifndef APPS_ATTRIBUTE_ATTRIBUTES_HPP
# define APPS_ATTRIBUTE_ATTRIBUTES_HPP
# include <boost/program_options.hpp>
# include <mln/core/image/image2d.hpp>
# include <mln/morpho/component_tree/component_tree.hpp>
namespace po = boost::program_options;
typedef mln::morpho::component_tree<unsigned, mln::image2d<unsigned> > tree_t;
namespace mln
{
po::variables_map
process_cmdline(int argc, char** argv, po::options_description desc, const char* usage = "");
tree_t
preprocess(const po::variables_map& vm);
property_map<tree_t, float>
postprocess(const po::variables_map& vm,
const tree_t& tree,
const property_map<tree_t, float>& energy);
void export_(const po::variables_map& vm,
tree_t& tree,
const property_map<tree_t, float>& saliency_map,
const char* names[],
std::function<float(tree_t::node_type)> vmaps[],
int sz);
template <class Amap>
std::function<float(tree_t::node_type)> _as_fun(const Amap& w) {
return [&w](tree_t::node_type x) -> float { return w[x]; };
}
}
#endif // ! APPS_ATTRIBUTE_ATTRIBUTES_HPP
#ifndef MLN_INPUT_VALUE_TYPE
# define MLN_INPUT_VALUE_TYPE mln::uint16
#endif
#include <mln/core/image/image2d.hpp>
#include <mln/core/neighb2d.hpp>
#include <mln/core/colors.hpp>
#include <mln/colors/rgba.hpp>
#include <mln/io/imread.hpp>
#include <mln/core/algorithm/transform.hpp>
#include <apps/tos/Kinterpolate.hpp>
#include <mln/morpho/component_tree/io.hpp>
//#include <mln/morpho/component_tree/graphviz.hpp>
#include <mln/morpho/component_tree/accumulate.hpp>
#include <mln/morpho/component_tree/reconstruction.hpp>
#include <mln/morpho/component_tree/filtering.hpp>
#include <mln/morpho/extinction.hpp>
#include <mln/accu/accumulators/accu_if.hpp>
#include <mln/accu/accumulators/count.hpp>
#include <apps/tos/Kinterpolate.hpp>
#include <apps/tos/topology.hpp>
#include "attributes.hpp"
#include "cMeaningFullNess.hpp"
#include <mln/io/imread.hpp>
#include <mln/io/imsave.hpp>
#include "cMeaningFullNess.hpp"
#include <fstream>
int main(int argc, char** argv)
{
if (argc < 4)
{
std::cerr << "Usage:" << argv[0] << "tree input.tiff λ ε α β γ t₁ out.tiff\n"
<< "Export a saliency map from this attribute\n"
<< "The tree and input must hold the 0 and 1 faces.\n"
<< " λ:\t Grain filter before anything else (number of 2F)\n"
<< " ε:\t The radius of the ball when considering the contextual energy (3-10).\n"
<< " α, β, γ:\tParameters for energy\n"
<< "\tE(Γ) = α V₁(Γ) + βV₂(Γ) + V₃(Γ)\n"
<< "\twith V₁(Γ) = (ExternalVar(Γ,ε) + InternalVar(Γ,ε)) / Var(Γ,ε)\n"
<< "\t V₂(Γ) = Mean Curvature(∂Γ)\n"
<< "\t V₂(Γ) = exp(-γ |∂Γ|/|∂Ω|)\n"
<< "\t Consider α=1, β=1, γ=10-100\n"
<< "\t (you can set one of these parameters to 0 to unactive the term)."
<< " t₁:\t Threshold above which node having an energy greater that t₁ are removed.\n";
std::abort();
}
using namespace mln;
const char* tree_path = argv[1];
const char* img_path = argv[2];
unsigned grain = std::atoi(argv[3]);
int eps = std::atoi(argv[4]);
float alpha = std::atof(argv[5]);
float beta = std::atof(argv[6]);
float gamma = std::atof(argv[7]);
float threshold1 = std::atof(argv[8]);
const char* output_path = argv[9];
typedef morpho::component_tree<unsigned, image2d<unsigned> > tree_t;
tree_t tree;
{
std::ifstream f(tree_path, std::ios::binary);
morpho::load(f, tree);
const char* usage =
"Use the energy E(Γ) = α V₁(Γ) + βV₂(Γ) + V₃(Γ)\n"
"\twith\n"
"\t ε: The radius of the ball when considering the contextual energy (3-10).\n"
"\t V₁(Γ) = (ExternalVar(Γ,ε) + InternalVar(Γ,ε)) / Var(Γ,ε)\n"
"\t V₂(Γ) = Mean Curvature(∂Γ)\n"
"\t V₂(Γ) = exp(-γ |∂Γ|/|∂Ω|)\n"
"\t Consider α=1, β=1, γ=10-100\n"
"\t (you can set one of these parameters to 0 to unactive the term).";
po::options_description desc("MSER Options");
desc.add_options()
("params,p", po::value< std::vector<float> >()->multitoken()->required(), "ε α β γ")
;
po::variables_map vm = process_cmdline(argc, argv, desc, usage);
std::vector<float> params = vm["params"].as< std::vector<float> >();
if (params.size() != 4) {
std::cerr << usage;
std::exit(1);
}
typedef MLN_INPUT_VALUE_TYPE V;
int eps = params[0];
float alpha = params[1], beta = params[2], gamma = params[3];
tree_t tree = preprocess(vm);
image2d<V> ima_, ima;
io::imread(img_path, ima_);
typedef rgb8 V;
ima = Kadjust_to(ima_, tree._get_data()->m_pmap.domain());
image2d<V> f;
io::imread(vm["input_path"].as<std::string>(), f, true);
if (ima.domain() != tree._get_data()->m_pmap.domain())
{
std::cerr << "Domain between image differ.\n"
<< ima.domain() << " vs "
<< tree._get_data()->m_pmap.domain() << std::endl;
}
image2d<V> F = Kadjust_to(f, tree._get_data()->m_pmap.domain());
f = unimmerse_k1(F);
image2d<V> F = ima;
image2d<V> f = unimmerse_k1(F);
// Curvature
image2d<float> C = compute_curvature(transform(f, [](V x) { return l1norm(x); }));
accu::accumulators::accu_if< accu::accumulators::count<>,
K1::is_face_2_t,
point2d > counter;
auto areamap = morpho::paccumulate(tree, F, counter);
auto pred = make_functional_property_map<tree_t::vertex_id_t>([&areamap, grain](tree_t::vertex_id_t x) {
return areamap[x] > grain;
});
morpho::filter_direct_inplace(tree, pred);
// V1
typedef std::conditional< std::is_scalar<V>::value, double,
vec<double, value_traits<V>::ndim> >::type SumType;
auto amap = compute_regional_energy(tree, F,
......@@ -100,15 +61,16 @@ int main(int argc, char** argv)
eps);
amap[tree.get_root()] = 0;
// V2
auto amap2 = compute_attribute_on_contour(tree, C,
accu::features::count<unsigned> () &
accu::features::mean<double> ());
// Compute energy and filter
// Compute energy
property_map<tree_t, float> energy(tree);
{
point2d shp = ima.domain().shape();
float domlength = 2*shp[0] + 2*shp[1] - 4;
float domlength = tree._get_data()->m_pmap.domain().size();
mln_foreach(auto x, tree.nodes())
{
float v1 = amap[x][2] > 0 ? (alpha * (amap[x][0]+amap[x][1])/amap[x][2]) : 0;
......@@ -117,21 +79,38 @@ int main(int argc, char** argv)
energy[x] = v1+v2+v3;
}
}
energy[tree.get_root()] = 0;
// convert to image and filter
auto ienergy = morpho::make_image(tree, energy);
{
mln_foreach(float& v, ienergy.values())
if (v > threshold1)
v = threshold1;
}
auto extincted = morpho::extinction(ienergy, morpho::tree_neighb_t());
auto smap = postprocess(vm, tree, energy);
{
auto& attr = extincted.get_vmap();
image2d<float> sal = set_value_on_contour(tree, attr);
io::imsave(sal, output_path);
}
const char* names[] = {"VIN", "VOUT", "VTOTAL", "V1", "Cont. Length",
"Curv. Avg", "Energy", "Extinction" };
typedef vec<double, 3> vec3d;
auto vin = make_functional_property_map<tree_t::node_type>([&amap](tree_t::node_type x) { return amap[x][0]; });
auto vout = make_functional_property_map<tree_t::node_type>([&amap](tree_t::node_type x) { return amap[x][1]; });
auto vtotal = make_functional_property_map<tree_t::node_type>([&amap](tree_t::node_type x) { return amap[x][2]; });
auto v1 = make_functional_property_map<tree_t::node_type>([&amap](tree_t::node_type x) {
return amap[x][2] == 0 ? 0 : (amap[x][0]+amap[x][1]) / amap[x][2];
});
auto cont_length = make_functional_property_map<tree_t::node_type>([&amap2](tree_t::node_type x) {
return accu::extractor::count(amap2[x]); });
auto cont_curv = make_functional_property_map<tree_t::node_type>([&amap2](tree_t::node_type x) {
return accu::extractor::mean(amap2[x]); });
std::function<float(tree_t::node_type)> attrs[] = {
_as_fun(vin),
_as_fun(vout),
_as_fun(vtotal),
_as_fun(v1),
_as_fun(cont_length),
_as_fun(cont_curv),
_as_fun(energy),
_as_fun(smap)
};
export_(vm, tree, smap, names, attrs, sizeof(names) / sizeof(char*));
}
#ifndef MLN_INPUT_VALUE_TYPE
# define MLN_INPUT_VALUE_TYPE mln::uint16
#endif
#include <mln/core/image/image2d.hpp>
#include <mln/core/colors.hpp>
#include <mln/colors/rgba.hpp>