
* green/mln : New dev directory. * green/mln/clustering : New directory. * green/mln/clustering/k_mean.hh : New k_mean source location. * green/tests : New unitary test directory. * green/tests/clustering : New directory. * green/tests/clustering/k_mean : New k_mean test directory. * green/tests/clustering/k_mean/Makefile.am: New makefle. * green/tests/clustering/k_mean/k_mean.cc : New unit tests location. --- trunk/milena/sandbox/ChangeLog | 13 + .../milena/sandbox/green/mln/clustering/k_mean.hh | 672 ++++++++++++++++++++ .../green/tests/clustering/k_mean/Makefile.am | 148 +++++ .../green/tests/clustering/k_mean/k_mean.cc | 450 +++++++++++++ 4 files changed, 1283 insertions(+), 0 deletions(-) create mode 100644 trunk/milena/sandbox/green/mln/clustering/k_mean.hh create mode 100644 trunk/milena/sandbox/green/tests/clustering/k_mean/Makefile.am create mode 100644 trunk/milena/sandbox/green/tests/clustering/k_mean/k_mean.cc diff --git a/trunk/milena/sandbox/ChangeLog b/trunk/milena/sandbox/ChangeLog index ed708ca..b0d54dd 100644 --- a/trunk/milena/sandbox/ChangeLog +++ b/trunk/milena/sandbox/ChangeLog @@ -1,3 +1,16 @@ +2009-09-07 Yann Jacquelet <jacquelet@lrde.epita.fr> + + Do some refactoring for best future olena integration. + + * green/mln : New dev directory. + * green/mln/clustering : New directory. + * green/mln/clustering/k_mean.hh : New k_mean source location. + * green/tests : New unitary test directory. + * green/tests/clustering : New directory. + * green/tests/clustering/k_mean : New k_mean test directory. + * green/tests/clustering/k_mean/Makefile.am: New makefle. + * green/tests/clustering/k_mean/k_mean.cc : New unit tests location. + 2009-09-07 Fabien Freling <fabien.freling@lrde.epita.fr> Fix output dimensions. diff --git a/trunk/milena/sandbox/green/mln/clustering/k_mean.hh b/trunk/milena/sandbox/green/mln/clustering/k_mean.hh new file mode 100644 index 0000000..db3f34c --- /dev/null +++ b/trunk/milena/sandbox/green/mln/clustering/k_mean.hh @@ -0,0 +1,672 @@ +// Copyright (C) 2007 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef MLN_CLUSTERING_K_MEAN_HH +#define MLN_CLUSTERING_K_MEAN_HH + +/// \file +/// +/// \brief Implements the K MEAN algorithm. +/// DO +/// - ASSIGNEMENT STEP +/// - UPDATE STEP +/// LOOP UNTIL CONVERGENCE +/// +/// ASSIGNEMENT STEP +/// BEGIN +/// - COMPUTE THE DISTANCE MATRIX FROM POINTS TO CENTERS +/// - COMPUTE GROUP MATRIX WHERE EACH POINT IS ASSIGNED TO NEAREST CENTER +/// END +/// +/// UPDATE STEP +/// BEGIN +/// - COMPUTE THE MEAN OF THE GROUPS WHICH ARE THE NEW CENTERS +/// END + +#include <limits.h> +#include <iostream> +#include <mln/trace/entering.hh> +#include <mln/trace/exiting.hh> + +#include <mln/core/contract.hh> +#include <mln/trait/value_.hh> + +#include <mln/algebra/mat.hh> +#include <mln/algebra/vec.hh> + +#include <mln/math/min.hh> +#include <mln/norm/l2.hh> + +#include <mln/core/concept/image.hh> +#include <mln/core/macros.hh> + +namespace mln +{ + + namespace clustering + { + + // Forward declaration. + template <unsigned n, unsigned k, unsigned p, typename T> + struct k_mean; + + } // end of namespace mln::clustering + + namespace clustering + { + + // + // PARAM : DIM VECTOR, NUMBER OF CLASSES + // INPUT : MATRIX 1..N (SAMPLE) + // WORK + // + + // n is the size of the sample. + // k is the number of centers. + // p is the number of attributes for a point. + // T is the type for computations. + template <unsigned n, unsigned k, unsigned p, typename T> + struct k_mean + { + + //------------------------------------------------------------------------ + // Constructor and destructor + //------------------------------------------------------------------------ + + k_mean(); + ~k_mean(); + + + //------------------------------------------------------------------------ + // Accessors + //------------------------------------------------------------------------ + + algebra::mat<n, p, T>& get_point(); + algebra::mat<k, p, T>& get_center(); + algebra::mat<n, k, T>& get_distance(); + algebra::mat<n, k, T>& get_group(); + algebra::vec<k, T>& get_variance(); + + + //------------------------------------------------------------------------ + // Tools + //------------------------------------------------------------------------ + + + /// \brief Return the euclidian distance beetween vector x and vector y. + /// + /// Compute the f$\sqrt(\sum_{i=0}^P ((x_i - y_i)^2)$f + /// + /// \param[in] x a vector of dimension 1xP. + /// \param[in] y a second vector of dimension 1xP. + /// \return the distance between x and y. + + mln_sum_product(T,T) euclidian_distance(const algebra::vec<p,T>& x, + const algebra::vec<p,T>& y) const; + + /// \brief Return the ith column. + /// + /// \param[in] m a matrix of dimension {N or K}xP. + /// \return the ith column as a vector of dimension 1xP. + + + /// \brief Return the ith column. + /// + /// \param[in] m a matrix of dimension {N or K}xP. + /// \return the ith column as a vector of dimension 1xP. + template <unsigned q, unsigned r> + algebra::vec<q,T> col(const algebra::mat<r, q, T>& m, + const unsigned _col) const; + + template <unsigned q, unsigned r> + algebra::vec<q,T>* ptr_col(const algebra::mat<r, q, T>& m, + const unsigned _col) const; + + template <unsigned q, unsigned r> + algebra::vec<r,T> row(const algebra::mat<r, q, T>& m, + const unsigned _row) const; + + template <unsigned q, unsigned r> + void div_col(algebra::mat<r, q, T>& m, + const unsigned _col, + const T value); + + template <unsigned q, unsigned r> + mln_sum(T) sum_row(const algebra::mat<r, q, T>& m, + const unsigned _row) const; + + template <unsigned q, typename M> + M min_col(const algebra::vec<q, M>& x) const; + + + //------------------------------------------------------------------------ + // Initializations of points and centers + //------------------------------------------------------------------------ + + /// \brief Initialize the matrix _point with the pixels of an image. + /// + /// \param[in] input an image which contains the data points. + template <typename I> + void init_point(const Image<I>& _input); + void init_center(); + + + //------------------------------------------------------------------------ + // Computations of distance, group, center, within variance + //------------------------------------------------------------------------ + + void update_distance(); + void update_group(); + void update_center(); + T update_variance(); + + + //------------------------------------------------------------------------ + // Main loop + //------------------------------------------------------------------------ + + template <typename I> + void loop(const Image<I>& _input); + //void loop(); + + private: + static const unsigned watch_dog = 100; + + /// \brief _points contains the concatenation of every data points. + /// + /// One data point is a vector 1xP where P is the number of attributes. + /// _points is a matrix NxP where N is the number of data points. + algebra::mat<n, p, T>* _point; + + /// \brief _distance contains all the euclidian distances between points + /// and the centers. + /// + /// _distance is a matrix NxK where N is the number of data points and + /// K the number of centers. + algebra::mat<n, k, mln_sum_product(T,T)>* _distance; + + /// \brief _group contains the point assignement to one center. + /// + /// _group is a boolean matrix NxK where N is the number of data points + /// and K the number of centers. + algebra::mat<n, k, T>* _group; + + /// \brief _center contains the coordonnate of the K centers. + /// + /// _center is a matrix KxP where K is the number of centers and P is the + /// number of attributes. + algebra::mat<k, p, T>* _center; + + algebra::vec<k, T>* _variance; + }; + +#ifndef MLN_INCLUDE_ONLY + + //-------------------------------------------------------------------------- + // Constructor and destructor + //-------------------------------------------------------------------------- + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + k_mean<n,k,p,T>::k_mean() + { + trace::entering("mln::clustering::k_mean::k_mean"); + + _point = new algebra::mat<n, p, T>(); + _distance = new algebra::mat<n, k, mln_sum_product(T,T)>(); + _group = new algebra::mat<n, k, T>(); + _center = new algebra::mat<k, p, T>(); + _variance = new algebra::vec<k,T>(); + + mln_postcondition(_point != 0); + mln_postcondition(_distance != 0); + mln_postcondition(_group != 0); + mln_postcondition(_center != 0); + mln_postcondition(_variance != 0); + + trace::exiting("mln::clustering::k_mean::k_mean"); + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + k_mean<n,k,p,T>::~k_mean() + { + trace::entering("mln::clustering::k_mean::~k_mean"); + + delete _point; + delete _distance; + delete _group; + delete _center; + delete _variance; + + trace::exiting("mln::clustering::k_mean::~k_mean"); + } + + //-------------------------------------------------------------------------- + // Accessors + //-------------------------------------------------------------------------- + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + algebra::mat<n, p, T>& + k_mean<n,k,p,T>::get_point() + { + trace::entering("mln::clustering::k_mean::get_point"); + trace::exiting("mln::clustering::k_mean::get_point"); + + return *_point; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + algebra::mat<k, p, T>& + k_mean<n,k,p,T>::get_center() + { + trace::entering("mln::clustering::k_mean::get_center"); + trace::exiting("mln::clustering::k_mean::get_center"); + + return *_center; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + algebra::mat<n, k, T>& + k_mean<n,k,p,T>::get_distance() + { + trace::entering("mln::clustering::k_mean::get_distance"); + trace::exiting("mln::clustering::k_mean::get_distance"); + + return *_distance; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + algebra::mat<n, k, T>& + k_mean<n,k,p,T>::get_group() + { + trace::entering("mln::clustering::k_mean::get_group"); + trace::exiting("mln::clustering::k_mean::get_group"); + + return *_group; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + algebra::vec<k, T>& + k_mean<n,k,p,T>::get_variance() + { + trace::entering("mln::clustering::k_mean::get_variance"); + trace::exiting("mln::clustering::k_mean::get_variance"); + + return *_variance; + } + + //-------------------------------------------------------------------------- + // Tools + //-------------------------------------------------------------------------- + + template <unsigned n, unsigned k, unsigned p, typename T> + template <unsigned q, unsigned r> + inline + algebra::vec<r, T> k_mean<n,k,p,T>::row(const algebra::mat<r, q, T>& m, + const unsigned _row) const + { + trace::entering("mln::clustering::k_mean::row"); + mln_precondition(q > _row); + + algebra::vec<r, T> result; + + for (unsigned i = 0; i < r; ++i) + result[i] = m(i, _row); + + trace::exiting("mln::clustering::k_mean::row"); + return result; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + template <unsigned q, unsigned r> + inline + algebra::vec<q,T> k_mean<n,k,p,T>::col(const algebra::mat<r, q, T>& m, + const unsigned _col) const + { + trace::entering("mln::clustering::k_mean::col"); + mln_precondition(r > _col); + + algebra::vec<q, T> result; + + for (unsigned j = 0; j < q; ++j) + result[j] = m(_col, j); + + trace::exiting("mln::clustering::k_mean::col"); + return result; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + template <unsigned q, unsigned r> + inline + algebra::vec<q,T>* k_mean<n,k,p,T>::ptr_col(const algebra::mat<r, q, T>& m, + const unsigned _col) const + { + trace::entering("mln::clustering::k_mean::ptr_col"); + mln_precondition(r > _col); + + algebra::vec<q, T> *result = new algebra::vec<q, T>(); + + for (unsigned j = 0; j < q; ++j) + (*result)[j] = m(_col, j); + + trace::exiting("mln::clustering::k_mean::ptr_col"); + return result; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + template <unsigned q, unsigned r> + inline + mln_sum(T) k_mean<n,k,p,T>::sum_row(const algebra::mat<r, q, T>& m, + const unsigned _row) const + { + trace::entering("mln::clustering::k_mean::sum_row"); + mln_precondition(q > _row); + + mln_sum(T) result; + + for (unsigned j = 0; j < r; ++j) + result += m(j, _row); + + trace::exiting("mln::clustering::k_mean::sum_row"); + return result; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + template <unsigned q, unsigned r> + inline + void k_mean<n,k,p,T>::div_col(algebra::mat<r, q, T>& m, + const unsigned _col, + const T value) + { + trace::entering("mln::clustering::k_mean::div_col"); + mln_precondition(r > _col); + + for (unsigned j = 0; j < q; ++j) + m(_col, j) /= value; + + trace::exiting("mln::clustering::k_mean::div_col"); + } + + template <unsigned n, unsigned k, unsigned p, typename T> + template <unsigned q, typename M> + inline + M k_mean<n,k,p,T>::min_col(const algebra::vec<q, M>& x) const + { + trace::entering("mln::clustering::k_mean<n,k,p,T>::min_col"); + + M result = x[0]; + + for (unsigned i = 1; i < q; ++i) + result = math::min(result, x[i]); + + trace::exiting("mln::clustering::k_mean<n,k,p,T>::min_col"); + return result; + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + mln_sum_product(T,T) + k_mean<n,k,p,T>::euclidian_distance(const algebra::vec<p, T>& x, + const algebra::vec<p, T>& y) const + { + trace::entering("mln::clustering::k_mean::euclidian_distance"); + //vec<p, double> tmp = norm::l2(x - y); + const algebra::vec<p, double> tmp = (x - y); + mln_sum_product(T,T) result = tmp*tmp; + + trace::exiting("mln::clustering::k_mean::euclidian_distance"); + return result; + } + + + //-------------------------------------------------------------------------- + // Initializations of points and centers + //-------------------------------------------------------------------------- + + template <unsigned n, unsigned k, unsigned p, typename T> + template <typename I> + inline + void k_mean<n,k,p,T>::init_point(const Image<I>& _input) + { + trace::entering("mln::clustering::k_mean<n,k,p,T>::init"); + + const I& input = exact(_input); + algebra::mat<n, p, T>& point = *_point; + + mln_precondition(input.is_valid()); + mln_precondition(n == input.nrows() * input.ncols()); + //mln_precondition(n == input.nrows()); + //mln_precondition(n == input.nrows() * input.ncols() * input.nslices()); + + mln_piter(I) pi(input.domain()); + unsigned i = -1; + + for_all(pi) + { + //std::cout << pi << std::endl; + + ++i; + for (unsigned j = 0; j < p; ++j) + { + + point(i,j) = input(pi).comp(j); + //point(i,j) = input(pi); + } + } + + trace::exiting("mln::clustering::k_mean<n,k,p,T>::init"); + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + void k_mean<n,k,p,T>::init_center() + { + trace::entering("mln::clustering::k_mean<n,k,p,T>::init_center"); + + algebra::mat<n, p, T>& point = *_point; + algebra::mat<k, p, T>& center = *_center; + + // A random point is choosen to be the initial value for a center. + for (unsigned i = 0; i < k; ++i) + { + unsigned random = rand() % n; + + for (unsigned j = 0; j < p; ++j) + { + center(i,j) = point(random, j); + } + + //std::cout << "center(" << i << ")" << col(center, i) << std::endl; + } + + trace::exiting("mln::clustering::k_mean<n,k,p,T>::init_center"); + } + + + //-------------------------------------------------------------------------- + // Computations of distance, group, center, within variance + //-------------------------------------------------------------------------- + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + void k_mean<n,k,p,T>::update_distance() + { + trace::entering("mln::clustering::k_mean::update_distance"); + //mln::trace::quiet = true; + + // the result is stored in _distance matrix. + algebra::mat<n, p, T>& point = *_point; + algebra::mat<n, k, T>& distance = *_distance; + algebra::mat<k, p, T>& center = *_center; + + // for all points in the data cloud. + for (unsigned i = 0; i < n; ++i) + { + // for every center + for (unsigned j = 0; j < k; ++j) + { + distance(i,j) = euclidian_distance(col(point,i),col(center,j)); + } + } + //mln::trace::quiet = false; + trace::exiting("mln::clustering::k_mean::update_distance"); + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + void k_mean<n,k,p,T>::update_group() + { + trace::entering("mln::clustering::k_mean<n,k,p,T>::update_group"); + + algebra::mat<n, k, mln_sum_product(T,T)>& distance = *_distance; + algebra::mat<n, k, T>& group = *_group; + + // for each point + for (unsigned i = 0; i < n; ++i) + { + // tell us the minimum distance from a specific point to a group + mln_sum_product(T,T) min_dist = min_col(col(distance, i)); + + // for each center + for (unsigned j = 0; j < k; ++j) + group(i,j) = (min_dist == distance(i,j))? 1.0 : 0.0; + } + + // mln_postcondition(sum(col(distance(i,j)) == 1) Vi + trace::exiting("mln::clustering::k_mean<n,k,p,T>::update_group"); + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + void k_mean<n,k,p,T>::update_center() + { + trace::entering("mln::clustering::k_mean<n,k,p,T>::update_center"); + + algebra::mat<n, p, T>& point = *_point; + algebra::mat<k, p, T>& center = *_center; + algebra::mat<n, k, T>& group = *_group; + + // center = (group.t() * point); + + for (unsigned i = 0; i < k; ++i) + { + for (unsigned j = 0; j < p; ++j) + { + center(i,j) = literal::zero; + + for (unsigned l = 0; l < n; ++l) + center(i,j) += group(l,i) * point(l,j); + } + } + + for (unsigned i = 0; i < k; ++i) + div_col(center, i, sum_row(group, i)); + + trace::exiting("mln::clustering::k_mean<n,k,p,T>::update_center"); + } + + template <unsigned n, unsigned k, unsigned p, typename T> + inline + T k_mean<n,k,p,T>::update_variance() + { + trace::entering("mln::clustering::k_mean<n,k,p,T>::update_variance"); + + algebra::vec<k, T>& variance = *_variance; + algebra::mat<n, k, T>& distance = *_distance; + algebra::mat<n, k, T>& group = *_group; + T result = literal::zero; + + // for every group + for (unsigned i = 0; i < k; ++i) + { + variance[i] = row(group, i) * row(distance, i); + result += variance[i]; + } + + trace::exiting("mln::clustering::k_mean<n,k,p,T>::update_variance"); + return result; + } + + + //-------------------------------------------------------------------------- + // Main loop + //-------------------------------------------------------------------------- + + template <unsigned n, unsigned k, unsigned p, typename T> + template <typename I> + inline + void k_mean<n,k,p,T>::loop(const Image<I>& _input) + //void k_mean<n,k,p,T>::loop() + { + trace::entering("mln::clustering::k_mean<n,k,p,T>::loop"); + + T within_variance = INT_MAX-1; + T old_variance = INT_MAX; + + init_point(_input); + init_center(); + + std::cout << "LOOP" << std::endl; + + for (unsigned i = 0; i < watch_dog && within_variance < old_variance; ++i) + { + update_distance(); + + std::cout << "DISTANCE DONE" << std::endl; + + update_group(); + + std::cout << "GROUP DONE" << std::endl; + + update_center(); + + std::cout << "UPDATE CENTER DONE" << std::endl; + + old_variance = within_variance; + within_variance = update_variance(); + + std::cout << i << " : " << within_variance << std::endl; + } + + trace::exiting("mln::clustering::k_mean<n,k,p,T>::loop"); + } + + + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace mln::clustering + +} // end of namespace mln::clustering + +#endif // ! MLN_CLUSTERING_K_MEAN_HH diff --git a/trunk/milena/sandbox/green/tests/clustering/k_mean/Makefile.am b/trunk/milena/sandbox/green/tests/clustering/k_mean/Makefile.am new file mode 100644 index 0000000..5f00678 --- /dev/null +++ b/trunk/milena/sandbox/green/tests/clustering/k_mean/Makefile.am @@ -0,0 +1,148 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green +CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/tests +BUILD__PATTERN= green/build/tests + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD) +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD) +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: print clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/trunk/milena/sandbox/green/tests/clustering/k_mean/k_mean.cc b/trunk/milena/sandbox/green/tests/clustering/k_mean/k_mean.cc new file mode 100644 index 0000000..27751fd --- /dev/null +++ b/trunk/milena/sandbox/green/tests/clustering/k_mean/k_mean.cc @@ -0,0 +1,450 @@ +// UNARY TESTS ON K_MEAN + +#include <mln/clustering/k_mean.hh> + +#include <iostream> + +#include <mln/pw/value.hh> + +#include <mln/value/int_u8.hh> +#include <mln/value/rgb8.hh> + +#include <mln/literal/colors.hh> + +#include <mln/algebra/vec.hh> +#include <mln/algebra/mat.hh> + +#include <mln/core/macros.hh> +#include <mln/core/contract.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/data/transform.hh> + +#include <mln/trait/value/print.hh> +#include <mln/trait/image/print.hh> + +#define SIZE_IMAGE 512 +#define SIZE_SAMPLE1 (512*512) +#define SIZE_SAMPLE2 4 +#define NB_CENTER 2 +#define DIM_POINT 3 +#define TYPE_POINT double +#define MAT_POINT1 mln::algebra::mat<SIZE_SAMPLE1, DIM_POINT, TYPE_POINT> +#define MAT_POINT2 mln::algebra::mat<SIZE_SAMPLE2, DIM_POINT, TYPE_POINT> +#define MAT_CENTER mln::algebra::mat<NB_CENTER, DIM_POINT, TYPE_POINT> +#define MAT_DISTANCE1 mln::algebra::mat<SIZE_SAMPLE1, NB_CENTER, TYPE_POINT> +#define MAT_DISTANCE2 mln::algebra::mat<SIZE_SAMPLE2, NB_CENTER, TYPE_POINT> +#define MAT_GROUP1 mln::algebra::mat<SIZE_SAMPLE1, NB_CENTER, TYPE_POINT> +#define MAT_GROUP2 mln::algebra::mat<SIZE_SAMPLE2, NB_CENTER, TYPE_POINT> +#define VEC_VAR mln::algebra::vec<NB_CENTER, TYPE_POINT> + + +void test_instantiation() +{ + mln::clustering::k_mean<SIZE_SAMPLE2,NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + // test the compilation + + std::cout << "test instantiation : ok" << std::endl; +} + +struct rgb8_to_4colors : mln::Function_v2v<rgb8_to_4colors> +{ + typedef mln::value::rgb8 result; + + mln::value::rgb8 operator()(const mln::value::rgb8& color) const + { + mln::value::rgb8 result; + unsigned hash = (color.red() + color.green() + color.blue()) % 4; + + switch (hash) + { + case 0: result = mln::literal::lime; break; + case 1: result = mln::literal::brown; break; + case 2: result = mln::literal::teal; break; + case 3: result = mln::literal::purple; break; + } + + return result; + } +}; + + +void print_color(const mln::value::rgb8& color) +{ + std::cout << "{r=" << color.red() << ", "; + std::cout << "g=" << color.green() << ", "; + std::cout << "b=" << color.blue() << "}" << std::endl; +} + +void fill_image_with_4colors(mln::image2d<mln::value::rgb8>& img) +{ + const mln::value::rgb8 lime = mln::literal::lime; + const mln::value::rgb8 brown = mln::literal::brown; + const mln::value::rgb8 teal = mln::literal::teal; + const mln::value::rgb8 purple = mln::literal::purple; + + img = mln::data::transform(img, rgb8_to_4colors()); + + //print_color(lime); + //print_color(brown); + //print_color(teal); + //print_color(purple); +} + +bool is_equivalent(const mln::image2d<mln::value::rgb8>& img, + const MAT_POINT1& point) +{ + mln_piter_(mln::image2d<mln::value::rgb8>) pi(img.domain()); + bool result = true; + unsigned index = -1; + + for_all(pi) + { + bool test = true; + + ++index; + + test = test && (point(index,0) == img(pi).red()); + test = test && (point(index,1) == img(pi).green()); + test = test && (point(index,2) == img(pi).blue()); + + if (!test) + { + std::cout << pi; + std::cout << "{r=" << img(pi).red() << ", "; + std::cout << "g=" << img(pi).green() << ", "; + std::cout << "b=" << img(pi).blue() << "}"; + std::cout << index; + std::cout << "[r=" << point(index,0) << ", "; + std::cout << "g=" << point(index,1) << ", "; + std::cout << "b=" << point(index,2) << "]" << std::endl; + + mln_assertion(test); + } + + result &= test; + } + + return result; +} + +void test_init_point() +{ + typedef mln::value::rgb8 rgb8; + mln::image2d<rgb8> img_ref; + + mln::clustering::k_mean<SIZE_SAMPLE1,NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + mln::io::ppm::load(img_ref, "/usr/local/share/olena/images/lena.ppm"); + //mln::io::ppm::save(img_ref, "verif.ppm"); + + fill_image_with_4colors(img_ref); + kmean.init_point(img_ref); + + mln_assertion(true == is_equivalent(img_ref, kmean.get_point())); + + std::cout << "Test init point : ok" << std::endl; +} + +void set_point(MAT_POINT2& point, + const unsigned index, + const mln::value::rgb8& color) +{ + point(index,0) = color.red(); + point(index,1) = color.green(); + point(index,2) = color.blue(); +} + +void fake_init_point(MAT_POINT2& point, + const mln::value::rgb8& point1, + const mln::value::rgb8& point2, + const mln::value::rgb8& point3, + const mln::value::rgb8& point4) +{ + set_point(point, 0, point1); + set_point(point, 1, point2); + set_point(point, 2, point3); + set_point(point, 3, point4); +} + +bool is_equal(const mln::value::rgb8& ref, + const MAT_CENTER& center, + const unsigned i) +{ + bool result = true; + + result = result && (center(i, 0) - ref.red() < 1.0); + result = result && (center(i, 1) - ref.green() < 1.0); + result = result && (center(i, 2) - ref.blue() < 1.0); + + return result; +} + +void test_init_center() +{ + mln::clustering::k_mean<SIZE_SAMPLE2, NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + const mln::value::rgb8 lime = mln::literal::lime; + const mln::value::rgb8 brown = mln::literal::brown; + const mln::value::rgb8 teal = mln::literal::teal; + const mln::value::rgb8 purple = mln::literal::purple; + + fake_init_point(kmean.get_point(), lime, brown, teal, purple); + kmean.init_center(); + + mln_assertion(is_equal(lime, kmean.get_center(), 0) || + is_equal(brown, kmean.get_center(), 0) || + is_equal(teal, kmean.get_center(), 0) || + is_equal(purple, kmean.get_center(), 0)); + + mln_assertion(is_equal(lime, kmean.get_center(), 1) || + is_equal(brown, kmean.get_center(), 1) || + is_equal(teal, kmean.get_center(), 1) || + is_equal(purple, kmean.get_center(), 1)); + + std::cout << "Test init center : ok" << std::endl; +} + +void set_center(MAT_CENTER& center, + const unsigned index, + const mln::value::rgb8& color) +{ + center(index,0) = color.red(); + center(index,1) = color.green(); + center(index,2) = color.blue(); +} + +void fake_init_center(MAT_CENTER& center, + const mln::value::rgb8 center1, + const mln::value::rgb8 center2) +{ + + set_center(center, 0, center1); + set_center(center, 1, center2); +} + + +double dist(mln::value::rgb8 color1, mln::value::rgb8 color2) +{ + double red = color1.red() - color2.red(); + double green = color1.green() - color2.green(); + double blue = color1.blue() - color2.blue(); + double result= red * red + green * green + blue * blue; + + return result; +} + +void test_update_distance() +{ + mln::clustering::k_mean<SIZE_SAMPLE2, NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + const mln::value::rgb8 lime = mln::literal::lime; + const mln::value::rgb8 brown = mln::literal::brown; + const mln::value::rgb8 teal = mln::literal::teal; + const mln::value::rgb8 purple = mln::literal::purple; + const mln::value::rgb8 c1 = lime; + const mln::value::rgb8 c2 = purple; + const MAT_DISTANCE2& dist_v = kmean.get_distance(); + + fake_init_point(kmean.get_point(), lime, brown, teal, purple); + fake_init_center(kmean.get_center(), c1, c2); + kmean.update_distance(); + + mln_assertion(dist(lime, c1) == dist_v(0,0)); + mln_assertion(dist(lime, c2) == dist_v(0,1)); + mln_assertion(dist(brown, c1) == dist_v(1,0)); + mln_assertion(dist(brown, c2) == dist_v(1,1)); + mln_assertion(dist(teal, c1) == dist_v(2,0)); + mln_assertion(dist(teal, c2) == dist_v(2,1)); + mln_assertion(dist(purple, c1) == dist_v(3,0)); + mln_assertion(dist(purple, c2) == dist_v(3,1)); + + std::cout << "Test update distance : ok" << std::endl; +} + +void set_distance(MAT_DISTANCE2& distance, + const unsigned index, + const double d1, + const double d2) +{ + distance(index,0) = d1; + distance(index,1) = d2; +} + +void fake_update_distance(MAT_DISTANCE2& distance, + const mln::value::rgb8& point1, + const mln::value::rgb8& point2, + const mln::value::rgb8& point3, + const mln::value::rgb8& point4, + const mln::value::rgb8& center1, + const mln::value::rgb8& center2) +{ + set_distance(distance, 0, dist(point1, center1), dist(point1, center2)); + set_distance(distance, 1, dist(point2, center1), dist(point2, center2)); + set_distance(distance, 2, dist(point3, center1), dist(point3, center2)); + set_distance(distance, 3, dist(point4, center1), dist(point4, center2)); + + /* + for (int i = 0; i < SIZE_SAMPLE2; ++i) + for (int j = 0; j < NB_CENTER; ++j) + std::cout << "d(" << i << "," << j << ") = " << distance(i,j) <<std::endl; + */ +} + +void test_update_group() +{ + mln::clustering::k_mean<SIZE_SAMPLE2, NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + const mln::value::rgb8 lime = mln::literal::lime; + const mln::value::rgb8 brown = mln::literal::brown; + const mln::value::rgb8 teal = mln::literal::teal; + const mln::value::rgb8 purple = mln::literal::purple; + const mln::value::rgb8 c1 = lime; + const mln::value::rgb8 c2 = purple; + const unsigned point1_min= 0; // lime near lime (c1) + const unsigned point2_min= 1; // brown near purple (c2) + const unsigned point3_min= 1; // teal near purple (c2) + const unsigned point4_min= 1; // purple near purple (c2) + const MAT_GROUP2& group = kmean.get_group(); + + fake_init_point(kmean.get_point(), lime, brown, teal, purple); + fake_init_center(kmean.get_center(), c1, c2); + fake_update_distance(kmean.get_distance(), lime, brown, teal, purple, c1, c2); + kmean.update_group(); + + mln_assertion(0.0 == group(0, 1 - point1_min)); + mln_assertion(1.0 == group(0, point1_min)); + mln_assertion(0.0 == group(1, 1 - point2_min)); + mln_assertion(1.0 == group(1, point2_min)); + mln_assertion(0.0 == group(2, 1 - point3_min)); + mln_assertion(1.0 == group(2, point3_min)); + mln_assertion(0.0 == group(3, 1 - point4_min)); + mln_assertion(1.0 == group(3, point4_min)); + + std::cout << "Test update group : ok" << std::endl; +} + +void set_group(MAT_GROUP2& group, + const unsigned index, + const unsigned min) +{ + group(index, min) = 1.0; + group(index, 1-min) = 0.0; +} + + +void fake_update_group(MAT_GROUP2& group, + const unsigned& point1_min, + const unsigned& point2_min, + const unsigned& point3_min, + const unsigned& point4_min) +{ + set_group(group, 0, point1_min); + set_group(group, 1, point2_min); + set_group(group, 2, point3_min); + set_group(group, 3, point4_min); +} + +void test_update_center() +{ + mln::clustering::k_mean<SIZE_SAMPLE2, NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + const mln::value::rgb8 lime = mln::literal::lime; + const mln::value::rgb8 brown = mln::literal::brown; + const mln::value::rgb8 teal = mln::literal::teal; + const mln::value::rgb8 purple = mln::literal::purple; + const mln::value::rgb8 c1 = lime; + const mln::value::rgb8 c2 = purple; + const unsigned pt1_min = 0; // lime near lime (c1) + const unsigned pt2_min = 1; // brown near purple (c2) + const unsigned pt3_min = 1; // teal near purple (c2) + const unsigned pt4_min = 1; // purple near purple (c2) + const mln::value::rgb8 mean_c1 = lime; + const mln::value::rgb8 mean_c2 = (brown+teal+purple)/3; + + fake_init_point(kmean.get_point(), lime, brown, teal, purple); + fake_init_center(kmean.get_center(), c1, c2); + fake_update_distance(kmean.get_distance(), lime, brown, teal, purple, c1, c2); + fake_update_group(kmean.get_group(), pt1_min, pt2_min, pt3_min, pt4_min); + kmean.update_center(); + + mln_assertion(is_equal(mean_c1, kmean.get_center(), 0)); + mln_assertion(is_equal(mean_c2, kmean.get_center(), 1)); + + std::cout << "Test update center : ok" << std::endl; +} + +void test_update_variance() +{ + mln::clustering::k_mean<SIZE_SAMPLE2, NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + const mln::value::rgb8 lime = mln::literal::lime; + const mln::value::rgb8 brown = mln::literal::brown; + const mln::value::rgb8 teal = mln::literal::teal; + const mln::value::rgb8 purple = mln::literal::purple; + const mln::value::rgb8 c1 = lime; + const mln::value::rgb8 c2 = purple; + const unsigned pt1_min = 0; // lime near lime (c1) + const unsigned pt2_min = 1; // brown near purple (c2) + const unsigned pt3_min = 1; // teal near purple (c2) + const unsigned pt4_min = 1; // purple near purple (c2) + const double v1 = 0; + const double v2 = dist(purple, brown) + dist(purple, teal); + const mln::value::rgb8 mean_c2 = (brown+teal+purple)/3; + const VEC_VAR& var = kmean.get_variance(); + + fake_init_point(kmean.get_point(), lime, brown, teal, purple); + fake_init_center(kmean.get_center(), c1, c2); + fake_update_distance(kmean.get_distance(), lime, brown, teal, purple, c1, c2); + fake_update_group(kmean.get_group(), pt1_min, pt2_min, pt3_min, pt4_min); + kmean.update_variance(); + + mln_assertion(v1 == var[0]); + mln_assertion(v2 == var[1]); + + std::cout << "Test update variance : ok" << std::endl; +} + +void test_loop() +{ + typedef mln::value::rgb8 rgb8; + mln::image2d<rgb8> img_ref; + + mln::clustering::k_mean<SIZE_SAMPLE1, NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + mln::io::ppm::load(img_ref, "/usr/local/share/olena/images/lena.ppm"); + //mln::io::ppm::save(img_ref, "verif.ppm"); + + fill_image_with_4colors(img_ref); + kmean.init_point(img_ref); + + kmean.loop(img_ref); + + + // std::cout << "Test update variance: ok" << std::endl; +} + + +int main() +{ + //test_instantiation(); + //test_init_point(); + //test_init_center(); + //test_update_distance(); + //test_update_group(); + //test_update_center(); + //test_update_variance(); + + // mln::trace::quiet = false; + + test_loop(); + + return 0; +} -- 1.5.6.5