diff --git a/CMakeLists.txt b/CMakeLists.txt
index fcd917f885a1a7b16c46277dec0d84720ac4f1ca..a7bb4914ffe9e2117c2a9380baa31cdbd56ade50 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,6 +109,7 @@ SET(${PROJECT_NAME}_HEADERS
   include/eigenpy/geometry.hpp
   include/eigenpy/geometry-conversion.hpp
   include/eigenpy/memory.hpp
+  include/eigenpy/numpy-type.hpp
   include/eigenpy/registration.hpp
   include/eigenpy/angle-axis.hpp
   include/eigenpy/quaternion.hpp
diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp
index 2f78813b5d9f5dbad20c36c4020785aaabfb458b..89ebdc4009ced99a7d0b40612ac89eaead33d377 100644
--- a/include/eigenpy/details.hpp
+++ b/include/eigenpy/details.hpp
@@ -13,6 +13,7 @@
 #include <iostream>
 
 #include "eigenpy/eigenpy.hpp"
+#include "eigenpy/numpy-type.hpp"
 #include "eigenpy/registration.hpp"
 #include "eigenpy/map.hpp"
 #include "eigenpy/exception.hpp"
@@ -64,125 +65,6 @@ namespace eigenpy
 
   namespace bp = boost::python;
 
-  enum NP_TYPE
-  {
-    MATRIX_TYPE,
-    ARRAY_TYPE
-  };
-  
-  struct NumpyType
-  {
-    
-    static NumpyType & getInstance()
-    {
-      static NumpyType instance;
-      return instance;
-    }
-
-    operator bp::object () { return getInstance().CurrentNumpyType; }
-
-    static bp::object make(PyArrayObject* pyArray, bool copy = false)
-    { return make((PyObject*)pyArray,copy); }
-    
-    static bp::object make(PyObject* pyObj, bool copy = false)
-    {
-      bp::object m;
-      if(isMatrix())
-        m = getInstance().NumpyMatrixObject(bp::object(bp::handle<>(pyObj)), bp::object(), copy);
-//        m = NumpyAsMatrixObject(bp::object(bp::handle<>(pyObj)));
-      else if(isArray())
-        m = bp::object(bp::handle<>(pyObj)); // nothing to do here
-
-      Py_INCREF(m.ptr());
-      return m;
-    }
-    
-    static void setNumpyType(bp::object & obj)
-    {
-      PyTypeObject * obj_type = PyType_Check(obj.ptr()) ? reinterpret_cast<PyTypeObject*>(obj.ptr()) : obj.ptr()->ob_type;
-      if(PyType_IsSubtype(obj_type,getInstance().NumpyMatrixType))
-        switchToNumpyMatrix();
-      else if(PyType_IsSubtype(obj_type,getInstance().NumpyArrayType))
-        switchToNumpyArray();
-    }
-    
-    static void switchToNumpyArray()
-    {
-      getInstance().CurrentNumpyType = getInstance().NumpyArrayObject;
-      getInstance().getType() = ARRAY_TYPE;
-    }
-    
-    static void switchToNumpyMatrix()
-    {
-      getInstance().CurrentNumpyType = getInstance().NumpyMatrixObject;
-      getInstance().getType() = MATRIX_TYPE;
-    }
-    
-    static NP_TYPE & getType()
-    {
-      return getInstance().np_type;
-    }
-    
-    static bp::object getNumpyType()
-    {
-      return getInstance().CurrentNumpyType;
-    }
-    
-    static const PyTypeObject * getNumpyMatrixType()
-    {
-      return getInstance().NumpyMatrixType;
-    }
-    
-    static const PyTypeObject * getNumpyArrayType()
-    {
-      return getInstance().NumpyArrayType;
-    }
-    
-    static bool isMatrix()
-    {
-      return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(getInstance().CurrentNumpyType.ptr()),
-                              getInstance().NumpyMatrixType);
-    }
-    
-    static bool isArray()
-    {
-      if(getInstance().isMatrix()) return false;
-      return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(getInstance().CurrentNumpyType.ptr()),
-                              getInstance().NumpyArrayType);
-    }
-
-  protected:
-    NumpyType()
-    {
-      pyModule = bp::import("numpy");
-#if PY_MAJOR_VERSION >= 3
-      // TODO I don't know why this Py_INCREF is necessary.
-      // Without it, the destructor of NumpyType SEGV sometimes.
-      Py_INCREF(pyModule.ptr());
-#endif
-      
-      NumpyMatrixObject = pyModule.attr("matrix");
-      NumpyMatrixType = reinterpret_cast<PyTypeObject*>(NumpyMatrixObject.ptr());
-      NumpyArrayObject = pyModule.attr("ndarray");
-      NumpyArrayType = reinterpret_cast<PyTypeObject*>(NumpyArrayObject.ptr());
-      //NumpyAsMatrixObject = pyModule.attr("asmatrix");
-      //NumpyAsMatrixType = reinterpret_cast<PyTypeObject*>(NumpyAsMatrixObject.ptr());
-      
-      CurrentNumpyType = NumpyArrayObject; // default conversion
-      np_type = ARRAY_TYPE;
-    }
-
-    bp::object CurrentNumpyType;
-    bp::object pyModule;
-    
-    // Numpy types
-    bp::object NumpyMatrixObject; PyTypeObject * NumpyMatrixType;
-    //bp::object NumpyAsMatrixObject; PyTypeObject * NumpyAsMatrixType;
-    bp::object NumpyArrayObject; PyTypeObject * NumpyArrayType;
-
-    NP_TYPE np_type;
-  };
-
   template<typename MatType, bool IsVectorAtCompileTime = MatType::IsVectorAtCompileTime>
   struct initEigenObject
   {
diff --git a/include/eigenpy/numpy-type.hpp b/include/eigenpy/numpy-type.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..07be4c99a252a54373c31207d33387d0b39511aa
--- /dev/null
+++ b/include/eigenpy/numpy-type.hpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2018-2020, INRIA
+*/
+
+#ifndef __eigenpy_numpy_type_hpp__
+#define __eigenpy_numpy_type_hpp__
+
+#include "eigenpy/fwd.hpp"
+
+#include <iostream>
+#include <patchlevel.h> // For PY_MAJOR_VERSION
+
+namespace eigenpy
+{
+  namespace bp = boost::python;
+   
+  enum NP_TYPE
+  {
+    MATRIX_TYPE,
+    ARRAY_TYPE
+  };
+  
+  struct NumpyType
+  {
+    
+    static NumpyType & getInstance()
+    {
+      static NumpyType instance;
+      return instance;
+    }
+
+    operator bp::object () { return getInstance().CurrentNumpyType; }
+
+    static bp::object make(PyArrayObject* pyArray, bool copy = false)
+    { return make((PyObject*)pyArray,copy); }
+    
+    static bp::object make(PyObject* pyObj, bool copy = false)
+    {
+      bp::object m;
+      if(isMatrix())
+        m = getInstance().NumpyMatrixObject(bp::object(bp::handle<>(pyObj)), bp::object(), copy);
+//        m = NumpyAsMatrixObject(bp::object(bp::handle<>(pyObj)));
+      else if(isArray())
+        m = bp::object(bp::handle<>(pyObj)); // nothing to do here
+
+      Py_INCREF(m.ptr());
+      return m;
+    }
+    
+    static void setNumpyType(bp::object & obj)
+    {
+      PyTypeObject * obj_type = PyType_Check(obj.ptr()) ? reinterpret_cast<PyTypeObject*>(obj.ptr()) : obj.ptr()->ob_type;
+      if(PyType_IsSubtype(obj_type,getInstance().NumpyMatrixType))
+        switchToNumpyMatrix();
+      else if(PyType_IsSubtype(obj_type,getInstance().NumpyArrayType))
+        switchToNumpyArray();
+    }
+    
+    static void switchToNumpyArray()
+    {
+      getInstance().CurrentNumpyType = getInstance().NumpyArrayObject;
+      getInstance().getType() = ARRAY_TYPE;
+    }
+    
+    static void switchToNumpyMatrix()
+    {
+      getInstance().CurrentNumpyType = getInstance().NumpyMatrixObject;
+      getInstance().getType() = MATRIX_TYPE;
+    }
+    
+    static NP_TYPE & getType()
+    {
+      return getInstance().np_type;
+    }
+    
+    static bp::object getNumpyType()
+    {
+      return getInstance().CurrentNumpyType;
+    }
+    
+    static const PyTypeObject * getNumpyMatrixType()
+    {
+      return getInstance().NumpyMatrixType;
+    }
+    
+    static const PyTypeObject * getNumpyArrayType()
+    {
+      return getInstance().NumpyArrayType;
+    }
+    
+    static bool isMatrix()
+    {
+      return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(getInstance().CurrentNumpyType.ptr()),
+                              getInstance().NumpyMatrixType);
+    }
+    
+    static bool isArray()
+    {
+      if(getInstance().isMatrix()) return false;
+      return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(getInstance().CurrentNumpyType.ptr()),
+                              getInstance().NumpyArrayType);
+    }
+
+  protected:
+    NumpyType()
+    {
+      pyModule = bp::import("numpy");
+#if PY_MAJOR_VERSION >= 3
+      // TODO I don't know why this Py_INCREF is necessary.
+      // Without it, the destructor of NumpyType SEGV sometimes.
+      Py_INCREF(pyModule.ptr());
+#endif
+      
+      NumpyMatrixObject = pyModule.attr("matrix");
+      NumpyMatrixType = reinterpret_cast<PyTypeObject*>(NumpyMatrixObject.ptr());
+      NumpyArrayObject = pyModule.attr("ndarray");
+      NumpyArrayType = reinterpret_cast<PyTypeObject*>(NumpyArrayObject.ptr());
+      //NumpyAsMatrixObject = pyModule.attr("asmatrix");
+      //NumpyAsMatrixType = reinterpret_cast<PyTypeObject*>(NumpyAsMatrixObject.ptr());
+      
+      CurrentNumpyType = NumpyArrayObject; // default conversion
+      np_type = ARRAY_TYPE;
+    }
+
+    bp::object CurrentNumpyType;
+    bp::object pyModule;
+    
+    // Numpy types
+    bp::object NumpyMatrixObject; PyTypeObject * NumpyMatrixType;
+    //bp::object NumpyAsMatrixObject; PyTypeObject * NumpyAsMatrixType;
+    bp::object NumpyArrayObject; PyTypeObject * NumpyArrayType;
+
+    NP_TYPE np_type;
+  };
+    
+}
+
+#endif // ifndef __eigenpy_numpy_type_hpp__