sgbaird commited on
Commit
32b5cd7
·
1 Parent(s): bd61f3f

Full app files (slightly tweaked from @kenzo-aspuru-takata)

Browse files
Files changed (9) hide show
  1. .gitignore +2 -0
  2. app.py +59 -2
  3. documentation.py +89 -0
  4. download.py +21 -0
  5. gui_control.py +92 -0
  6. key_request.py +205 -0
  7. livestream.py +12 -0
  8. microscope_demo_client.py +182 -0
  9. requirements.txt +5 -0
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ my_secrets.py
2
+ __pycache__/*
app.py CHANGED
@@ -1,4 +1,61 @@
1
  import streamlit as st
2
 
3
- x = st.slider('Select a value')
4
- st.write(x, 'squared is', x * x)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
 
3
+
4
+ def sidebar():
5
+ st.sidebar.title("Navigation")
6
+ selection = st.sidebar.radio(
7
+ "Go to",
8
+ [
9
+ "About",
10
+ "Request Key",
11
+ "Livestream",
12
+ "Download",
13
+ "GUI Control",
14
+ "Python Documentation",
15
+ ],
16
+ )
17
+ return selection
18
+
19
+
20
+ if "current_page" not in st.session_state:
21
+ st.session_state.current_page = "Home"
22
+
23
+
24
+ def main():
25
+ selection = sidebar()
26
+
27
+ if st.session_state.current_page != selection:
28
+ st.session_state.current_page = selection
29
+
30
+ st.session_state.button_clicked = False
31
+
32
+ if selection == "About":
33
+ st.title("AC Microscope")
34
+ st.write(
35
+ "This is a request site for credentials to use remote access to Openflexure Microscopes in the AC lab. You can either control the microscopes over python or the GUI with the help of a temporary key. You can view the live camera feed on a livestream. One person can use a microscope at once. Currently only Microscope2 is functional, but they will all be functional in the future" # noqa: E501
36
+ )
37
+
38
+ elif selection == "Request Key":
39
+ import key_request
40
+
41
+ key_request.show()
42
+ elif selection == "Livestream":
43
+ import livestream
44
+
45
+ livestream.show()
46
+ elif selection == "Download":
47
+ import download
48
+
49
+ download.show()
50
+ elif selection == "GUI Control":
51
+ import gui_control
52
+
53
+ gui_control.show()
54
+ elif selection == "Python Documentation":
55
+ import documentation
56
+
57
+ documentation.show()
58
+
59
+
60
+ if __name__ == "__main__":
61
+ main()
documentation.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def show():
2
+ import streamlit as st
3
+
4
+ st.title("AC Microscope Client Documentation")
5
+
6
+ st.subheader("Move")
7
+ st.write("Required parameters:")
8
+ st.code("move(x,y)")
9
+ st.write("Optional parameters:")
10
+ st.code("move(x,y,z,relative)")
11
+ st.write("Default values:")
12
+ st.code("z=False, relative=False")
13
+ st.write(
14
+ "The move() function moves to given coordinates x, y (and z if it is set to any integer value, if it is set to False which is the default setting, the z value wont change). If relative is True, then it will move relative to the current position instead of moving to the absolute coordinates. Coordinates arent based on screen percentage. The size of the screen is around 3600x2700." # noqa: E501
15
+ )
16
+ st.write("")
17
+ st.subheader("Take image")
18
+ st.code("take_image()")
19
+ st.write(
20
+ "The take_image() function takes an image on the microscope camera and returns an image object" # noqa: E501
21
+ )
22
+ st.write("")
23
+ st.subheader("Get position")
24
+ st.code("get_pos()")
25
+ st.write(
26
+ "The get_pos() returns a dictionary with x, y, and z coordinates eg. {'x':1,'y':2,'z':3}" # noqa: E501
27
+ )
28
+ st.write("")
29
+ st.subheader("Focus")
30
+ st.write("Required parameters:")
31
+ st.code("focus()")
32
+ st.write("Optional parameters:")
33
+ st.code("focus(amount)")
34
+ st.write("Default value:")
35
+ st.code('amount="fast"')
36
+ st.write(
37
+ 'The focus() function auto focuses by different amounts: "huge", "fast", "medium", "fine", or any integer value' # noqa: E501
38
+ )
39
+ st.write("")
40
+ st.subheader("End connection")
41
+ st.code("end_connection()")
42
+ st.write(
43
+ "The end_connection() function ends the connection to the microscope at the end of your script. This is not required, but is reccomended to place at the end of your script." # noqa: E501
44
+ )
45
+ st.write("")
46
+ st.subheader("Scan")
47
+ st.write("Required parameters:")
48
+ st.code("scan(c1,c2)")
49
+ st.write("Optional parameters:")
50
+ st.code("scan(c1,c2,ov,foc)")
51
+ st.write("Default values:")
52
+ st.code("ov=1200,foc=0")
53
+ st.write(
54
+ 'The scan() function returns a list of image objects. Takes images to scan an entire area specified by two corners. you can input the corner coordinates as "x1 y1", "x2 y2" or [x1, y1], [x2, y2]. ov refers to the overlap between the images and foc refers to how much the microscope should focus between images (0 to disable)' # noqa: E501
55
+ )
56
+ st.subheader("Scan and stitch")
57
+ st.write("Required parameters:")
58
+ st.code("scan_and_stitch(c1,c2,temp)")
59
+ st.write("Optional parameters:")
60
+ st.code("scan_and_stitch(c1,c2,temp,ov,foc,output)")
61
+ st.write("Default values:")
62
+ st.code('ov = 1200,foc = 0,output="Downloads/stitched.jpeg"')
63
+ st.write(
64
+ "The scan_and_stitch() function takes a scan with the same inputs + 2 more and outputs a stitched image. Output is the directory the stitched image will go to and temp is the temporary directory to stitch the image (make sure this is an empty directory (it will also be created automatically if it doesn't exist) the program will clear this directory) otherwise it works just like scan(). You need Openflexure Stitching installed and you need to define path_to_openflexure_stitching when instantiating your class for this to work" # noqa: E501
65
+ )
66
+ st.write("")
67
+ st.subheader("Instantiating the class")
68
+ st.code(
69
+ 'Microscope = MicroscopeDemo(host, port, key, microscope, path_to_openflexure_stitching) #path_to_openflexure_stitching is optional, an example for the microscope field would be "microscope2"' # noqa: E501
70
+ )
71
+ st.write("")
72
+ st.subheader("Example code")
73
+ st.code(
74
+ """
75
+ print(microscope.get_pos())
76
+ microscope.take_image().show()
77
+ microscope.scan([2000, 2000], "-2000 -2000")
78
+ for i in microscope.scan("2000 2000", "-2000 -2000", ov=1500, foc=1000):
79
+ i.show()
80
+ microscope.scan_and_stitch([2000, 2000], [-2000, -2000], temp="c:/Users/-/Downloads/scanimages", output="c:/Users/-/Downloads/scanstitch.jpg") # noqa: E501
81
+ microscope.move(0,0)
82
+ microscope.focus(700)
83
+ microscope.move(0,1000,relative=True)
84
+ microscope.focus("fast")
85
+ microscope.move(1000,0,z=3000)
86
+ microscope.focus()
87
+ microscope.end_connection()
88
+ """
89
+ )
download.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def show():
2
+ import streamlit as st
3
+
4
+ st.title("Download")
5
+ st.write("You can install this program with")
6
+ st.code(
7
+ "pip install git+https://github.com... #PLACEHOLDER THIS METHOD OF INSTALLATION IS NOT READY YOU NEED TO DOWNLOAD IT MANUALLY FROM THE GITHUB AND ALSO PIP INSTALL ALL OF THE IMPORTS" # noqa: E501
8
+ )
9
+ st.write("")
10
+ st.write("or use the AC github")
11
+ st.link_button(
12
+ "AC github",
13
+ "https://github.com/AccelerationConsortium/ac-training-lab/tree/main/src%2Fac_training_lab%2Fopenflexure", # noqa: E501
14
+ )
15
+ st.write("")
16
+ st.write(
17
+ "You also need to install the Openflexure Stitching library if you want to stitch anything" # noqa: E501
18
+ )
19
+ st.link_button(
20
+ "Openflexure Stitching", "https://gitlab.com/openflexure/openflexure-stitching"
21
+ )
gui_control.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def show():
2
+ # note to self you can check for empty with if statement st.image(image,
3
+ # caption='PIL Image', use_column_width=True)
4
+ import streamlit as st
5
+ from microscope_demo_client import MicroscopeDemo
6
+ from my_secrets import HIVEMQ_BROKER
7
+
8
+ port = 8883
9
+ microscopes = [
10
+ "microscope",
11
+ "microscope2",
12
+ "deltastagetransmission",
13
+ "deltastagereflection",
14
+ ]
15
+
16
+ def get_pos_button():
17
+ microscope = MicroscopeDemo(
18
+ HIVEMQ_BROKER,
19
+ port,
20
+ microscopeselection + "clientuser",
21
+ access_key,
22
+ microscopeselection,
23
+ )
24
+ # "acmicroscopedemo" is a placeholder until access keys are implemented
25
+ pos = microscope.get_pos()
26
+ st.write("x: " + str(pos["x"]))
27
+ st.write("y: " + str(pos["y"]))
28
+ st.write("z: " + str(pos["z"]))
29
+ microscope.end_connection()
30
+
31
+ def take_image_button():
32
+ microscope = MicroscopeDemo(
33
+ HIVEMQ_BROKER,
34
+ port,
35
+ microscopeselection + "clientuser",
36
+ access_key,
37
+ microscopeselection,
38
+ )
39
+ # "acmicroscopedemo" is a placeholder until access keys are implemented
40
+ st.image(
41
+ microscope.take_image(),
42
+ caption="Taken from the microscope camera",
43
+ use_column_width=True,
44
+ )
45
+ microscope.end_connection()
46
+
47
+ def focus_button():
48
+ microscope = MicroscopeDemo(
49
+ HIVEMQ_BROKER,
50
+ port,
51
+ microscopeselection + "clientuser",
52
+ access_key,
53
+ microscopeselection,
54
+ )
55
+ # "acmicroscopedemo" is a placeholder until access keys are implemented
56
+ microscope.focus(focusamount)
57
+ st.write("Autofocus complete")
58
+ microscope.end_connection()
59
+
60
+ def move_button():
61
+ microscope = MicroscopeDemo(
62
+ HIVEMQ_BROKER,
63
+ port,
64
+ microscopeselection + "clientuser",
65
+ access_key,
66
+ microscopeselection,
67
+ )
68
+ # "acmicroscopedemo" is a placeholder until access keys are implemented
69
+ microscope.move(xmove, ymove)
70
+ st.write("Move complete")
71
+ microscope.end_connection()
72
+
73
+ st.title("GUI control")
74
+
75
+ microscopeselection = st.selectbox(
76
+ "Choose a microscope:", microscopes, index=microscopes.index("microscope2")
77
+ )
78
+
79
+ access_key = st.text_input(label="Enter your access key here:", max_chars=1000)
80
+
81
+ st.button("Get position", on_click=get_pos_button)
82
+ st.write("")
83
+ st.button("Take image", on_click=take_image_button)
84
+ st.write("")
85
+ focusamount = st.number_input(
86
+ "Autofocus amount 1-5000", min_value=1, max_value=5000, step=100, value=1000
87
+ )
88
+ st.button("Focus", on_click=focus_button)
89
+ st.write("")
90
+ xmove = st.number_input("X", min_value=-20000, max_value=20000, step=250, value=0)
91
+ ymove = st.number_input("Y", min_value=-20000, max_value=20000, step=250, value=0)
92
+ st.button("Move", on_click=move_button)
key_request.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def show():
2
+ import random
3
+ import time
4
+
5
+ # import pymongo.mongo_client
6
+ import requests
7
+ import streamlit as st
8
+ from my_secrets import HIVEMQ_API_TOKEN, HIVEMQ_BASE_URL, HIVEMQ_BROKER, MONGODB_URI
9
+ from pymongo.mongo_client import MongoClient
10
+
11
+ # SET UP ON DATABASE you need to make a variable for the time called the
12
+ # same as the microscope prior to the running of the program running the
13
+ # function update_variable_test() will work
14
+ microscope = "microscope2"
15
+ access_time = 900
16
+ database_name = "openflexure-microscope"
17
+ collection_name = "Cluster0"
18
+ microscopes = [
19
+ "microscope",
20
+ "microscope2",
21
+ "deltastagetransmission",
22
+ "deltastagereflection",
23
+ ]
24
+ brokerport = "8883"
25
+
26
+ client = MongoClient(MONGODB_URI)
27
+ db = client[database_name]
28
+ collection = db[collection_name]
29
+
30
+ try:
31
+ client.admin.command("ping")
32
+ print("Pinged your deployment. You successfully connected to MongoDB!")
33
+ except Exception as e:
34
+ st.write(e)
35
+
36
+ def check_variable(variable_name):
37
+ try:
38
+
39
+ document = collection.find_one({"variable_name": variable_name})
40
+ if document:
41
+ return document.get("value", "Variable not found.")
42
+ else:
43
+ return "Variable not found in the collection."
44
+ except Exception as e:
45
+ return f"An error occurred: {e}"
46
+
47
+ def create_user(username, password):
48
+ api_url = HIVEMQ_BASE_URL + "/mqtt/credentials"
49
+ headers = {
50
+ "Authorization": f"Bearer {HIVEMQ_API_TOKEN}",
51
+ "Content-Type": "application/json",
52
+ }
53
+
54
+ new_user = {"credentials": {"username": username, "password": password}}
55
+
56
+ requests.post(api_url, json=new_user, headers=headers)
57
+
58
+ def delete_user(username):
59
+ headers = {
60
+ "Authorization": f"Bearer {HIVEMQ_API_TOKEN}",
61
+ "Content-Type": "application/json",
62
+ }
63
+
64
+ api_url = HIVEMQ_BASE_URL + "/mqtt/credentials/username/" + username
65
+ requests.delete(api_url, headers=headers)
66
+
67
+ def role_user(username, role):
68
+ headers = {
69
+ "Authorization": f"Bearer {HIVEMQ_API_TOKEN}",
70
+ "Content-Type": "application/json",
71
+ }
72
+ api_url = HIVEMQ_BASE_URL + "/user/" + username + "/roles/" + role + "/attach"
73
+ requests.put(api_url, headers=headers)
74
+
75
+ def update_variable(variable_name, new_value):
76
+ try:
77
+ result = collection.update_one(
78
+ {"variable_name": variable_name},
79
+ {"$set": {"value": new_value}},
80
+ upsert=True,
81
+ )
82
+ if result.matched_count > 0:
83
+ return "Variable updated successfully."
84
+ else:
85
+ return "Variable created and updated successfully."
86
+ except Exception as e:
87
+ return f"An error occurred: {e}"
88
+
89
+ def update_variable_test():
90
+ update_variable(microscope, random.randint(1, 10))
91
+
92
+ def check_variable_test():
93
+ st.write(check_variable(microscope))
94
+
95
+ def get_current_time():
96
+ # api_url = "http://worldtimeapi.org/api/timezone/Etc/UTC"
97
+ # try:
98
+ # response = requests.get(api_url)
99
+ # response.raise_for_status()
100
+ # data = response.json()
101
+ # return data['unixtime']
102
+ # except requests.RequestException as e:
103
+ # return f"Error: {e}"
104
+ unix_time = int(time.time())
105
+ return unix_time
106
+
107
+ def button():
108
+ st.session_state.button_clicked = True
109
+
110
+ if "button_clicked" not in st.session_state:
111
+ st.session_state.button_clicked = False
112
+ if "previous_selected_value" not in st.session_state:
113
+ st.session_state.previous_selected_value = microscopes[1]
114
+
115
+ st.write("Keys will last 30 minutes before being overridable")
116
+ st.write("Broker IP:")
117
+ st.code(HIVEMQ_BROKER)
118
+ st.write("Broker port:")
119
+ st.code(brokerport)
120
+ st.write("Usernames:")
121
+ st.code(
122
+ """
123
+ microscope -> microscopeclientuser
124
+ microscope2 -> microscope2clientuser
125
+ deltastagereflection -> deltastagereflectionclientuser
126
+ deltastagetransmission -> deltastagetransmissionclientuser
127
+ """
128
+ )
129
+
130
+ microscope = st.selectbox(
131
+ "Choose a microscope:", microscopes, index=microscopes.index("microscope2")
132
+ )
133
+ if microscope != st.session_state.get("previous_selected_value", microscope):
134
+
135
+ st.session_state.button_clicked = False
136
+
137
+ st.session_state["previous_selected_value"] = microscope
138
+
139
+ st.button(
140
+ "Request temporary access",
141
+ help="If somebody is using the microscope, you will need to wait",
142
+ on_click=button,
143
+ )
144
+
145
+ if st.session_state.button_clicked:
146
+ display_text = st.empty()
147
+ ctime = get_current_time()
148
+ var = check_variable(microscope)
149
+ if ctime >= var + access_time:
150
+
151
+ access_key = "Microscope" + str(random.randint(10000000, 99999999))
152
+ delete_user(microscope + "clientuser")
153
+ create_user(microscope + "clientuser", access_key)
154
+ if microscope == "microscope2":
155
+ role_user(microscope + "clientuser", "3")
156
+ elif microscope == "microscope":
157
+ role_user(microscope + "clientuser", "4")
158
+ elif microscope == "deltastagereflection":
159
+ role_user(microscope + "clientuser", "5")
160
+ elif microscope == "deltastagetransmission":
161
+ role_user(microscope + "clientuser", "6")
162
+
163
+ display_text.success(
164
+ "Access key: " + access_key
165
+ ) # change placeholder to access_key
166
+ update_variable(microscope, ctime)
167
+
168
+ else:
169
+ while True:
170
+ if access_time - ctime + var <= 0:
171
+ display_text.success("Access key ready!")
172
+ break
173
+ if (access_time - ctime + var) % 60 < 10:
174
+ seconds = "0" + str((access_time - ctime + var) % 60)
175
+ else:
176
+ seconds = str((access_time - ctime + var) % 60)
177
+ display_text.error(
178
+ "Please wait "
179
+ + str(
180
+ int(
181
+ (
182
+ access_time
183
+ - ctime
184
+ + var
185
+ - (access_time - ctime + var) % 60
186
+ )
187
+ / 60
188
+ )
189
+ )
190
+ + ":"
191
+ + seconds
192
+ )
193
+
194
+ ctime = ctime + 1
195
+ if ctime % 15 == 0:
196
+ ctime = get_current_time() + 1
197
+ time.sleep(1)
198
+ while True:
199
+ time.sleep(5)
200
+ cutime = get_current_time()
201
+ var = check_variable(microscope)
202
+ if cutime <= var + access_time:
203
+ display_text.error("The access key was taken!")
204
+ break
205
+ time.sleep(10)
livestream.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def show():
2
+ import streamlit as st
3
+
4
+ st.title("Livestream")
5
+ # livestreammicroscope1 = ""
6
+ livestreammicroscope2 = "https://www.youtube.com/live/xbWFNAgEIDQ"
7
+ # livestreamdeltastagetransmission = ""
8
+ # livestreamdeltastagereflection = ""
9
+ st.write("Here are the live microscope camera views of all the microscopes")
10
+
11
+ st.write("Microscope 2:")
12
+ st.link_button("Microscope 2 Livestream", livestreammicroscope2)
microscope_demo_client.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import io
3
+ import json
4
+ import os
5
+ import shutil
6
+ import time
7
+ from io import BytesIO
8
+ from queue import Queue
9
+
10
+ import paho.mqtt.client as mqtt
11
+ from PIL import Image
12
+
13
+
14
+ class MicroscopeDemo:
15
+ def __init__(
16
+ self,
17
+ host,
18
+ port,
19
+ username,
20
+ password,
21
+ microscope,
22
+ path_to_openflexure_stitching="OPTIONAL",
23
+ ):
24
+ self.host = host
25
+ self.port = port
26
+ self.username = username
27
+ self.password = password
28
+ self.microscope = microscope
29
+ self.path_to_openflexure_stitching = path_to_openflexure_stitching
30
+
31
+ self.client = mqtt.Client()
32
+ self.client.tls_set()
33
+ self.client.username_pw_set(self.username, self.password)
34
+
35
+ self.receiveq = Queue()
36
+
37
+ def on_message(client, userdata, message):
38
+ received = json.loads(message.payload.decode("utf-8"))
39
+ self.receiveq.put(received)
40
+ if len(json.dumps(received)) <= 300:
41
+ print(received)
42
+ else:
43
+ try:
44
+ print(json.dumps(received)[:300] + "...")
45
+ except Exception as e:
46
+ print(f"Command printing error (program will continue) {e}")
47
+
48
+ self.client.on_message = on_message
49
+
50
+ self.client.connect(self.host, port=self.port, keepalive=60, bind_address="")
51
+
52
+ self.client.loop_start()
53
+
54
+ self.client.subscribe(self.microscope + "/return", qos=2)
55
+
56
+ def scan_and_stitch(
57
+ self, c1, c2, temp, ov=1200, foc=0, output="Downloads/stitched.jpeg"
58
+ ):
59
+ """takes a scan with the same inputs + 2 more and outputs a stitched
60
+ image. output is the directory the stitched image will go to and temp is
61
+ the temporary directory to stitch the image otherwise it works just like
62
+ scan()"""
63
+ command = json.dumps(
64
+ {"command": "scan", "c1": c1, "c2": c2, "ov": ov, "foc": foc}
65
+ )
66
+ self.client.publish(
67
+ self.microscope + "/command", payload=command, qos=2, retain=False
68
+ )
69
+ while self.receiveq.empty():
70
+ time.sleep(0.05)
71
+ image = self.receiveq.get()
72
+ image_list = image["images"]
73
+ if os.path.isdir(temp):
74
+ shutil.rmtree(temp)
75
+ os.makedirs(temp)
76
+ for i in range(len(image_list)):
77
+ image_bytes = base64.b64decode(image_list[i])
78
+ with io.BytesIO(image_bytes) as buffer:
79
+ img = Image.open(buffer)
80
+ img.save(
81
+ temp + "/" + str(i) + ".jpeg",
82
+ format="JPEG",
83
+ exif=img.info.get("exif"),
84
+ )
85
+ os.system(
86
+ "cd "
87
+ + self.path_to_openflexure_stitching
88
+ + " && python -m venv .venv && .\\.venv\\Scripts\\activate && openflexure-stitch " # noqa: E501
89
+ + temp
90
+ )
91
+ # edit the cd commands so it can find the stitching program on your
92
+ # machine and make sure that openflexure-stitching is installed
93
+
94
+ pos = len(temp)
95
+ for char in ("/", "\\"):
96
+ index = temp.rfind(char)
97
+ if index != -1 and index < pos:
98
+ pos = index
99
+ result = temp[pos + 1 :] if pos < len(temp) else temp
100
+
101
+ shutil.move(temp + "/" + result + "_stitched.jpg", output)
102
+
103
+ def move(self, x, y, z=False, relative=False):
104
+ """moves to given coordinates x, y (and z if it is set to any integer
105
+ value, if it is set to False the z value wont change). If relative is
106
+ True, then it will move relative to the current position instead of
107
+ moving to the absolute coordinates"""
108
+ command = json.dumps(
109
+ {"command": "move", "x": x, "y": y, "z": z, "relative": relative}
110
+ )
111
+ self.client.publish(
112
+ self.microscope + "/command", payload=command, qos=2, retain=False
113
+ )
114
+ while self.receiveq.empty():
115
+ time.sleep(0.05)
116
+ return self.receiveq.get()
117
+
118
+ def scan(self, c1, c2, ov=1200, foc=0):
119
+ """returns a list of image objects. Takes images to scan an entire area
120
+ specified by two corners. you can input the corner coordinates as "x1
121
+ y1", "x2, y2" or [x1, y1], [x2, y2]. ov refers to the overlap between
122
+ the images (useful for stitching) and foc refers to how much the
123
+ microscope should focus between images (0 to disable)"""
124
+ command = json.dumps(
125
+ {"command": "scan", "c1": c1, "c2": c2, "ov": ov, "foc": foc}
126
+ )
127
+ self.client.publish(
128
+ self.microscope + "/command", payload=command, qos=2, retain=False
129
+ )
130
+ while self.receiveq.empty():
131
+ time.sleep(0.05)
132
+ image_l = self.receiveq.get()
133
+ image_list = image_l["images"]
134
+ for i in range(len(image_list)):
135
+ image = image_list[i]
136
+ image_bytes = base64.b64decode(image)
137
+ image_object = Image.open(BytesIO(image_bytes))
138
+ image_list[i] = image_object
139
+ return image_list
140
+
141
+ def focus(self, amount="fast"):
142
+ """focuses by different amounts: huge, fast, medium, fine, or any
143
+ integer value"""
144
+ command = json.dumps({"command": "focus", "amount": amount})
145
+ self.client.publish(
146
+ self.microscope + "/command", payload=command, qos=2, retain=False
147
+ )
148
+ while self.receiveq.empty():
149
+ time.sleep(0.05)
150
+ return self.receiveq.get()
151
+
152
+ def get_pos(
153
+ self,
154
+ ):
155
+ """returns a dictionary with x, y, and z coordinates eg.
156
+ {'x':1,'y':2,'z':3}"""
157
+ command = json.dumps({"command": "get_pos"})
158
+ self.client.publish(
159
+ self.microscope + "/command", payload=command, qos=2, retain=False
160
+ )
161
+ while self.receiveq.empty():
162
+ time.sleep(0.05)
163
+ pos = self.receiveq.get()
164
+ return pos["pos"]
165
+
166
+ def take_image(self):
167
+ """returns an image object"""
168
+ command = json.dumps({"command": "take_image"})
169
+ self.client.publish(
170
+ self.microscope + "/command", payload=command, qos=2, retain=False
171
+ )
172
+ while self.receiveq.empty():
173
+ time.sleep(0.05)
174
+ image = self.receiveq.get()
175
+ image_string = image["image"]
176
+ image_bytes = base64.b64decode(image_string)
177
+ image_object = Image.open(BytesIO(image_bytes))
178
+ return image_object
179
+
180
+ def end_connection(self): # ends the connection
181
+ self.client.loop_stop()
182
+ self.client.disconnect()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ paho-mqtt
2
+ Pillow
3
+ pymongo
4
+ requests
5
+ streamlit