diff --git a/.gitignore b/.gitignore
index 55d415e946c5a7bf10daad0c221d82000c9259cd..e0940c2cf00a46463738a744bbe37c0691bb50d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,16 @@
 .idea/
 .mypy_cache/
+__pycache__/
 
 __material__/
-__pycache__/
+__runtime__/
 __version__/
 
 data/
-runtime/
 
 mprofile_*.dat
+
+brick/processing/frangi_c/obj/
+brick/processing/frangi_c/src/original-formatting/
+brick/processing/frangi_c/vesselness.so
+brick/vesselness.so
diff --git a/connection.py b/brick/component/connection.py
similarity index 93%
rename from connection.py
rename to brick/component/connection.py
index 8942fd51fdc845905aaae5a3b8882ea3311ec374..dc0efe6b093ea9e277b8ea660943a6dad22fb675 100644
--- a/connection.py
+++ b/brick/component/connection.py
@@ -29,10 +29,10 @@
 # The fact that you are presently reading this means that you have had
 # knowledge of the CeCILL license and that you accept its terms.
 
-import dijkstra_1_to_n as dk_
-from extension import extension_t
-from soma import soma_t
-from type import array_t, site_h, site_path_h
+import brick.processing.dijkstra_1_to_n as dk_
+from brick.component.extension import extension_t
+from brick.component.soma import soma_t
+from brick.general.type import array_t, site_h, site_path_h
 
 import itertools as it_
 from typing import Callable, Sequence, Tuple
diff --git a/extension.py b/brick/component/extension.py
similarity index 83%
rename from extension.py
rename to brick/component/extension.py
index a84f00bdfa62efbe0753f30abf1e0f61a7b3ae4f..0f8a80b7db0f10efda3b7af970e4640390aad19a 100644
--- a/extension.py
+++ b/brick/component/extension.py
@@ -31,10 +31,10 @@
 
 from __future__ import annotations
 
-import frangi_3d as fg_
-from glial_cmp import glial_cmp_t
-import map_labeling as ml_
-from type import array_t, site_h
+# import brick.processing.frangi_py.frangi_3d as fg_
+import brick.processing.map_labeling as ml_
+from brick.component.glial_cmp import glial_cmp_t
+from brick.general.type import array_t, site_h
 
 from typing import Optional, Sequence, Tuple
 
@@ -48,6 +48,35 @@ from scipy import ndimage as im_
 min_area_c = 100
 
 
+
+import ctypes as ct_
+import os.path as ph_  # Pathlib not necessary
+
+folder, script = ph_.split(__file__)
+if folder.__len__() == 0:
+    folder = "./"
+c_extension = ct_.CDLL(
+    ph_.join(folder, "..", "vesselness.so")
+)
+
+vesselness_C = c_extension.vesselness
+vesselness_C.argtypes = (
+    ct_.c_void_p,
+    ct_.c_int,
+    ct_.c_int,
+    ct_.c_int,
+    ct_.c_void_p,
+    ct_.c_float,
+    ct_.c_float,
+    ct_.c_float,
+    ct_.c_float,
+    ct_.c_float,
+    ct_.c_float,
+)
+vesselness_C.restype = None
+
+
+
 class extension_t(glial_cmp_t):
     #
     # soma_uid: connected to a soma somewhere upstream
@@ -156,17 +185,33 @@ class extension_t(glial_cmp_t):
             image, size=2, mode="constant", cval=0.0, origin=0
         )
 
-        enhanced_img, scale_map = fg_.FrangiEnhancement(
-            preprocessed_img,
-            scale_range=(0.1, 3),
-            scale_step=1,
-            alpha=0.8,
-            beta=0.5,
-            frangi_c=500,
-            bright_on_dark=True,
-            in_parallel=in_parallel,
+        preprocessed_img = preprocessed_img.astype(np_.float32)
+        enhanced_img = np_.empty(preprocessed_img.shape, dtype=np_.float32)
+        scale_map = np_.empty(preprocessed_img.shape, dtype=np_.float32)
+
+        vesselness_C(
+            preprocessed_img.ctypes.data,
+            *preprocessed_img.shape,
+            enhanced_img.ctypes.data,
+            0.1,
+            3.2,
+            1.0,
+            0.8,
+            0.5,
+            500.0,
         )
 
+        # enhanced_img, scale_map = fg_.FrangiEnhancement(
+        #     preprocessed_img,
+        #     scale_range=(0.1, 3),
+        #     scale_step=1,
+        #     alpha=0.8,
+        #     beta=0.5,
+        #     frangi_c=500,
+        #     bright_on_dark=True,
+        #     in_parallel=in_parallel,
+        # )
+
         # np_.savez_compressed(
         #     "./runtime/frangi.npz", enhanced_img=enhanced_img, scale_map=scale_map
         # )
diff --git a/glial_cmp.py b/brick/component/glial_cmp.py
similarity index 97%
rename from glial_cmp.py
rename to brick/component/glial_cmp.py
index 10c150387cfcba8f7fa6e9e26d227865ff848b17..a4584e32fd131112361e1b8c99027ed09e05e3b3 100644
--- a/glial_cmp.py
+++ b/brick/component/glial_cmp.py
@@ -31,7 +31,7 @@
 
 from __future__ import annotations
 
-from type import array_t, np_array_picker_h, py_array_picker_h, site_path_h
+from brick.general.type import array_t, np_array_picker_h, py_array_picker_h, site_path_h
 
 from typing import Dict, List, Optional, Tuple
 
diff --git a/soma.py b/brick/component/soma.py
similarity index 98%
rename from soma.py
rename to brick/component/soma.py
index 85c2cdf3c3ecee584078a5080c2f8dbeef1ce33d..7bdedbaf9b056bf73a573eec7edf95dd3d7e9978 100644
--- a/soma.py
+++ b/brick/component/soma.py
@@ -31,9 +31,9 @@
 
 from __future__ import annotations
 
-from glial_cmp import glial_cmp_t
-import map_labeling as ml_
-from type import array_t, py_array_picker_h, site_h
+from brick.component.glial_cmp import glial_cmp_t
+import brick.processing.map_labeling as ml_
+from brick.general.type import array_t, py_array_picker_h, site_h
 
 from collections import namedtuple as namedtuple_t
 import sys as sy_
diff --git a/features_ext.py b/brick/feature/features_ext.py
similarity index 100%
rename from features_ext.py
rename to brick/feature/features_ext.py
diff --git a/features_soma.py b/brick/feature/features_soma.py
similarity index 100%
rename from features_soma.py
rename to brick/feature/features_soma.py
diff --git a/feedback.py b/brick/general/feedback.py
similarity index 96%
rename from feedback.py
rename to brick/general/feedback.py
index f077e16f93075863d521cd3ab98efecb6aae69ec..c2a341beb1bdcc746f24cd6ef85fc2f3bbe522e5 100644
--- a/feedback.py
+++ b/brick/general/feedback.py
@@ -1,4 +1,3 @@
-from extension import extension_t
 # Copyright CNRS/Inria/UNS
 # Contributor(s): Eric Debreuve (since 2019)
 #
@@ -30,8 +29,9 @@ from extension import extension_t
 # The fact that you are presently reading this means that you have had
 # knowledge of the CeCILL license and that you accept its terms.
 
-import plot as ot_
-from soma import soma_t
+import brick.processing.plot as ot_
+from brick.component.extension import extension_t
+from brick.component.soma import soma_t
 
 from typing import Sequence, Tuple
 
diff --git a/type.py b/brick/general/type.py
similarity index 100%
rename from type.py
rename to brick/general/type.py
diff --git a/brick/processing/dijkstra_1_to_n.py b/brick/processing/dijkstra_1_to_n.py
new file mode 120000
index 0000000000000000000000000000000000000000..a9cb6fef877e89f67f892c39597ec194b2d42fe1
--- /dev/null
+++ b/brick/processing/dijkstra_1_to_n.py
@@ -0,0 +1 @@
+../../../../../../brick/def/dijkstra-img/dijkstra_1_to_n.py
\ No newline at end of file
diff --git a/brick/processing/frangi_c/makefile b/brick/processing/frangi_c/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f9fb57da2213b22f5278a165102678c0d8da364
--- /dev/null
+++ b/brick/processing/frangi_c/makefile
@@ -0,0 +1,43 @@
+SRC_PATH = ./src
+SRCS = $(SRC_PATH)/detector.cpp $(SRC_PATH)/processing.cpp $(SRC_PATH)/kernel3D.cpp $(SRC_PATH)/main.cpp
+TARGET = vesselness
+
+OBJ_PATH = ./obj
+
+CC = g++ -std=c++0x
+RM = rm -f
+
+INCLUDES += -I $(SRC_PATH)
+
+#  -g    adds debugging information to the executable file
+#  -Wall turns on most, but not all, compiler warnings
+#  -pg   adds profiling information to the executable file
+#  -m64  compile for 64-bit
+#  -Wno-comment disable warnings about multiline comments
+# CFLAGS   = -Wall -Wno-reorder -m64 -fopenmp -Wno-comment -O2 -Wl,--no-as-needed
+CFLAGS   = -Wall -O2 -fopenmp
+CFLAGS  +=`pkg-config --cflags /usr/lib/pkgconfig/opencv4.pc`
+
+# LFLAGS += -Wl,--no-as-needed -pthread
+LFLAGS += -O2 -pthread -fopenmp -lm
+LFLAGS += `pkg-config --libs /usr/lib/pkgconfig/opencv4.pc`
+
+OBJS = $(SRCS:$(SRC_PATH)/%.cpp=$(OBJ_PATH)/%.o)
+
+all: $(OBJS)
+	mkdir -p $(OBJ_PATH)
+	$(CC) $(OBJS) -o $(TARGET) $(LFLAGS) $(PY_LFLAGS) $(COVERAGE) $(LIBS)
+
+py: PY_CFLAGS = -fpic
+py: PY_LFLAGS = -shared
+py: all
+	mv $(TARGET) $(TARGET).so
+
+cov: COVERAGE = -fprofile-arcs -ftest-coverage
+cov: all
+
+$(OBJ_PATH)/%.o: $(SRC_PATH)/%.cpp
+	$(CC) -c $< -o $@ $(CFLAGS) $(PY_CFLAGS) $(COVERAGE) $(INCLUDES)
+
+clean:
+	$(RM) $(OBJ_PATH)/*.o $(TARGET)
diff --git a/brick/processing/frangi_c/src/__coverage__.txt b/brick/processing/frangi_c/src/__coverage__.txt
new file mode 100644
index 0000000000000000000000000000000000000000..968961f44f3355c3b6f84129d2d636804d82ea85
--- /dev/null
+++ b/brick/processing/frangi_c/src/__coverage__.txt
@@ -0,0 +1,7 @@
+gcc -Wall -fprofile-arcs -ftest-coverage cov.c
+
+Then run executable
+
+gcov cov.c
+
+Then check: cov.c.gcov
diff --git a/brick/processing/frangi_c/src/__formatting__.txt b/brick/processing/frangi_c/src/__formatting__.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4a0ec6418bbd083730d5e247fae88b3ac2da3211
--- /dev/null
+++ b/brick/processing/frangi_c/src/__formatting__.txt
@@ -0,0 +1 @@
+astyle --style=stroustrup --recursive src/*.cpp,*.h
diff --git a/brick/processing/frangi_c/src/data3D.h b/brick/processing/frangi_c/src/data3D.h
new file mode 100644
index 0000000000000000000000000000000000000000..9eb64e5bff691d3ad0aa8bac768d9d0f7dd82b6c
--- /dev/null
+++ b/brick/processing/frangi_c/src/data3D.h
@@ -0,0 +1,338 @@
+#pragma once
+
+#include "type_info.h"
+
+#include <iostream>
+#include <opencv2/core/core.hpp>
+#include <opencv2/highgui/highgui.hpp>
+#include <typeinfo>
+#include <string.h>
+#include "smart_assert.h"
+
+template<typename T>
+class Data3D {
+public:
+    Data3D( const cv::Vec3i& n_size = cv::Vec3i(0,0,0));
+    Data3D( const int width, const int height, const int depth );
+    Data3D( const cv::Vec3i& n_size, const T& value );
+    template <class T2>
+    Data3D( const Data3D<T2>& src );
+    const Data3D<T>& operator=( const Data3D<T>& src );
+
+    virtual ~Data3D(void) { }
+
+    inline void reset( void )
+    {
+        memset( _mat.data, 0, _size_total * sizeof(T) );
+    }
+    inline void reset( const cv::Vec3i& n_size )
+    {
+        _size = n_size;
+        long size_slice = _size[0] * _size[1];
+        _size_total = size_slice * _size[2];
+        _mat = cv::Mat_<T>( _size[2], size_slice );
+        memset( _mat.data, 0, _size_total * sizeof(T) );
+    }
+    inline void reset( const int width, const int height, const int depth )
+    {
+        _size = cv::Vec3i(width, height, depth);
+        long size_slice = _size[0] * _size[1];
+        _size_total = size_slice * _size[2];
+        _mat = cv::Mat_<T>( _size[2], size_slice );
+        memset( _mat.data, 0, _size_total * sizeof(T) );
+    }
+    void reset( const cv::Vec3i& n_size, const T& value )
+    {
+        resize( n_size );
+        for( cv::MatIterator_<T> it=_mat.begin(); it<_mat.end(); it++ )
+        {
+            (*it) = value;
+        }
+    }
+
+    void resize( const cv::Vec3i& n_size )
+    {
+        _size = n_size;
+        long size_slice = _size[0] * _size[1];
+        _size_total = size_slice * _size[2];
+        _mat = cv::Mat_<T>( _size[2], size_slice );
+    }
+
+    inline const int& SX(void) const
+    {
+        return _size[0];
+    }
+    inline const int& SY(void) const
+    {
+        return _size[1];
+    }
+    inline const int& SZ(void) const
+    {
+        return _size[2];
+    }
+    inline const int& get_width(void)  const
+    {
+        return _size[0];
+    }
+    inline const int& get_height(void) const
+    {
+        return _size[1];
+    }
+    inline const int& get_depth(void)  const
+    {
+        return _size[2];
+    }
+    inline const int& get_size_x(void) const
+    {
+        return _size[0];
+    }
+    inline const int& get_size_y(void) const
+    {
+        return _size[1];
+    }
+    inline const int& get_size_z(void) const
+    {
+        return _size[2];
+    }
+    inline const int& get_size(int i) const
+    {
+        return _size[i];
+    }
+    inline const long& get_size_total(void) const
+    {
+        return _size_total;
+    }
+    inline const cv::Vec3i& get_size(void) const
+    {
+        return _size;
+    }
+    inline const bool is_empty( void ) const
+    {
+        return get_size_total()==0;
+    }
+
+    virtual const inline T& at( const int& i ) const
+    {
+        return _mat( i );
+    }
+    virtual inline T& at( const int& i )
+    {
+        return _mat( i );
+    }
+    virtual inline const T& at( const int& x, const int& y, const int& z ) const
+    {
+        return _mat( z, y * _size[0] + x );
+    }
+    virtual inline T& at( const int& x, const int& y, const int& z )
+    {
+        return _mat( z, y * _size[0] + x );
+    }
+    virtual inline const T& at( const cv::Vec3i& pos ) const
+    {
+        return at( pos[0], pos[1], pos[2] );
+    }
+    virtual inline T& at( const cv::Vec3i& pos )
+    {
+        return at( pos[0], pos[1], pos[2] );
+    }
+
+    inline cv::Mat_<T>& getMat()
+    {
+        return _mat;
+    }
+    inline const cv::Mat_<T>& getMat() const
+    {
+        return _mat;
+    }
+    inline cv::Mat_<T> getMat( int slice ) const
+    {
+        return _mat.row(slice).reshape( 0, get_height() ).clone();
+    }
+
+    void SetFromArray( const T* array );
+    void CopyToArray( T* array );
+
+    template<typename T1, typename T2>
+    friend const Data3D<T1>& operator*=( Data3D<T1>& left, const T2& right );
+    template<typename T1, typename T2, typename T3>
+    friend bool subtract3D( const Data3D<T1>& src1, const Data3D<T2>& src2, Data3D<T3>& dst );
+    template<typename T1, typename T2, typename T3>
+    friend bool multiply3D( const Data3D<T1>& src1, const Data3D<T2>& src2, Data3D<T3>& dst );
+
+    cv::Vec<T, 2> get_min_max_value() const
+    {
+        cv::Vec<T, 2> min_max;
+        cv::Point minLoc, maxLoc;
+        cv::minMaxLoc( _mat, NULL, NULL, &minLoc, &maxLoc);
+        min_max[0] = _mat( minLoc );
+        min_max[1] = _mat( maxLoc );
+        return min_max;
+    }
+
+public:
+    /* copy data of dimension i to a new Image3D structure
+       e.g. this is useful when trying to visualize vesselness which is
+       a multidimensional data structure
+       Yuchen: p.s. I have no idea how to move the funciton body out the
+       class definition nicely. Let me figure it later maybe. */
+    template<typename T2>
+    void copyDimTo( Data3D<T2>& dst, int dim ) const
+    {
+        dst.reset( this->get_size() );
+        int x, y, z;
+        for( z=0; z<get_size_z(); z++ ) for( y=0; y<get_size_y(); y++ ) for( x=0; x<get_size_x(); x++ ) {
+                    dst.at(x,y,z) = this->at(x,y,z)[dim];
+                }
+    }
+
+
+    // return true if a index is valide for the data
+    inline bool isValid( const int& x, const int& y, const int& z ) const
+    {
+        return ( x>=0 && x<get_size_x() &&
+                 y>=0 && y<get_size_y() &&
+                 z>=0 && z<get_size_z() );
+    }
+    inline bool isValid( const cv::Vec3i& v ) const
+    {
+        return isValid( v[0], v[1], v[2] );
+    }
+
+    inline const T* getData(void) const
+    {
+        return (T*) getMat().data;
+    }
+
+    inline T* getData(void)
+    {
+        return (T*) getMat().data;
+    }
+
+protected:
+    cv::Vec3i _size;
+    long _size_total;
+    cv::Mat_<T> _mat;
+};
+
+
+// Default Constructor
+template <typename T>
+Data3D<T>::Data3D( const cv::Vec3i& n_size )
+{
+    reset(n_size);
+}
+template <typename T>
+Data3D<T>::Data3D( const int width, const int height, const int depth )
+{
+    reset(width, height, depth);
+}
+
+// Constructor with size and value
+template <typename T>
+Data3D<T>::Data3D( const cv::Vec3i& n_size, const T& value )
+{
+    reset(n_size, value);
+}
+
+
+// Copy Constructor
+template<typename T>
+template<typename T2>
+Data3D<T>::Data3D( const Data3D<T2>& src )
+{
+    // resize
+    this->resize( src.get_size() );
+    // copy the data over
+    cv::MatIterator_<T>       this_it;
+    cv::MatConstIterator_<T2> src_it;
+    for( this_it = this->getMat().begin(), src_it = src.getMat().begin();
+            this_it < this->getMat().end(),   src_it < src.getMat().end();
+            this_it++, src_it++ ) {
+        // Yuchen: The convertion from T2 to T may be unsafe
+        *(this_it) = T( *(src_it) );
+    }
+}
+
+
+template<typename T>
+const Data3D<T>& Data3D<T>::operator=( const Data3D<T>& src )
+{
+    // resize
+    this->resize( src.get_size() );
+
+    // copy the data over
+    cv::MatIterator_<T>       this_it;
+    cv::MatConstIterator_<T> src_it;
+    for( this_it = this->getMat().begin(), src_it = src.getMat().begin();
+            this_it < this->getMat().end(),   src_it < src.getMat().end();
+            this_it++, src_it++ ) {
+        *(this_it) = *(src_it);
+    }
+
+    return *this;
+}
+
+
+template <typename T>
+void Data3D<T>::SetFromArray( const T* array )
+{
+    memcpy(_mat.data, array, _size_total * sizeof(T));
+}
+
+template <typename T>
+void Data3D<T>::CopyToArray( T* array )
+{
+    memcpy(array, _mat.data, _size_total * sizeof(T));
+}
+
+//////////////////////////////////////////////////////////////////////
+// Operator Overloading and other friend functions
+
+template<typename T, typename T2>
+const Data3D<T>& operator*=( Data3D<T>& left, const T2& right )
+{
+    left._mat*=right;
+    return left;
+}
+
+template<typename T1, typename T2, typename T3>
+bool subtract3D( const Data3D<T1>& src1, const Data3D<T2>& src2, Data3D<T3>& dst )
+{
+    smart_return( src1.get_size()==src2.get_size(), "Sizes should match.", false);
+
+    if( dst.get_size() != src1.get_size() ) {
+        dst.reset( src1.get_size() );
+    }
+
+    int x, y, z;
+    for( z=0; z<src1.get_size_z(); z++ ) {
+        for( y=0; y<src1.get_size_y(); y++ ) {
+            for( x=0; x<src1.get_size_x(); x++ ) {
+                dst.at(x,y,z) = T3(src1.at(x,y,z) - src2.at(x,y,z));
+            }
+        }
+    }
+    return true;
+}
+
+
+template<typename T1, typename T2, typename T3>
+bool multiply3D( const Data3D<T1>& src1, const Data3D<T2>& src2, Data3D<T3>& dst )
+{
+    smart_return_false( src1.get_size()==src2.get_size(),
+                        "Source sizes are supposed to be cv::Matched.");
+
+    if( dst.get_size() != src1.get_size() ) {
+        dst.reset( src1.get_size() );
+    }
+
+    int x, y, z;
+    for( z=0; z<src1.get_size_z(); z++ ) {
+        for( y=0; y<src1.get_size_y(); y++ ) {
+            for( x=0; x<src1.get_size_x(); x++ ) {
+                dst.at(x,y,z) = src1.at(x,y,z) * src2.at(x,y,z);
+            }
+        }
+    }
+    return true;
+}
diff --git a/brick/processing/frangi_c/src/detector.cpp b/brick/processing/frangi_c/src/detector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d68f8de970fca36af79a3c47be5ebb35f5b4a24
--- /dev/null
+++ b/brick/processing/frangi_c/src/detector.cpp
@@ -0,0 +1,181 @@
+#include "detector.h"
+#include "data3D.h"
+#include "processing_code.h"
+#include "types.h"
+#include "eigen.cpp" // Otherwise the template mechanism does not work. TODO: solve this
+
+using namespace std;
+
+Vesselness hessien_thread_func( const Data3D<float>& src,
+                                const int& x, const int& y, const int& z,
+                                const float& alpha, const float& beta, const float& gamma )
+{
+
+    ////////////////////////////////////////////////////////////////////
+    // The following are being computed in this function
+    // 1) derivative of images;
+    // 2) Hessian matrix;
+    // 3) Eigenvalue decomposition;
+    // 4) vesselness measure.
+
+    // 1) derivative of the image
+    float im_dx2 = -2.0f * src.at(x,y,z) + src.at(x-1,y,z) + src.at(x+1,y,z);
+    float im_dy2 = -2.0f * src.at(x,y,z) + src.at(x,y-1,z) + src.at(x,y+1,z);
+    float im_dz2 = -2.0f * src.at(x,y,z) + src.at(x,y,z-1) + src.at(x,y,z+1);
+    // 1) derivative of the image (alternative approach, the one above is more accurate)
+    //float im_dx2 = -0.5f * src.at(x,y,z) + 0.25f * src.at(x-2,y,z) + 0.25f * src.at(x+2,y,z);
+    //float im_dy2 = -0.5f * src.at(x,y,z) + 0.25f * src.at(x,y-2,z) + 0.25f * src.at(x,y+2,z);
+    //float im_dz2 = -0.5f * src.at(x,y,z) + 0.25f * src.at(x,y,z-2) + 0.25f * src.at(x,y,z+2);
+
+    float im_dxdy = (
+                        + src.at(x-1, y-1, z)
+                        + src.at(x+1, y+1, z)
+                        - src.at(x-1, y+1, z)
+                        - src.at(x+1, y-1, z) ) * 0.25f;
+    float im_dxdz = (
+                        + src.at(x-1, y, z-1)
+                        + src.at(x+1, y, z+1)
+                        - src.at(x+1, y, z-1)
+                        - src.at(x-1, y, z+1) ) * 0.25f;
+    float im_dydz = (
+                        + src.at(x, y-1, z-1)
+                        + src.at(x, y+1, z+1)
+                        - src.at(x, y+1, z-1)
+                        - src.at(x, y-1, z+1) ) * 0.25f;
+
+    // 3) Eigenvalue decomposition
+    // Reference: http://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices
+    // Given a real symmetric 3x3 matrix Hessian, compute the eigenvalues
+    const float Hessian[6] = {im_dx2, im_dxdy, im_dxdz, im_dy2, im_dydz, im_dz2};
+
+    float eigenvalues[3];
+    float eigenvectors[3][3];
+    eigen_decomp( Hessian, eigenvalues, eigenvectors );
+
+    // order eigenvalues so that |lambda1| < |lambda2| < |lambda3|
+    int i=0, j=1, k=2;
+//     if( abs(eigenvalues[i]) > abs(eigenvalues[j]) ) std::swap( i, j );
+//     if( abs(eigenvalues[i]) > abs(eigenvalues[k]) ) std::swap( i, k );
+//     if( abs(eigenvalues[j]) > abs(eigenvalues[k]) ) std::swap( j, k );
+
+    // 4) vesselness measure
+    Vesselness vn;
+    // vesselness value
+    if( eigenvalues[j] > 0 || eigenvalues[k] > 0 ) {
+        vn.rsp = 0.0f;
+    }
+    else {
+        float lmd1 = abs( eigenvalues[i] );
+        float lmd2 = abs( eigenvalues[j] );
+        float lmd3 = abs( eigenvalues[k] );
+
+        float A = (lmd3>1e-5) ? lmd2/lmd3 : 0;
+        float B = (lmd2*lmd3>1e-5) ? lmd1 / sqrt( lmd2*lmd3 ) : 0;
+        float S = sqrt( lmd1*lmd1 + lmd2*lmd2 + lmd3*lmd3 );
+        vn.rsp = ( 1.0f-exp(-A*A/alpha) )* exp( B*B/beta ) * ( 1-exp(-S*S/gamma) );
+    }
+
+    // orientation of vesselness is corresponding to the eigenvector of the
+    // smallest eigenvalue
+    for( int d=0; d<3; d++ ) {
+        vn.dir[d] = eigenvectors[i][d];
+    }
+
+    return vn;
+}
+
+
+
+bool VesselDetector::hessien( const Data3D<float>& src, Data3D<Vesselness>& dst,
+                              int ksize, float sigma,
+                              float alpha, float beta, float gamma )
+{
+    if( ksize!=0 && sigma<1e-3 ) { // sigma is not set
+        sigma = 0.15f * ksize + 0.35f;
+    }
+    else if ( ksize==0 && sigma>1e-3 ) { // size is not set
+        ksize = int( 6*sigma+1 );
+        // make sure size is an odd number
+        if ( ksize%2==0 ) ksize++;
+    }
+    else {
+        std::cerr << "At lease size or sigma has to be set." << std::endl;
+        return false;
+    }
+
+    Data3D<float> im_blur;
+    bool flag = ImageProcessing::GaussianBlur3D( src, im_blur, ksize, sigma );
+    smart_return( flag, "Gaussian Blur Failed.", false );
+
+    im_blur *= std::pow(sigma, 1.2); //Normalizing for different scale
+
+    dst.reset( im_blur.get_size(), Vesselness(0.0f) );
+
+    #pragma omp parallel
+    {
+        #pragma omp for
+        for( int z = 1; z < src.get_size_z()-1; z++ ) {
+            for( int y = 1; y < src.get_size_y()-1; y++ ) {
+                for( int x = 1; x < src.get_size_x()-1; x++ ) {
+                    dst.at(x, y, z) = hessien_thread_func( im_blur, x, y, z, alpha, beta, gamma);
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+
+
+int VesselDetector::compute_vesselness(
+    const Data3D<float>& src,
+    Data3D<Vesselness_Sig>& dst,
+    float sigma_from, float sigma_to, float sigma_step,
+    float alpha, float beta, float gamma )
+{
+    std::cout << "Computing Vesselness, it will take a while... " << std::endl;
+    std::cout << "Vesselness will be computed from sigma = " << sigma_from << " to sigma = " << sigma_to << std::endl;
+
+    dst.reset( src.get_size() ); // reszie data, and it will also be clear to zero
+
+    // Error for input parameters
+    smart_return( sigma_from < sigma_to,
+                  "sigma_from should be smaller than sigma_to ", 0 );
+    smart_return( sigma_step > 0,
+                  "sigma_step should be greater than 0 ", 0 );
+
+    int x,y,z;
+    float max_sigma = sigma_from;
+    float min_sigma = sigma_to;
+
+    Data3D<Vesselness> temp;
+    for( float sigma = sigma_from; sigma < sigma_to; sigma += sigma_step ) {
+        cout << '\r' << "Vesselness for sigma = " << sigma << "    " << "\b\b\b\b";
+        cout.flush();
+
+        VesselDetector::hessien( src, temp, 0, sigma, alpha, beta, gamma );
+
+        const int margin = 1;
+        for( z=margin; z<src.get_size_z()-margin; z++ ) {
+            for( y=margin; y<src.get_size_y()-margin; y++ ) {
+                for( x=margin; x<src.get_size_x()-margin; x++ ) {
+                    // Update vesselness if the new response is greater
+                    if( dst.at(x, y, z).rsp < temp.at(x, y, z).rsp ) {
+                        dst.at(x, y, z).rsp = temp.at(x, y, z).rsp;
+                        dst.at(x, y, z).dir = temp.at(x, y, z).dir;
+                        dst.at(x, y, z).sigma = sigma;
+                        max_sigma = std::max( sigma, max_sigma );
+                        min_sigma = std::min( sigma, min_sigma );
+                    }
+                }
+            }
+        }
+    }
+
+    std::cout << std::endl << "The range for sigma is [";
+    std::cout << min_sigma << ", " << max_sigma << "]" << std::endl;
+    std::cout << "Done. " << std::endl << std::endl;
+
+    return 0;
+}
diff --git a/brick/processing/frangi_c/src/detector.h b/brick/processing/frangi_c/src/detector.h
new file mode 100644
index 0000000000000000000000000000000000000000..e08e6eb644f6c9f5f6e71fb32a76e222f36461c7
--- /dev/null
+++ b/brick/processing/frangi_c/src/detector.h
@@ -0,0 +1,31 @@
+#ifndef VESSELDETECTOR_H
+#define VESSELDETECTOR_H
+
+#include "types.h"
+
+template<typename T> class Data3D;
+
+class Vesselness;
+class Vesselness_Sig;
+class Vesselness_Nor;
+class Vesselness_All;
+
+namespace VesselDetector {
+bool hessien(
+    const Data3D<float>& src, Data3D<Vesselness>& dst,
+    int ksize, float sigma,
+    float alpha, float beta, float gamma );
+
+int compute_vesselness(
+    const Data3D<float>& src,
+    Data3D<Vesselness_Sig>& dst,
+    float sigma_from, float sigma_to, float sigma_step,
+    float alpha = 1.0e-1f,
+    float beta  = 5.0e0f,
+    float gamma = 3.5e5f );
+};
+
+namespace VD = VesselDetector;
+namespace VF = VesselDetector;
+
+#endif
diff --git a/brick/processing/frangi_c/src/eigen.cpp b/brick/processing/frangi_c/src/eigen.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..71ab97db79e43db9dd132037b0e045558f42d8ed
--- /dev/null
+++ b/brick/processing/frangi_c/src/eigen.cpp
@@ -0,0 +1,576 @@
+#include "eigen.h"
+
+/* Compute the eigen value decomposition of a 3 by 3 symetric matrix
+Input:
+A = [ A11  A12  A13     = [ A[0]  A[1]  A[2]
+A21  A22  A23          0    A[3]  A[4]
+A31  A32  A33 ];       0     0    A[5] ];
+Output:
+eigenvalues, eigenvectors
+
+Reference: http://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices
+*/
+#include <cmath>
+#include <assert.h>
+#include <algorithm>
+#include <iostream>
+// #include <opencv2/core/types_c.h>
+// #include <opencv2/core/core_c.h>
+
+
+#if !defined(SmallEpsilon) && !defined(BigEpsilon)
+#define SmallEpsilon 1.0e-18
+#define BigEpsilon 1.0e-4
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#include "math.h"
+#ifdef MAX
+#undef MAX
+#endif
+#define MAX(a, b) ((a)>(b)?(a):(b))
+#define n 3
+
+// address/from/source: https://github.com/fethallah/neurons/blob/master/frangi_filter_version2a/eig3volume.c
+
+/* Eigen decomposition code for symmetric 3x3 matrices, copied from the public
+ * domain Java Matrix library JAMA. */
+static float hypot2(float x, float y) { return (float) sqrt(x*x+y*y); }
+
+__inline float absd(float val){ if(val>0){ return val;} else { return -val;} };
+
+/* Symmetric Householder reduction to tridiagonal form. */
+static void tred2(float V[n][n], float d[n], float e[n]) {
+
+/*  This is derived from the Algol procedures tred2 by */
+/*  Bowdler, Martin, Reinsch, and Wilkinson, Handbook for */
+/*  Auto. Comp., Vol.ii-Linear Algebra, and the corresponding */
+/*  Fortran subroutine in EISPACK. */
+
+//     std::cout << "Entering tred2" << std::endl;
+
+    int i, j, k;
+    float scale;
+    float f, g, h;
+    float hh;
+    for (j = 0; j < n; j++) {d[j] = V[n-1][j]; }
+
+    /* Householder reduction to tridiagonal form. */
+
+    for (i = n-1; i > 0; i--) {
+        /* Scale to avoid under/overflow. */
+        scale = 0.0;
+        h = 0.0;
+        for (k = 0; k < i; k++) { scale = scale + fabs(d[k]); }
+        if (scale == 0.0) {
+            e[i] = d[i-1];
+            for (j = 0; j < i; j++) { d[j] = V[i-1][j]; V[i][j] = 0.0;  V[j][i] = 0.0; }
+        } else {
+
+            /* Generate Householder vector. */
+
+            for (k = 0; k < i; k++) { d[k] /= scale; h += d[k] * d[k]; }
+            f = d[i-1];
+            g = sqrt(h);
+            if (f > 0) { g = -g; }
+            e[i] = scale * g;
+            h = h - f * g;
+            d[i-1] = f - g;
+            for (j = 0; j < i; j++) { e[j] = 0.0; }
+
+            /* Apply similarity transformation to remaining columns. */
+
+            for (j = 0; j < i; j++) {
+                f = d[j];
+                V[j][i] = f;
+                g = e[j] + V[j][j] * f;
+                for (k = j+1; k <= i-1; k++) { g += V[k][j] * d[k]; e[k] += V[k][j] * f; }
+                e[j] = g;
+            }
+            f = 0.0;
+            for (j = 0; j < i; j++) { e[j] /= h; f += e[j] * d[j]; }
+            hh = f / (h + h);
+            for (j = 0; j < i; j++) { e[j] -= hh * d[j]; }
+            for (j = 0; j < i; j++) {
+                f = d[j]; g = e[j];
+                for (k = j; k <= i-1; k++) { V[k][j] -= (f * e[k] + g * d[k]); }
+                d[j] = V[i-1][j];
+                V[i][j] = 0.0;
+            }
+        }
+        d[i] = h;
+    }
+
+    /* Accumulate transformations. */
+
+    for (i = 0; i < n-1; i++) {
+        V[n-1][i] = V[i][i];
+        V[i][i] = 1.0;
+        h = d[i+1];
+        if (h != 0.0) {
+            for (k = 0; k <= i; k++) { d[k] = V[k][i+1] / h;}
+            for (j = 0; j <= i; j++) {
+                g = 0.0;
+                for (k = 0; k <= i; k++) { g += V[k][i+1] * V[k][j]; }
+                for (k = 0; k <= i; k++) { V[k][j] -= g * d[k]; }
+            }
+        }
+        for (k = 0; k <= i; k++) { V[k][i+1] = 0.0;}
+    }
+    for (j = 0; j < n; j++) { d[j] = V[n-1][j]; V[n-1][j] = 0.0; }
+    V[n-1][n-1] = 1.0;
+    e[0] = 0.0;
+
+//     std::cout << "Leaving tred2" << std::endl;
+}
+
+/* Symmetric tridiagonal QL algorithm. */
+static void tql2(float V[n][n], float d[n], float e[n]) {
+
+/*  This is derived from the Algol procedures tql2, by */
+/*  Bowdler, Martin, Reinsch, and Wilkinson, Handbook for */
+/*  Auto. Comp., Vol.ii-Linear Algebra, and the corresponding */
+/*  Fortran subroutine in EISPACK. */
+
+//     std::cout << "Entering tql2" << std::endl;
+
+    int i, j, k, l, m;
+    float f;
+    float tst1;
+    float eps;
+    int iter;
+    float g, p, r;
+    float dl1, h, c, c2, c3, el1, s, s2;
+
+    for (i = 1; i < n; i++) { e[i-1] = e[i]; }
+    e[n-1] = 0.0;
+
+    f = 0.0;
+    tst1 = 0.0;
+    eps = pow(2.0, -52.0);
+    for (l = 0; l < n; l++) {
+
+        /* Find small subdiagonal element */
+
+        tst1 = MAX(tst1, fabs(d[l]) + fabs(e[l]));
+        m = l;
+        while (m < n) {
+            if (fabs(e[m]) <= eps*tst1) { break; }
+            m++;
+        }
+
+        /* If m == l, d[l] is an eigenvalue, */
+        /* otherwise, iterate. */
+
+        if (m > l) {
+            iter = 0;
+            do {
+                iter = iter + 1;  /* (Could check iteration count here.) */
+                /* Compute implicit shift */
+                g = d[l];
+                p = (d[l+1] - g) / (2.0 * e[l]);
+                r = hypot2(p, 1.0);
+                if (p < 0) { r = -r; }
+                d[l] = e[l] / (p + r);
+                d[l+1] = e[l] * (p + r);
+                dl1 = d[l+1];
+                h = g - d[l];
+                for (i = l+2; i < n; i++) { d[i] -= h; }
+                f = f + h;
+                /* Implicit QL transformation. */
+                p = d[m]; c = 1.0; c2 = c; c3 = c;
+                el1 = e[l+1]; s = 0.0; s2 = 0.0;
+                for (i = m-1; i >= l; i--) {
+                    c3 = c2;
+                    c2 = c;
+                    s2 = s;
+                    g = c * e[i];
+                    h = c * p;
+                    r = hypot2(p, e[i]);
+                    e[i+1] = s * r;
+                    s = e[i] / r;
+                    c = p / r;
+                    p = c * d[i] - s * g;
+                    d[i+1] = h + s * (c * g + s * d[i]);
+                    /* Accumulate transformation. */
+                    for (k = 0; k < n; k++) {
+                        h = V[k][i+1];
+                        V[k][i+1] = s * V[k][i] + c * h;
+                        V[k][i] = c * V[k][i] - s * h;
+                    }
+                }
+                p = -s * s2 * c3 * el1 * e[l] / dl1;
+                e[l] = s * p;
+                d[l] = c * p;
+
+                /* Check for convergence. */
+            } while (fabs(e[l]) > eps*tst1);
+        }
+        d[l] = d[l] + f;
+        e[l] = 0.0;
+    }
+
+    /* Sort eigenvalues and corresponding vectors. */
+    for (i = 0; i < n-1; i++) {
+        k = i;
+        p = d[i];
+        for (j = i+1; j < n; j++) {
+            if (d[j] < p) {
+                k = j;
+                p = d[j];
+            }
+        }
+        if (k != i) {
+            d[k] = d[i];
+            d[i] = p;
+            for (j = 0; j < n; j++) {
+                p = V[j][i];
+                V[j][i] = V[j][k];
+                V[j][k] = p;
+            }
+        }
+    }
+
+//     std::cout << "Leaving tql2" << std::endl;
+}
+
+void eigen_decomposition(float A[n][n], float V[n][n], float d[n]) {
+    float e[n];
+    float da[3];
+    float dt, dat;
+    float vet[3];
+    int i, j;
+
+//     std::cout << "Entering egien_decompositiom" << std::endl;
+
+    for (i = 0; i < n; i++) {
+        for (j = 0; j < n; j++) {
+            V[i][j] = A[i][j];
+        }
+    }
+    tred2(V, d, e);
+    tql2(V, d, e);
+
+    /* Sort the eigen values and vectors by abs eigen value */
+    da[0]=absd(d[0]); da[1]=absd(d[1]); da[2]=absd(d[2]);
+    if((da[0]>=da[1])&&(da[0]>da[2]))
+    {
+        dt=d[2];   dat=da[2];    vet[0]=V[0][2];    vet[1]=V[1][2];    vet[2]=V[2][2];
+        d[2]=d[0]; da[2]=da[0];  V[0][2] = V[0][0]; V[1][2] = V[1][0]; V[2][2] = V[2][0];
+        d[0]=dt;   da[0]=dat;    V[0][0] = vet[0];  V[1][0] = vet[1];  V[2][0] = vet[2];
+    }
+    else if((da[1]>=da[0])&&(da[1]>da[2]))
+    {
+        dt=d[2];   dat=da[2];    vet[0]=V[0][2];    vet[1]=V[1][2];    vet[2]=V[2][2];
+        d[2]=d[1]; da[2]=da[1];  V[0][2] = V[0][1]; V[1][2] = V[1][1]; V[2][2] = V[2][1];
+        d[1]=dt;   da[1]=dat;    V[0][1] = vet[0];  V[1][1] = vet[1];  V[2][1] = vet[2];
+    }
+    if(da[0]>da[1])
+    {
+        dt=d[1];   dat=da[1];    vet[0]=V[0][1];    vet[1]=V[1][1];    vet[2]=V[2][1];
+        d[1]=d[0]; da[1]=da[0];  V[0][1] = V[0][0]; V[1][1] = V[1][0]; V[2][1] = V[2][0];
+        d[0]=dt;   da[0]=dat;    V[0][0] = vet[0];  V[1][0] = vet[1];  V[2][0] = vet[2];
+    }
+
+//     std::cout << "Leaving egien_decompositiom" << std::endl;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+template<typename T=float>
+inline void normalize( T v[3] )
+{
+    T length = 0;
+    for( int i=0; i<3; i++ ) {
+        length += v[i] * v[i];
+    }
+//     assert( length > SmallEpsilon && "Cannot normalized zero vector" );
+    if ( length <= SmallEpsilon )
+        return;
+
+    length = sqrt( length );
+    for( int i=0; i<3; i++ ) {
+        v[i] /= length;
+    }
+}
+
+template<typename T=float>
+inline void cross_product( const T v1[3], const T v2[3], T v3[3] )
+{
+    v3[0] = v1[1] * v2[2] - v1[2] * v2[1];
+    v3[1] = v1[2] * v2[0] - v1[0] * v2[2];
+    v3[2] = v1[0] * v2[1] - v1[1] * v2[0];
+    normalize( v3 );
+}
+
+template<typename T=float>
+void normal_vectors(  const T v1[3], T v2[3], T v3[3] )
+{
+    // an arbitrary combination of the following two vectors
+    // is a normal to v1
+    // alpha * (-v1[1], v1[0], 0) + beta * (0, -v1[2], v[1])
+
+    if( abs(v1[0])>BigEpsilon || abs(v1[1])>BigEpsilon ) {
+        v2[0] = -v1[1];
+        v2[1] =  v1[0];
+        v2[2] =  0;
+    }
+    else {
+        v2[0] =  0;
+        v2[1] = -v1[2];
+        v2[2] =  v1[1];
+    }
+
+    normalize( v2 );
+
+    cross_product( v1, v2, v3 );
+}
+*/
+
+template<typename T=float>
+void eigen_decomp( const T A[6], T eigenvalues[3], T eigenvectors[3][3] )
+{
+    float Asym[n][n];
+    Asym[0][0] = A[0]; Asym[0][1] = A[1]; Asym[0][2] = A[2];
+    Asym[1][0] = A[1]; Asym[1][1] = A[3]; Asym[1][2] = A[4];
+    Asym[2][0] = A[2]; Asym[2][1] = A[4]; Asym[2][2] = A[5];
+
+    /* Function below sorts the eigen values and vectors by abs eigen value */
+    eigen_decomposition(Asym, eigenvectors, eigenvalues);
+
+    return;
+
+//     static CvMat mat, *eigenVec, *eigenVal;
+//     static float Ain1D[9];
+//
+//     int cnt = 0;
+//     Ain1D[cnt++] = A[0];
+//     Ain1D[cnt++] = A[1];
+//     Ain1D[cnt++] = A[2];
+//     Ain1D[cnt++] = A[1];
+//     Ain1D[cnt++] = A[3];
+//     Ain1D[cnt++] = A[4];
+//     Ain1D[cnt++] = A[2];
+//     Ain1D[cnt++] = A[4];
+//     Ain1D[cnt++] = A[5];
+//
+//     int i;
+//     std::cout << std::endl;
+//     for (i = 0; i < 9; i++) {
+//         std::cout << Ain1D[i] << " ";
+//         if ((i == 1) || (i == 4))
+//             std::cout << std::endl;
+//     }
+//     std::cout << std::endl << std::endl;
+//
+//     mat = cvMat(3, 3, CV_32FC1, Ain1D);
+//
+//     cvEigenVV(&mat, eigenVec, eigenVal, 1e-300);
+//
+//     for(int i=0;i < 3;i++){
+//         eigenvalues[i] = cvmGet(eigenVal,i,0); //Fetching Eigen Value
+//
+//         for(int j=0;j < 3;j++){
+//             eigenvectors[i][j] = cvmGet(eigenVec,i,j); //Fetching each component of Eigenvector i
+//         }
+//     }
+//
+//     return;
+
+/*
+    const T& A11 = A[0];
+    const T& A12 = A[1];
+    const T& A13 = A[2];
+    const T& A22 = A[3];
+    const T& A23 = A[4];
+    const T& A33 = A[5];
+
+    T& eig1 = eigenvalues[0];
+    T& eig2 = eigenvalues[1];
+    T& eig3 = eigenvalues[2];
+
+    T p1 = A12 * A12 + A13 * A13 + A23 * A23;
+    if( p1 < SmallEpsilon ) { // if A is diagonal
+        eig1 = A11;
+        eig2 = A22;
+        eig3 = A33;
+        memset( eigenvectors, 0, sizeof(T) * 9 );
+        eigenvectors[0][0] = eigenvectors[1][1] = eigenvectors[2][2] = 1;
+    }
+    else {   // if A is not diagonal
+        // Compute 1/3 of the trace of matrix A: trace(A)/3
+        T q = ( A11 + A22 + A33 ) / 3;
+        T p2 = (A11-q)*(A11-q) + (A22-q)*(A22-q) + (A33-q)*(A33-q) + 2 * p1;
+        T p = sqrt( p2 / 6);
+
+        // Construct matrix B
+        // B = (1 / p) * (A - q * I), where I is the identity matrix
+        T B11 = (1 / p) * (A11-q);
+        T B12 = (1 / p) * A12;
+        T& B21 = B12;
+        T B13 = (1 / p) * A13;
+        T& B31 = B13;
+        T B22 = (1 / p) * (A22-q);
+        T B23 = (1 / p) * A23;
+        T& B32 = B23;
+        T B33 = (1 / p) * (A33-q);
+
+        // Determinant of a 3 by 3 matrix B
+        // Reference: http://www.mathworks.com/help/aeroblks/determinantof3x3matrix.html
+        T detB = B11*(B22*B33-B23*B32) - B12*(B21*B33-B23*B31) + B13*(B21*B32-B22*B31);
+
+        // In exact arithmetic for a symmetric matrix  -1 <= r <= 1
+        // but computation error can leave it slightly outside this range.
+        T r = detB / 2;
+        T phi;
+        const T M_PI3 = T(3.14159265 / 3);
+        if( r <= -1.0f ) {
+            phi = M_PI3;
+        }
+        else if (r >= 1.0f)
+            phi = 0;
+        else {
+            phi = acos(r) / 3;
+        }
+
+        // The eigenvalues satisfy eig3 <= eig2 <= eig1
+        // Notice that: trace(A) = eig1 + eig2 + eig3
+        eig1 = q + 2 * p * cos( phi );
+        eig3 = q + 2 * p * cos( phi + 2 * M_PI3 );
+        eig2 = 3 * q - eig1 - eig3;
+
+        // make sure that |eig1| >= |eig2| >= |eig3|
+        if( abs(eig1) < abs(eig2) ) std::swap(eig1, eig2);
+        if( abs(eig2) < abs(eig3) ) std::swap(eig2, eig3);
+        if( abs(eig1) < abs(eig2) ) std::swap(eig1, eig2);
+
+        // If eig1 is too small, it is not worth to compute the eigenvectors
+        if( abs(eig1) < BigEpsilon ) {
+            memset( eigenvectors, 0, sizeof(T) * 9 );
+            eigenvectors[0][0] = eigenvectors[1][1] = eigenvectors[2][2] = 1;
+        }
+
+        // Compute the corresponding eigenvectors
+        // Reference: [Cayley-Hamilton_theorem](http://en.wikipedia.org/wiki/Cayley-Hamilton_theorem)
+        // If eig1, eig2, eig3 are the distinct eigenvalues of the matrix A;
+        // that is: eig1 != eig2 != eig3. Then
+        // (A - eig1 * I)(A - eig2 * I)(A - eig3 * I) = 0
+        // Thus the columns of the product of any two of these matrices
+        // will contain an eigenvector for the third eigenvalue.
+        else if( abs(eig1-eig2)<BigEpsilon && abs(eig2-eig3)<BigEpsilon ) {
+            memset( eigenvectors, 0, sizeof(T) * 9 );
+            eigenvectors[0][0] = eigenvectors[1][1] = eigenvectors[2][2] = 1;
+        }
+        else if ( abs(eig1-eig2)<BigEpsilon ) {
+            // tested
+            eigenvector_from_value( A, eig1, eig2, eigenvectors[2] );
+            normal_vectors( eigenvectors[2], eigenvectors[1], eigenvectors[0] );
+        }
+        else if ( abs(eig2-eig3)<BigEpsilon ) {
+            // tested
+            eigenvector_from_value( A, eig2, eig3, eigenvectors[0] );
+            normal_vectors( eigenvectors[0], eigenvectors[1], eigenvectors[2] );
+        }
+        else {
+            // eig1!=eig2 && eig2!=eig3 && eig1!=eig3
+            // tested
+            eigenvector_from_value( A, eig2, eig3, eigenvectors[0] );
+            eigenvector_from_value( A, eig1, eig3, eigenvectors[1] );
+            eigenvector_from_value( A, eig1, eig2, eigenvectors[2] );
+        }
+    }
+*/
+}
+
+/*
+template<typename T=float>
+inline void eigenvector_from_value( const T A[6],
+                                    const T& eig1, const T& eig2, T eigenvector[3] )
+{
+    const T& A11 = A[0];
+    const T& A12 = A[1];
+    const T& A13 = A[2];
+    const T& A22 = A[3];
+    const T& A23 = A[4];
+    const T& A33 = A[5];
+
+    T tempLength = 0, length = 0;
+    T tempVector[3];
+    T colum[3];
+    for( int i=0; i<3; i++ ) {
+        switch( i ) {
+        case 0:
+            colum[0] = A11 - eig2;
+            colum[1] = A12;
+            colum[2] = A13;
+            break;
+        case 1:
+            colum[0] = A12;
+            colum[1] = A22 - eig2;
+            colum[2] = A23;
+            break;
+        default:
+            colum[0] = A13;
+            colum[1] = A23;
+            colum[2] = A33 - eig2;
+            break;
+        }
+        tempVector[0] = colum[0] * (A11 - eig1) + colum[1] * A12 + colum[2] * A13;
+        tempVector[1] = colum[0] * A12 + colum[1] * (A22 - eig1) + colum[2] * A23;
+        tempVector[2] = colum[0] * A13 + colum[1] * A23 + colum[2] * (A33 - eig1);
+
+        tempLength = 0;
+        for( int k=0; k<3; k++ ) {
+            tempLength += tempVector[k] * tempVector[k];
+        }
+
+        // There are three columns in each resulting matrix of ( A-lambda_i * I ) * ( A-lambda_j * I )
+        // We choose the one with the maximum length for the sake of accuracy.
+        if( length < tempLength ) {
+            length = tempLength;
+            memcpy( eigenvector, tempVector, sizeof(T)*3);
+        }
+
+        if( length > BigEpsilon ) break;
+    }
+
+    // The vector is almost zero,
+//     assert( length >= SmallEpsilon && "All eigenvector are zero. " );
+
+    // TODO: for debuging
+//     if( length < SmallEpsilon ) {
+//         std::cerr << "Lenght of vector is too small. Maybe problematic. " << std::endl;
+//     }
+
+    // nromailized the vector
+    length = sqrt( length );
+    for( int k=0; k<3; k++ ) eigenvector[k] /= length;
+}
+*/
diff --git a/brick/processing/frangi_c/src/eigen.h b/brick/processing/frangi_c/src/eigen.h
new file mode 100644
index 0000000000000000000000000000000000000000..1585f407dc309aa6c8cca77a482207f6911d6cd3
--- /dev/null
+++ b/brick/processing/frangi_c/src/eigen.h
@@ -0,0 +1,37 @@
+/* Compute the eigen value decomposition of a 3 by 3 symetric matrix
+Input:
+A = [ A11  A12  A13     = [ A[0]  A[1]  A[2]
+A21  A22  A23          0    A[3]  A[4]
+A31  A32  A33 ];       0     0    A[5] ];
+Output:
+eigenvalues, eigenvectors
+
+Reference: http://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices
+*/
+// #include <cmath>
+// #include <assert.h>
+// #include <algorithm>
+// #include <iostream>
+
+
+
+
+// eigenvalue decomposition of a three by three symmetric matrix
+template<typename T=float>
+void eigen_decomp( const T A[6], T eigenvalues[3], T eigenvectors[3][3] );
+
+// give a 3 by 3 symmetric matrix and its eigenvalues, compute the corresponding eigenvectors
+template<typename T=float>
+inline void eigenvector_from_value( const T A[6], const T& eig1, const T& eig2, T eigenvector[3] );
+
+// normalize a vector
+template<typename T=float>
+inline void normalize( T v[3] );
+
+// compute the cross product of two vectors
+template<typename T=float>
+inline void cross_product( const T v1[3], const T v2[3], T v3[3] );
+
+// given a vector v1, compute two arbitrary normal vectors v2 and v3
+template<typename T=float>
+void normal_vectors(  const T v1[3], T v2[3], T v3[3] ) ;
diff --git a/brick/processing/frangi_c/src/kernel3D.cpp b/brick/processing/frangi_c/src/kernel3D.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..61c440fcabafeabf08b82051b2cc19be6d9ee73f
--- /dev/null
+++ b/brick/processing/frangi_c/src/kernel3D.cpp
@@ -0,0 +1,96 @@
+#include "kernel3D.h"
+#include <opencv2/imgproc/imgproc.hpp>
+
+
+template<typename T>
+Kernel3D<T>::Kernel3D( const cv::Vec3i& n_size )
+{
+    reset( n_size );
+}
+
+
+template<typename T>
+void Kernel3D<T>::reset( const cv::Vec3i& n_size )
+{
+    Data3D<T>::reset( n_size, 0 );
+
+    for( int i=0; i<3; i++ )
+    {
+        center[i] = n_size[i]/2;
+        min_p[i] = -center[i];
+        max_p[i] = ( n_size[i]%2!=0 ) ? ( center[i]+1 ) : ( center[i] );
+    }
+}
+
+
+template <typename U>
+std::ostream& operator<<(std::ostream& out, const Kernel3D<U>& data)
+{
+    // diable output if the size is too big
+    for( int i=0; i<3; i++ )
+    {
+        if ( data.get_size(i)>9 )
+        {
+            std::cout << "I am so sorry. data size is too big to display." << std::endl;
+            return out;
+        }
+    }
+    // output data
+    int x, y, z;
+    for ( z=0; z<data.get_size(2); z++ )
+    {
+        out << "Level " << z << std::endl;
+        for ( y=0; y<data.get_size(1); y++ )
+        {
+            std::cout << "\t";
+            for( x=0; x<data.get_size(0); x++ )
+            {
+                out.precision(3);
+                out << std::scientific << data.at( x, y, z ) << " ";
+            }
+            out << std::endl;
+        }
+    }
+    return out;
+}
+
+
+
+template<typename T>
+Kernel3D<T> Kernel3D<T>::GaussianFilter3D( cv::Vec3i size )
+{
+    smart_assert( typeid(T)==typeid(float) || typeid(T)==typeid(double),
+                  "Can only use float or double" );
+
+    for( int i=0; i<3; i++ )
+        smart_assert( size[i]>0 && size[i]%2!=0, "size should be possitive odd number" );
+
+    // Calculate sigma from size:
+    //         sigma = 0.3 * ( (ksize-1)/2 - 1 ) + 0.8 = 0.15*ksize + 0.35
+    // reference: OpenCv Documentation 2.6.4.0 getGaussianKernel
+    // http://docs.opencv.org/modules/imgproc/doc/filtering.html#creategaussianfilter
+    // If we calculate sigma based on 99.7% Rule:
+    //         sigma = ( size - 1 ) /6 = 0.17 * size - 0.17
+    // But we are flowing the definition of OpenCv here
+    cv::Mat gaussian[3];
+    for( int i=0; i<3; i++ ) gaussian[i] = cv::getGaussianKernel( size[i], 0, CV_64F );
+
+    Kernel3D<T> kernel(size);
+    int x, y, z;
+    for( z=0; z<kernel.get_size_z(); z++ )
+    {
+        for( y=0; y<kernel.get_size_y(); y++ )
+        {
+            for( x=0; x<kernel.get_size_x(); x++ )
+            {
+                kernel.at(x,y,z)
+                    = gaussian[0].at<double>(x)
+                      * gaussian[1].at<double>(y)
+                      * gaussian[2].at<double>(z);
+            }
+        }
+    }
+
+    return kernel;
+}
+
diff --git a/brick/processing/frangi_c/src/kernel3D.h b/brick/processing/frangi_c/src/kernel3D.h
new file mode 100644
index 0000000000000000000000000000000000000000..be82fb603c43bcb807c430ed7e8c6adbc7b95f5b
--- /dev/null
+++ b/brick/processing/frangi_c/src/kernel3D.h
@@ -0,0 +1,78 @@
+#include "data3D.h"
+#include <opencv2/imgproc/imgproc.hpp>
+
+// template<typename T> class Data3D;
+
+template<typename T = float>
+class Kernel3D : public Data3D<T>
+{
+    // operator overload
+    template <typename U>
+    friend std::ostream& operator<<(std::ostream& out, const Kernel3D<U>& data);
+
+public:
+    // constructor and destructors
+    Kernel3D() {};
+    Kernel3D( const cv::Vec3i& n_size );
+    virtual ~Kernel3D() {}
+    // getters
+    const int& min_pos(int i) const
+    {
+        return min_p[i];
+    }
+    const int& max_pos(int i) const
+    {
+        return max_p[i];
+    }
+    virtual const inline T& offset_at(const int& x, const int& y, const int& z) const
+    {
+        return Data3D<T>::at(x+center[0], y+center[1], z+center[2]);
+    }
+    virtual inline T& offset_at(const int& x, const int& y, const int& z)
+    {
+        return Data3D<T>::at(x+center[0], y+center[1], z+center[2]);
+    }
+
+    // setters
+    void reset( const cv::Vec3i& n_size );
+private:
+    // minimun and maximum possition
+    cv::Vec3i min_p, max_p, center;
+public:
+    // Some Global Functions for Getting 3D Kernel's that are commonly used.
+    static Kernel3D<T> GaussianFilter3D( cv::Vec3i size );
+    inline static Kernel3D<T> GaussianFilter3D( int ksize )
+    {
+        smart_assert( typeid(T)==typeid(float) || typeid(T)==typeid(double),
+                      "Can only use float or double" );
+        return GaussianFilter3D( cv::Vec3i(ksize, ksize, ksize) );
+    }
+    inline static Kernel3D<T> dx()
+    {
+        smart_assert( typeid(T)==typeid(float) || typeid(T)==typeid(double),
+                      "Can only use float or double" );
+        Kernel3D<T> dx( cv::Vec3i(3,1,1) );
+        dx.at( 0, 0, 0 ) = -0.5;
+        dx.at( 2, 0, 0 ) =  0.5;
+        return dx;
+    }
+    inline static Kernel3D<T> dy()
+    {
+        smart_assert( typeid(T)==typeid(float) || typeid(T)==typeid(double),
+                      "Can only use float or double" );
+        Kernel3D<T> dy( cv::Vec3i(1,3,1) );
+        dy.at( 0, 0, 0 ) = -0.5;
+        dy.at( 0, 2, 0 ) =  0.5;
+        return dy;
+    }
+    inline static Kernel3D<T> dz()
+    {
+        smart_assert( typeid(T)==typeid(float) || typeid(T)==typeid(double),
+                      "Can only use float or double" );
+        Kernel3D<T> dz( cv::Vec3i(1,1,3) );
+        dz.at( 0, 0, 0 ) = -0.5;
+        dz.at( 0, 0, 2 ) =  0.5;
+        return dz;
+    }
+
+};
diff --git a/brick/processing/frangi_c/src/main.cpp b/brick/processing/frangi_c/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..21584090561e5bd5894df9713389b182bea23283
--- /dev/null
+++ b/brick/processing/frangi_c/src/main.cpp
@@ -0,0 +1,64 @@
+#include <iostream>
+#include "detector.h"
+#include "data3D.h"
+#include "processing.h"
+
+using namespace std;
+using namespace cv;
+
+extern "C" {
+void vesselness(
+    float* array, const int width, const int height, const int depth,
+    float* response,
+    float sigma_from = 1.0f,
+    float sigma_to   = 8.10f,
+    float sigma_step = 0.3f,
+    float alpha = 1.0e-1f,
+    float beta  = 5.0e0f,
+    float gamma = 3.5e5f )
+{
+    // Sigma: Parameters for Vesselness
+    // [sigma_from, sigma_to]: the potential size rang of the vessels
+    // sigma_step: precision of computation
+    // Parameters for vesselness, please refer to Frangi's paper
+    // or this [blog](http://yzhong.co/?p=351)
+
+    Data3D<float> im_short(width, height, depth);
+    im_short.SetFromArray(array);
+
+    Data3D<Vesselness_Sig> vn_sig;
+    VesselDetector::compute_vesselness( im_short, vn_sig, sigma_from, sigma_to, sigma_step,
+                                        alpha, beta, gamma );
+
+//     Data3D<Vesselness_Sig> vn_sig_nms;
+//     IP::non_max_suppress( vn_sig, vn_sig_nms );
+//
+//     Data3D<Vesselness_Sig> vn_sig_et;
+//     IP::edge_tracing( vn_sig_nms, vn_sig_et, 0.45f, 0.05f );
+
+    int x,y,z;
+    float* array_cursor = response;
+    for( z=0; z<vn_sig.get_size_z(); z++ ) {
+        for( y=0; y<vn_sig.get_size_y(); y++ ) {
+            for( x=0; x<vn_sig.get_size_x(); x++ ) {
+                *array_cursor = vn_sig.at(x,y,z).rsp;
+                array_cursor++;
+            }
+        }
+    }
+//     vn_sig_et.CopyToArray(array);
+//     for (int idx=0; idx < 20; idx++) {
+//         cout << array[idx] << endl;
+//     }
+}
+}
+
+int main(void)
+{
+    float* array = new float[100*100*100];
+    float* response = new float[100*100*100];
+
+    vesselness( array, 100,100,100, response );
+
+    return 0;
+}
diff --git a/brick/processing/frangi_c/src/processing.cpp b/brick/processing/frangi_c/src/processing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..984d645050895c2a97a4e70119a6e88cd7981f51
--- /dev/null
+++ b/brick/processing/frangi_c/src/processing.cpp
@@ -0,0 +1,156 @@
+#include <opencv2/core/core.hpp>
+#include <queue>
+#include <iostream>
+#include "types.h"
+
+#include "processing.h"
+#include "processing_code.h"
+
+using namespace cv;
+using namespace std;
+
+void ImageProcessing::non_max_suppress( const Data3D<Vesselness_Sig>& src, Data3D<Vesselness_Sig>& dst )
+{
+    dst.reset( src.get_size() );
+
+    static const float s2 = sqrt(1/2.0f);
+    static const float s3 = sqrt(1/3.0f);
+
+    // 13 major orientations in 3d
+    static const int MAJOR_DIR_NUM = 13;
+    static const Vec3f major_dirs[MAJOR_DIR_NUM] = {
+        // directions along the axis, there are 3 of them
+        Vec3f(1, 0, 0), Vec3f(0, 1, 0), Vec3f(0, 0, 1),
+        // directions that lie within the 3D plane of two axis, there are 6 of them
+        Vec3f(s2, s2,0), Vec3f(0,s2, s2), Vec3f(s2,0, s2),
+        Vec3f(s2,-s2,0), Vec3f(0,s2,-s2), Vec3f(s2,0,-s2),
+        // directions that are equally in between three axis, there are 4 of them
+        Vec3f(s3,s3,s3), Vec3f(s3,s3,-s3), Vec3f(s3,-s3,s3), Vec3f(s3,-s3,-s3)
+    };
+
+    // there are 26 directions in 3D
+    static const int NUM_DIR_3D = 26;
+    Vec3i neighbor3d[NUM_DIR_3D] = {
+        // directions along the axis, there are 6 of them
+        Vec3i(0,0, 1), Vec3i(0, 1,0),  Vec3i( 1,0,0),
+        Vec3i(0,0,-1), Vec3i(0,-1,0),  Vec3i(-1,0,0),
+        // directions that lie within the 3D plane of two axis, there are 12 of them
+        Vec3i(0,1,1), Vec3i(0, 1,-1), Vec3i( 0,-1,1), Vec3i( 0,-1,-1),
+        Vec3i(1,0,1), Vec3i(1, 0,-1), Vec3i(-1, 0,1), Vec3i(-1, 0,-1),
+        Vec3i(1,1,0), Vec3i(1,-1, 0), Vec3i(-1, 1,0), Vec3i(-1,-1, 0),
+        // directions that are equally in between three axis, there are 8 of them
+        Vec3i( 1,1,1), Vec3i( 1,1,-1), Vec3i( 1,-1,1), Vec3i( 1,-1,-1),
+        Vec3i(-1,1,1), Vec3i(-1,1,-1), Vec3i(-1,-1,1), Vec3i(-1,-1,-1),
+    };
+
+    // cross section for the major orientation
+    vector<Vec3i> cross_section[MAJOR_DIR_NUM];
+
+    // Setting offsets that are perpendicular to dirs
+    for( int i=0; i<MAJOR_DIR_NUM; i++ ) {
+        for( int j=0; j<NUM_DIR_3D; j++ ) {
+            // multiply the two directions
+            float temp = major_dirs[i].dot( neighbor3d[j] );
+            // the temp is 0, store this direciton
+            if( abs(temp)<1.0e-5 ) {
+                cross_section[i].push_back( neighbor3d[j] );
+            }
+        }
+    }
+
+    int x,y,z;
+    for( z=0; z<src.get_size_z(); z++ ) {
+        for( y=0; y<src.get_size_y(); y++ ) {
+            for( x=0; x<src.get_size_x(); x++ ) {
+                // non-maximum surpression
+                bool isMaximum = true;
+
+                // Method I:
+                // find the major orientation
+                // assigning the orientation to one of the 13 categories
+                const Vec3f& cur_dir = src.at(x,y,z).dir;
+                int mdi = 0; // major direction id
+                float max_dot_product = 0;
+                for( int di=0; di<MAJOR_DIR_NUM; di++ ) {
+                    float current_dot_product = abs( cur_dir.dot(major_dirs[di]) );
+                    if( max_dot_product < current_dot_product ) {
+                        max_dot_product = current_dot_product;
+                        mdi = di;// update the major direction id
+                    }
+                }
+                for( unsigned int i=0; i<cross_section[mdi].size(); i++ ) {
+                    int ox = x + cross_section[mdi][i][0];
+                    int oy = y + cross_section[mdi][i][1];
+                    int oz = z + cross_section[mdi][i][2];
+                    if( src.isValid(ox,oy,oz) && src.at(x,y,z).rsp < src.at(ox,oy,oz).rsp ) {
+                        isMaximum = false;
+                        break;
+                    }
+                }
+
+                // Method II: Based on Sigma
+                //int sigma = (int) ceil( src.at(x,y,z).sigma );
+                //for( int i = -sigma; i <= sigma; i++ ) for( int j = -sigma; j <= sigma; j++ ) {
+                //	Vec3i offset = src.at(x,y,z).normals[0] * i + src.at(x,y,z).normals[1] * j;
+                //	int ox = x + offset[0];
+                //	int oy = y + offset[1];
+                //	int oz = z + offset[2];
+                //	if( src.isValid(ox,oy,oz) && src.at(x,y,z).rsp < src.at(ox,oy,oz).rsp ) {
+                //		isMaximum = false; break;
+                //	}
+                //}
+
+                if( isMaximum ) {
+                    dst.at(x,y,z) = src.at(x,y,z);
+                }
+            }
+        }
+    }
+}
+
+
+void ImageProcessing::edge_tracing( Data3D<Vesselness_Sig>& src, Data3D<Vesselness_Sig>& dst, const float& thres1, const float& thres2 )
+{
+    Data3D<float> src1d;
+    src.copyDimTo( src1d, 0 );
+    IP::normalize( src1d, 1.0f );
+
+    int x, y, z;
+    std::queue<Vec3i> q;
+    Data3D<unsigned char> mask( src.get_size() );
+    for(z=0; z<src.SZ(); z++) for (y=0; y<src.SY(); y++) for(x=0; x<src.SX(); x++) {
+                if( src1d.at(x,y,z) > thres1 ) {
+                    q.push( Vec3i(x,y,z) );
+                    mask.at(x,y,z) = 255;
+                }
+            }
+
+    Vec3i dir[26];
+    for( int i=0; i<26; i++ ) {
+        int index = (i + 14) % 27;
+        dir[i][0] = index/9%3 - 1;
+        dir[i][1] = index/3%3 - 1;
+        dir[i][2] = index/1%3 - 1;
+    }
+
+    while( !q.empty() ) {
+        Vec3i pos = q.front();
+        q.pop();
+        Vec3i off_pos;
+        for( int i=0; i<26; i++ ) {
+            off_pos = pos + dir[i];
+            if( src.isValid(off_pos) && !mask.at( off_pos ) && src1d.at(off_pos) > thres2 ) {
+                mask.at( off_pos ) = 255;
+                q.push( off_pos );
+            }
+        }
+    }
+
+    dst.reset( src.get_size() );
+    for(z=0; z<src.SZ(); z++) for (y=0; y<src.SY(); y++) for(x=0; x<src.SX(); x++) {
+                if( mask.at( x,y,z ) ) {
+                    dst.at( x,y,z ) = src.at( x,y,z );
+                    // dst.at( x,y,z ).rsp = sqrt( src1d.at( x,y,z ) );
+                }
+            }
+}
diff --git a/brick/processing/frangi_c/src/processing.h b/brick/processing/frangi_c/src/processing.h
new file mode 100644
index 0000000000000000000000000000000000000000..ae736119cc88d2dd7943759c5958e880195c0b78
--- /dev/null
+++ b/brick/processing/frangi_c/src/processing.h
@@ -0,0 +1,14 @@
+#include "data3D.h"
+
+
+namespace ImageProcessing {
+
+void non_max_suppress( const Data3D<Vesselness_Sig>& src, Data3D<Vesselness_Sig>& dst );
+
+void edge_tracing( Data3D<Vesselness_Sig>& src, Data3D<Vesselness_Sig>& dst, const float& thres1, const float& thres2 );
+
+void dir_tracing( Data3D<Vesselness_All>& src_vn, Data3D<Vesselness_Sig>& dst );
+
+void edge_tracing_mst( Data3D<Vesselness_All>& src_vn, Data3D<Vesselness_Sig>& dst, const float& thres1, const float& thres2  );
+
+}
diff --git a/brick/processing/frangi_c/src/processing_code.h b/brick/processing/frangi_c/src/processing_code.h
new file mode 100644
index 0000000000000000000000000000000000000000..6221073d2ed8a22a021de99afbc58688af4ca803
--- /dev/null
+++ b/brick/processing/frangi_c/src/processing_code.h
@@ -0,0 +1,121 @@
+#pragma once
+
+#include <vector>
+#include "data3D.h"
+#include "kernel3D.h"
+#include "smart_assert.h"
+
+
+class Vesselness;
+class Vesselness_Sig;
+class Vesselness_Nor;
+class Vesselness_All;
+
+namespace ImageProcessing {
+template<typename T1, typename T2>
+bool GaussianBlur3D( const Data3D<T1>& src, Data3D<T2>& dst, int ksize, double sigma = 0.0 );
+
+template<typename T>
+void normalize( Data3D<T>& data, T norm_max = 1);
+
+
+///////////////////////////////////////////////////////////////////////////
+// NON-MAXIMUM SUPPRESSION
+void non_max_suppress( const Data3D<Vesselness_Sig>& src, Data3D<Vesselness_Sig>& dst );
+void edge_tracing( Data3D<Vesselness_Sig>& src, Data3D<Vesselness_Sig>& dst, const float& thres1 = 0.85f, const float& thres2 = 0.10f );
+void dir_tracing( Data3D<Vesselness_All>& src, Data3D<Vesselness_Sig>& res_dt );
+void edge_tracing_mst( Data3D<Vesselness_All>& src, Data3D<Vesselness_Sig>& dst, const float& thres1 = 0.85f, const float& thres2 = 0.10f );
+}
+
+namespace IP = ImageProcessing;
+
+
+
+template<typename T1, typename T2>
+bool ImageProcessing::GaussianBlur3D( const Data3D<T1>& src, Data3D<T2>& dst, int ksize, double sigma )
+{
+    smart_return( ksize%2!=0, "kernel size should be odd number", false );
+
+    //////////////////////////////////////////////////////////////////////////////////////
+    // Relationship between Sigma and Kernal Size (ksize)
+    /////////////////////////////
+    // Option I - Based on OpenCV
+    //   Calculate sigma from size:
+    //         sigma = 0.3 * ( (ksize-1)/2 - 1 ) + 0.8 = 0.15*ksize + 0.35
+    //   Calculate size from sigma:
+    //         ksize = ( sigma - 0.35 ) / 0.15 = 6.67 * sigma - 2.33
+    //   Reference: OpenCv Documentation 2.6.4.0 getGaussianKernel
+    //     http://docs.opencv.org/modules/imgproc/doc/filtering.html#creategaussianfilter
+    // Option II - Based on the traditional 99.7%
+    //   Calculate size from sigma:
+    //         ksize = 6 * sigma + 1
+    //   Calculate sigma from ksize:
+    //         sigma = (size - 1)/6 = 0.17 * size - 0.17
+
+    cv::Mat gk = cv::getGaussianKernel( ksize, sigma, CV_64F );
+
+    int hsize = ksize/2;
+    smart_assert( 2*hsize+1==ksize, "Alert: Bug!" );
+
+    cv::Vec3i spos, epos;
+    spos = cv::Vec3i(0, 0, 0);
+    epos = src.get_size();
+
+    // gaussian on x-direction
+    Data3D<T2> tmp1( src.get_size(), T2(0) ); // the data will be set to zero
+    #pragma omp parallel for
+    for( int z=spos[2]; z<epos[2]; z++ ) for( int y=spos[1]; y<epos[1]; y++ ) for( int x=spos[0]; x<epos[0]; x++ ) {
+                double sum = 0.0;
+                for( int i=0; i<ksize; i++) {
+                    int x2 = x+i-hsize;
+                    if( x2>=0 && x2<epos[0] ) {
+                        tmp1.at(x, y, z) = T2( gk.at<double>(i) * src.at(x2, y, z) + tmp1.at(x, y, z));
+                        sum += gk.at<double>(i);
+                    }
+                }
+                tmp1.at(x, y, z) = T2( tmp1.at(x, y, z)/sum );
+            }
+
+
+    // gaussian on y-direction
+    Data3D<T2> tmp2( src.get_size(), T2(0) ); // the data will be set to zero
+    #pragma omp parallel for
+    for( int z=spos[2]; z<epos[2]; z++ ) for( int y=spos[1]; y<epos[1]; y++ ) for( int x=spos[0]; x<epos[0]; x++ ) {
+                double sum = 0.0;
+                for( int i=0; i<ksize; i++) {
+                    int y2 = y+i-hsize;
+                    if( y2>=0 && y2<epos[1] ) {
+                        tmp2.at(x, y, z) = T2( gk.at<double>(i) * tmp1.at(x, y2, z) + tmp2.at(x, y, z));
+                        sum += gk.at<double>(i);
+                    }
+                }
+                tmp2.at(x, y, z) = T2( tmp2.at(x, y, z)/sum );
+            }
+
+    tmp1.reset(); // tmp1 is no long in use. release memory
+
+    // gaussian on z-direction
+    dst.reset( src.get_size() );
+    #pragma omp parallel for
+    for( int z=spos[2]; z<epos[2]; z++ ) for( int y=spos[1]; y<epos[1]; y++ ) for( int x=spos[0]; x<epos[0]; x++ ) {
+                double sum = 0.0;
+                for( int i=0; i<ksize; i++) {
+                    int z2 = z+i-hsize;
+                    if( z2>=0 && z2<epos[2] ) {
+                        dst.at(x, y, z) = T2( dst.at(x, y, z) + gk.at<double>(i) * tmp2.at(x, y, z2) );
+                        sum += gk.at<double>(i);
+                    }
+                }
+                dst.at(x, y, z) = T2( dst.at(x, y, z)/sum );
+            }
+
+    return true;
+}
+
+
+template<typename T>
+void ImageProcessing::normalize( Data3D<T>& data, T norm_max )
+{
+    cv::Vec<T, 2> min_max = data.get_min_max_value();
+    data.getMat() = 1.0 * norm_max / (min_max[1]-min_max[0]) * (data.getMat() - min_max[0]);
+}
diff --git a/brick/processing/frangi_c/src/smart_assert.h b/brick/processing/frangi_c/src/smart_assert.h
new file mode 100644
index 0000000000000000000000000000000000000000..a6d38fddb212e1cf8bf02fcdde4f048a8044c683
--- /dev/null
+++ b/brick/processing/frangi_c/src/smart_assert.h
@@ -0,0 +1,41 @@
+/* Smart Assertion
+ *  - Show assertion under debug mode
+ *  - Print assertion message udner release mode
+ *
+ * Created by Yuchen on Apr 30th, 2013
+ * Last Modified by Yuchen on Jul 2nd, 2013
+ *
+ * Example:
+	Code:
+		int i = -1;
+		smart_assert(i>=0 && i<3, "Index out of bound" );
+	Output:
+		Assertion failed: i>0 && i<3
+		  Messages: Index out of bound
+		  Location: file main.cpp, line 17
+ */
+#ifndef SMART_ASSERT_H
+#define SMART_ASSERT_H
+#include <assert.h>
+#include <iostream>
+
+// The following color is defined using [ANSI colour codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+#define SMA_RED   "\033[0;31m"
+#define SMA_BLACK "\x1b[0;49m"
+
+#define smart_assert( condition, message ) \
+	if( !(condition) ) { \
+		std::cerr << std::endl; \
+		std::cerr << SMA_RED << "Assertion failed: " << (#condition) << std::endl; \
+		std::cerr << "  Messages: " << message << std::endl; \
+		std::cerr << "  Location: file "<< __FILE__ << ", line " << __LINE__ << std::endl; \
+		std::cerr << SMA_BLACK << std::endl;\
+	}
+
+#define smart_return( condition, message, return_value ) \
+    smart_assert( condition, message )\
+	if( !(condition) ) { \
+        return return_value;\
+	}
+
+#endif
diff --git a/brick/processing/frangi_c/src/type_info.h b/brick/processing/frangi_c/src/type_info.h
new file mode 100644
index 0000000000000000000000000000000000000000..b339e18de228817e6fd7d2ddf27e1705b5537486
--- /dev/null
+++ b/brick/processing/frangi_c/src/type_info.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <string>
+#include <opencv2/core/core.hpp>
+
+template <class T> struct TypeInfo {
+    static std::string str()
+    {
+        return "undefined";
+    }
+    static int CV_TYPE()
+    {
+        return -1;
+    }
+};
+
+template <> struct TypeInfo <int> {
+    static std::string str()
+    {
+        return "int";
+    }
+    static int CV_TYPE()
+    {
+        return CV_32S;
+    }
+};
+
+template <> struct TypeInfo <short> {
+    static std::string str()
+    {
+        return "short";
+    }
+    static int CV_TYPE()
+    {
+        return CV_16S;
+    }
+};
+
+template <> struct TypeInfo <float> {
+    static std::string str()
+    {
+        return "float";
+    }
+    static int CV_TYPE()
+    {
+        return CV_32F;
+    }
+};
+
+template <> struct TypeInfo <double> {
+    static std::string str()
+    {
+        return "double";
+    }
+    static int CV_TYPE()
+    {
+        return CV_64F;
+    }
+};
+
+template <> struct TypeInfo <unsigned short> {
+    static std::string str()
+    {
+        return "ushort";
+    }
+    static int CV_TYPE()
+    {
+        return CV_16U;
+    }
+};
+
+template <> struct TypeInfo <unsigned char> {
+    static std::string str()
+    {
+        return "uchar";
+    }
+    static int CV_TYPE()
+    {
+        return CV_8U;
+    }
+};
diff --git a/brick/processing/frangi_c/src/types.h b/brick/processing/frangi_c/src/types.h
new file mode 100644
index 0000000000000000000000000000000000000000..3b647840ee5491e422b164b835965e8328196ac6
--- /dev/null
+++ b/brick/processing/frangi_c/src/types.h
@@ -0,0 +1,297 @@
+#pragma once
+
+#include <opencv2/core/core.hpp>
+#include "type_info.h"
+#include "smart_assert.h"
+
+// VesselnessTypes.h
+// Define 4 different vesselness data types
+// 1) class Vesselness
+//		vesselness response (rsp) + vessel orientation (dir)
+// 2) class Vesselness_Sig
+//		vesselness response (rsp) + vessel orientation (dir) + vessel size (sigma)
+// 3) class Vesselness_Nor
+//		vesselness response (rsp) + vessel orientation (dir) + vessel orientations normals (normals)
+// 4) class Vesselness_All
+//		vesselness response (rsp) + vessel orientation (dir) + vessel orientations normals (normals)
+
+struct Vesselness_Data;
+struct Vesselness_Sig_Data;
+struct Vesselness_Nor_Data;
+struct Vesselness_All_Data;
+
+class Vesselness;
+class Vesselness_Sig;
+class Vesselness_Nor;
+class Vesselness_All;
+
+struct Vesselness_Data {
+    float rsp;			// vesselness response
+    cv::Vec3f dir;			// vessel dirction
+
+    Vesselness_Data( float s = 0.0f ) : rsp( s ), dir( cv::Vec3f(1,0,0) ) { }
+    bool operator>( const Vesselness_Data& right ) const
+    {
+        return ( (this->rsp) > right.rsp );
+    }
+    bool operator<( const Vesselness_Data& right ) const
+    {
+        return ( (this->rsp) < right.rsp );
+    }
+};
+
+struct Vesselness_Sig_Data : public Vesselness_Data {
+    float sigma;		// relative size of the vessel
+
+    Vesselness_Sig_Data( float s = 0.0f ) : Vesselness_Data ( s ) { }
+};
+
+struct Vesselness_Nor_Data : public Vesselness_Data {
+    cv::Vec3f normals[2];	// normals of vessel orientation
+};
+
+struct Vesselness_All_Data : public Vesselness_Data {
+    float sigma;		// relative size of the vessel
+    cv::Vec3f normals[2];	// normals of vessel orientation
+};
+
+
+
+
+
+class Vesselness : public Vesselness_Data {
+public:
+    Vesselness( const float& val = 0 ) : Vesselness_Data( val ) { }
+
+    // Size tells us how much number of float we are using in the structure.
+    // The returned value should be:
+    //  4 for Vesselness;
+    //  5 for Vesselness_Sig;
+    // 10 for Vesselness_Nor;
+    // 11 for Vesselness_All;
+    // This virtual function here will take 2*4 = 8 bytes of data
+    static const int _size = sizeof(Vesselness_Data)/sizeof(float);
+    int size(void) const
+    {
+        return _size;
+    }
+
+    const float& operator[]( const int& i ) const
+    {
+        smart_return( i>=0&&i<_size, "index invalid", *(float*)(this) );
+        return *((float*)(this)+i);
+    }
+
+    float& operator[]( const int& i )
+    {
+        smart_return( i>=0&&i<_size, "index invalid", *(float*)(this) );
+        return *((float*)(this)+i);
+    }
+};
+
+
+class Vesselness_Nor : public Vesselness_Nor_Data {
+public:
+    static const int _size = sizeof(Vesselness_Nor_Data)/sizeof(float);
+
+    const float& operator[]( const int& i ) const
+    {
+        smart_return( i>=0&&i<_size, "index invalid", *(float*)(this) );
+        return *((float*)(this)+i);
+    }
+
+    float& operator[]( const int& i )
+    {
+        smart_return( i>=0&&i<_size, "index invalid", *(float*)(this) );
+        return *((float*)(this)+i);
+    }
+};
+
+class Vesselness_All : public Vesselness_All_Data {
+public:
+    static const int _size = sizeof(Vesselness_All_Data)/sizeof(float);
+
+    Vesselness_All() {}
+
+    Vesselness_All( const Vesselness_Nor& v_Nor, const float& s )
+    {
+        this->dir        = v_Nor.dir;
+        this->normals[0] = v_Nor.normals[0];
+        this->normals[1] = v_Nor.normals[1];
+        this->rsp        = v_Nor.rsp;
+        this->sigma      = s;
+    }
+
+    const float& operator[]( const int& i ) const
+    {
+        smart_return( i>=0&&i<_size, "index invalid", *(float*)(this) );
+        return *((float*)(this)+i);
+    }
+
+    float& operator[]( const int& i )
+    {
+        smart_return( i>=0&&i<_size, "index invalid", *(float*)(this) );
+        return *((float*)(this)+i);
+    }
+};
+
+
+class Vesselness_Sig : public Vesselness_Sig_Data {
+    Vesselness_Sig& operator=( const float& s );
+public:
+    static const int _size = sizeof(Vesselness_Sig_Data)/sizeof(float);
+
+    Vesselness_Sig() { }
+    Vesselness_Sig( float s ) : Vesselness_Sig_Data( s ) { }
+    operator Vesselness( )
+    {
+        Vesselness vn;
+        vn.rsp = this->rsp;
+        vn.dir = this->dir;
+        return vn;
+    }
+    operator float()
+    {
+        return this->rsp;
+    }
+
+    Vesselness_Sig( const Vesselness_All& src )
+    {
+        this->rsp   = src.rsp;
+        this->dir   = src.dir;
+        this->sigma = src.sigma;
+    }
+
+    Vesselness_Sig& operator=( const Vesselness_All& src )
+    {
+        this->rsp   = src.rsp;
+        this->dir   = src.dir;
+        this->sigma = src.sigma;
+        return (*this);
+    }
+
+    const float& operator[]( const int& i ) const
+    {
+        smart_return( i>=0&&i<_size, "index invalid", *(float*)(this) );
+        return *((float*)(this)+i);
+    }
+
+    float& operator[]( const int& i )
+    {
+        smart_return( i>=0&&i<_size, "index invalid", *(float*)(this) );
+        return *((float*)(this)+i);
+    }
+};
+
+// The namespace cv here is necessary.
+// Reference: http://stackoverflow.com/questions/2282349/specialization-of-templateclass-tp-struct-stdless-in-different-namespace
+namespace cv {
+template< > class DataType<     Vesselness     > : public DataType<     Vec<float, Vesselness::_size> > { };
+// template< > class DataDepth<    Vesselness     > : public DataDepth<    Vec<float, Vesselness::_size> > { };
+template< > class traits::Type< Vesselness     > : public traits::Type< Vec<float, Vesselness::_size> > { };
+
+template< > class DataType<     Vesselness_Sig > : public DataType<     Vec<float, Vesselness_Sig::_size> > { };
+// template< > class DataDepth<    Vesselness_Sig > : public DataDepth<    Vec<float, Vesselness_Sig::_size> > { };
+template< > class traits::Type< Vesselness_Sig > : public traits::Type< Vec<float, Vesselness_Sig::_size> > { };
+
+template< > class DataType<     Vesselness_Nor > : public DataType<     Vec<float, Vesselness_Nor::_size> > { };
+// template< > class DataDepth<    Vesselness_Nor > : public DataDepth<    Vec<float, Vesselness_Nor::_size> > { };
+template< > class traits::Type< Vesselness_Nor > : public traits::Type< Vec<float, Vesselness_Nor::_size> > { };
+
+template< > class DataType<     Vesselness_All > : public DataType<     Vec<float, Vesselness_All::_size> > { };
+// template< > class DataDepth<    Vesselness_All > : public DataDepth<    Vec<float, Vesselness_All::_size> > { };
+template< > class traits::Type< Vesselness_All > : public traits::Type< Vec<float, Vesselness_All::_size> > { };
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Reference: These following are defined in <core.hpp>
+//
+//template<> class DataType<int>
+//{
+//public:
+//    typedef int value_type;
+//    typedef value_type work_type;
+//    typedef value_type channel_type;
+//    typedef value_type vec_type;
+//    enum { generic_type = 0, depth = DataDepth<channel_type>::value, channels = 1,
+//           fmt=DataDepth<channel_type>::fmt,
+//           type = CV_MAKETYPE(depth, channels) };
+//};
+//
+//template<> class DataType<float>
+//{
+//public:
+//    typedef float value_type;
+//    typedef value_type work_type;
+//    typedef value_type channel_type;
+//    typedef value_type vec_type;
+//    enum { generic_type = 0, depth = DataDepth<channel_type>::value, channels = 1,
+//           fmt=DataDepth<channel_type>::fmt,
+//           type = CV_MAKETYPE(depth, channels) };
+//};
+//
+//template<typename _Tp, int cn> class DataType<Vec<_Tp, cn> >
+//{
+//public:
+//    typedef Vec<_Tp, cn> value_type;
+//    typedef Vec<typename DataType<_Tp>::work_type, cn> work_type;
+//    typedef _Tp channel_type;
+//    typedef value_type vec_type;
+//    enum { generic_type = 0, depth = DataDepth<channel_type>::value, channels = cn,
+//           fmt = ((channels-1)<<8) + DataDepth<channel_type>::fmt,
+//           type = CV_MAKETYPE(depth, channels) };
+//};
+
+
+template <> struct TypeInfo <Vesselness> {
+    static std::string str()
+    {
+        std::stringstream ss;
+        ss << "float," << Vesselness::_size;
+        return ss.str();
+    }
+    static int CV_TYPE()
+    {
+        return CV_32FC( Vesselness::_size );
+    }
+};
+
+template <> struct TypeInfo <Vesselness_Sig> {
+    static std::string str()
+    {
+        std::stringstream ss;
+        ss << "float," << Vesselness_Sig::_size;
+        return ss.str();
+    }
+    static int CV_TYPE()
+    {
+        return CV_32FC( Vesselness_Sig::_size );
+    }
+};
+
+template <> struct TypeInfo <Vesselness_Nor> {
+    static std::string str()
+    {
+        std::stringstream ss;
+        ss << "float," << Vesselness_Nor::_size;
+        return ss.str();
+    }
+    static int CV_TYPE()
+    {
+        return CV_32FC( Vesselness_Nor::_size );
+    }
+};
+
+template <> struct TypeInfo <Vesselness_All> {
+    static std::string str()
+    {
+        std::stringstream ss;
+        ss << "float," << Vesselness_All::_size;
+        return ss.str();
+    }
+    static int CV_TYPE()
+    {
+        return CV_32FC( Vesselness_All::_size );
+    }
+};
diff --git a/frangi_3d.pxd b/brick/processing/frangi_py/frangi_3d.pxd
similarity index 100%
rename from frangi_3d.pxd
rename to brick/processing/frangi_py/frangi_3d.pxd
diff --git a/frangi_3d.py b/brick/processing/frangi_py/frangi_3d.py
similarity index 96%
rename from frangi_3d.py
rename to brick/processing/frangi_py/frangi_3d.py
index 1f40db5e6ad2e99131feecfbe9160e1930570619..9fbff6bf0aa14fb4f3039566bd2ccfda43856493 100644
--- a/frangi_3d.py
+++ b/brick/processing/frangi_py/frangi_3d.py
@@ -33,7 +33,7 @@
 Follows: https://github.com/ellisdg/frangi3d
 '''
 
-from type import array_t
+from brick.general.type import array_t
 
 import multiprocessing as pl_
 from ctypes import c_float as c_float_t
diff --git a/map_labeling.py b/brick/processing/map_labeling.py
similarity index 98%
rename from map_labeling.py
rename to brick/processing/map_labeling.py
index ee2dfa8609ceb2a652304e664971d05b9d20896c..ca5ff6f13ddf9407a47cbcea5882d731d316cf36 100644
--- a/map_labeling.py
+++ b/brick/processing/map_labeling.py
@@ -29,7 +29,7 @@
 # The fact that you are presently reading this means that you have had
 # knowledge of the CeCILL license and that you accept its terms.
 
-from type import array_t
+from brick.general.type import array_t
 
 import numpy as np_
 
diff --git a/plot.py b/brick/processing/plot.py
similarity index 97%
rename from plot.py
rename to brick/processing/plot.py
index 6b4924b99a7f69504c22a587504d06eef5e0c7b3..d9f36493b7f983a0c6d0298d6d1925a26f0c363b 100644
--- a/plot.py
+++ b/brick/processing/plot.py
@@ -29,10 +29,10 @@
 # The fact that you are presently reading this means that you have had
 # knowledge of the CeCILL license and that you accept its terms.
 
-import dijkstra_1_to_n as dk_
-from extension import extension_t
-from soma import soma_t
-from type import array_t, py_array_picker_h
+import brick.processing.dijkstra_1_to_n as dk_
+from brick.component.extension import extension_t
+from brick.component.soma import soma_t
+from brick.general.type import array_t, py_array_picker_h
 
 from typing import Any, Optional, Sequence, Tuple, Union
 
diff --git a/dijkstra_1_to_n.py b/dijkstra_1_to_n.py
deleted file mode 120000
index c28f560f82fc17f05e899094160a79e5174f378a..0000000000000000000000000000000000000000
--- a/dijkstra_1_to_n.py
+++ /dev/null
@@ -1 +0,0 @@
-../../../../brick/def/dijkstra-img/dijkstra_1_to_n.py
\ No newline at end of file
diff --git a/main.py b/nutrimorph.py
similarity index 97%
rename from main.py
rename to nutrimorph.py
index 674d423ffeaed54ef5b3ce4adb384725ad65eae6..41fdf358980bfce8bee4542bc2c14601054b6710 100644
--- a/main.py
+++ b/nutrimorph.py
@@ -37,11 +37,11 @@
 #     mprof run main.py
 #     mprof plot
 
-import connection as cn_
-import extension as xt_
-import feedback as fb_
+import brick.component.connection as cn_
+import brick.component.extension as xt_
+import brick.component.soma as sm_
+import brick.general.feedback as fb_
 # from main_prm import *
-import soma as sm_
 
 import os as os_
 import sys as sy_
@@ -73,7 +73,7 @@ max_weighted_length_c = None
 min_area_c = None
 in_parallel = None
 
-execfile(sy_.argv[1])
+exec(open(sy_.argv[1]).read())
 
 soma_t = sm_.soma_t
 extension_t = xt_.extension_t
diff --git a/main_prm.py b/parameters.py
similarity index 100%
rename from main_prm.py
rename to parameters.py
diff --git a/auto-py-to-exe.json b/standalone/auto-py-to-exe.json
similarity index 100%
rename from auto-py-to-exe.json
rename to standalone/auto-py-to-exe.json
diff --git a/setup.py b/standalone/setup.py
similarity index 100%
rename from setup.py
rename to standalone/setup.py
diff --git a/standalone-2019-12-06.zip b/standalone/standalone-2019-12-06.zip
similarity index 100%
rename from standalone-2019-12-06.zip
rename to standalone/standalone-2019-12-06.zip