Register3D

Register3D is 3D point cloud registration/alignment program I wrote during my PhD (circa 2005). Given two point clouds and an initial guess of the transform, it will iteratively find an optimal transform between the two sets, such that the Euclidean distance between the first set and its nearest neighbours in the second set are minimized.

The initial guess is done by asking the user to select 4 registration points between the two sets. The better the guess the less iterations that are required.

At the time, Register3D was written for a niche file format, which turned out to be useless for most folks. I have since then switched over to use the more common PLY file format, hopefully make it more useful to everyone.

The registration part uses the Trimmed Iterative Closest Point (TrICP) [Chetverikov et al., 2002] algorithm.

Download

https://github.com/nghiaho12/Register3D

This has only been tested on Ubuntu 18.04. It should compile on Windows/MacOS with some effort, but should be do-able because it uses only cross platform libraries.

Samples

Here are some PLY you can download to test out.

NearE1_02_06.ply.zip (67MB)
NearE1_02_07.ply.zip (68MB)

40 thoughts on “Register3D”

  1. Dear Dr. Nghia Ho
    Would it be possible to get Register3D_wx source code?

    I am trying to stitch 3D point cloud (x,y,z) images taken horizontally covering 360 degree.

    I would like to test if your approach is a good start point for this project.

    Best,

    DI

  2. I appreciate your fixing the link.
    Compiled and generated a binary.
    But, is there any sample file I may be able to test?
    Regards,
    DI

  3. I use Cmake to make the project, but the following messages are given. I had set the glew and gsl dir to cmake option.
    Adjust CMAKE_MODULE_PATH to find FindGLEW.cmake or set GLEW_DIR to the
    directory containing a CMake configuration file for GLEW. The file will
    have one of the following names:

    GLEWConfig.cmake
    glew-config.cmake

    1. Hi,

      Those files are included in the subdirectory CMakeModules. It is odd it can’t find it. If you look inside CMakeLists.txt there is a line that says:

      set(CMAKE_MODULE_PATH CMakeModules)

      perhaps try setting the full path. Make sure you are running “cmake .” inside the directory.

  4. thx, I compile the project on windows 7. I fix the problems. which versons are the gsl and wxWidgets library?

    1. I assume it tries to find whatever version is available on the system and uses that.

  5. There are some error examples:
    Error 145 error LNK2001: unresolved external symbol “__declspec(dllimport) public: unsigned short const * __thiscall std::basic_string<unsigned short,struct std::char_traits,class std::allocator >::begin(void)const ” (__imp_?begin@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QBEPBGXZ) wxbase29ud.lib
    Error 340 error LNK2019: unresolved external symbol _cblas_zaxpy referenced in function _gsl_blas_zaxpy libgsl.a

    1. Seems like it has trouble finding the libraries. When you do a Cmake in Windows it will indicate whether it has found wxWidgets and GSL. Check those first.

  6. I should set the GSL and wxWidgets path; I dont know what is wrong with linking the libs.

    1. One other thing to check is to make sure you downloaded the development files for GSL and wxWidgets, and not just the runtime binary,

  7. There are still link error in Register3D_wx on vs2005, could you help me?

    >Linking…
    2>MyANN.obj : error LNK2019: unresolved external symbol “__declspec(dllimport) void __cdecl annClose(void)” (__imp_?annClose@@YAXXZ) referenced in function “public: void __thiscall ANN::Free(void)” (?Free@ANN@@QAEXXZ)
    2>MyANN.obj : error LNK2019: unresolved external symbol “__declspec(dllimport) void __cdecl annDeallocPts(double * * &)” (__imp_?annDeallocPts@@YAXAAPAPAN@Z) referenced in function “public: void __thiscall ANN::Free(void)” (?Free@ANN@@QAEXXZ)
    2>MyANN.obj : error LNK2019: unresolved external symbol “__declspec(dllimport) public: __thiscall ANNkd_tree::~ANNkd_tree(void)” (__imp_??1ANNkd_tree@@QAE@XZ) referenced in function “public: void * __thiscall ANNkd_tree::`scalar deleting destructor'(unsigned int)” (??_GANNkd_tree@@QAEPAXI@Z)
    2>MyANN.obj : error LNK2019: unresolved external symbol “__declspec(dllimport) public: __thiscall ANNkd_tree::ANNkd_tree(double * *,int,int,int,enum ANNsplitRule)” (__imp_??0ANNkd_tree@@QAE@PAPANHHHW4ANNsplitRule@@@Z) referenced in function “public: void __thiscall ANN::SetPoints(class std::vector<struct ICPPoint,class std::allocator > &)” (?SetPoints@ANN@@QAEXAAV?$vector@UICPPoint@@V?$allocator@UICPPoint@@@std@@@std@@@Z)
    2>MyANN.obj : error LNK2019: unresolved external symbol “__declspec(dllimport) double * * __cdecl annAllocPts(int,int)” (__imp_?annAllocPts@@YAPAPANHH@Z) referenced in function “public: void __thiscall ANN::SetPoints(class std::vector<struct ICPPoint,class std::allocator > &)” (?SetPoints@ANN@@QAEXAAV?$vector@UICPPoint@@V?$allocator@UICPPoint@@@std@@@std@@@Z)
    2>MyANN.obj : error LNK2019: unresolved external symbol “__declspec(dllimport) double * __cdecl annAllocPt(int,double)” (__imp_?annAllocPt@@YAPANHN@Z) referenced in function “public: void __thiscall ANN::SetPoints(class std::vector<struct ICPPoint,class std::allocator > &)” (?SetPoints@ANN@@QAEXAAV?$vector@UICPPoint@@V?$allocator@UICPPoint@@@std@@@std@@@Z)

    1. Hi,

      I got around to compiling a Windows version which you can download from the Download section. It was a real pain in the ass to get working, I feel your pain! It was compiled using CodeBlock + Mingw (gcc). I had little hope getting it working on Visual Studio 2010 Express, and don’t have any other copy of Visual Studio to try out.

      Try it out and tell me how it goes!

  8. I fixed the problems in Register3D_wx on vs2005, but some warning boxes are popped when it was running. I could upload the vs2005 project that can be successly linked into exe program. Would you like to make the vs2005 version better?

    1. That’s good news. You should use the latest source code (version 1.1) and overwrite the old one. There’s a limitation with the Windows random number generator not being very good that I addressed.

      If the error is about missing files on startup then you need to copy about.png, help, icon.ico, template.png to the working directory of the exe.

      Did you have to compile wxWidgets on Windows or used wxPack? I won’t be able to test the vs2005 version since I don’t have it, and it’s starting to appear obsolete. I’m happy to upload it for others who may use vs2005.

  9. I am trying code::blocks+mingw32 to build the project, but ld.exe connot find -lGLEW.
    I had built the GLEW in Mingw envirnment.
    btw, how could I upload the vs2005 version?

  10. It is my fault. I used the unix version in windows. I got the win version. It is funning

  11. How do you generate the Cmake file in verson 1.0? I am interesting in cross-platform building-project.

    1. You can email me the vs2005 package and I’ll upload it on this page.

      The Cmake file was generated manually by hand from some tutorial. At the time I was interested in knowing how to use it. But in the future I’ll probably just stick to an ordinary Makefile, easier for small projects.

  12. When I was trying cb+mingw debug version,there are some errors. I dont know why, because I was set the wx include dir. I am newbie in cb+mingw. Could you help me?
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.h|20|error: expected class-name before ‘{‘ token|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|10|error: ‘wxGLCanvas’ has not been declared|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|11|error: invalid static_cast from type ‘void (GLCanvas::*)(wxPaintEvent&)’ to type ‘void (wxEvtHandler::*)(wxPaintEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|12|error: invalid static_cast from type ‘void (GLCanvas::*)(wxMouseEvent&)’ to type ‘void (wxEvtHandler::*)(wxMouseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|13|error: invalid static_cast from type ‘void (GLCanvas::*)(wxKeyEvent&)’ to type ‘void (wxEvtHandler::*)(wxKeyEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|14|error: invalid static_cast from type ‘void (GLCanvas::*)(wxSizeEvent&)’ to type ‘void (wxEvtHandler::*)(wxSizeEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|15|error: invalid static_cast from type ‘void (GLCanvas::*)(wxEraseEvent&)’ to type ‘void (wxEvtHandler::*)(wxEraseEvent&)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|18|error: ‘WX_GL_RGBA’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|18|error: ‘WX_GL_DOUBLEBUFFER’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc||In constructor ‘GLCanvas::GLCanvas(wxWindow*, wxWindowID, ModeType)’:|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|21|error: class ‘GLCanvas’ does not have any field named ‘wxGLCanvas’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc||In member function ‘void GLCanvas::OnPaint(wxPaintEvent&)’:|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|50|error: no matching function for call to ‘wxPaintDC::wxPaintDC(GLCanvas* const)’|
    wxWidgets-2.8.12\include\wx\msw\dcclient.h|96|note: candidates are: wxPaintDC::wxPaintDC(const wxPaintDC&)|
    wxWidgets-2.8.12\include\wx\msw\dcclient.h|82|note: wxPaintDC::wxPaintDC(wxWindow*)|
    wxWidgets-2.8.12\include\wx\msw\dcclient.h|79|note: wxPaintDC::wxPaintDC()|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|52|error: ‘GetContext’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|54|error: ‘SetCurrent’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc||In member function ‘void GLCanvas::OnMouse(wxMouseEvent&)’:|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|78|error: ‘GetContext’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|80|error: ‘SetCurrent’ was not declared in this scope|
    e:\program files\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.4.1\..\..\..\..\include\winuser.h|3987|error: too few arguments to function ‘HWND__* SetFocus(HWND__*)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|82|error: at this point in file|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|101|error: ‘GetGrandParent’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|168|error: ‘Refresh’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc||In member function ‘void GLCanvas::OnResize(wxSizeEvent&)’:|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|173|error: ‘GetContext’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|175|error: ‘SetCurrent’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|179|error: ‘GetClientSize’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc||In member function ‘void GLCanvas::OnKeyDown(wxKeyEvent&)’:|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|191|error: ‘GetContext’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|193|error: ‘SetCurrent’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|274|error: ‘Refresh’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc||In member function ‘void GLCanvas::RenderScene()’:|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|281|error: ‘IsEnabled’ was not declared in this scope|
    e:\program files\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.4.1\..\..\..\..\include\wingdi.h|3025|error: too few arguments to function ‘BOOL SwapBuffers(HDC__*)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|413|error: at this point in file|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc||In member function ‘void GLCanvas::LoadPoints(std::vector<Point, std::allocator >)’:|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|802|error: ‘GetContext’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|804|error: ‘SetCurrent’ was not declared in this scope|
    e:\program files\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.4.1\..\..\..\..\include\wingdi.h||In member function ‘void GLCanvas::RenderMerged()’:|
    e:\program files\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.4.1\..\..\..\..\include\wingdi.h|3025|error: too few arguments to function ‘BOOL SwapBuffers(HDC__*)’|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|1032|error: at this point in file|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc||In member function ‘bool GLCanvas::Draw()’:|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|1077|error: ‘GetContext’ was not declared in this scope|
    H:\Code_4work\register3D_wx\Register3D_wx_1.1_win32\Register3D_wx_1.1_win32\GLCanvas.cc|1079|error: ‘SetCurrent’ was not declared in this scope|
    ||=== Build finished: 46 errors, 0 warnings ===|

    1. Looks like wxWidget wasn’t compiled with OpenGL enabled. For some reason it’s not enabled by default. If it helps I compiled my wxWidgets using instructions based on http://wiki.codeblocks.org/index.php?title=Compiling_wxWidgets_2.8.9_Monolithic_Build_with_openGL_for_Windows

      Follow the bit that says “Editing setup.h”.

      The next step “Building wxWidgets”, I used my own compiling command because I had trouble getting it to compile/link correctly (forgot what problems exactly). This is mine:

      mingw32-make -f makefile.gcc MONOLITHIC=0 SHARED=0 UNICODE=0 USE_OPENGL=1 BUILD=release

      then link all the *.lib files it created with Register3D_wx. The advantage is the program does not require any wxWidgets DLL to run (but the exe is bigger). I got rid of unicode support because it appears to solved one problem I had.

      1. Thx, I follow these steps to rebuild debug verson. It is OK! Could you upload more data (three / four /five data sets) in these area, because it seems difficult to find the matching result right only from two data set.

        1. Glad you got it working. I’ll have a look around my old HD to see what I can find. The two I uploaded is a good sample because there is a large overlap, only separated by a few metres. If you register it correctly you can view them both merged and see how well they align (eg. the walls). Have a look at the Help menu, it will show a screenshot of what I mean. Make sure the “help” directory is in the same path as the executable.

  13. HI,
    I have installed Linux version on Ubuntu 10.10.
    Register3D_wx_1.1.1.tar.bz2 got compiled successfully. But when i run ‘bin/Release/Register3D_wx’
    I got segmentation fault.

    Please help,

    1. Run ./glxgears from the terminal to see if OpenGL is setup correctly first. If you see an animation then it’s working. If not, you need the correct graphics driver. If that still doesn’t work then try the Debug/Register3D_wx binary. Run it through gdb via:

      gdb Debug/Register3D_wx
      run
      (wait for crash)
      bt

      and copy and paste the output to me.

  14. Hi

    Thanks heaps for your work in getting this working on win.

    I am having a problem getting the scans that you have given to opening up in any other program after it has been saved in your Register3D program. raw.gz doesn’t seam to be a file format that any of them like.

    Could you explain how you turned the original scan file into a raw.gz file?

    Also could you explain how I can change it back to a file that other programs like “3DReshaper” can use? like .pts or something.

    Ps.. you could also add the mingwm10.dll file to the bin/release dir to make things simpler for noobs like me.

    Thanks

    1. Hi,

      It’s a non-standard format I used durig my PhD. It’s basically a gziped (compression) file, where the raw file is a binary format of the following struct:

      struct Point
      {
      float x, y, z;
      unsigned char r, g, b, ID;
      };

      It’s documented in the Help menu.

      Writing a program/script to convert it into whatever format you is easy. First decompress the file using 7zip/WinZip. If you’re using C/C++, you can then read it in using:

      FILE *fp = fopen(“scan.raw”, “rb”);
      Point pt;

      while(fread(&pt, sizeof(Point), 1, fp)) {
      // use pt as desired
      }

      1. OK… thanks… I guess I have some scripting to lean ..

        I don’t suppose you would want to change the program so that it could read and save .ply files.. I would think a program like that could make a bit of money. as the only other programs that do point cloud registration are thousands of dollars.

        Put an add on some 3d scanning forms. Sell it for 100 buck a pop.

        Thanks

        1. That’s a reasonable suggestion. But I haven’t touched this program or field for so long, not sure if I’ll get around to it. On the other hand if you need help writing scripts to convert between my format and another I’ll be happy to help. Just let me know.

Comments are closed.