Commit 8f2912e5 authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Add saliency based apps.

	*  apps/CMakeLists.txt,
	*  apps/saliency/CMakeLists.txt,
	*  apps/saliency/closure.hpp,
	*  apps/saliency/dispsaliency.cpp,
	*  apps/saliency/dispsaliency.hpp,
	*  apps/saliency/extinction.hpp,
	*  apps/saliency/saliency.cpp,
	*  apps/saliency/saliency.hpp: New.
parent b99cf8cb
......@@ -6,3 +6,4 @@ add_subdirectory(maxtree_comparison)
add_subdirectory(simplification)
add_subdirectory(clattice)
add_subdirectory(tosgui)
add_subdirectory(saliency)
FIND_PACKAGE(Qt4 REQUIRED)
INCLUDE(${QT_USE_FILE})
link_libraries(-lfreeimage)
QT4_WRAP_CPP(dispsaliency_HEADERS_MOC
${CMAKE_HOME_DIRECTORY}/mln/qt/mainwin.hpp
dispsaliency.hpp
)
add_executable(dispsaliency dispsaliency.cpp
${dispsaliency_HEADERS_MOC}
${CMAKE_HOME_DIRECTORY}/mln/qt/qtimage.cpp
${CMAKE_HOME_DIRECTORY}/mln/qt/mainwin.cpp
${CMAKE_HOME_DIRECTORY}/mln/qt/graphics_pixmap_item.cpp
)
target_link_libraries(dispsaliency ${QT_QTMAIN_LIBRARY} ${QT_LIBRARIES})
add_executable(saliency saliency.cpp
${CMAKE_HOME_DIRECTORY}/apps/attributes/MSERArgparser.cpp
${CMAKE_HOME_DIRECTORY}/apps/attributes/meaningfullnessArgparser.cpp)
target_link_libraries(saliency ${Boost_PROGRAM_OPTIONS_LIBRARY})
add_executable(tosview tosview.cpp)
\ No newline at end of file
#ifndef MLN_APPS_SALIENCY_CLOSURE_HPP
# define MLN_APPS_SALIENCY_CLOSURE_HPP
#include <mln/core/image/image2d.hpp>
template <typename V, class Compare = std::less<unsigned> >
mln::image2d<float>
area_close(const mln::image2d<float>& attr,
const mln::image2d<V>& K,
const mln::image2d<unsigned>& parent,
const std::vector<unsigned>& S,
unsigned lambda,
Compare cmp = Compare ());
template <typename V, class Compare = std::less<float> >
mln::image2d<float>
height_close(const mln::image2d<float>& attr,
const mln::image2d<V>& K,
const mln::image2d<unsigned>& parent,
const std::vector<unsigned>& S,
float lambda,
Compare cmp = Compare ());
/************************/
/** Implementation **/
/************************/
namespace internal
{
struct child_t
{
static constexpr unsigned UNDEF = -1;
unsigned first_child = UNDEF;
unsigned next_sibling = UNDEF;
};
template <typename V>
mln::image2d<child_t>
getchilds(const mln::image2d<V>& K,
const mln::image2d<unsigned>& parent,
const std::vector<unsigned>& S)
{
using namespace mln;
image2d<child_t> childs;
resize(childs, K).init(child_t());
for (unsigned x: S)
{
unsigned q = parent[x];
if (K[q] != K[x]) // Handle the root outside
{
if (childs[q].first_child == child_t::UNDEF)
childs[q].first_child = x;
else
{
q = childs[q].first_child;
while (childs[q].next_sibling != child_t::UNDEF)
q = childs[q].next_sibling;
childs[q].next_sibling = x;
}
}
}
return childs;
}
template <typename V>
std::vector<unsigned>
getnodes(const mln::image2d<V>& K,
const mln::image2d<unsigned>& parent,
const std::vector<unsigned>& S)
{
std::vector<unsigned> nodes;
nodes.reserve(S.size());
nodes.push_back(S[0]);
for (unsigned x: S)
{
unsigned y = parent[x];
if (K[x] != K[y]) // x is a node
nodes.push_back(x);
}
nodes.shrink_to_fit();
return nodes;
}
# ifndef INTERNAL_ZFINDROOT
# define INTERNAL_ZFINDROOT
unsigned
zfindroot_(mln::image2d<unsigned>& par, unsigned x)
{
if (par[x] != x)
par[x] = zfindroot_(par, par[x]);
return par[x];
}
# endif
}
template <typename V, class Compare>
mln::image2d<float>
area_close(const mln::image2d<float>& attr,
const mln::image2d<V>& K,
const mln::image2d<unsigned>& parent,
const std::vector<unsigned>& S,
unsigned lambda,
Compare cmp)
{
using namespace mln;
image2d< ::internal::child_t > childs = ::internal::getchilds(K, parent, S);
std::vector<unsigned> nodes = ::internal::getnodes(K, parent, S);
std::sort(nodes.begin(), nodes.end(), [cmp,&attr](unsigned x, unsigned y) {
return cmp(attr[x], attr[y]);
});
// perform the closure.
static const unsigned UNDEF = ::internal::child_t::UNDEF;
image2d<unsigned> par, area;
resize(par, K).init(UNDEF);
resize(area, K);
for (unsigned x: nodes)
{
// make set
par[x] = x;
area[x] = 1;
unsigned cchild = childs[x].first_child;
unsigned n = parent[x];
do // foreach neighbor n in tree topo
{
if (par[n] != UNDEF) // dejavu(n) ?
{
unsigned r = ::internal::zfindroot_(par, n);
if (r != x) // merge set
{
if (area[r] < lambda) // r should be filtered
{
par[r] = x;
area[x] += area[r];
}
else
{
// r should merge with x but no need since
// r is not filtered. Just set Area[x] = lambda
// to say that x should not be filtered
area[x] = lambda;
}
}
}
}
while ((n = cchild) != UNDEF and (cchild = childs[cchild].next_sibling, true) );
}
// filter now:
image2d<float> out;
resize(out, attr);
out[nodes[nodes.size()-1]] = value_traits<unsigned, Compare>::max();
for (int i = nodes.size()-1; i >= 0; --i)
{
unsigned x = nodes[i];
if (area[x] < lambda)
out[x] = out[par[x]];
else
out[x] = attr[x];
}
return out;
}
template <typename V, class Compare>
mln::image2d<float>
height_close(const mln::image2d<float>& attr,
const mln::image2d<V>& K,
const mln::image2d<unsigned>& parent,
const std::vector<unsigned>& S,
float lambda,
Compare cmp)
{
using namespace mln;
image2d< ::internal::child_t > childs = ::internal::getchilds(K, parent, S);
std::vector<unsigned> nodes = ::internal::getnodes(K, parent, S);
std::sort(nodes.begin(), nodes.end(), [cmp,&attr](unsigned x, unsigned y) {
return cmp(attr[x], attr[y]);
});
// perform the closure.
static const unsigned UNDEF = ::internal::child_t::UNDEF;
image2d<unsigned> par;
image2d<float> minimum;
resize(par, K).init(UNDEF);
resize(minimum, K);
for (unsigned x: nodes)
{
// make set
par[x] = x;
minimum[x] = attr[x];
unsigned cchild = childs[x].first_child;
unsigned n = parent[x];
float clevel = attr[x];
do // foreach neighbor n in tree topo
{
if (par[n] != UNDEF) // dejavu(n) ?
{
unsigned r = ::internal::zfindroot_(par, n);
if (r != x) // merge set
{
if (std::abs(clevel - minimum[r]) < lambda) // r should be filtered
{
par[r] = x;
minimum[x] = std::min(minimum[x], minimum[r], cmp);
}
else
{
// r should merge with x but no need since
// r is not filtered. Just set Min[x] = ^(float, <)
// to say that x should not be filtered
minimum[x] = value_traits<float, Compare>::min();
}
}
}
}
while ((n = cchild) != UNDEF and (cchild = childs[cchild].next_sibling, true) );
}
// filter now:
image2d<float> out;
resize(out, attr);
out[nodes[nodes.size()-1]] = value_traits<float, Compare>::max();
for (int i = nodes.size()-1; i >= 0; --i)
{
unsigned x = nodes[i];
if ( std::abs(attr[x] - minimum[x]) < lambda )
out[x] = out[par[x]];
else
out[x] = attr[x];
}
return out;
}
#endif // ! MLN_APPS_SALIENCY_CLOSURE_HPP
#include <mln/core/image/image2d.hpp>
#include <mln/io/imread.hpp>
#include <apps/tos/addborder.hpp>
#include "dispsaliency.hpp"
#include <mln/io/imsave.hpp>
int main(int argc, char** argv)
{
if (argc < 2) {
std::cerr << "Usage:" << argv[0] << " saliency.tiff [original.tiff]" << std::endl
<< "Saliency must be a saliency map (n x m) with saliency on edges." << std::endl
<< "Original must be either an rgb or gray level image with size (n-3)/2 x (m-3)/2 (wo border)"
<< std::endl;
std::terminate();
}
QApplication a(argc, argv);
const char* s_input = argv[1];
const char* s_input_2 = NULL;
if (argc == 3)
s_input_2 = argv[2];
image2d<float> smap;
io::imread(s_input, smap);
if (s_input_2 != NULL)
{
image2d<rgb8> ref;
try {
io::imread(s_input_2, ref);
} catch (io::MLNIOException) {
image2d<uint8> ref_;
io::imread(s_input_2, ref_);
ref = transform(ref_, [](uint8 x) { return rgb8{x,x,x}; });
}
ref = addborder(ref, lexicographicalorder_less<rgb8>() );
point2d sz = smap.domain().shape();
point2d sz2 = ref.domain().shape();
if (sz != (sz2 * 2 - 1))
throw std::runtime_error("Domains have invalid size");
Displayer disp(smap, &ref, argv[1]);
a.exec();
}
else
{
Displayer disp(smap, NULL, argv[1]);
a.exec();
}
}
#ifndef DISPSALIENCY_HPP
# define DISPSALIENCY_HPP
#include <QObject>
#include <QtGui>
#include <QSlider>
#include <mln/qt/mainwin.hpp>
#include <mln/core/algorithm/accumulate.hpp>
#include <mln/accu/accumulators/minmax.hpp>
#include <mln/core/image/image2d.hpp>
#include <mln/core/neighb2d.hpp>
#include <mln/core/image/sub_image.hpp>
#include <mln/labeling/blobs.hpp>
#include <mln/labeling/accumulate.hpp>
#include <mln/accu/accumulators/mean.hpp>
#include <mln/io/imprint.hpp>
using namespace mln;
struct Displayer : public QObject
{
Q_OBJECT
public:
Displayer(const image2d<float>& f,
const image2d<rgb8>* ref = NULL,
const QString& title = "")
: m_ori(f),
m_ref (ref),
m_ima((f.nrows()+1)/2, (f.ncols()+1)/2),
m_win(m_ima)
{
float vmin, vmax;
std::tie(vmin, vmax) = accumulate(f, accu::features::minmax<> ());
m_slider.setOrientation(Qt::Horizontal);
m_slider.setMinimum(vmin * 1000);
m_slider.setMaximum(vmax * 1000);
std::cout << vmin << " / " << vmax << std::endl;
QObject::connect(&m_slider, SIGNAL(sliderReleased()),
this, SLOT(on_filtering()));
this->on_filtering();
m_win.setWindowTitle(title);
m_win.show();
m_slider.show();
}
static
rgb8 crandom_lut(unsigned x)
{
auto rol = [](int x, int n) {
return (x << n) | (x > (16-n));
};
unsigned k = x % 6;
std::tuple<uint8, uint8,uint8> v {(rol(x,2) & 255),
(rol(x,5) & 255),
(rol(x,7) & 255) };
uint8 r,g,b;
switch (k) {
case 0: std::tie(r,g,b) = v; break;
case 1: std::tie(r,b,g) = v; break;
case 2: std::tie(g,r,b) = v; break;
case 3: std::tie(g,b,r) = v; break;
case 4: std::tie(b,g,r) = v; break;
default: std::tie(b,r,g) = v; break;
};
return rgb8{r,g,b};
}
public slots:
void on_filtering()
{
float v = m_slider.value() / 1000.0;
auto bin = m_ori <= v;
image2d<unsigned> lbl;
unsigned nlabel;
std::tie(lbl, nlabel) = labeling::blobs(bin, c4, 0u);
std::cout << "Label: " << nlabel << std::endl;
// K1 -> K0
sbox2d dom = {{0,0}, {m_ori.nrows(), m_ori.ncols()}, {2,2}};
if (m_ref == NULL)
{
auto tmp = imtransform(lbl | dom, &Displayer::crandom_lut);
copy(tmp, m_ima);
}
else
{
auto means = labeling::v_accumulate(lbl | dom, *m_ref, nlabel, accu::features::mean<> ());
for (auto x: means)
std::cout << x << std::endl;
auto tmp = imtransform(lbl | dom, [&means](unsigned i) { return means[i]; });
copy(tmp, m_ima);
}
m_win.reset();
m_win.update();
}
private:
image2d<float> m_ori;
const image2d<rgb8>* m_ref;
image2d<rgb8> m_ima;
qt::MainWindow<rgb8> m_win;
QSlider m_slider;
};
#endif // ! DISPSALIENCY_HPP
#ifndef APPS_SALIENCY_EXTINCTION_HPP
# define APPS_SALIENCY_EXTINCTION_HPP
# include <mln/core/image/image2d.hpp>
# include <mln/core/trace.hpp>
# include <vector>
template <typename V, typename T, class Compare = std::less<V> >
mln::image2d<V>
extinction(const mln::image2d<V>& a,
const mln::image2d<T>& K,
const mln::image2d<unsigned>& parent,
const std::vector<unsigned>& S,
Compare cmp = Compare()
);
/************************************/
/** Implementation **/
/************************************/
namespace internal
{
# ifndef INTERNAL_ZFINDROOT
# define INTERNAL_ZFINDROOT
unsigned
zfindroot_(mln::image2d<unsigned>& par, unsigned x)
{
if (par[x] != x)
par[x] = zfindroot_(par, par[x]);
return par[x];
}
# endif
}
template <typename V, typename T, class Compare>
mln::image2d<V>
extinction(const mln::image2d<V>& a,
const mln::image2d<T>& K,
const mln::image2d<unsigned>& parent,
const std::vector<unsigned>& S,
Compare cmp)
{
using namespace mln;
static const unsigned UNDEF = -1;
trace::entering("extinction");
// Retrieve the list of nodes
// and sort them
// also retrieve the child
struct child_t {
unsigned first_child = UNDEF;
unsigned next_sibling = UNDEF;
};
std::vector<unsigned> nodes;
image2d<child_t> childs;
{
resize(childs, K).init(child_t());
nodes.reserve(S.size());
nodes.push_back(S[0]);
for (unsigned x: S)
{
unsigned q = parent[x];
if (K[q] != K[x]) // Handle the root outside
{
// std::cout << "Setting node " << x << std::endl
// << " parent: " << q << " / fc: " << childs[q].first_child << std::endl;
nodes.push_back(x);
if (childs[q].first_child == UNDEF)
childs[q].first_child = x;
else
{
q = childs[q].first_child;
while (childs[q].next_sibling != UNDEF)
q = childs[q].next_sibling;
childs[q].next_sibling = x;
}
}
}
nodes.shrink_to_fit();
std::sort(nodes.begin(), nodes.end(),
[&a, cmp](unsigned x, unsigned y) { return cmp(a[x], a[y]); });
}
// Compute extinction values.
image2d<V> extmap;
image2d<unsigned> par; // union-find struct
resize(extmap, K).init(0);
resize(par, K).init(UNDEF);
for (unsigned x: nodes)
{
//std::cout << "Setting node" << x << std::endl;
// Make set.
unsigned zx = x;
par[x] = zx;
unsigned cchild = childs[x].first_child;
// U-F
// parent
unsigned n = parent[x];
//std::cout << "Doing: " << x << " > " << a[x] << std::endl;
do
{
//std::cout << " Nbh = " << n << " > " << a[n] << std::endl;
if (par[n] != UNDEF) // deja_vu n ?
{
// zn is the local minimum of the neighboring component
unsigned zn = ::internal::zfindroot_(par, n);