Basic 3D Scanner for Digital 3D Mapping

Introduction: Basic 3D Scanner for Digital 3D Mapping

About: Electronic Engineer

In this project, I will describe and explain the basic foundations of 3D scanning and reconstruction applied primarily to the scanning of small semi-plane objects, and whose operation can be extended to scanning and reconstruction systems that can be installed on remote control aircraft to obtain a 3D model. of the places where the airplane that takes them installed flies

The final idea is to obtain a 3D scan of some place or area, either its exterior or interior, to use it as a digital map (as in the movie of Prometeus)

Step 1:

the idea is to install the entire 3d scanning system on a remote-controlled plane, in order to digitize the virtual map of any area over which it flies in 3d, but for this we started from the beginning of the operation of the laser triangulation
the method of scanning or 3d reconstruction by laser triangulation basically consists of passing a laser beam through a prism that generates a laser stripe to obtain an entire laser stripe that will be projected onto an object to be scanned, and once this laser projection has been obtained on the surface surface From the place to scan, the image must be captured with some type of camera and preferably knowing the angle that is formed with respect to the projection angle of the emitted laser stripe, since each of these images captures the projected laser strips. On the surface of the object, they will be preprocessed to extract the dimensional characteristics of the object to be scanned, and simply scan strip by strip above the object to obtain the profile of its surface in that transverse segment of the object, and subsequently capture the projected strip of the following cross section of the object, to add all the projected stripes together Before all cross sections of the obto we obtain a three-dimensional scan of its surface

Step 2:

Since we have identified our objective, the next step knowing that to take off you must first have your feet firmly on the ground, so we started on the ground with an experimental prototype of a linear 3d scanner, to validate the correct operation of the basic 3d scanner
and as you can see in the image above, I used a PC, OpenCV, Glut of OpenGL, a webcam, a laser, laser farm generator (in this case through a rotational mirror) an electronic linear displacement system (made with a rail and system extracted from an old printer) from a base on which I place the objects to be scanned, wood and plasticine and as you can see in the photo, on the computer: I managed to generate and display with Glut from OpenGL a three-dimensional model reproduced based on the scanned real object (in this case a toy spider)

so it is more than evident that the operating principle is functional, and that with its respective adjustments and adaptations to a flying system it will be able to scan and reproduce a 3d map of the area in which it flies.

But this system will only serve to obtain 3D maps of the external surface of the places it flies over???...

Step 3:

mapping the interior of the caves and ducts (just like in the Prometeus movie)
This 3D scanning system also serves to reconstruct three-dimensional models of the interior of large and hollow objects such as caves, buildings, tunnels, etc. its operating principle is exactly the same as already described and which basically consists of the following:

  1. capture the photo of each projection of the laser stripe on the surface to be scanned
  2. filter and remove color from image
  3. binarize the color with a dynamic image threshold
  4. apply an edge detector to recognize the captured profile of each laser projection cross section
  5. and using segmentation select the appropriate border for the 3d representation of that cross section of the object to be scanned and reconstructed on the virtual 3D map
  6. then these steps are simply repeated for each photo taken in a sub-manner of the laser stripes continuously projected by each sub-section in sub-section.
  7. layer by layer of the representation of the cross sections are added successively until obtaining a point cloud formed by many representations of cross sections of the object to be mapped

Step 4:

Then I pass the programs for image processing of the projections of the superficial laser strips. and of the virtual 3d reconstruction of these sussive transversal representations in the elaborated three-dimensional map model:

image processing:

#include "cv.h" #include "highgui.h" #include //#include #include #include #include

char f=0; char name[]={"0.jpg"}; int n=0,s,x,y; CvScalar sp; FILE *NuPu;

void Writepoints() { char bufferx[33],buffery[33]; itoa (x,bufferx,10); itoa (y,buffery,10); fprintf(NuPu,bufferx); fprintf(NuPu,"\t"); fprintf(NuPu,buffery); fprintf(NuPu,"\n"); }

void noteblockInit() { NuPu=fopen("NuPu.txt","w"); fseek(NuPu,0,0); fprintf(NuPu,"NP:"); fprintf(NuPu,"\n"); }

int main() { char argstr[128]; noteblockInit(); cout<<"Teklea!...:"<>f; name[0]=f; cout<

IplImage* img0=cvLoadImage("00.jpg",0); if(f=='0') { for(y=1;yheight-2;y++) { for(x=1;x<=img0->width-2;x++) { sp=cvGet2D(img0,y,x); if(sp.val[0]>50){Writepoints();n++;} } } } else { for(y=1;yheight-2;y++) { for(x=1;x<=img1->width-2;x++) { sp=cvGet2D(img1,y,x); if(sp.val[0]>50){Writepoints();n++;} } } } char buffer[33]; itoa (n,buffer,10); fprintf(NuPu,"Fin:"); fprintf(NuPu,buffer); fprintf(NuPu,"\n"); fclose(NuPu);

cvWaitKey(0); //_execlp("calc.exe", "calc.exe", argstr, NULL); cvDestroyAllWindows(); cvReleaseImage(&image); cvReleaseImage(&img); cvReleaseImage(&img0); cvReleaseImage(&img1); cvReleaseImage(&img2); return 0; }

3D reconstruction:

////////////////// #ifdef __APPLE__ #include #else #include #include #endif #include #include #include #include #include #include

#define violeta glColor3f(1,0,1) #define azul glColor3f(0,0,1) #define turkeza glColor3f(0,1,1) #define verde glColor3f(0,1,0) #define amarillo glColor3f(1,1,0) #define naranja glColor3f(1,.3,0) #define rojo glColor3f(1,0,0) using namespace std; int s,Boton=1,Pulbut=1; float mx=0,my=0,mtx=0,mty=0,mtz=-5.0; const int Avance=1; string line,Aux; char Caracter='H'; FILE *NuPu; int NP,h,w; float G=0,n=0,cx[5000],cy[5000],x,y,ax,ay,az; int font=(int)GLUT_BITMAP_8_BY_13; static char label[100]; char buffer[3]; GLfloat anguloCuboX = 0.0f; GLfloat anguloCuboY = 0.0f; GLfloat anguloEsfera = 0.0f; GLint ancho=500; GLint alto=500; int hazPerspectiva = 0; void reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(hazPerspectiva) gluPerspective(23.0f, (GLfloat)width/(GLfloat)height, 1.0f, 20.0f); else glOrtho(-1,1, -1, 1,-10, 10); glMatrixMode(GL_MODELVIEW); ancho = width; alto = height; } void Kolorear(int K) { float Hip; x=(cx[s]-320)/480; y=(cy[s]-240)/640; Hip=sqrt( pow(x,2)+pow(y,2) ); if( (Hip>=0)&&(Hip<=.07) ){rojo;} if( (Hip>=.07)&&(Hip<=.14) ){naranja;} if( (Hip>=.14)&&(Hip<=.21) ){amarillo;} if( (Hip>=.21)&&(Hip<=.28) ){verde;} if( (Hip>=.28)&&(Hip<=.35) ){turkeza;} if( (Hip>=.35)&&(Hip<=.42) ){azul;} if( (Hip>=.42)&&(Hip<=.49) ){violeta;} } void drawNuPu(void) { glColor3f(1,1,1); glBegin(GL_LINES); glVertex3f(.2,0,0); glVertex3f(-.2,0,0); glVertex3f(0,.2,0); glVertex3f(0,-.2,0); glEnd(); rojo; glBegin(GL_POINTS); for(n=0;n<10;n++) { for(s=0;s void setOrthographicProjection() { glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, w, 0, h); glScalef(1, -1, 1); glTranslatef(0, -h, 0); glMatrixMode(GL_MODELVIEW); } void renderBitmapString(float x, float y, void *font,char *string) { char *c; glRasterPos2f(x, y); for (c=string; *c != '\0'; c++) { glutBitmapCharacter(font, *c); } } void display() { //mx=468; itoa (mx,buffer,10); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// glLoadIdentity(); glColor3f(1.0, 1.0, 1.0); glRasterPos2f(-1,.9); //glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, "Hello Text"); for(s=0;s<3;s++) { glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24,buffer[s]); } glTranslatef(mty, -mtx, mtz); glRotatef(mx, 1.0f, 0.0f, 0.0f); glRotatef(my, 0.0f, 1.0f, 0.0f); drawNuPu(); /*glColor3f(1.0, 1.0, 1.0); glRasterPos2f(.5,.5); //glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, "Hello Text"); glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, '7');*/ /*glColor3f(1.0f,1.0f,1.0f); setOrthographicProjection(); glPushMatrix(); glLoadIdentity(); renderBitmapString(30,15,(void *)font,"GLUT Tutorial ---________------___________@ 3D Tech");*/ glFlush(); glutSwapBuffers(); anguloCuboX+=0.1f; anguloCuboY+=0.1f; anguloEsfera+=0.2f; } void init() { glClearColor(0,0,0,0); glEnable(GL_DEPTH_TEST); ancho = 500; alto = 500; } void leer() { ifstream myfile("A:/Respaldo sept 2016/D/Respaldos/Respaldo compu CICATA abril 2015/usb1/rekostruccion 3D en Especialidad CICATA/Software/Reconstruccion 3D/R3d_0\bin/Debug/NuPu.txt"); if (myfile.is_open()) { s=0; while(getline(myfile,line)) { if( (line[0]!='N')&&(line[0]!='F')) { Aux=line; line[0]=48; line[1]=48; line[2]=48; line[3]=48; cy[s]=atoi(line.c_str()); Aux[4]=48; Aux[5]=48; Aux[6]=48; //Aux[7]=48; cx[s]=atoi(Aux.c_str()); s++; } } myfile.close(); } else cout << "Unable to open file"; } void Init() { ax=0; ay=0; az=0; //llamar Imagen.exe //char argstr[128]; //_execlp("Imagen.exe", "Imagen.exe", argstr, NULL); ifstream myfile("/NuPu.txt"); if (myfile.is_open()) { while(getline(myfile,line)) { if(line[0]=='F') { line[0]=48; line[1]=48; line[2]=48; line[3]=48; NP=atoi(line.c_str()); //if(NP>1780)NP=1700; cout< void idle() { display(); } void keyboard(unsigned char key, int x, int y) { switch(key) { case 'p': case 'P': hazPerspectiva=1; reshape(ancho,alto); break; case 'o': case 'O': hazPerspectiva=0; reshape(ancho,alto); break; case 27: // escape exit(0); break; } } void raton(int button, int state, int x, int y) { /* GLUT_LEFT_BUTTON 0 GLUT_MIDDLE_BUTTON 1 GLUT_RIGHT_BUTTON 2 GLUT_DOWN 0 GLUT_UP 1 */ Boton=button; Pulbut=state; //mx=y; display(); } void ratmov(int x,int y) { if((Boton==0)&(Pulbut==0)) { mx=y; my=x; } if((Boton==2)&(Pulbut==0)) { mtx=(y/200)-1; mty=(x/200)-1; } if((Boton==1)&(Pulbut==0)) { mtz=-(y/40)-5; } display(); } int main(int argc, char **argv) { /*glutAddMenuEntry() glutAddSubMenu() glutAttachMenu() glutCreateMenu() glutSetMenu() glutStrokeCharacter() glutStrokeLength()*/ /*glReadPixels() read a block of pixels from the frame buffer glGetPixelMapfv() return the specified pixel map glGetPixelMapuiv() return the specified pixel map glGetPointerv() Returns the address of the specified pointer.*/ Init(); leer(); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowPosition(50, 50); glutInitWindowSize(ancho, alto); glutCreateWindow("Cubo 1"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutIdleFunc(idle); glutMouseFunc(raton); glutMotionFunc(ratmov); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }

Step 5:

for the moment I have to stop! ...
but in the next chapter I promise you that I will implement it on my raspberry pi 3 or my jetson nanoboard, already mounted on some remote-controlled aircraft, or on some spider robot to scan the interior of caves

This story will continue! ...

Maps Challenge

Participated in the
Maps Challenge

Be the First to Share


    • Soup & Stew Speed Challenge

      Soup & Stew Speed Challenge
    • Lamps and Lighting Contest

      Lamps and Lighting Contest
    • Make it Move Challenge

      Make it Move Challenge



    1 year ago

    Cool, thanks for sharing! : )


    Reply 1 year ago

    thanks to you!