Commit 638f7806 authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Add hierarchical segmentation apps.

	*  apps/CMakeLists.txt,
	*  apps/hierachical_seg-gui/brush.cpp,
	*  apps/hierachical_seg-gui/brush.hpp,
	*  apps/hierachical_seg-gui/hierachical_seg-cli.cpp,
	*  apps/hierachical_seg-gui/hierachical_seg-gui.cpp,
	*  apps/hierachical_seg-gui/supervised-cli.cpp: New
parent e952de72
......@@ -9,3 +9,4 @@ add_subdirectory(clattice)
add_subdirectory(tosgui)
add_subdirectory(saliency)
add_subdirectory(supervised-gui)
add_subdirectory(hierachical_seg-gui)
#include "brush.hpp"
#include <QtGui>
#include <iostream>
#include <mln/io/imsave.hpp>
inline
void brush(QPixmap* pixmap, const QPointF& position, QColor color, int r)
{
QPainter painter(pixmap);
painter.setBrush(color);
painter.setPen(Qt::NoPen);
QRectF rectangle(position.x() - r, position.y() - r, 2*r, 2*r);
painter.drawEllipse(rectangle);
}
MyBrush::MyBrush(mln::qt::ImageViewer* viewer,
std::function<void(const mln::image2d<mln::rgb8>&, mln::image2d<mln::rgb8>&)> callback)
: m_viewer (viewer),
m_callback(callback),
m_scene(viewer->getScene()),
m_pixmap(viewer->getPixmap()),
m_ima(m_pixmap->pixmap()),
m_active(false),
m_radius(5)
{
m_ori = mln::clone(viewer->getView());
}
bool
MyBrush::eventFilter(QObject* obj, QEvent* ev)
{
(void) obj;
if (m_active and ev->type() == QEvent::GraphicsSceneMouseMove)
{
QGraphicsSceneMouseEvent* event = static_cast<QGraphicsSceneMouseEvent*>(ev);
if (event->buttons() & Qt::LeftButton)
{
brush(&m_ima, m_pixmap->mapFromScene(event->scenePos()), m_color, m_radius);
m_pixmap->setPixmap(m_ima);
}
}
return false;
}
void
MyBrush::setColor1()
{
m_active = true;
m_color = Qt::red;
}
void
MyBrush::setColor2()
{
m_active = true;
m_color = Qt::blue;
}
void
MyBrush::setRadius(int k)
{
m_radius = k;
}
void
MyBrush::run()
{
std::cout << "Running." << std::endl;
m_viewer->notify();
mln::image2d<mln::rgb8>& view = m_viewer->getView();
m_callback(view, view);
m_viewer->update();
}
void
MyBrush::revert()
{
m_pixmap->setPixmap(m_ima);
}
void
MyBrush::reload()
{
m_viewer->getView() = mln::clone(m_ori);
m_viewer->update();
m_ima = m_pixmap->pixmap(); // FLUSH THE MARKERS
}
#ifndef BRUSH_HPP
# define BRUSH_HPP
# include <mln/qt/imageviewer.hpp>
class MyBrush : public QObject
{
Q_OBJECT;
public:
MyBrush(mln::qt::ImageViewer* viewer,
std::function<void(const mln::image2d<mln::rgb8>&, mln::image2d<mln::rgb8>&)> callback);
bool eventFilter(QObject* obj, QEvent* ev);
public slots:
void setColor1();
void setColor2();
void setRadius(int k);
void run();
void reload();
void revert();
private:
mln::qt::ImageViewer* m_viewer;
std::function<void(const mln::image2d<mln::rgb8>&,mln::image2d<mln::rgb8>&)> m_callback;
QGraphicsScene* m_scene;
QGraphicsPixmapItem* m_pixmap;
QPixmap m_ima;
mln::image2d<mln::rgb8> m_ori;
bool m_active;
QColor m_color;
int m_radius;
};
#endif //!BRUSH_HPP
#include <mln/core/image/image2d.hpp>
#include <mln/io/imread.hpp>
#include <mln/io/imsave.hpp>
#include <mln/core/colors.hpp>
#include <mln/core/algorithm/transform.hpp>
#include <mln/morpho/component_tree/component_tree.hpp>
#include <mln/morpho/component_tree/reconstruction.hpp>
#include <mln/morpho/maxtree/maxtree.hpp>
#include <apps/tos/Kinterpolate.hpp>
#include <apps/tos/topology.hpp>
using namespace mln;
typedef morpho::component_tree<unsigned, image2d<unsigned> > tree_t;
///
///
/// \param tree The component tree of the saliency map
/// \param markers The image with markers
image2d<bool>
segmentation(const tree_t& tree,
const image2d<uint8>& markers__)
{
enum colortag { BLANC = 0, ROUGE = 1, NOIR = 2, NONE = 3 };
auto markers = transform(markers__,
[](const uint8& v) -> uint8 {
if (v == 0) return BLANC; // No tag
else if (v == 255) return ROUGE; // Object
else return NOIR; // Bg
});
property_map<tree_t, uint8> tags(tree, NONE);
mln_foreach(auto px, markers.pixels())
{
tree_t::node_type x = tree.get_node_at(px.index());
colortag v = (colortag)px.val();
if (K1::is_face_2(px.point()) and v != BLANC)
{
if (tags[x] == NONE)
tags[x] = v;
else if (tags[x] != v)
tags[x] = BLANC;
}
}
tags[tree.get_root()] = NOIR; // Root is background
// Propagate up
mln_reverse_foreach(auto x, tree.nodes()) {
if (tags[x] != BLANC) {
auto q = x.parent();
if (tags[q] == BLANC) // Ok propagate color
tags[q] = tags[x];
else if (tags[q] != tags[x]) // Either bg thus both red/black => thus black
tags[q] = NOIR;
}
}
// Propagate down
mln_foreach(auto x, tree.nodes_without_root()) {
auto q = x.parent();
mln_assertion(tags[q] != BLANC);
if (tags[x] == BLANC)
tags[x] = tags[q];
}
//
image2d<bool> out;
resize(out, markers__);
auto vmap = make_functional_property_map<tree_t::node_type>
( [&tags](const tree_t::node_type& x) { return tags[x] == ROUGE; } );
morpho::reconstruction(tree, vmap, out);
return out;
}
int main(int argc, char** argv)
{
if (argc != 4) {
std::cout << "Usage: " << argv[0] << "saliency(float) markers.pgm output.pbm" << std::endl;
std::exit(1);
}
image2d<float> sal;
image2d<uint8> markers;
image2d<bool> out;
tree_t tree;
io::imread(argv[1], sal);
io::imread(argv[2], markers);
box2d dom = markers.domain();
markers = Kadjust_to(markers, sal.domain());
tree = morpho::mintree_indexes(sal, c4);
out = segmentation(tree, markers);
out = Kadjust_to(out, dom);
io::imsave(out, argv[3]);
}
#include <mln/core/image/image2d.hpp>
#include <mln/io/imread.hpp>
#include <mln/core/colors.hpp>
#include <mln/colors/literal.hpp>
#include <mln/morpho/component_tree/component_tree.hpp>
#include <mln/morpho/maxtree/maxtree.hpp>
#include <mln/data/stretch.hpp>
#include <QApplication>
#include <QtGui>
#include <mln/qt/imageviewer.hpp>
#include <apps/tos/topology.hpp>
#include <apps/tos/Kinterpolate.hpp>
#include "brush.hpp"
#include "myheap.hpp"
using namespace mln;
typedef morpho::component_tree<unsigned, image2d<unsigned> > tree_t;
///
///
/// \param tree The component tree of the saliency map
/// \param f The original image to filter out
/// \param markers__ The image with markers
/// \param[out] out The image to store the result in.
void
segmentation(const tree_t& tree,
const image2d<rgb8>& f,
const image2d<rgb8>& markers__,
image2d<rgb8>& out)
{
enum colortag { BLANC = 0, ROUGE = 1, NOIR = 2 };
auto markers = imtransform(markers__,
[](const rgb8& v) -> uint8 {
if (v == colors::literal::red) return NOIR;
else if (v == colors::literal::blue) return ROUGE;
else return BLANC;
});
property_map<tree_t, uint8> tags(tree, BLANC);
mln_foreach(auto px, markers.pixels())
{
tree_t::node_type x = tree.get_node_at(px.index());
colortag v = (colortag)px.val();
if (K1::is_face_2(px.point()) and v != BLANC)
tags[x] = v;
}
tags[tree.get_root()] = NOIR; // Root is background
// Propagate up
mln_reverse_foreach(auto x, tree.nodes()) {
if (tags[x] != BLANC) {
auto q = x.parent();
if (tags[q] == BLANC) // Ok propagate color
tags[q] = tags[x];
else if (tags[q] != tags[x]) // Either bg thus both red/black => thus black
tags[q] = NOIR;
}
}
// Propagate down
mln_foreach(auto x, tree.nodes_without_root()) {
auto q = x.parent();
mln_assertion(tags[q] != BLANC);
if (tags[x] == BLANC)
tags[x] = tags[q];
}
//
{
mln_pixter(pxin, pxout, f, out);
mln_forall(pxin, pxout)
{
auto x = tree.get_node_at(pxin->index());
if (tags[x] == NOIR)
pxout->val() = 192 + pxin->val() / 4;
else
pxout->val() = pxin->val();
}
}
}
int main(int argc, char** argv)
{
QApplication a(argc, argv);
if (argc < 3) {
std::cout << "Usage: " << argv[0] << " saliency(float) original.ppm" << std::endl;
std::exit(1);
}
image2d<float> sal;
io::imread(argv[1], sal);
tree_t tree = morpho::mintree_indexes(sal, c4);
image2d<rgb8> ima;
io::imread(argv[2], ima);
auto sal2 = data::stretch<uint8>(sal);
ima = Kadjust_to(ima, sal.domain());
mln_pixter(px1, px2, ima, sal2);
mln_forall(px1, px2)
if (px2->val())
px1->val() = px2->val();
QMainWindow win;
qt::ImageViewer* viewer = new qt::ImageViewer(ima);
auto callback = std::bind(segmentation, tree, ima,
std::placeholders::_1,
std::placeholders::_2);
QGraphicsScene* scene = viewer->getScene();
MyBrush brush(viewer, callback);
scene->installEventFilter(&brush);
QToolBar* toolbar = new QToolBar(&win);
QAction* action1 = toolbar->addAction("Bg");
QAction* action2 = toolbar->addAction("Fg");
QAction* action3 = toolbar->addAction("Run");
QAction* action4 = toolbar->addAction("Revert");
QAction* action5 = toolbar->addAction("Reload");
QObject::connect(action1, SIGNAL(triggered()),
&brush, SLOT(setColor1()));
QObject::connect(action2, SIGNAL(triggered()),
&brush, SLOT(setColor2()));
QObject::connect(action3, SIGNAL(triggered()),
&brush, SLOT(run()));
QObject::connect(action4, SIGNAL(triggered()),
&brush, SLOT(revert()));
QObject::connect(action5, SIGNAL(triggered()),
&brush, SLOT(reload()));
QActionGroup agroup(&win);
agroup.addAction(action1);
agroup.addAction(action2);
action1->setCheckable(true);
action2->setCheckable(true);
win.setCentralWidget(viewer);
win.addToolBar(toolbar);
win.show();
// QImage image("../../../img/small.pgm");
// QGraphicsPixmapItem item(QPixmap::fromImage(image));
// QGraphicsScene* m_scene = new QGraphicsScene;
// m_scene->addItem(&item);
// QGraphicsView* view = new QGraphicsView(m_scene);
// QMainWindow win;
// win.setCentralWidget(view);
// win.show();
a.exec();
}
#include <mln/core/image/image2d.hpp>
#include <mln/io/imread.hpp>
#include <mln/io/imsave.hpp>
#include <mln/core/colors.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 <apps/tos/Kinterpolate.hpp>
#include "myheap.hpp"
using namespace mln;
typedef morpho::component_tree<unsigned, image2d<unsigned> > tree_t;
property_map<tree_t, float> run_djikstra(const tree_t& tree,
property_map<tree_t, uint8>& colormap,
const property_map<tree_t, rgb<int> >& vmap,
int color)
{
property_map<tree_t, float> distancemap(tree, value_traits<float>::max());
property_map<tree_t, int> posmap(tree, -1);
myheap heap(tree, distancemap, posmap);
mln_foreach(auto node, tree.nodes())
if (colormap[node] == (uint8)-1)
colormap[node] = 0;
else if (colormap[node] == color) {
distancemap[node] = 0;
heap.push(node);
}
{
mln_entering("Running djikstra");
tree_t::node_type p;
morpho::tree_neighb_t nbh;
mln_iter(q, nbh(p));
while (not heap.empty())
{
p = heap.pop();
assert(posmap[p] == -1);
mln_forall(q)
{
float d = l2norm(vmap[p] - vmap[*q]) + distancemap[p];
if (d < distancemap[*q])
{
distancemap[*q] = d;
if (posmap[*q] == -1) { // not in queue, insert
heap.push(*q);
} else {
heap.update(*q);
}
}
}
}
mln_exiting();
}
return distancemap;
}
image2d<rgb8>
segmentation_(const tree_t& tree,
const image2d<rgb8>& ima_,
const image2d<rgb8>& markers__)
{
image2d<uint8> markers_ = transform(markers__,
[](const rgb8& v) -> uint8 {
if (v == colors::literal::red) return 1;
else if (v == colors::literal::blue) return 2;
else return 0;
});
image2d<rgb8> ima = Kadjust_to(ima_, tree._get_data()->m_pmap.domain());
image2d<uint8> marker = Kadjust_to(markers_, tree._get_data()->m_pmap.domain(), "zero");
auto vmap = morpho::vaccumulate_proper(tree, ima, accu::features::mean<>());
property_map<tree_t, uint8> colormap(tree, 0);
property_map<tree_t, float> distancemap(tree, value_traits<float>::max());
property_map<tree_t, int> posmap(tree, -1);
mln_foreach(auto px, marker.pixels())
{
tree_t::node_type node = tree.get_node_at(px.index());
if (colormap[node] == 0)
colormap[node] = px.val();
else if (colormap[node] != px.val())
colormap[node] = -1;
}
myheap heap(tree, distancemap, posmap);
mln_foreach(auto node, tree.nodes())
if (colormap[node] == (uint8)-1)
colormap[node] = 0;
else if (colormap[node] != 0) {
distancemap[node] = 0;
heap.push(node);
}
// Run djisktra
{
mln_entering("Running djikstra");
tree_t::node_type p;
morpho::tree_neighb_t nbh;
mln_iter(q, nbh(p));
while (not heap.empty())
{
p = heap.pop();
assert(posmap[p] == -1);
mln_forall(q)
{
float d = l2norm(vmap[p] - vmap[*q]) + distancemap[p];
if (d < distancemap[*q])
{
colormap[*q] = colormap[p];
distancemap[*q] = d;
if (posmap[*q] == -1) { // not in queue, insert
heap.push(*q);
} else {
heap.update(*q);
}
}
}
}
mln_exiting();
}
image2d<float> dist;
image2d<uint8> color;
resize(dist, ima);
resize(color, ima);
morpho::reconstruction(tree, colormap, color);
morpho::reconstruction(tree, distancemap, dist);
io::imsave(color, "/tmp/colormap.tiff");
io::imsave(dist, "/tmp/distancemap.tiff");
auto rec = transform(imzip(color, ima), [](const std::tuple<uint8, rgb8>& v) {
return std::get<0>(v) == 2 ? std::get<1>(v) : rgb8(0);
});
return Kadjust_to(rec, ima_.domain());
}
image2d<bool>
segmentation(const tree_t& tree,
const image2d<rgb8>& ima_,
const image2d<uint8>& markers__)
{
image2d<uint8> markers_ = transform(markers__,
[](const uint8& v) -> uint8 {
if (v == 0) return 0; // No tag
else if (v == 255) return 2; // Object
else return 1; // Bg
});
image2d<rgb8> ima = Kadjust_to(ima_, tree._get_data()->m_pmap.domain());
image2d<uint8> marker = Kadjust_to(markers_, tree._get_data()->m_pmap.domain(), "zero");
auto vmap = morpho::vaccumulate_proper(tree, ima, accu::features::mean<>());
property_map<tree_t, uint8> colormap(tree, 0);
mln_foreach(auto px, marker.pixels())
{
tree_t::node_type node = tree.get_node_at(px.index());
if (colormap[node] == 0)
colormap[node] = px.val();
else if (colormap[node] != px.val())
colormap[node] = -1;
}
property_map<tree_t, float> dist1 = run_djikstra(tree, colormap, vmap, 1);