charlesnchr commited on
Commit
b899ae9
1 Parent(s): 66f3801

First commit

Browse files
.gitattributes CHANGED
@@ -32,3 +32,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ *.tiff filter=lfs diff=lfs merge=lfs -text
36
+ *.tif filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,325 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ ----------------------------------------
2
+ * Creation Time : Tue Jun 13 14:18:20 2023
3
+ * Author : Charles N. Christensen
4
+ * Github : github.com/charlesnchr
5
+ ----------------------------------------"""
6
+
7
+ import streamlit as st
8
+ import matplotlib.pyplot as plt
9
+ import matplotlib.patches as patches
10
+ import numpy as np
11
+ from skimage import io
12
+ import tifffile as tiff
13
+ import os
14
+
15
+
16
+ # Function to draw a rectangle given four points
17
+ def draw_rectangle(points, ax):
18
+ # Compute the centroid of the points
19
+ centroid = np.mean(points, axis=0)
20
+
21
+ # Compute the width and height
22
+ width = np.linalg.norm(points[1] - points[0])
23
+ height = np.linalg.norm(points[2] - points[1])
24
+
25
+ # Compute the angle
26
+ angle = np.arctan2(points[1, 1] - points[0, 1], points[1, 0] - points[0, 0])
27
+
28
+ # Create the rectangle
29
+ rect = patches.Rectangle(
30
+ centroid,
31
+ width,
32
+ height,
33
+ angle=np.degrees(angle),
34
+ linewidth=1,
35
+ edgecolor="gray",
36
+ facecolor="none",
37
+ transform=ax.transData,
38
+ )
39
+
40
+ # Add the rectangle
41
+ ax.add_patch(rect)
42
+ ax.set_aspect("equal")
43
+
44
+
45
+ def draw_rectangle_from_coords(ax, ridx, cidx):
46
+ l = np.sqrt(2) / 2 # unit length
47
+ o = 8 # origin (x,y)
48
+
49
+ shift = 1 if ridx % 2 == 0 else 0
50
+
51
+ x1 = o - 2 * l * cidx + shift * l
52
+ y1 = o - l * ridx
53
+ x2 = o - l - 2 * l * cidx + shift * l
54
+ y2 = o + l - l * ridx
55
+ x3 = o - 2 * l * cidx + shift * l
56
+ y3 = o + 2 * l - l * ridx
57
+ x4 = o + l - 2 * l * cidx + shift * l
58
+ y4 = o + l - l * ridx
59
+
60
+ # apply shift based on row and column
61
+
62
+ # Parse points
63
+ points = np.array([x1, y1, x2, y2, x3, y3, x4, y4]).reshape(-1, 2)
64
+
65
+ draw_rectangle(points, ax)
66
+
67
+ # annotate ridx and cidx in the center of the rectangle
68
+ ax.annotate(
69
+ f"{ridx},{cidx}",
70
+ xy=(x2, (y1 + y3) / 2),
71
+ horizontalalignment="center",
72
+ verticalalignment="center",
73
+ color="orange",
74
+ )
75
+
76
+ # return min max x and y
77
+ return (
78
+ min(x1, x2, x3, x4),
79
+ max(x1, x2, x3, x4),
80
+ min(y1, y2, y3, y4),
81
+ max(y1, y2, y3, y4),
82
+ )
83
+
84
+
85
+ def plot_patch(patch):
86
+ fig, ax = plt.subplots()
87
+
88
+ bounds_list = []
89
+
90
+ # iterate through patch and render if pixel is 1
91
+ for row in range(patch.shape[0]):
92
+ for col in range(patch.shape[1]):
93
+ if patch[row, col] == 255:
94
+ bounds = draw_rectangle_from_coords(ax, row, col)
95
+ print(bounds)
96
+ bounds_list.append(bounds)
97
+
98
+ # Get the min and max x and y values
99
+ xmin = min([b[0] for b in bounds_list])
100
+ xmax = max([b[1] for b in bounds_list])
101
+ ymin = min([b[2] for b in bounds_list])
102
+ ymax = max([b[3] for b in bounds_list])
103
+
104
+ # Set the limits of the plot
105
+ plt.xlim(xmin - 2, xmax + 1)
106
+ plt.ylim(ymin - 1, ymax + 1)
107
+
108
+ return fig, xmin, xmax, ymin, ymax
109
+
110
+
111
+ def DMDPixelTransform(input_img, dmdMapping, xoffset=0, yoffset=0):
112
+ # Initialize an array of zeros with same size as the input image
113
+ transformed_img = np.zeros_like(input_img)
114
+
115
+ # Get the dimensions of the input image
116
+ rows, cols = input_img.shape
117
+
118
+ # Iterate over the pixels of the input image
119
+ for i in range(rows):
120
+ for j in range(cols):
121
+ # Calculate the new coordinates for the pixel
122
+ ip = i + yoffset
123
+ jp = j + xoffset
124
+
125
+ # Apply the dmdMapping transformation if set
126
+ if dmdMapping > 0:
127
+ transformed_i = jp + ip - 2
128
+ transformed_j = (jp - ip + 4) // 2
129
+ else:
130
+ transformed_i = ip
131
+ transformed_j = jp
132
+
133
+ # If the new coordinates are within the bounds of the image, copy the pixel value
134
+ if 0 <= transformed_i < rows and 0 <= transformed_j < cols:
135
+ transformed_img[transformed_i, transformed_j] = input_img[i, j]
136
+
137
+ # Return the transformed image
138
+ return transformed_img
139
+
140
+
141
+ # Streamlit app
142
+ st.title("ONI DMD emulator")
143
+
144
+ # add author info and context
145
+ st.markdown("Charles N. Christensen, ONI — 2023/06/14")
146
+
147
+
148
+ tabs = st.tabs(
149
+ ["Basic rendering", "Pregenerated pattern with padding", "Pattern with offsets"]
150
+ )
151
+
152
+
153
+ with tabs[0]:
154
+ st.header("Render DMD layout")
155
+ # Create a new figure
156
+ fig, ax = plt.subplots()
157
+
158
+ # streamlit input for number of columns and rows
159
+ cols = st.number_input("Number of columns", min_value=1, max_value=20, value=5)
160
+ rows = st.number_input("Number of rows", min_value=1, max_value=20, value=5)
161
+
162
+ bounds_list = []
163
+
164
+ for col in range(cols):
165
+ for row in range(rows):
166
+ bounds = draw_rectangle_from_coords(ax, row, col)
167
+ bounds_list.append(bounds)
168
+
169
+ # Get the min and max x and y values
170
+ xmin = min([b[0] for b in bounds_list])
171
+ xmax = max([b[1] for b in bounds_list])
172
+ ymin = min([b[2] for b in bounds_list])
173
+ ymax = max([b[3] for b in bounds_list])
174
+
175
+ # Set the limits of the plot
176
+ plt.xlim(xmin - 2, xmax + 1)
177
+ plt.ylim(ymin - 1, ymax + 1)
178
+
179
+ fig.set_size_inches(cols + 0.5, rows + 0.5)
180
+
181
+ st.pyplot(fig)
182
+
183
+
184
+ with tabs[1]:
185
+ st.subheader("Upload pregenerated pattern or use default")
186
+ # upload pattern image
187
+ pattern = st.file_uploader("Upload pattern image", type=["tif", "tiff"])
188
+
189
+ # fallback
190
+ if pattern is None:
191
+ cur_dir = os.path.dirname(os.path.abspath(__file__))
192
+ def_pattern = f"{cur_dir}/patterns_spotSize_2_Nspots_5_dmdMapping_1.tif"
193
+ img = io.imread(def_pattern)
194
+ st.markdown(
195
+ "**No pattern uploaded**: Loading default image with 2x2 spots and 5x5 spot grids: `%s`"
196
+ % os.path.basename(def_pattern)
197
+ )
198
+ else:
199
+ print("loading image", pattern)
200
+ img = tiff.imread(pattern)
201
+
202
+ st.subheader("Parameters")
203
+ cols = st.columns(2)
204
+ # streamlit number input for padding
205
+ with cols[0]:
206
+ x_padding = st.number_input("X padding", min_value=0, max_value=10, value=0)
207
+ with cols[1]:
208
+ y_padding = st.number_input("Y padding", min_value=0, max_value=10, value=0)
209
+
210
+ # streamlit number input for global offset
211
+ cols = st.columns(2)
212
+ with cols[0]:
213
+ x_offset_img = st.number_input(
214
+ "X image offset (change to test near edges of DMD)",
215
+ min_value=0,
216
+ max_value=30,
217
+ value=10,
218
+ )
219
+ with cols[1]:
220
+ y_offset_img = st.number_input(
221
+ "Y image offset (change to test near edges of DMD)",
222
+ min_value=0,
223
+ max_value=30,
224
+ value=20,
225
+ )
226
+
227
+ cols = st.columns(2)
228
+ with cols[0]:
229
+ rows_img = st.number_input(
230
+ "Number of rows", min_value=5, max_value=100, value=20
231
+ )
232
+ with cols[1]:
233
+ cols_img = st.number_input(
234
+ "Number of columns", min_value=5, max_value=100, value=20
235
+ )
236
+
237
+ cols = st.columns(2)
238
+ with cols[0]:
239
+ frame_idx = st.number_input(
240
+ "Plot frame index start", min_value=0, max_value=400, value=0
241
+ )
242
+ with cols[1]:
243
+ frame_count = st.number_input(
244
+ "Plot frame index range", min_value=1, max_value=5, value=2
245
+ )
246
+
247
+ global_bounds = None
248
+
249
+ # plot patches in two frames
250
+ for i in range(frame_idx, frame_idx + frame_count):
251
+ patch = img[
252
+ i,
253
+ y_offset_img : y_offset_img + rows_img,
254
+ x_offset_img : x_offset_img + cols_img,
255
+ ]
256
+
257
+ # add 1pixel padding in top to patch
258
+ patch = np.pad(
259
+ patch, ((x_padding, 0), (y_padding, 0)), "constant", constant_values=0
260
+ )
261
+
262
+ fig, xmin, xmax, ymin, ymax = plot_patch(patch)
263
+
264
+ if global_bounds is None:
265
+ global_bounds = xmin, xmax, ymin, ymax
266
+
267
+ xmin, xmax, ymin, ymax = global_bounds
268
+ plt.xlim(xmin - 2, xmax + 1)
269
+ plt.ylim(ymin - 2, ymax + 1)
270
+ fig.set_size_inches(xmax - xmin + 4, ymax - ymin + 4)
271
+ st.subheader(f"Frame {i}")
272
+ st.pyplot(fig)
273
+
274
+
275
+ with tabs[2]:
276
+ # 5x5 array of st checkboxes
277
+ st.subheader("Select pixels to turn on in tilted coordinate system")
278
+ grid = np.zeros((5, 5))
279
+ for i in range(5):
280
+ cols = st.columns(5)
281
+ for j in range(5):
282
+ with cols[j]:
283
+ # set value based on i,j = 0,0 1,0 0,1 1,1
284
+
285
+ value = False
286
+ if i == 1 or i == 2:
287
+ if j == 1 or j == 2:
288
+ value = True
289
+
290
+ grid[i, j] = st.checkbox(f"{i},{j}", value=value, key=f"{i},{j}")
291
+ grid = 255 * grid
292
+
293
+ # streamlit number input for global offset
294
+ x_offset = st.number_input(
295
+ "X global offset (change to test near edges of DMD)",
296
+ min_value=0,
297
+ max_value=30,
298
+ value=10,
299
+ )
300
+ y_offset = st.number_input(
301
+ "Y global offset (change to test near edges of DMD)",
302
+ min_value=0,
303
+ max_value=30,
304
+ value=20,
305
+ )
306
+
307
+ global_bounds = None
308
+
309
+ # plot with offsets of 1px in x and y
310
+ for xoffset in range(0, 2):
311
+ for yoffset in range(0, 2):
312
+ patch = np.zeros((40, 40))
313
+ patch[x_offset : x_offset + 5, y_offset : y_offset + 5] = grid
314
+ patch = DMDPixelTransform(patch, 1, xoffset, yoffset)
315
+ fig, xmin, xmax, ymin, ymax = plot_patch(patch)
316
+
317
+ if global_bounds is None:
318
+ global_bounds = xmin, xmax, ymin, ymax
319
+
320
+ xmin, xmax, ymin, ymax = global_bounds
321
+ plt.xlim(xmin - 2, xmax + 1)
322
+ plt.ylim(ymin - 2, ymax + 1)
323
+ fig.set_size_inches(xmax - xmin + 4, ymax - ymin + 4)
324
+ st.subheader(f"Frame with offset x={xoffset} and y={yoffset}")
325
+ st.pyplot(fig)
patterns_spotSize_2_Nspots_5_dmdMapping_1.tif ADDED

Git LFS Details

  • SHA256: 8213b62d57cda8ec7d3569fcc424154c46a32c556514568c1339683ee9746a07
  • Pointer size: 132 Bytes
  • Size of remote file: 2.08 MB