forked from IBM/MicroscoPy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMicroscoPy.py
452 lines (366 loc) · 16.4 KB
/
MicroscoPy.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
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
# Program to control camera settings, microscope stepper motors, and LED illumination from keyboard
# Camera: Pi camera v2
# Enable the camera: Preferences --> Raspberry Pi Configuration --> Interfaces --> Camera --> Enable --> Reboot
# Increase GPU memory (Raspberry Pi 4, max. camera resolution): Preferences --> Raspberry Pi Configuration --> Performance --> GPU memory --> Change from 128 to 144 --> Reboot
# Arduino USB communication baud rate: 57600
# The code can also be used to control the camera settings of any Pi camera without having a microscope.
# In this case, set the variable KeyboardControl = False so that the code does not expect an Arduino board connected to the USB.
## IMPORTANT! For Raspberry Pi zero, and Raspberry Pi 3, reduce the resolution by setting HighResolution = False
HighResolution = True # True: (3280, 2464), False: (1920, 1080)
KeyboardControl = False # set to "True" if the Arduino is connected to the Raspberry Pi via USB and if you want to control the stepper motors via Keyboard
import sys
import easygui
import serial
import time
from datetime import datetime
from pynput.keyboard import Key, KeyCode, Listener
### camera
from picamera import PiCamera
camera = PiCamera()
brightness = 50 #start-up brightness
contrast= 0 #start-up contrast
EV = 0 #start-up exposure compensation
saturation = 0 #start-up saturation
### serial communication with Arduino
if KeyboardControl == True:
ser = serial.Serial('/dev/ttyACM0',57600) # type "ls /dev/tty*" to the terminal to check the serial port
### predefined motor speeds
slow = 10
medium = 100
fast = 500
speed = medium #start-up speed:medium, Ctrl key changes the speed
zoom = 1.0 #start-up digital zoom factor
LEDintensity = 20 #start-up LED intensity
#will be multiplied by 5 to get 0-100% range in 5% steps to make it compatible with
#the potentiometer on the Arduino joystick controller
filename = "" #default filename prefix
path = "/home/pi/Pictures" #default path
arrowkey_mode = False #Alt key toggles this variable to select between X-Y (start-up) and Rotate-Tilt control
recording_mode = False #Tab key toggles between the photo mode (start-up) and the video mode
recording = False #variable to set during recording
def camera_reset():
global brightness, contrast, EV, saturation
brightness = 50
camera.brightness = brightness
contrast= 0
camera.contrast = contrast
EV = 0
saturation = 0
camera.saturation = saturation
camera.exposure_compensation = 0
camera.sensor_mode = 2 # full field of view
if HighResolution == True:
camera.resolution = (3280, 2464) # Pi camera v2 max resolution, 8MP
else:
camera.resolution = (1920, 1080) # reduced resolution, Full HD or (2592 x 1944) for the 5MP camera v1
camera.rotation=180
camera.annotate_text_size = 100
camera.annotate_text = ""
camera.iso = 0
camera.shutter_speed = 0
camera.framerate = 30
camera.exposure_mode = 'auto'
camera.awb_mode = 'auto'
#camera.preview_fullscreen = False # optional
#camera.preview_window = (0, 50, 1280, 960) #optional
camera.start_preview()
def shortcuts(): # keyboard shortcuts
camera.preview_alpha=0
easygui.msgbox("F1: keyboard shortcuts\
\n----------------------------------------------------------\
\nTab: toggle between photo and video modes\
\nP: start camera preview, p: stop camera preview\
\n----------------------------------------------------------\
\nB: increase brightness, b: decrease brightness\
\nC: increase contrast, c: decrease contrast\
\nV: increase EV, v: decrease EV\
\nS: increase saturation, s: decrease saturation\
\n----------------------------------------------------------\
\n+: digital zoom in, -: digital zoom out\
\n----------------------------------------------------------\
\ni: ISO, e: exposure time, r: framerate\
\nE: exposure mode, W: white-balance mode\
\n----------------------------------------------------------\
\n0: reset camera settings\
\n----------------------------------------------------------\
\nF: select folder, f: enter filename prefix (optional)\
\n----------------------------------------------------------\
\nEnter: save image or start\stop video depending on the mode\
\n----------------------------------------------------------\
\nArrow keys: X-Y or Rotate-Tilt\
\nPage up and Page down: Z up and down\
\nHome and End: Camera up and down\
\n----------------------------------------------------------\
\nCtrl: change motor speed (3 predefined speeds)\
\nAlt: toggle between X-Y and Rotate-Tilt\
\n----------------------------------------------------------\
\nEsc: exit","Keyboard shortcuts")
camera.preview_alpha=255
shortcuts() #display the keyboard shortcuts at the beginning
camera_reset() # start the camera preview
def on_press(key):
global brightness, contrast, EV, saturation, zoom
global speed, LEDintensity
global arrowkey_mode, path, filename, recording_mode
### F1: keyboard shortcuts
if key == Key.f1:
shortcuts()
######## CAMERA
### Camera preview modes, video or photo
if key == Key.tab:
if recording_mode == False:
camera.resolution = (1920, 1080)
camera.annotate_text = "Video mode"
recording_mode = True
elif recording_mode == True:
if HighResolution == True:
camera.resolution = (3280, 2464) # Pi camera v2 max resolution, 8MP
else:
camera.resolution = (1920, 1080) # reduced resolution, Full HD
camera.annotate_text = "Photo mode"
recording_mode = False
### Reset camera settings
if key == KeyCode(char='0'):
camera_reset()
### digital zoom
if key == KeyCode(char='+'):
if zoom > 0.2:
zoom = zoom - 0.1
camera.zoom = (0,0,zoom,zoom)
annotate_text = "Digital zoom:" + str(round(1/zoom,2)) + "X"
camera.annotate_text = annotate_text
if key == KeyCode(char='-'):
if zoom < 1.0:
zoom = zoom + 0.1
camera.zoom = (0,0,zoom,zoom)
annotate_text = "Digital zoom:" + str(round(1/zoom,2)) + "X"
camera.annotate_text = annotate_text
### start and stop camera preview
if key == KeyCode(char='P'):
camera.start_preview()
if key == KeyCode(char='p'):
camera.stop_preview()
### brightness
if key == KeyCode(char='B'):
if brightness !=100:
brightness += 5
camera.brightness = brightness
annotate_text = "Brightness:" + str(brightness) + "%"
camera.annotate_text = annotate_text
if key == KeyCode(char='b'):
if brightness !=0:
brightness -= 5
camera.brightness = brightness
annotate_text = "Brightness:" + str(brightness) + "%"
camera.annotate_text = annotate_text
### contrast
if key == KeyCode(char='C'):
if contrast !=100:
contrast += 5
camera.contrast = contrast
annotate_text = "Contrast:" + str(contrast) + "%"
camera.annotate_text = annotate_text
if key == KeyCode(char='c'):
if contrast !=-100:
contrast -= 5
camera.contrast = contrast
annotate_text = "Contrast:" + str(contrast) + "%"
camera.annotate_text = annotate_text
### EV compensation
if key == KeyCode(char='V'):
if EV !=25:
EV += 1
camera.exposure_compensation = EV
annotate_text = "EV:" + str(EV)
camera.annotate_text = annotate_text
if key == KeyCode(char='v'):
if EV !=-25:
EV -= 1
camera.exposure_compensation = EV
annotate_text = "EV:" + str(EV)
camera.annotate_text = annotate_text
### saturation
if key == KeyCode(char='S'):
if saturation !=100:
saturation += 5
camera.saturation = saturation
annotate_text = "Saturation:" + str(saturation) + "%"
camera.annotate_text = annotate_text
if key == KeyCode(char='s'):
if saturation !=-100:
saturation -= 5
camera.saturation = saturation
annotate_text = "Saturation:" + str(saturation) + "%"
camera.annotate_text = annotate_text
### ISO
if key == KeyCode(char='i'):
camera.preview_alpha=0
ISO_choices = ["0","100","200","320","400","500","640","800"]
ISO_selected = easygui.choicebox("Select ISO (default: 0 for auto)", "ISO", ISO_choices)
if ISO_selected is not None:
camera.iso = int(ISO_selected)
camera.preview_alpha=255
### white balance
if key == KeyCode(char='W'):
camera.preview_alpha=0
WB_choices = ["off","auto","sunlight","cloudy","shade","tungsten","fluorescent","flash","horizon"]
WB_selected = easygui.choicebox("Select white balance mode (default: auto)", "White Balance", WB_choices)
if WB_selected is not None:
camera.awb_mode = WB_selected
if WB_selected == "off":
WB_gain = easygui.enterbox("White balance gain - typical values between 0.9 and 1.9", "White balance gain", "1.0")
if WB_gain is not None:
camera.awb_gains = float(WB_gain)
camera.preview_alpha=255
### frame rate
if key == KeyCode(char='r'):
camera.preview_alpha=0
Framerate_choices = ["30","20", "15","10","5","2","1", "0.5", "0.1", "manual"]
Framerate_selected = easygui.choicebox("Select framerate","Framerate", Framerate_choices)
if Framerate_selected == "manual":
Framerate_selected = easygui.enterbox("Framerate", "Input", "")
if Framerate_selected is not None:
camera.framerate = float(Framerate_selected)
camera.preview_alpha=255
### exposure time (shutter speed)
if key == KeyCode(char='e'):
camera.preview_alpha=0
Exposure_choices = ["0","10","20","50","100","200", "500", "1000", "manual"]
Exposure_selected = easygui.choicebox("Select exposure time in ms (default: 0, auto)\n\
Maximum exposure time is determined by the framerate","Exposure time (shutter speed)", Exposure_choices)
if Exposure_selected == "manual":
Exposure_selected = easygui.enterbox("Exposure time (ms)", "Input", "")
if Exposure_selected is not None:
camera.shutter_speed = int(Exposure_selected)*1000
camera.preview_alpha=255
### exposure modes
if key == KeyCode(char='E'):
camera.preview_alpha=0
ExposureMode_choices = ["off","auto","night","nightpreview","backlight","spotlight"]
ExposureMode_selected = easygui.choicebox("Exposure mode", "Exposure mode", ExposureMode_choices)
if ExposureMode_selected is not None:
camera.exposure_mode = ExposureMode_selected
camera.preview_alpha=255
### select folder and set filename prefix
if key == KeyCode(char='F'):
camera.preview_alpha=0
path = easygui.diropenbox()
camera.preview_alpha=255
if key == KeyCode(char='f'):
camera.preview_alpha=0
filename = easygui.enterbox("Filename prefix", "Input", "")
camera.preview_alpha=255
######## MOTORIZED STAGE
### Ctrl: change motor speed
if key == Key.ctrl_l or key == Key.ctrl_r:
if speed == slow:
speed = medium
camera.annotate_text = "Motor speed = medium"
elif speed == medium:
speed = fast
camera.annotate_text = "Motor speed = fast"
elif speed == fast:
speed = slow
camera.annotate_text = "Motor speed = slow"
### keyboard arrow keys, Alt key toggles between X-Y and Rotate-Tilt
### send Arduino a string to control the motors, e.g. "-100X," or "500Y,"
### the integer defines the direction and the speed of the motor, the letter selects the motor
if key == Key.alt_l or key == Key.alt_r:
if arrowkey_mode == True:
arrowkey_mode = False
camera.annotate_text = "X-Y"
elif arrowkey_mode == False:
arrowkey_mode = True
camera.annotate_text = "Rotate-Tilt"
if not KeyboardControl:
return
if key == Key.left:
if arrowkey_mode == False:
data = str(speed*-1) + "X,"
else:
data = str(speed*-1) + "R,"
ser.write(data.encode())
if key == Key.right:
if arrowkey_mode == False:
data = str(speed) + "X,"
else:
data = str(speed) + "R,"
ser.write(data.encode())
if key == Key.down:
if arrowkey_mode == False:
data = str(speed*-1) + "Y,"
else:
data = str(speed*-1) + "T,"
ser.write(data.encode())
if key == Key.up:
if arrowkey_mode == False:
data = str(speed) + "Y,"
else:
data = str(speed) + "T,"
ser.write(data.encode())
### Z up and down, Page up and Page down
if key == Key.page_up:
data = str(speed) + "Z,"
ser.write(data.encode())
if key == Key.page_down:
data = str(speed*-1) + "Z,"
ser.write(data.encode())
### Camera up and down, Home and End
if key == Key.home:
data = str(speed) + "C,"
ser.write(data.encode())
if key == Key.end:
data = str(speed*-1) + "C,"
ser.write(data.encode())
######## LED
### LED intensity, max. value in the data is set to 20 to make it compatible with the joystick
### PWM of the LED driver works in the opposite direction
### Intensity is controlled in 5% increments
if key == KeyCode(char='L'):
if LEDintensity !=20:
LEDintensity += 1
data = str(20-LEDintensity) + "L,"
ser.write(data.encode())
annotate_text = "LED intensity:" + str(LEDintensity*5) + "%"
camera.annotate_text = annotate_text
if key == KeyCode(char='l'):
if LEDintensity !=0:
LEDintensity -= 1
data = str(20-LEDintensity) + "L,"
ser.write(data.encode())
annotate_text = "LED intensity:" + str(LEDintensity*5) + "%"
camera.annotate_text = annotate_text
def on_release(key):
global path, filename, recording
### Esc: exit
if key == Key.esc:
sys.exit()
### Enter: save image
if key == Key.enter:
camera.annotate_text = ""
now = datetime.now()
if recording_mode == False: #photo mode
now = datetime.now()
output = path+"/"+ filename+now.strftime("%d-%m-%H%M%S.jpg")
time.sleep(1)
camera.capture(output, quality=100)
camera.annotate_text = "Photo saved"
if recording_mode == True: #video mode
if recording == False:
output = path+"/"+ filename+now.strftime("%d-%m-%H%M%S.h264")
camera.annotate_text = "Recording will start in 2 seconds..."
time.sleep(2)
camera.annotate_text = ""
camera.start_recording(output)
recording = True
elif recording == True:
camera.stop_recording()
camera.annotate_text = "Recording stopped"
recording = False
### Stop motors when key is released
if KeyboardControl and (key == Key.up or key == Key.down or key == Key.left or key == Key.right\
or key == Key.page_up or key == Key.page_down or key == Key.home\
or key == Key.end):
ser.write(b"0O,") ## send to Arduino "0O," to stop the motors
with Listener(on_press=on_press,on_release=on_release) as listener:
listener.join()