├── LICENSE.txt ├── README.md ├── images ├── 220v.jpg ├── camera.jpg ├── example-of-processing.jpg ├── fig5-ball-schematic.jpg ├── fig6math.jpg ├── filter13_15_29.png ├── first-scann--it-works.jpg ├── laser.jpg └── scanner-hardware1.jpg ├── loadgit.py ├── oeGPIO.py ├── previous_releases ├── scan020 │ └── scan.py └── scan030 │ ├── oeGPIO.py │ ├── oeGPIO.pyc │ ├── oeHelp.py │ ├── oeHelp.pyc │ ├── scan.py │ └── setup.py ├── scannHelp.py ├── scannInit.py ├── scannSetup.py └── scannStart.py /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Octopusengine.eu 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple3dscanner 2 | 3 | It is only the first step - and my first project here.
4 | I would be very happy to see people "testing" or forking this and creating bigger and better versions ;-)
5 | I'm also grateful for any advice, error correction or recommendations how to proceed...
6 | 7 | fig5-ball-schematic.jpg 8 | 9 | Also look at pictures in a directory images - it was a hard job to explain how easy principle I use ;-)
10 | 11 | What do you need for program testing?
12 | For imperfect testing you can use only Raspberry pi 2 and lowcost webcam, without step motor.
13 | set only:
14 | piCamera=0
15 | webCamera=1
16 | 17 | But I recommend faster Raspberry pi 3 and Raspberrypi camera (full HD)
18 | piCamera=1 is deafult setting
19 | 20 | Program scannStart.py works in Python 2.7 with Pygame module
21 |
22 | How setup ramdisk (temporary files)?
23 | sudo mkdir /home/pi/ramdisk
24 | sudo nano /etc/fstab
25 | and add the line
26 | tmpfs /home/pi/ramdisk tmpfs defaults,size=100M 0 0
27 |
28 | 29 |
30 | simple starting on your Raspberry pi:
31 | sudo python scannStart.py [projectName] [numberOfScann]
32 | numberOfScann = rotation steps
33 | 34 | What more do you need for scanner testing?
35 | red line laser, voltage source 12V + step motor with driver
36 | I am using Nema and A4988
37 | 38 | picture of hardware - first alpha edition (with one laser):
39 | scanner-hardware1.jpg
40 | 41 | schematic:
42 | http://www.octopusengine.eu/2016gal/scanner-schematic.gif
43 |
44 | 220V > 12V
45 | 220V.jpg
46 |
47 | Camera and laser detail:
48 | camera.jpg
49 | laser.jpg
50 | 51 | 52 | 53 |
54 | simple idea - how to create points cloud: 55 | 56 | loop { 57 | 58 | > take a picture 59 | 60 | > recognize red line 61 | 62 | > transform to xyz 63 | 64 | } 65 | 66 | example of processing
67 | https://www.instagram.com/p/BEYRg99R7TE/?taken-by=octopusengine 68 |
69 | 70 | fig6math.jpg 71 |
72 | 73 | How to convert found a red dot to xyz coordinates?
74 | https://www.mathsisfun.com/polar-cartesian-coordinates.html
75 | When we know a point in Polar Coordinates (r, θ), and we want it in Cartesian Coordinates (x,y) we solve a right triangle with a known long side and angle
76 | 77 |
78 | Point cloud file xyz is simple "txt" format:
79 | x1 y1 z1
80 | x2 y2 z2
81 | ...
82 | xn yn zn
83 |
84 | for exaple 5 points:
85 | -14.7657331345 -8.525 177
86 | -10.7387150069 -6.2 178
87 | -1.34233937587 -0.775 179
88 | 32.2161450208 18.6 288
89 | 41.6125206518 24.025 311
90 | 49.666556907 28.675 318
91 |
92 |
93 | 94 | For treatment use program MeshLab
95 | MeshLab is an open source, portable, and extensible system for the processing and editing of unstructured 3D triangular meshes. 96 | The system is aimed to help the processing of the typical not-so-small unstructured models arising in 3D scanning, providing a set of tools for editing, cleaning, healing, inspecting, rendering and converting this kind of meshes.
97 | 98 | How import point cloud (yourFile.xyz) to MeshLab software?
99 | Basic step:
100 | File / Import mesh...
101 |
102 | 103 |
104 |

History:

105 | 106 | 2016/04
107 | 0.10 first experiment, it works! I was really surprised ;-)
108 | 0.20 my first "open source" post for GitHub
109 | 0.30 oeHelp, oeGPIO
110 | 0.33 filter1 - digital filter Xn=aver(Xn-1,Xn,Xn+1)
111 | 2016/08
112 | testing on Raspberry Pi 3
113 | small bug correcting
114 |
115 | 116 |

ToDo:

117 | 118 | better user input / GUI (Pygame?)
119 | faster computing / C library or Cython testing
120 | second line laser (FET transistor switching?)
121 | digital filter - RAW data 2D filtering
122 | light detection / daylight.. color of object..
123 | converting to STL
124 | 125 |

Licence:

126 | MIT
127 | Copyright (c) 2016 Octopusengine.eu
128 | -------------------------------------------------------------------------------- /images/220v.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/220v.jpg -------------------------------------------------------------------------------- /images/camera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/camera.jpg -------------------------------------------------------------------------------- /images/example-of-processing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/example-of-processing.jpg -------------------------------------------------------------------------------- /images/fig5-ball-schematic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/fig5-ball-schematic.jpg -------------------------------------------------------------------------------- /images/fig6math.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/fig6math.jpg -------------------------------------------------------------------------------- /images/filter13_15_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/filter13_15_29.png -------------------------------------------------------------------------------- /images/first-scann--it-works.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/first-scann--it-works.jpg -------------------------------------------------------------------------------- /images/laser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/laser.jpg -------------------------------------------------------------------------------- /images/scanner-hardware1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/images/scanner-hardware1.jpg -------------------------------------------------------------------------------- /loadgit.py: -------------------------------------------------------------------------------- 1 | import random, sys, os, time 2 | from time import sleep 3 | 4 | import urllib2 5 | import zipfile 6 | import shutil 7 | 8 | import subprocess 9 | import string 10 | 11 | #---------------------------------- 12 | ramdiskPath = "/home/pi/ramdisk/" 13 | updaPathDw = r"/home/pi/github/" 14 | urlUpdate='https://raw.githubusercontent.com/octopusengine/simple3dscanner/master/' 15 | #https://github.com/octopusengine/simple3dscanner/blob/master/scan.py 16 | #https://raw.githubusercontent.com/octopusengine/simple3dscanner/master/scan.py 17 | 18 | 19 | numUpdaOk=0 20 | numUpda=0 21 | 22 | #---------------------------------- 23 | def doNetUpdate(upath,ufile): 24 | global numUpda, numUpdaOk 25 | print ufile 26 | #hh.neXtxt2("d0",ufile) 27 | try: 28 | ufileNet=ufile 29 | #ufileNet=ufile.lower() 30 | urlr = urllib2.urlopen(urlUpdate+ufileNet) 31 | utemp = urlr.read() 32 | urlr.close() 33 | 34 | fwu = open(ramdiskPath+ufile,"w") 35 | fwu.write(utemp) 36 | fwu.close() 37 | #hh.neXtxt2("p2",ramdiskPath+ufile) 38 | 39 | shutil.copy(ramdiskPath+ufile,upath+ufile) 40 | hh.iSRAdd("net[ok] >> "+ufile) 41 | numUpdaOk=numUpdaOk+1 42 | print "file update OK: "+str(numUpdaOk) 43 | time.sleep(0.5) 44 | 45 | except: 46 | numUpda=numUpda+1 47 | #hh.infoRC(".....",tr2,"WHI") 48 | #hh.iSRAdd("net[   ] .. "+ufile) 49 | #hh.neXtxt2("p2","file update OK: "+str(numUpdaOk)) 50 | #hh.neXtxt2("p3","no update... "+str(numUpda)) 51 | 52 | proc = (numUpda+numUpdaOk)*10 53 | #hh.neXcmd("page print") 54 | #hh.neXval("j0",str(int(proc))) 55 | #hh.neXtxt2("pp",str(int(proc))+"%") 56 | 57 | 58 | print "load scan.py > github" 59 | 60 | doNetUpdate(updaPathDw,'scannStart.py') 61 | doNetUpdate(updaPathDw,'oeGPIO.py') 62 | doNetUpdate(updaPathDw,'scannHelp.py') 63 | doNetUpdate(updaPathDw,'scannInit.py') 64 | doNetUpdate(updaPathDw,'scannSetup.py') 65 | 66 | -------------------------------------------------------------------------------- /oeGPIO.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : oeGPIO.py 3 | 4 | #----------------------------------- 5 | ## simple 3d scanner 6 | ##---------------------------------- 7 | import time 8 | from time import sleep 9 | import RPi.GPIO as GPIO # for step motor 10 | 11 | EN2 = 22 12 | DIR2 = 27 13 | STEP2 = 17 14 | dStep=0.00001 15 | 16 | #--------------------------------- 17 | 18 | def oeSetupGPIO(): 19 | global EN2,DIR2,STEP2 20 | GPIO.setwarnings(False) 21 | GPIO.setmode(GPIO.BCM) 22 | 23 | #GPIO.setup(END2, GPIO.IN, pull_up_down = GPIO.PUD_DOWN) 24 | GPIO.setup(EN2, GPIO.OUT) 25 | GPIO.setup(DIR2, GPIO.OUT) 26 | GPIO.setup(STEP2, GPIO.OUT) 27 | 28 | #--------------------------------- 29 | 30 | def oeMotCCWs(steps,speed): 31 | global EN2,DIR2,STEP2, dStep 32 | #step motor - count clock wise 33 | nas=2 #1600/ot #8=400 #16=200 for 360 34 | GPIO.output(EN2, False) 35 | GPIO.output(DIR2, False) 36 | for tx in range(steps*nas): 37 | time.sleep(dStep*speed) 38 | GPIO.output(STEP2, True) 39 | time.sleep(dStep*speed) 40 | GPIO.output(STEP2, False) 41 | GPIO.output(EN2, True) #aret. 42 | 43 | #--------------------------------- 44 | 45 | def oeMotOff(): 46 | GPIO.output(EN2, True) #motor switch off 47 | 48 | #---end--- 49 | -------------------------------------------------------------------------------- /previous_releases/scan020/scan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : scan.py 3 | 4 | #----------------------------------- 5 | ## simple 3d scanner 6 | ## 2016/04 - I am beginner, but it works ;-) 7 | ## 0.20 - only create xyz point cloud - directly posible import to MeshLab 8 | ##---------------------------------- 9 | import os, math, pygame, time 10 | from datetime import datetime 11 | from time import sleep 12 | import RPi.GPIO as GPIO # for step motor 13 | import picamera 14 | 15 | width = 1600 #1024 #800 16 | height = 1200 #768 #600 17 | screen_width=width 18 | screen_height=height 19 | #-----------------------------main---setup 20 | dayLight=1 21 | lightObject=0 22 | brownObject=0 23 | blueObject=1 24 | 25 | loop=600 #testing 10/20/40/100/200/400... 26 | name="robot" #scanning object name 27 | 28 | sWidth =100 # width scann 29 | sTop=430 # 10 liska 11cm #150 vetsi / 300 ping 3.6cm ###12cm max 3 cm min --0-500 30 | sBott=550 31 | 32 | axisX=width/2-120 #800 ##1200 33 | endX=axisX-sWidth+50 #50 nejde vubec za osu 34 | startx=width-axisX-sWidth-20 35 | nasDef = 1.9 36 | 37 | rad =height-sTop-sBott+1 #rows 38 | #------------------------------/main-setup 39 | sMat = [[ 0 for i in range(loop+1)] for j in range(rad+1) ] 40 | 41 | ramdiskPath = "/home/pi/ramdisk/" #temporary data storage 42 | #print "0,0", sMat[0][0] 43 | #print math.sin(0) 44 | pi=math.pi 45 | 46 | #define colors for painting 47 | cRed=(255,0,0) 48 | cGre=(0,255,0) 49 | cBlu=(0,0,255) 50 | cWhi=(255,255,255) 51 | 52 | kroky=1 53 | bb=0 #num points 54 | 55 | #------------------------------------ 56 | pygame.init() 57 | 58 | datName=name+datetime.now().strftime("%Y_%m_%d_%H_%M") 59 | xyzFile = ramdiskPath+datName+'.xyz' 60 | 61 | #cam = pygame.camera.Camera("/dev/video0",(width,height),"RGB") 62 | cam= picamera.PiCamera() 63 | if dayLight: 64 | cam.brightness = 30 ##30ok #25 #35 65 | else: 66 | cam.brightness = 60 67 | 68 | if lightObject: 69 | cam.brightness = 10 #15ok 70 | cam.contrast=30 71 | 72 | cam.start_preview() 73 | #cam.annotate_background = cBlu #Color('blue') 74 | #cam.annotate_foreground = cWhi #Color('yellow') 75 | cam.annotate_text = "octopusengine 3D scanner" 76 | sleep(3) 77 | cam.stop_preview() 78 | 79 | #--------------------------------- 80 | EN2 = 22 81 | DIR2 = 27 82 | STEP2 = 17 83 | 84 | GPIO.setwarnings(False) 85 | GPIO.setmode(GPIO.BCM) 86 | 87 | #GPIO.setup(END2, GPIO.IN, pull_up_down = GPIO.PUD_DOWN) 88 | GPIO.setup(EN2, GPIO.OUT) 89 | GPIO.setup(DIR2, GPIO.OUT) 90 | GPIO.setup(STEP2, GPIO.OUT) 91 | #--------------------------------- 92 | 93 | dStep=0.00001 94 | def motCCWs(steps,slow): #down - modif old slow = step.Tilt / new mot2 95 | nas=2 #1600/ot #8=400 #16=200 for 360 96 | GPIO.output(EN2, False) 97 | GPIO.output(DIR2, False) 98 | for tx in range(steps*nas): 99 | time.sleep(dStep*slow) 100 | GPIO.output(STEP2, True) 101 | time.sleep(dStep*slow) 102 | GPIO.output(STEP2, False) 103 | GPIO.output(EN2, True) #aretace 104 | 105 | def oneScan(angleStep): #=angle 106 | global sMat, bb 107 | global fw 108 | filename = "temp"+datName+".jpg" 109 | print filename 110 | filepath = ramdiskPath+filename 111 | cam.capture(filepath) 112 | 113 | screen=pygame.display.set_mode([screen_width,screen_height]) 114 | 115 | obr = pygame.image.load(filepath) 116 | obrRect = obr.get_rect() 117 | screen.blit(obr, obrRect) 118 | pygame.display.flip() 119 | 120 | rr=0 121 | x=startx 122 | y=sTop 123 | # x,y points of screen 124 | pygame.draw.line(screen,cBlu,(10,sTop),(width-10,sTop),2) 125 | pygame.draw.line(screen,cBlu,(10,height-sBott),(width-10,height-sBott),2) 126 | pygame.draw.line(screen,cBlu,(width/2,sTop),(width/2,height-sBott),2) 127 | pygame.draw.line(screen,cBlu,(width-startx,sTop),(width-startx,height-sBott),2) 128 | pygame.draw.line(screen,cBlu,(endX,sTop),(endX,height-sBott),2) 129 | pygame.draw.line(screen,cWhi,(axisX,sTop),(axisX,height-10),2) 130 | screen.set_at((10,10),cRed) 131 | screen.set_at((11,11),cRed) 132 | pygame.display.flip() 133 | 134 | ##hard experiments (light set) 135 | if dayLight: 136 | fR=60 #64 #50 137 | fG=64 138 | fB=64 139 | else: 140 | fR=80 141 | fG=50 142 | fB=50 143 | 144 | if brownObject: 145 | fR=70 #64 146 | fG=50 147 | fB=64 148 | 149 | if blueObject: 150 | fR=16 #64 151 | fG=160 152 | fB=160 153 | 154 | while yfR and cG-200: 171 | angle=float(2*pi/(loop-1)*angleStep) 172 | rx=float(math.sin(angle)*xx*nasDef) 173 | ry=float(math.cos(angle)*xx*nasDef) 174 | rz = y 175 | co = str(rx)+" "+str(ry)+" "+str(rz) 176 | cop = str(angle)+" > "+str(xx)+" > "+co 177 | #print cop 178 | fp.write(co+"\n") 179 | 180 | y=y+kroky 181 | x=startx 182 | #else: 183 | x=x+1 184 | if x>width-endX: 185 | x=startx 186 | y=y+kroky 187 | screen.set_at((width-x,y),cBlu) 188 | 189 | 190 | #screen.blit(obr, obrRect) 191 | pygame.display.flip() 192 | time.sleep(2) 193 | 194 | #============================================================================= 195 | scanname = ramdiskPath+datName+'.csv' 196 | fp = open(xyzFile,"a") 197 | for st in range (loop-1): 198 | oneScan(st) 199 | print "--------------" 200 | co=str(st+1)+"/"+str(loop-1)+" ("+str(bb)+")" 201 | print co 202 | cam.annotate_text = co 203 | motCCWs(1600/(loop-1),500) #1600 na 360 #100:22.5st,16ot # 204 | fp.close() 205 | #---------------------------------------- 206 | #control display data 207 | posun=6 208 | 209 | scanname = ramdiskPath+datName +'.txt' 210 | scannami = ramdiskPath+datName +'.jpg' 211 | fw = open(scanname,"a") 212 | screen=pygame.display.set_mode([screen_width,screen_height]) 213 | for u in range (loop-1): 214 | for r in range (rad): 215 | x=sMat[r][u] 216 | fw.write(str(x)+",") 217 | screen.set_at((u*posun+x,r),cGre) 218 | pygame.display.flip() 219 | fw.write("\n") 220 | fw.close() 221 | 222 | sx =1200 #center 223 | sy =800 224 | screen.set_at((sx,sy),cGre) 225 | for u in range (0,360): 226 | x=200 227 | angle=float(u*2*pi) 228 | rx=int(float(sx+math.sin(angle)*x)) 229 | ry=int(float(sy+math.cos(angle)*x)) 230 | screen.set_at((rx,ry),cRed) 231 | 232 | pygame.display.flip() 233 | 234 | GPIO.output(EN2, True) #motor switch off 235 | #---end--- 236 | -------------------------------------------------------------------------------- /previous_releases/scan030/oeGPIO.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : oeGPIO.py 3 | 4 | #----------------------------------- 5 | ## simple 3d scanner 6 | ##---------------------------------- 7 | import time 8 | from time import sleep 9 | import RPi.GPIO as GPIO # for step motor 10 | 11 | EN2 = 22 12 | DIR2 = 27 13 | STEP2 = 17 14 | dStep=0.00001 15 | 16 | #--------------------------------- 17 | 18 | def oeSetupGPIO(): 19 | global EN2,DIR2,STEP2 20 | GPIO.setwarnings(False) 21 | GPIO.setmode(GPIO.BCM) 22 | 23 | #GPIO.setup(END2, GPIO.IN, pull_up_down = GPIO.PUD_DOWN) 24 | GPIO.setup(EN2, GPIO.OUT) 25 | GPIO.setup(DIR2, GPIO.OUT) 26 | GPIO.setup(STEP2, GPIO.OUT) 27 | 28 | #--------------------------------- 29 | 30 | def oeMotCCWs(steps,speed): 31 | global EN2,DIR2,STEP2, dStep 32 | #step motor - coun clock wise 33 | nas=2 #1600/ot #8=400 #16=200 for 360 34 | GPIO.output(EN2, False) 35 | GPIO.output(DIR2, False) 36 | for tx in range(steps*nas): 37 | time.sleep(dStep*speed) 38 | GPIO.output(STEP2, True) 39 | time.sleep(dStep*speed) 40 | GPIO.output(STEP2, False) 41 | GPIO.output(EN2, True) #aret. 42 | 43 | #--------------------------------- 44 | 45 | def oeMotOff(): 46 | GPIO.output(EN2, True) #motor switch off 47 | 48 | #---end--- 49 | -------------------------------------------------------------------------------- /previous_releases/scan030/oeGPIO.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/previous_releases/scan030/oeGPIO.pyc -------------------------------------------------------------------------------- /previous_releases/scan030/oeHelp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : oeHelp.py 3 | 4 | #----------------------------------- 5 | ## simple 3d scanner - octopusengine.eu 6 | 7 | def help(): 8 | print ("---------------------------------------") 9 | print ("octopusengine/simple3dscanner 0.30 help") 10 | print ("---------------------------------------") 11 | print ("scan.py [projectName] [numberOfScann]") 12 | print ("Correct input example: scan.py test 100") 13 | print ("First argument > project name (default: noname)" ) 14 | print ("Second argument > number of scann (default: 6, for quick test)") 15 | print ("---------------------------------------") 16 | 17 | 18 | #---end--- 19 | -------------------------------------------------------------------------------- /previous_releases/scan030/oeHelp.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/simple3dscanner/4c62e0ba1bd4b1217106d81ce9ac0a2c6851244a/previous_releases/scan030/oeHelp.pyc -------------------------------------------------------------------------------- /previous_releases/scan030/scan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : scan.py 3 | 4 | #----------------------------------- 5 | ## simple 3d scanner - octopusengine.eu 6 | ## 2016/04 - I am beginner, but it works ;-) 7 | ## 0.21 create xyz point cloud - directly posible import to MeshLab 8 | ## 0.30 oeGPIO, oeHelp 9 | ##---------------------------------- 10 | import os, sys, math, pygame, time 11 | from datetime import datetime 12 | from time import sleep 13 | #import RPi.GPIO as GPIO # for step motor 14 | import picamera 15 | 16 | from oeGPIO import * #oeSetupGPIO 17 | from oeHelp import * 18 | 19 | width = 1600 #1024 #800 20 | height = 1200 #768 #600 21 | screen_width=width 22 | screen_height=height 23 | 24 | #-----------------------------main---setup 25 | #print 'Number of arguments:', len(sys.argv), 'arguments.' 26 | #print 'Argument List:', str(sys.argv) 27 | try: 28 | name=str(sys.argv[1]) 29 | #name="robot" #scanning object name 30 | except: 31 | help() 32 | name="noname" 33 | print ("Default project name: "+ name ) 34 | 35 | if name=="help": 36 | help() 37 | sys.exit() 38 | #err.stop 39 | else: 40 | print ("First argument > project name: "+ name ) 41 | 42 | try: 43 | print ("Second argument - number scan: %s" % str(sys.argv[2])) 44 | loop = int(sys.argv[2])+1 45 | except: 46 | loop=6 #testing 10/20/40/100/200/400... 47 | print ("Default loop: "+ str(loop) ) 48 | 49 | #--------------------------------- 50 | dayLight=1 51 | lightObject=0 52 | brownObject=0 53 | blueObject=1 54 | 55 | sWidth =100 # width scann 56 | sTop=430 # 10 liska 11cm #150 vetsi / 300 ping 3.6cm ###12cm max 3 cm min --0-500 57 | sBott=550 58 | 59 | axisX=width/2-120 #800 ##1200 60 | endX=axisX-sWidth+50 #50 nejde vubec za osu 61 | startx=width-axisX-sWidth-20 62 | nasDef = 1.9 63 | 64 | rad =height-sTop-sBott+1 #rows 65 | #------------------------------/main-setup 66 | sMat = [[ 0 for i in range(loop+1)] for j in range(rad+1) ] 67 | 68 | ramdiskPath = "/home/pi/ramdisk/" #temporary data storage 69 | #print "0,0", sMat[0][0] 70 | #print math.sin(0) 71 | pi=math.pi 72 | 73 | #define colors for painting 74 | cRed=(255,0,0) 75 | cGre=(0,255,0) 76 | cBlu=(0,0,255) 77 | cWhi=(255,255,255) 78 | 79 | kroky=1 80 | bb=0 #num points 81 | 82 | #------------------------------------ 83 | pygame.init() 84 | 85 | datName=name+datetime.now().strftime("%Y_%m_%d_%H_%M") 86 | xyzFile = ramdiskPath+datName+'.xyz' 87 | 88 | #cam = pygame.camera.Camera("/dev/video0",(width,height),"RGB") 89 | cam= picamera.PiCamera() 90 | if dayLight: 91 | cam.brightness = 30 ##30ok #25 #35 92 | else: 93 | cam.brightness = 60 94 | 95 | if lightObject: 96 | cam.brightness = 10 #15ok 97 | cam.contrast=30 98 | 99 | cam.start_preview() 100 | #cam.annotate_background = cBlu #Color('blue') 101 | #cam.annotate_foreground = cWhi #Color('yellow') 102 | cam.annotate_text = "octopusengine 3D scanner" 103 | sleep(3) 104 | cam.stop_preview() 105 | 106 | #--------------------------------- 107 | #oeGPIO.setupGPIO() 108 | oeSetupGPIO() 109 | #--------------------------------- 110 | ##hard experiments 111 | if dayLight: 112 | fR=60 #64 #50 113 | fG=64 114 | fB=64 115 | else: 116 | fR=80 117 | fG=50 118 | fB=50 119 | 120 | if brownObject: 121 | fR=70 #64 122 | fG=50 123 | fB=64 124 | 125 | if blueObject: 126 | fR=16 #64 127 | fG=160 128 | fB=160 129 | #--------------------------------- 130 | 131 | def oneScan(angleStep): #=angle 132 | global sMat, bb 133 | global fw 134 | filename = "temp"+datName+".jpg" 135 | print filename 136 | filepath = ramdiskPath+filename 137 | cam.capture(filepath) 138 | 139 | screen=pygame.display.set_mode([screen_width,screen_height]) 140 | 141 | obr = pygame.image.load(filepath) 142 | obrRect = obr.get_rect() 143 | screen.blit(obr, obrRect) 144 | pygame.display.flip() 145 | 146 | # x,y points of screen 147 | pygame.draw.line(screen,cBlu,(10,sTop),(width-10,sTop),2) 148 | pygame.draw.line(screen,cBlu,(10,height-sBott),(width-10,height-sBott),2) 149 | pygame.draw.line(screen,cBlu,(width/2,sTop),(width/2,height-sBott),2) 150 | pygame.draw.line(screen,cBlu,(width-startx,sTop),(width-startx,height-sBott),2) 151 | pygame.draw.line(screen,cBlu,(endX,sTop),(endX,height-sBott),2) 152 | pygame.draw.line(screen,cWhi,(axisX,sTop),(axisX,height-10),2) 153 | screen.set_at((10,10),cRed) 154 | screen.set_at((11,11),cRed) 155 | pygame.display.flip() 156 | 157 | rr=0 158 | x=startx #camera picture X 159 | y=sTop #camera picture Y 160 | 161 | while yfR and cG main scann data 174 | sMat[y-sTop][angleStep]=xx 175 | bb = bb+1 176 | 177 | if xx!=0 and xx>-200: 178 | angle=float(2*pi/(loop-1)*angleStep) 179 | rx=float(math.sin(angle)*xx*nasDef) 180 | ry=float(math.cos(angle)*xx*nasDef) 181 | rz = y 182 | co = str(rx)+" "+str(ry)+" "+str(rz) 183 | cop = str(angle)+" > "+str(xx)+" > "+co 184 | #print cop 185 | fp.write(co+"\n") 186 | 187 | y=y+kroky 188 | x=startx 189 | #else: 190 | x=x+1 191 | if x>width-endX: 192 | x=startx 193 | y=y+kroky 194 | screen.set_at((width-x,y),cBlu) 195 | 196 | 197 | #screen.blit(obr, obrRect) 198 | pygame.display.flip() 199 | time.sleep(2) 200 | 201 | #======================================== main scan loop ===================================== 202 | scanname = ramdiskPath+datName+'.csv' 203 | fp = open(xyzFile,"a") 204 | for st in range (loop-1): 205 | oneScan(st) 206 | print "--------------" 207 | co=name+" "+str(st+1)+"/"+str(loop-1)+" ("+str(bb)+")" 208 | print co 209 | cam.annotate_text = co 210 | oeMotCCWs(1600/(loop-1),100) #1600 na 360 #100:22.5st,16ot # 211 | fp.close() 212 | #======================================== /main scan loop ===================================== 213 | 214 | #control display data 215 | posun=6 216 | 217 | scanname = ramdiskPath+datName +'.txt' #RAW scann data 218 | scannami = ramdiskPath+datName +'.jpg' 219 | fw = open(scanname,"a") 220 | screen=pygame.display.set_mode([screen_width,screen_height]) 221 | for u in range (loop-1): 222 | for r in range (rad): 223 | x=sMat[r][u] 224 | fw.write(str(x)+",") 225 | screen.set_at((u*posun+x,r),cGre) 226 | pygame.display.flip() 227 | fw.write("\n") 228 | fw.close() 229 | 230 | sx =1200 #center 231 | sy =800 232 | screen.set_at((sx,sy),cGre) 233 | for u in range (0,360): 234 | x=200 235 | angle=float(u*2*pi) 236 | rx=int(float(sx+math.sin(angle)*x)) 237 | ry=int(float(sy+math.cos(angle)*x)) 238 | screen.set_at((rx,ry),cRed) 239 | 240 | pygame.display.flip() 241 | 242 | oeMotOff() 243 | time.sleep(10) 244 | #---end--- 245 | -------------------------------------------------------------------------------- /previous_releases/scan030/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import setup, find_packages 4 | 5 | setup( 6 | name='scan', 7 | version='0.03', 8 | description='Simple 3D scanner', 9 | long_description=''.join(open('README.rst').readlines()), 10 | keywords='3dscanner', 11 | author='Jan Copak', 12 | author_email='jan.copak@soukroma.cz', 13 | license='MIT', 14 | url='https://github.com/octopusengine/simple3dscanner', 15 | packages=[p for p in find_packages() if p != 'test'], 16 | package_data={'scan': ['templates/*']}, 17 | install_requires=['jinja2'], 18 | classifiers=[ 19 | 'Development Status :: 3 - Alpha', 20 | 'Intended Audience :: Developers', 21 | 'License :: OSI Approved :: MIT License', 22 | 'Operating System :: POSIX :: Linux', 23 | 'Programming Language :: Python', 24 | 'Programming Language :: Python :: 2.7' 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /scannHelp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : scannHelp.py - 2014/05 3 | #----------------------------------- 4 | ## simple 3d scanner - octopusengine.eu 5 | 6 | def help(): 7 | print("-----------------------------------------------------") 8 | print("simple3dscanner 0.35 --- help --- ") 9 | print("-----------------------------------------------------") 10 | print("scannStart.py [projectName] [numberOfScann]") 11 | print("Correct input example: scannStart.py test 100") 12 | print(">> First argument > test => project name" ) 13 | print(" (default: noname)" ) 14 | print(" create file projectName-dateTime.xzy" ) 15 | print(">> Second argument > 100 => number of scann") 16 | print(" 100 > one step 360/100 degrees") 17 | print(" (default: 6, for quick test)") 18 | print("-----------------------------------------------------") 19 | print("(c) https://github.com/octopusengine/simple3dscanner") 20 | #---end--- 21 | -------------------------------------------------------------------------------- /scannInit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : scanInit.py 3 | #----------------------------------- 4 | ## simple 3d scanner - octopusengine.eu 5 | ## 2016/05 - external init file 6 | ##---------------------------------- 7 | import os, sys, math 8 | global dayLight,lightObject,brownObject,blueObject 9 | 10 | width = 1600 #1024 #800 11 | height = 1200 #768 #600 12 | screen_width=width 13 | screen_height=height 14 | 15 | #define colors for painting 16 | cRed=(255,0,0) 17 | cGre=(0,255,0) 18 | cBlu=(0,0,255) 19 | cYel=(255,255,0) 20 | cWhi=(255,255,255) 21 | 22 | #------------------another 23 | pi=math.pi 24 | #--------------------------------- 25 | 26 | 27 | #---end--- 28 | -------------------------------------------------------------------------------- /scannSetup.py: -------------------------------------------------------------------------------- 1 | # Filename : scanSetup.py 2 | #----------------------------------- 3 | ## simple 3d scanner - octopusengine.eu 4 | ## 2016/05 - external setup file 5 | ##---------------------------------- 6 | global dayLight,lightObject,brownObject,blueObject 7 | 8 | #--- camera selection: 9 | piCamera=1 #ok-beta 10 | webCamera=0 #alpha-testing 11 | mobilCamera=0 #no 12 | 13 | #--- experimental light and object setup: 14 | dayLight=1 15 | lightObject=1 16 | brownObject=0 17 | blueObject=0 18 | 19 | #--- filters 20 | filter13=1 #ok-beta 21 | filter15=1 #ok-beta 22 | filter29=1 #alpha 23 | ## ---------------------------- 24 | 25 | 26 | ##hard experiments 27 | if dayLight: 28 | fR=60 #64 #50 29 | fG=64 30 | fB=64 31 | else: 32 | fR=90 33 | fG=50 34 | fB=50 35 | 36 | if brownObject: 37 | fR=70 #64 38 | fG=50 39 | fB=64 40 | 41 | if blueObject: 42 | fR=16 #64 43 | fG=160 44 | fB=160 45 | 46 | #if lightObject: 47 | # fR=150 #64 48 | # fG=64 49 | # fB=64 50 | 51 | #---end--- 52 | -------------------------------------------------------------------------------- /scannStart.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : scan.py 3 | #----------------------------------- 4 | ## simple 3d scanner - octopusengine.eu 5 | ## 2016/04 - I am beginner, but it works ;-) 6 | ## 0.21 create xyz point cloud - directly posible import to MeshLab 7 | ## 0.33 oeGPIO, scannHelp, filter1 8 | ## 0.34 prepare for piCamera/webCamera/mobilCamera 9 | ## 0.35 scanSetup, scanInit, filter2 10 | ##---------------------------------- 11 | import os, sys, math, pygame, time 12 | from datetime import datetime 13 | from time import sleep 14 | #import RPi.GPIO as GPIO # for step motor 15 | from oeGPIO import * #oeSetupGPIO 16 | from scannHelp import * 17 | from scannSetup import * 18 | from scannInit import * 19 | #---------------------------------- 20 | pygame.init() 21 | 22 | if piCamera: 23 | import picamera 24 | cam= picamera.PiCamera() 25 | 26 | if webCamera: 27 | import pygame.camera 28 | pygame.camera.init() 29 | 30 | width = 800 #1024 #800 31 | height = 600 #768 #600 32 | cam = pygame.camera.Camera("/dev/video0",(width,height),"RGB") 33 | 34 | 35 | #-----------------------------main---setup 36 | #print 'Number of arguments:', len(sys.argv), 'arguments.' 37 | #print 'Argument List:', str(sys.argv) 38 | try: 39 | name=str(sys.argv[1]) 40 | #name="robot" #scanning object name 41 | except: 42 | help() 43 | name="noname" 44 | print ("Default project name: "+ name ) 45 | datName=name+datetime.now().strftime("%Y_%m_%d_%H_%M") 46 | 47 | if name=="help": 48 | help() 49 | sys.exit() 50 | #err.stop 51 | else: 52 | print ("First argument > project name: "+ name ) 53 | 54 | try: 55 | print ("Second argument - number scan: %s" % str(sys.argv[2])) 56 | loop = int(sys.argv[2])+1 57 | except: 58 | loop=6 #testing 10/20/40/100/200/400... 59 | print ("Default rotation steps: "+ str(loop) ) 60 | 61 | if piCamera: 62 | sWidth =100 # width scann 63 | sTop=430 ##430 # 10 ...11cm #150 // ping 3.6cm ###12cm max 3 cm min --0-500 64 | sBott=550 65 | 66 | if webCamera: 67 | sWidth =120 68 | sTop=100 # height-height/9 69 | sBott=200 70 | 71 | axisX=width/2-120 #800 ##1200 72 | endX=axisX-sWidth+50 #50 only right side from axis 73 | startx=width-axisX-sWidth-20 74 | nasDef = 1.55 # 75 | 76 | rad =height-sTop-sBott+1 #rows 77 | 78 | sMat = [[ 0 for i in range(loop+1)] for j in range(rad+1) ] #main data matrix 79 | fMat = [[ 0 for i in range(loop+1)] for j in range(rad+1) ] #filter data matrix 80 | sVec = [ 0 for j in range(rad+1) ] #auxiliary vector 81 | ramdiskPath = "/home/pi/ramdisk/" #temporary data storage 82 | 83 | xyzFile = ramdiskPath+datName+'.xyz' 84 | #------------------------------/main-setup 85 | kroky=1 86 | bb=0 #num points 87 | #------------------------------------ 88 | if piCamera: 89 | if dayLight: 90 | cam.brightness = 30 ##30ok #25 #35 91 | else: 92 | cam.brightness = 60 93 | 94 | if lightObject: 95 | cam.brightness = 10 #15ok 96 | cam.contrast=30 97 | 98 | cam.start_preview() 99 | #cam.annotate_background = cBlu #Color('blue') 100 | #cam.annotate_foreground = cWhi #Color('yellow') 101 | cam.annotate_text = "octopusengine 3D scanner" 102 | sleep(3) 103 | cam.stop_preview() 104 | 105 | #--------------------------------- 106 | #oeGPIO.setupGPIO() 107 | oeSetupGPIO() 108 | #--------------------------------- 109 | # one image: 110 | def oneScan(angleStep): #=angle 111 | global sMat, bb, fw 112 | filename = "temp"+datName+".jpg" 113 | print filename 114 | filepath = ramdiskPath+filename 115 | 116 | 117 | if piCamera: 118 | cam.capture(filepath) 119 | 120 | if webCamera: 121 | cam.start() 122 | image = cam.get_image() 123 | cam.stop() 124 | pygame.image.save(image, filepath) 125 | 126 | screen=pygame.display.set_mode([screen_width,screen_height]) 127 | 128 | obr = pygame.image.load(filepath) 129 | obrRect = obr.get_rect() 130 | screen.blit(obr, obrRect) 131 | pygame.display.flip() 132 | 133 | # x,y points of screen 134 | #pygame.draw.line(screen,cRed,(1000,sTop),(1000,height-10),2) 135 | pygame.draw.line(screen,cBlu,(10,sTop),(width-10,sTop),2) 136 | pygame.draw.line(screen,cBlu,(10,height-sBott),(width-10,height-sBott),2) 137 | pygame.draw.line(screen,cBlu,(width/2,sTop),(width/2,height-sBott),2) 138 | pygame.draw.line(screen,cBlu,(width-startx,sTop),(width-startx,height-sBott),2) 139 | pygame.draw.line(screen,cBlu,(endX,sTop),(endX,height-sBott),2) 140 | pygame.draw.line(screen,cWhi,(axisX,sTop),(axisX,height-10),2) 141 | screen.set_at((10,10),cRed) 142 | screen.set_at((11,11),cRed) 143 | pygame.display.flip() 144 | 145 | rr=0 146 | x=startx #camera picture X 147 | y=sTop #camera picture Y 148 | 149 | #main loop - search between (sBott and sTop) x (startx and endX) - for every angle 150 | while yfR and cGcR: 161 | x=x-1 162 | if cR2>cR1: 163 | x=x-1 164 | #sensitivity to red color 165 | rr=rr+1 166 | #print rr,x,y,cR,cG,cB 167 | #screen.set_at((width-x,y),cGre) 168 | #screen.set_at((width-x-1,y),cGre) 169 | xx=width-x-axisX # = distance from axis > main scann data 170 | sMat[y-sTop][angleStep]=xx # matrix RAW data y,a,d (y-sTop,angleStep,xx) 171 | bb = bb+1 172 | 173 | y=y+kroky 174 | x=startx 175 | #else: 176 | x=x+1 177 | if x>width-endX: 178 | x=startx 179 | y=y+kroky 180 | screen.set_at((width-x,y),cBlu) 181 | 182 | #screen.blit(obr, obrRect) 183 | 184 | 185 | y=sTop+2 186 | while y-200: 241 | angle=float(2*pi/(loop-1)*angleStep) 242 | rx=float(math.sin(angle)*xx*nasDef) 243 | ry=float(math.cos(angle)*xx*nasDef) 244 | rz = y 245 | co = str(rx)+" "+str(ry)+" "+str(rz) 246 | #cop = str(angle)+" > "+str(xx)+" > "+co 247 | #print cop 248 | fp.write(co+"\n") 249 | y=y+kroky 250 | #time.sleep(0.2) 251 | 252 | def filter29(): #=angle 253 | global sMat, fMat, bb, fp 254 | for angleStep in range (2,loop-2): 255 | y=sTop+1 256 | while y0 and a2>0 and a3>0): 287 | averOk = (a1+a2+a3)/3 288 | else: 289 | if (a1==0 and a2 and a3): 290 | averOk = (a2+a3)/2 291 | if (a1 and a2==0 and a3): 292 | averOk = (a1+a3)/2 293 | if (a1 and a2 and a3==0): 294 | averOk = (a1+a2)/2 295 | return averOk 296 | 297 | 298 | #======================================== main scan loop ===================================== 299 | scanname = ramdiskPath+datName+'.csv' 300 | #fp = open(xyzFile,"a") 301 | for st in range (loop-1): 302 | oneScan(st) 303 | print "--------------" 304 | co=name+" "+str(st+1)+"/"+str(loop-1)+" ("+str(bb)+")" 305 | print co 306 | if piCamera: 307 | cam.annotate_text = co 308 | oeMotCCWs(1600/(loop-1),100) #1600 na 360 #100:22.5st,16ot # 309 | #fp.close() 310 | 311 | if filter29: 312 | print "--------------" 313 | print "filter29" 314 | filter29() 315 | 316 | print "filter29 again" 317 | filter29() 318 | 319 | #------------------------ 320 | fp = open(xyzFile,"a") 321 | print "--------------" 322 | print "save xyz" 323 | for st in range (loop-1): 324 | oneSave(st) 325 | fp.close() 326 | #======================================== /main scan loop ===================================== 327 | #control display data 328 | shift=5 329 | zoom=2 330 | 331 | scannRaw = ramdiskPath+datName +'.txt' #RAW scann data 332 | scannImg = ramdiskPath+datName +'.jpg' 333 | fw = open(scannRaw,"a") 334 | screen=pygame.display.set_mode([screen_width,screen_height]) 335 | for u in range (loop-1): #angle 336 | for r in range (rad): #row 337 | x=sMat[r][u] 338 | fw.write(str(x)+",") 339 | screen.set_at((u*shift+x*zoom,r*zoom),cGre) 340 | pygame.display.flip() 341 | fw.write("\n") 342 | fw.close() 343 | 344 | rawImage() 345 | 346 | pygame.display.flip() 347 | pygame.image.save(screen,scannImg) 348 | 349 | oeMotOff() 350 | time.sleep(6) 351 | 352 | #---end--- 353 | --------------------------------------------------------------------------------