├── 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 |
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 | 
40 |
41 | schematic:
42 | http://www.octopusengine.eu/2016gal/scanner-schematic.gif
43 |
44 | 220V > 12V
45 | 
46 |
47 | Camera and laser detail:
48 | 
49 | 
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 |
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 |
--------------------------------------------------------------------------------