Commit 1b4175e3 authored by Edwin Carlinet's avatar Edwin Carlinet

Merge branch 'dev' into 'master'

Freeze 0.0.1 version in master branch

See merge request !96
parents 0c188180 18c9e684
Pipeline #18243 passed with stages
in 5 minutes and 23 seconds
# We'll use defaults from the LLVM style, but with 4 columns indentation.
BasedOnStyle: LLVM
IndentWidth: 2
---
Language: Cpp
# Force pointers to the type for C++.
DerivePointerAlignment: false
PointerAlignment: Left
# 120 cols
ColumnLimit: 120
Cpp11BracedListStyle: true
# break brace style
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
NamespaceIndentation: All
FixNamespaceComments: true
IndentWrappedFunctionNames: true
AllowShortFunctionsOnASingleLine: Inline
AlwaysBreakTemplateDeclarations: true
# For loop
ForEachMacros: ['mln_forall', 'mln_foreach']
/build
/doc
\ No newline at end of file
/build*
/cmake-build-*
/doc/build
/doc/html
/doc/doxyxml
/doc/source/images/*.png
.vscode
.vs
.idea
*.pyc
compile_commands.json
\ No newline at end of file
stages:
- build
- test
- deploy
image: carlinet/buildimage
.job-build-linux-base: &distcheck-linux-base
stage: test
script:
- mkdir build && cd build
- cmake .. -DCMAKE_BUILD_TYPE=$PYLENE_CONFIGURATION -DPYLENE_BUILD_BENCHMARKS=OFF
- cmake --build . --target check
distcheck-linux-gcc-release:
<<: *distcheck-linux-base
variables:
PYLENE_CONFIGURATION: "Release"
CXX: "g++"
CC: "gcc"
distcheck-linux-clang-release:
<<: *distcheck-linux-base
variables:
PYLENE_CONFIGURATION: "Release"
CXX: "clang++"
CC: "clang"
distcheck-linux-gcc-debug:
<<: *distcheck-linux-base
variables:
PYLENE_CONFIGURATION: "Debug"
CXX: "g++"
CC: "gcc"
distcheck-linux-clang-debug:
<<: *distcheck-linux-base
variables:
PYLENE_CONFIGURATION: "Debug"
CXX: "clang++"
CC: "clang"
build-doc-images:
stage: build
script:
- mkdir build && cd build
- cmake .. -DCMAKE_BUILD_TYPE=Release -DPYLENE_BUILD_BENCHMARKS=OFF
- cmake --build . --target build-images
artifacts:
paths:
- doc/source/images
expire_in: 2 days
#################
# Documentation #
#################
.documentation-base: &documentation-base
stage: test
script:
- cd doc
- doxygen
- sphinx-build -b html source ../public
artifacts:
name: "documentation"
expire_in: 2 days
paths:
- public/
# Generate doc only
documentation:
<<: *documentation-base
except:
- dev
# Deploy
pages:
<<: *documentation-base
only:
- dev
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.8.2)
project(MILENA)
include(FindBoost)
# Add FindTBB directory to CMake's module path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
find_package(Boost 1.42 COMPONENTS unit_test_framework REQUIRED)
find_package(Boost 1.42 COMPONENTS program_options REQUIRED)
find_package(FreeImage REQUIRED)
find_package(TBB)
find_package(Qt4)
if(Boost_FOUND)
message(STATUS, "Boost trouvé ${Boost_INCLUDE_DIRS}")
include_directories(${Boost_INCLUDE_DIRS})
# CONFIGURATION OPTIONS
set(MLN_USE_TBB YES CACHE BOOL "Set to NO to disable use of TBB and parallelization")
option(PYLENE_BUILD_BENCHMARKS "Require Google Benchmark library. Set to YES to enable the compilation of benchmarks." YES)
# Proper project creation
set(MLN_INCLUDE_DIR "${PROJECT_SOURCE_DIR}")
if(MSVC OR APPLE)
#dummy static project not linked anywere needed so that a vcxproj is generated with the sources in the solution
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
file(GLOB_RECURSE MLN_SOURCES "${MLN_INCLUDE_DIR}/*.hpp")
add_library(Mln_srcs EXCLUDE_FROM_ALL STATIC ${MLN_SOURCES})
set_target_properties(Mln_srcs PROPERTIES LINKER_LANGUAGE CXX)
# target_compile_features(Mln_srcs PUBLIC cxx_std_14)
target_include_directories(Mln_srcs PUBLIC ${MLN_INCLUDE_DIR})
source_group(TREE ${MLN_INCLUDE_DIR} FILES ${MLN_SOURCES})
endif()
add_library(Mln INTERFACE)
target_include_directories(Mln INTERFACE ${MLN_INCLUDE_DIR})
include_directories(SYSTEM ${Boost_INCLUDE_DIR})
include_directories(${FreeImage_INCLUDE_DIRS})
link_directories(${FreeImage_LIBRARY_DIR})
link_directories(${Boost_LIBRARY_DIRS})
if (TBB_FOUND AND MLN_USE_TBB)
include_directories(${TBB_INCLUDE_DIRS})
link_directories(${TBB_LIBRARY_DIRS})
link_libraries(${TBB_LIBRARIES})
else()
message(FATAL_ERROR, "Boost pas trouvé")
set(MLN_USE_TBB OFF)
add_definitions(-DMLN_NO_TBB)
endif()
if (UNIX AND NOT APPLE)
link_libraries(rt)
endif()
# Compiler configurations
target_compile_features(Mln INTERFACE cxx_std_17)
if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
add_compile_options("-Wextra" "-Wall" "-Werror")
endif()
#### MSVC Compiler Configuration
if (MSVC)
## This hack is for Ninja generator from VS CODE
string(REGEX REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
##
# add source directory to find mln path
include_directories("${PROJECT_SOURCE_DIR}")
add_compile_options("/wd4458" # masque le membre de classe
"/wd4456" # masque la déclaration locale précédente
"/wd5030" # l'attribut 'noinline' n'est pas reconnu
"/wd4457" # la déclaration de '?' masque le paramètre de fonction
)
set(CMAKE_CXX_FLAGS "-W -Wextra -Wall -std=c++0x")
set(CMAKE_CXX_FLAGS_DEBUG "-W -Wextra -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "-W -Wextra -Wall -O3 -DNDEBUG")
add_compile_options("/WX" "/W4" "/wd4244" "/wd4127" "/permissive-")
add_compile_definitions(_SCL_SECURE_NO_WARNINGS)
add_compile_definitions(_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
add_compile_definitions(_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS)
endif(MSVC)
#
# default buidling type
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE "Release")
endif()
# Testing
ENABLE_TESTING()
SUBDIRS(tests)
#### Configuration of the Compiler ###
# set BOOST to use variadic macros
add_definitions(-D BOOST_PP_VARIADICS=1)
# Subdirectories
add_subdirectory(doc EXCLUDE_FROM_ALL)
add_subdirectory(tests EXCLUDE_FROM_ALL)
if (PYLENE_BUILD_BENCHMARKS)
add_subdirectory(bench EXCLUDE_FROM_ALL)
endif()
add_subdirectory(apps EXCLUDE_FROM_ALL)
add_subdirectory(cli EXCLUDE_FROM_ALL)
set(CPACK_SOURCE_GENERATOR "TBZ2")
set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_BINARY_DIR}/;/\\\\.git/;.*~;#.*#")
INCLUDE(CPack)
add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
include(CMakeForceCompiler)
set(CMAKE_CROSSCOMPILING TRUE)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR "i386")
set(CMAKE_FIND_ROOT_PATH "/usr/lib32")
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY "ONLY")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
set(ENV{TBB_ARCH_PLATFORM} "ia32")
\ No newline at end of file
# What is Pylene ?
Pylene is a fork of [Milena](http://www.lrde.epita.fr/olena), an image processing
library targeting genericity and efficiency. Pylene is a modernized version of
Milena with the following objectives:
* Simplicity: both python bindings and simple C++ syntax
* Efficiency: write algorithms in a simple way and run them as if they were written in C. We follow one guideline: zero-cost abstraction.
* Genericity: write algorithms that are able to run on many kind of images with, yet, zero-cost abstraction.
* Interopability: run pylene algorithms on image coming from external libraries (even on your own image type).
# Requirements
Pylene is developed in modern C++. You need a modern C++ compatible compiler:
* GCC 7
* Clang 6
* Microsoft Visual Studio 2017
External dependancies:
[FreeImage](www.freeimage.sourceforge.net) (only for IO)
# Status
* GCC 8.2.0
* Clang 6.0.1
* MSVC 15.8.1
[![pipeline status](https://gitlab.lrde.epita.fr/olena/pylene/badges/dev/pipeline.svg)](https://gitlab.lrde.epita.fr/olena/pylene/commits/dev)
# Documentation
Link to the [C++ Documentation](http://olena.pages.lrde.epita.fr/pylene/)
## Presentations
Other resources (mind the dates, the library probably has changed since then):
* [A Modern C++ Library for Generic and Efficient Image Processing (GTGDMM'18)](https://www.lrde.epita.fr/dload/presentations/2018-06-19-geraud.2018.gtgdmmm.pdf)
project(apps)
add_subdirectory(tests)
add_subdirectory(imview)
add_subdirectory(maxtree_comparison)
add_subdirectory(attributes)
add_subdirectory(simplification)
add_subdirectory(clattice)
add_subdirectory(g2)
add_subdirectory(mumford_shah_on_tree)
add_subdirectory(tosgui)
add_subdirectory(saliency)
add_subdirectory(supervised-gui)
add_subdirectory(hierachical_seg-gui)
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)
#include <mln/accu/accumulators/max.hpp>
#include <mln/core/algorithm/accumulate.hpp>
#include <mln/core/image/image2d.hpp>
#include <mln/morpho/algebraic_filter.hpp>
#include <mln/morpho/component_tree/accumulate.hpp>
#include <mln/morpho/component_tree/filtering.hpp>
#include <mln/morpho/component_tree/io.hpp>
#include <mln/morpho/component_tree/reconstruction.hpp>
#include "cMSER.hpp"
#include <apps/tos/topology.hpp>
#include <mln/accu/accumulators/accu_if.hpp>
#include <mln/accu/accumulators/count.hpp>
#include <mln/morpho/datastruct/checktree.hpp>
#include <mln/morpho/extinction.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₁:\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]);
float threshold1 = std::atof(argv[5]);
float threshold2 = std::atof(argv[6]);
const char* out_path = argv[7];
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]);
}
#ifndef APPS_ATTRIBUTES_MSER_HPP
#define APPS_ATTRIBUTES_MSER_HPP
#include <mln/core/image/image2d.hpp>
#include <mln/core/trace.hpp>
#include <cstdlib>
#include <vector>
enum eMSER_attribute
{
MSER_DIFF,
MSER_RATIO,
MSER_NORM
};
///
///
/// \param f
/// \param K
/// \param parent
/// \param S
/// \param eps Threshold value to select the ancestor with wich the delta area will be computed
/// \param amser Attribute type:
/// * MSER_DIFF for the delta area, \$ f(q) - f(p) \$,
/// * MSER_RATIO for the ratio of the areas (between 0 and 1) \$ f(p) / f(q) \$
/// * MSER_NORM for the delta area but normalized with the current area. \$ \frac{(f(q) - f(p))}{f(p)} \$
/// \param dist The distance function between two elements V (defaults to the absolute value)
/// \return
///
template <typename V, typename T, class Distance>
mln::image2d<float> compute_MSER_attribute(const mln::image2d<V>& f, const mln::image2d<T>& K,
const mln::image2d<unsigned>& parent, const std::vector<unsigned>& S,
typename std::result_of<Distance(V, V)>::type eps,
eMSER_attribute amser = MSER_DIFF, Distance dist = Distance());
template <typename V, typename T, typename T2>
mln::image2d<float> compute_MSER_attribute(const mln::image2d<V>& f, const mln::image2d<T>& K,
const mln::image2d<unsigned>& parent, const std::vector<unsigned>& S, T2 eps,
eMSER_attribute amser = MSER_DIFF);
/********************/
/** Implementation **/
/********************/
template <typename V, typename T, typename T2>
mln::image2d<float> compute_MSER_attribute(const mln::image2d<V>& f, const mln::image2d<T>& K,
const mln::image2d<unsigned>& parent, const std::vector<unsigned>& S, T2 eps,
eMSER_attribute amser)
{
auto fun = [](V x, V y) { return std::abs(x - y); };
return compute_MSER_attribute(f, K, parent, S, eps, amser, fun);
}
template <typename V, typename T, class Distance>
mln::image2d<float> compute_MSER_attribute(const mln::image2d<V>& f, const mln::image2d<T>& K,
const mln::image2d<unsigned>& parent, const std::vector<unsigned>& S,
typename std::result_of<Distance(V, V)>::type eps, eMSER_attribute amser,
Distance dist)
{
using namespace mln;
mln_entering("compute_MSER_attribute");
// compute area
image2d<unsigned> area;
image2d<unsigned> maxarea; // the max area of the children in Δh
{
resize(area, K).init(0);
resize(maxarea, K).init((unsigned)0);
area[S[0]] = 1;
for (int i = S.size() - 1; i > 0; --i)
{
unsigned x = S[i];
++area[x];
area[parent[x]] += area[x];
if (K[x] != K[parent[x]])
{ // canonical propagate to parent
unsigned y = parent[x];
unsigned a = area[x];
V v = f[x];
while (parent[y] != y and dist(v, f[y]) < eps)
{
y = parent[y];
}
maxarea[y] = std::max(a, maxarea[y]);
}
}
}
// MSER attr
image2d<float> mser;
resize(mser, K);
{
// mser[S[0]] = 0; // FIXME !!
for (int i = S.size() - 1; i > 0; --i)
{
unsigned x = S[i];
if (K[parent[x]] == K[x]) // not a canonical element
continue;
V v = f[x];
unsigned y = parent[x];
unsigned smally = parent[x];
while (dist(v, f[y]) < eps and y != parent[y])
{
if (dist(v, f[y]) < (eps / 3))
smally = y;
y = parent[y];
}
switch (amser)
{
case MSER_DIFF:
mser[x] = area[y] - maxarea[x];
break;
case MSER_RATIO:
mser[x] = //(float) area[x] / area[y]; break;
// mser[x] = std::max(10.0f, (float) (area[y] - maxarea[x]) / area[x]); break;
mser[x] = (float)(area[smally] - area[x]) / (area[y] - area[x]);
break;
case MSER_NORM:
mser[x] = (float)(area[y] - maxarea[x]) / maxarea[x];
break;
}
}
if (amser == MSER_DIFF or amser == MSER_NORM)
mser[S[0]] = 0;
else
mser[S[0]] = 1;
}
mln_exiting();
return mser;
}
#endif // ! APPS_ATTRIBUTES_MSER_HPP
#include "MSERArgparser.hpp"
#include <boost/program_options.hpp>
MSERArgParser::MSERArgParser() : m_desc("MSER options")
{
namespace po = boost::program_options;
m_desc.add_options()("mode,m", po::value<std::string>()->default_value("MSER_RATIO"),