-
Notifications
You must be signed in to change notification settings - Fork 1
/
table-py.py
372 lines (266 loc) · 15.8 KB
/
table-py.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
#!/usr/bin/python2
"""
Todo:
make the nested ifs more human readable by moving them to functions
"""
# import all python 2.7 native modules
import serial
import sys
import os
import subprocess
import time
import atexit
# import SerialException class from non-native module: serial
from serial import SerialException
def exit_handler(serial_name):
"""In the event of the user interrupting the program using a 'ctrl + c' command
or 'keyboard interrupt', this function will attempt to send the off command
'N' to the arduino in order to switch off the motors and give feedback to the
user if the off command was written to the USB serial port.
Args:
serial_name (str): The name of the serial port as defined in main()
Returns:
N/A
"""
# define a serial object using serial_name and connect at the baud rate in use by the arduino
ser = serial.Serial(serial_name, 9600)
# write an 'N' character to the serial buffer
ser.write('N')
# print a message to the terminal telling the user the program is now closing
print "\n\nApplication terminating, closing down motor, Goodbye!\n"
def main():
# try until user uses a keyboard interrupt
try:
# register the exit_handler function to exit event
atexit.register(exit_handler)
# serial name will be different for a Mac or Linux/GNU
serial_name = "/dev/cu.usbmodem1411" # set for Mac
# define working directory by grabbing the path of the current script
# can be changed to something like '/home/user/Documents/subfolder' but currently just grabs the directory the python script is in!
working_directory = sys.path[0]
# try establishing a serial connection
try:
# define serial using name grabbed above
ser = serial.Serial(serial_name, 9600)
# look for flags passed as arguments in the system command line
options = sys.argv
# if there is an override '--o' flag then the user will be prompted for the custom number of steps they want the table to make, it's usually around 35 per turn.
if "--o" in options:
# loop until user gives correct answer
while True:
# ask user to define how many pictures they need per 360 degs via the terminal
iterations = raw_input(
"\nHow many iterations do you need? ")
# try to convert the answer given by the user to a integer
try:
iterations = int(iterations)
break
# if variable isnt convertable to an integer ask again for correct input
except:
print "\nThis is not an integer (whole number), please type in an integer value!\n"
# otherwise use the default value.
else:
iterations = 38
# if there is an override '--c' flag then the user will be prompted for the number of seconds they want the delay to be between each step and photo. The camera will turn off and the table will just turn with the object on.
if "--c" in options:
# do not use the camera
camera_override = True
# loop until user gives correct answer
while True:
# ask user to define how many seconds they want to wait between each step (to simulate presence of a camera)
time_interval = raw_input("\nHow many seconds would you like inbetween each step? ")
# try to convert the answer given by the user to a integer
try:
time_interval = int(time_interval)
break
# if variable isnt convertable to an integer ask again for correct input
except:
print "\nThis is not an integer (whole number), please type in an integer value!\n"
# otherwise assume a camera is attached!
else:
# loop until user gives correct answer re: institution ID
while True:
# loop until user gives correct answer re: motor type
while True:
# ask for user to choose between a 5v or a 12v motor by typing a 1 or a 2
motor_type = raw_input("\nWhat type of motor are you using (e.g. 1 for 5v, 2 for 12v)? ")
# if answer isn't a 1 or a 2 ask again for correct input
if motor_type not in ['1', '2']:
print "\nPlease select only a '1' or a '2'"
else:
break
# terminal text spacer to make terminal more readable
print ""
# ask for user to define a unique sample code for each set of photos
institution_ID = raw_input("What is the institutional code and unique id for this specimen (e.g. NMBE_XXXX)? ")
# terminal text spacer to make terminal more readable
print ""
# ask user to confirm their choice of sample code
confirmation = raw_input("Are you sure you wish to use this ID#: " + institution_ID + "? Type y/n: ")
# if the user confirms move on
if confirmation == "y":
# terminal text spacer to make terminal more readable
print ""
break
# if the user rejects ask for input again
elif confirmation == "n":
# terminal text spacer to make terminal more readable
print ""
# loop until the user defines if the sample being photographed is the dorsal or ventral side
while True:
# ask user to use a 'd' or a 'v' to select dorsal or ventral
position = raw_input("Dorsal or Ventral? Type d/v: ")
# terminal text spacer to make terminal more readable
print ""
# ask user to confirm their choice of side
confirmation = raw_input("Are you sure you want to use this position: " + position + "? Type y/n: ")
# if the user confirms move on
if confirmation == "y":
# if dorsal selected set position variable to "DORSAL"
if position == "d":
position = "DORSAL"
break
# if ventral selected set position variable to "VENTRAL"
elif position == "v":
position = "VENTRAL"
break
# if neither dorsal or ventral selected then ask user to reconfirm their input
else:
print "\n'd' or 'v' has not been selected, please specify ventral or dorsal by following the instructions!\n"
# if the user rejects ask for input again
elif confirmation == "n":
# terminal text spacer to make terminal more readable
print ""
# flag camera as in user
camera_override = False
# adjust the timings if you need to here, default is 0 but if the camera is slow, user can set this higher
time_interval = 0
# warn the user of the default settings and the override options.
if "--o" in options and "--c" in options:
print "\nRunning program with user specified steps and camera overridden!\n"
elif "--o" in options and "--c" not in options:
print "\nRunning program with user specified steps and camera active!\n"
elif "--o" not in options and "--c" in options:
print "\nRunning program with default steps and camera overridden!\n"
else:
print "Running program in default mode: of 35 photos and steps.\n\nIf you require more per revolution include the flag '--o'.\n\nIf you want to turn off the camera include '--c'.\n"
# define gphoto2 command to capture images
cmd = ['gphoto2', '--auto-detect', '--capture-image', '--keep']
# change to true to attempt test image capture at the start of capture
check = False
# here the for loop runs three times prompting the user to change camera positions
for i in range(0, 3):
for a in range(1, int(iterations)+1):
# code to execute in loop only if camera has been overidden using the --c flag
if camera_override == True:
if motor_type == '1':
ser.write('1')
elif motor_type == '2':
ser.write('2')
# pause program for time interval defined earlier
time.sleep(time_interval)
else:
# check for camera errors! If it can't connect to the camera to take a picture it will
if check != True:
print "Taking a test image to see if the camera is working!"
# attempt to take a picture using gphoto to control the camera
try:
# use subprocess to run the gphoto2 terminal program shelless and pass it the command list defined above and the working directory
subprocess.check_output(
cmd, cwd=working_directory)
# terminal text spacer to make terminal more readable
print ""
print "Camera works, runing through iterations now!\n"
# flag check as True once complete so that no more test photos get taken with each loop
check = True
# if the subprocess fails tell the user and exit the program
except:
print "/nThere seems to be an issue communicating with your camera, please check it is plugged in, the battery is charged, it is unmounted, and a supported model for gphoto2!\n"
break
# use subprocess to run the gphoto2 terminal program shelless and pass it the command list defined above and the working directory
process = subprocess.Popen(cmd, cwd=working_directory)
output = process.communicate()[0]
time.sleep(0.25)
print output
process.wait()
# reset proceed variable each iteration
proceed = ''
# write to the serial port which motor is in use
if motor_type == '1':
ser.write('1')
print str(a) + " Using motor 1"
elif motor_type == '2':
ser.write('2')
print str(a) + " Using motor 2"
# wait for the arduino to signal it is done turning by reading the serial port and looking for a 'D'
while (proceed != 'D'):
proceed = ser.read()
print proceed
pass
print ""
# define the last_int variable by grabbing last count of loop
last_int = a-1
# wait for 3 seconds
time.sleep(3)
# write an 'N' to the USB serial port to instruct the arudino to turn off the motor
ser.write('N')
# wait for the user to confirm if they wish to keep taking photographs and finish 3 rounds at 3 camera angles?
while True:
# if number of rounds is not equal to 3 (index 0) then keep asking user for input each round
if i != 2:
continue_run = raw_input("Run " + str(i+1) + " of 3 complete, please reposition the camera, type 'y' and enter to confirm or 'n' to cancel and stop the run. ")
# check if user has used a 'y' or an 'n'
if continue_run != 'y':
# if user rejects continue with an 'n' then break loop
if continue_run == 'n':
print "\nEnding run, continuing with image processing...\n"
break
# ask user again if answer was not a 'y' or a 'n'
else:
print "\nPlease type either 'y' or 'n'\n"
# if user confirms with a 'y' then break loop
elif continue_run == 'y':
break
else:
print "\nPlease type either 'y' or 'n'\n"
# if user rejects continue with an 'n' then break loop
if continue_run == 'n':
break
# if number of rounds is equal to 3 (index 0) then break loop
if i == 2:
print "Run 3 of 3 complete, moving on to image processing!"
break
print ""
# define gphoto2 command to list files on camera
cmd = ['gphoto2', '--auto-detect', '--list-files']
# use subprocess to run the gphoto2 terminal program shelless and pass it the command list defined above and the working directory
process = subprocess.Popen(
cmd, cwd=working_directory, stdout=subprocess.PIPE)
process.wait()
# define a variable to hold the list of photographs
files = []
# iterate through the output of subprocess and append the file names to the files variable
for line in iter(process.stdout.readline, ''):
if "#" in line.rstrip():
files.append(line.rstrip())
# define the number of the last picture taken
last_pic = int(files[-1].split(" ")[0].strip("#"))
# loop in reverse using number of photos taken and last file number in order to calculate which files to rename and download
for i in reversed(range(1, last_int+1)):
# define the gphoto2 command to download a target image to the working directory
cmd = ['gphoto2', '--auto-detect', "--filename=" + institution_ID +
"_" + position + "_" + str(i) + ".jpg", '--get-file', str(last_pic-i)]
# use subprocess to run the gphoto2 terminal program shelless and pass it the command list defined above and the working directory
process = subprocess.Popen(cmd, cwd=working_directory)
process.wait()
# tell the user that capture is done
print "Done!"
# if serial connection fails to initiate tell the user this is what the issue is
except SerialException:
print "Port not found, please make sure the arduino is plugged into the USB port!"
# trigger keyboard interrupt function and shut down the arduino motors
except KeyboardInterrupt:
sys.tracebacklimit = 0
# if script is running from this file run main()
if __name__ == "__main__":
main()