microlive 1.0.15__py3-none-any.whl → 1.0.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- microlive/__init__.py +1 -1
- microlive/gui/app.py +107 -3
- {microlive-1.0.15.dist-info → microlive-1.0.17.dist-info}/METADATA +2 -1
- {microlive-1.0.15.dist-info → microlive-1.0.17.dist-info}/RECORD +7 -7
- {microlive-1.0.15.dist-info → microlive-1.0.17.dist-info}/WHEEL +0 -0
- {microlive-1.0.15.dist-info → microlive-1.0.17.dist-info}/entry_points.txt +0 -0
- {microlive-1.0.15.dist-info → microlive-1.0.17.dist-info}/licenses/LICENSE +0 -0
microlive/__init__.py
CHANGED
|
@@ -23,7 +23,7 @@ Authors:
|
|
|
23
23
|
Nathan L. Nowling, Brian Munsky, Ning Zhao
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
__version__ = "1.0.
|
|
26
|
+
__version__ = "1.0.17"
|
|
27
27
|
__author__ = "Luis U. Aguilera, William S. Raymond, Rhiannon M. Sears, Nathan L. Nowling, Brian Munsky, Ning Zhao"
|
|
28
28
|
|
|
29
29
|
# Package name (for backward compatibility)
|
microlive/gui/app.py
CHANGED
|
@@ -101,6 +101,7 @@ from matplotlib.backends.backend_qt5agg import (
|
|
|
101
101
|
FigureCanvasQTAgg as FigureCanvas,
|
|
102
102
|
NavigationToolbar2QT as NavigationToolbar,)
|
|
103
103
|
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
|
104
|
+
from mpl_toolkits.mplot3d import Axes3D # For 3D intensity profile visualization
|
|
104
105
|
from functools import partial
|
|
105
106
|
from scipy.optimize import curve_fit
|
|
106
107
|
from scipy.ndimage import gaussian_filter, label, center_of_mass, distance_transform_edt
|
|
@@ -2841,12 +2842,21 @@ class GUI(QMainWindow):
|
|
|
2841
2842
|
if hasattr(self, 'time_slider_display'):
|
|
2842
2843
|
self.time_slider_display.setEnabled(False)
|
|
2843
2844
|
self.time_slider_display.setValue(0)
|
|
2845
|
+
self.time_slider_display.setMaximum(0)
|
|
2846
|
+
if hasattr(self, 'frame_label_display'):
|
|
2847
|
+
self.frame_label_display.setText("0/0")
|
|
2844
2848
|
if hasattr(self, 'play_button_display'):
|
|
2845
2849
|
self.play_button_display.setEnabled(False)
|
|
2846
2850
|
if hasattr(self, 'time_slider_tracking'):
|
|
2847
2851
|
self.time_slider_tracking.setValue(0)
|
|
2852
|
+
self.time_slider_tracking.setMaximum(0)
|
|
2853
|
+
if hasattr(self, 'frame_label_tracking'):
|
|
2854
|
+
self.frame_label_tracking.setText("0/0")
|
|
2848
2855
|
if hasattr(self, 'time_slider_tracking_vis'):
|
|
2849
2856
|
self.time_slider_tracking_vis.setValue(0)
|
|
2857
|
+
self.time_slider_tracking_vis.setMaximum(0)
|
|
2858
|
+
if hasattr(self, 'frame_label_tracking_vis'):
|
|
2859
|
+
self.frame_label_tracking_vis.setText("0/0")
|
|
2850
2860
|
|
|
2851
2861
|
# Stop any playing timers
|
|
2852
2862
|
self.stop_all_playback()
|
|
@@ -2937,12 +2947,21 @@ class GUI(QMainWindow):
|
|
|
2937
2947
|
if hasattr(self, 'time_slider_display'):
|
|
2938
2948
|
self.time_slider_display.setEnabled(False)
|
|
2939
2949
|
self.time_slider_display.setValue(0)
|
|
2950
|
+
self.time_slider_display.setMaximum(0)
|
|
2951
|
+
if hasattr(self, 'frame_label_display'):
|
|
2952
|
+
self.frame_label_display.setText("0/0")
|
|
2940
2953
|
if hasattr(self, 'play_button_display'):
|
|
2941
2954
|
self.play_button_display.setEnabled(False)
|
|
2942
2955
|
if hasattr(self, 'time_slider_tracking'):
|
|
2943
2956
|
self.time_slider_tracking.setValue(0)
|
|
2957
|
+
self.time_slider_tracking.setMaximum(0)
|
|
2958
|
+
if hasattr(self, 'frame_label_tracking'):
|
|
2959
|
+
self.frame_label_tracking.setText("0/0")
|
|
2944
2960
|
if hasattr(self, 'time_slider_tracking_vis'):
|
|
2945
2961
|
self.time_slider_tracking_vis.setValue(0)
|
|
2962
|
+
self.time_slider_tracking_vis.setMaximum(0)
|
|
2963
|
+
if hasattr(self, 'frame_label_tracking_vis'):
|
|
2964
|
+
self.frame_label_tracking_vis.setText("0/0")
|
|
2946
2965
|
|
|
2947
2966
|
# Stop any playing timers
|
|
2948
2967
|
self.stop_all_playback()
|
|
@@ -13154,7 +13173,18 @@ class GUI(QMainWindow):
|
|
|
13154
13173
|
spot_coord = (int(dfm.iloc[0]['y']), int(dfm.iloc[0]['x']))
|
|
13155
13174
|
found_spot = True
|
|
13156
13175
|
else:
|
|
13157
|
-
|
|
13176
|
+
# Particle not in current frame - jump to first valid frame for this particle
|
|
13177
|
+
df_particle = self.df_tracking[self.df_tracking[particle_col] == pid]
|
|
13178
|
+
if not df_particle.empty:
|
|
13179
|
+
first_frame = int(df_particle['frame'].min())
|
|
13180
|
+
self.current_frame = first_frame
|
|
13181
|
+
if hasattr(self, 'time_slider_tracking_vis'):
|
|
13182
|
+
self.time_slider_tracking_vis.setValue(first_frame)
|
|
13183
|
+
dfm = df_particle[df_particle['frame'] == first_frame]
|
|
13184
|
+
spot_coord = (int(dfm.iloc[0]['y']), int(dfm.iloc[0]['x']))
|
|
13185
|
+
found_spot = True
|
|
13186
|
+
else:
|
|
13187
|
+
spot_coord = (0, 0)
|
|
13158
13188
|
else:
|
|
13159
13189
|
spot_coord = (0, 0)
|
|
13160
13190
|
else:
|
|
@@ -13213,7 +13243,12 @@ class GUI(QMainWindow):
|
|
|
13213
13243
|
else:
|
|
13214
13244
|
main_img = norm_stack[selected_channelIndex]
|
|
13215
13245
|
main_cmap = cmap_list_imagej[selected_channelIndex % len(cmap_list_imagej)]
|
|
13216
|
-
|
|
13246
|
+
|
|
13247
|
+
# Always show 3D intensity profile for spot quality assessment
|
|
13248
|
+
show_3d_profile = True
|
|
13249
|
+
|
|
13250
|
+
# 3-column layout: main image + 2D crops + 3D surfaces
|
|
13251
|
+
gs = fig.add_gridspec(1, 3, width_ratios=[3, 1, 1.5], hspace=0.1, wspace=0.15)
|
|
13217
13252
|
ax_main = fig.add_subplot(gs[0, 0])
|
|
13218
13253
|
|
|
13219
13254
|
# Store main axes reference and recreate RectangleSelector
|
|
@@ -13229,8 +13264,15 @@ class GUI(QMainWindow):
|
|
|
13229
13264
|
props=dict(facecolor='cyan', edgecolor='white', alpha=0.3, linewidth=2)
|
|
13230
13265
|
)
|
|
13231
13266
|
|
|
13267
|
+
# 2D crop subgrid
|
|
13232
13268
|
gs2 = gs[0, 1].subgridspec(C, 1, hspace=0.1)
|
|
13233
|
-
axes_zoom = [fig.add_subplot(gs2[i, 0]) for i in range(C)]
|
|
13269
|
+
axes_zoom = [fig.add_subplot(gs2[i, 0]) for i in range(C)]
|
|
13270
|
+
|
|
13271
|
+
# 3D profile subgrid (only create if enabled)
|
|
13272
|
+
axes_3d = []
|
|
13273
|
+
if show_3d_profile:
|
|
13274
|
+
gs3 = gs[0, 2].subgridspec(C, 1, hspace=0.15)
|
|
13275
|
+
axes_3d = [fig.add_subplot(gs3[i, 0], projection='3d') for i in range(C)]
|
|
13234
13276
|
# remove background if requested
|
|
13235
13277
|
if hasattr(self, 'checkbox_remove_bg') and self.checkbox_remove_bg.isChecked():
|
|
13236
13278
|
seg_mask = getattr(self, 'segmentation_mask', None)
|
|
@@ -13284,6 +13326,15 @@ class GUI(QMainWindow):
|
|
|
13284
13326
|
rect = patches.Rectangle((x0, y0), crop_sz, crop_sz, edgecolor='white', facecolor='none', linewidth=2)
|
|
13285
13327
|
ax_main.add_patch(rect)
|
|
13286
13328
|
ax_main.axis('off')
|
|
13329
|
+
|
|
13330
|
+
# Add thin border to show image boundaries (matching Tracking tab style)
|
|
13331
|
+
# Use -0.5 origin and full size to place border outside the image
|
|
13332
|
+
if self.image_stack is not None:
|
|
13333
|
+
img_H, img_W = main_img.shape[:2]
|
|
13334
|
+
img_border = patches.Rectangle((-0.5, -0.5), img_W, img_H, linewidth=0.8,
|
|
13335
|
+
edgecolor='#555555', facecolor='none', linestyle='-')
|
|
13336
|
+
ax_main.add_patch(img_border)
|
|
13337
|
+
|
|
13287
13338
|
for ci, ax in enumerate(axes_zoom):
|
|
13288
13339
|
if found_spot:
|
|
13289
13340
|
crop = norm_stack[ci, y0:y1, x0:x1]
|
|
@@ -13291,6 +13342,59 @@ class GUI(QMainWindow):
|
|
|
13291
13342
|
crop = np.zeros((crop_sz, crop_sz))
|
|
13292
13343
|
ax.imshow(crop, cmap=cmap_list_imagej[ci % len(cmap_list_imagej)], interpolation='nearest', vmin=0, vmax=1)
|
|
13293
13344
|
ax.axis('off')
|
|
13345
|
+
# Add gray frame border to 2D crops using Rectangle (spines hidden by axis('off'))
|
|
13346
|
+
# Use -0.5 origin and full size to place border outside the image (matplotlib centers pixels at integers)
|
|
13347
|
+
crop_h, crop_w = crop.shape[:2]
|
|
13348
|
+
crop_border = patches.Rectangle((-0.5, -0.5), crop_w, crop_h, linewidth=0.8,
|
|
13349
|
+
edgecolor='#555555', facecolor='none', linestyle='-')
|
|
13350
|
+
ax.add_patch(crop_border)
|
|
13351
|
+
|
|
13352
|
+
# Render 3D intensity profiles if enabled
|
|
13353
|
+
if show_3d_profile and axes_3d:
|
|
13354
|
+
# Create meshgrid for surface plot (only once, using crop dimensions)
|
|
13355
|
+
crop_example = norm_stack[0, y0:y1, x0:x1] if found_spot else np.zeros((crop_sz, crop_sz))
|
|
13356
|
+
Y_grid, X_grid = np.meshgrid(np.arange(crop_example.shape[0]),
|
|
13357
|
+
np.arange(crop_example.shape[1]), indexing='ij')
|
|
13358
|
+
|
|
13359
|
+
for ci, ax3d in enumerate(axes_3d):
|
|
13360
|
+
if found_spot:
|
|
13361
|
+
crop = norm_stack[ci, y0:y1, x0:x1]
|
|
13362
|
+
else:
|
|
13363
|
+
crop = np.zeros((crop_sz, crop_sz))
|
|
13364
|
+
|
|
13365
|
+
# Get channel colormap
|
|
13366
|
+
cmap = cmap_list_imagej[ci % len(cmap_list_imagej)]
|
|
13367
|
+
|
|
13368
|
+
# Plot surface with matching colormap
|
|
13369
|
+
ax3d.plot_surface(X_grid, Y_grid, crop, cmap=cmap,
|
|
13370
|
+
edgecolor='none', alpha=0.9, antialiased=True)
|
|
13371
|
+
|
|
13372
|
+
# Style the 3D axes for dark theme
|
|
13373
|
+
ax3d.set_facecolor('black')
|
|
13374
|
+
ax3d.set_xlabel('X', fontsize=7, color='white', labelpad=-2)
|
|
13375
|
+
ax3d.set_ylabel('Y', fontsize=7, color='white', labelpad=-2)
|
|
13376
|
+
ax3d.set_zlabel('I', fontsize=7, color='white', labelpad=-2)
|
|
13377
|
+
ax3d.tick_params(axis='both', which='major', labelsize=5, colors='white', pad=0)
|
|
13378
|
+
ax3d.tick_params(axis='z', which='major', labelsize=5, colors='white', pad=0)
|
|
13379
|
+
|
|
13380
|
+
# Set consistent Z limits for comparison across channels
|
|
13381
|
+
ax3d.set_zlim(0, 1)
|
|
13382
|
+
|
|
13383
|
+
# Make pane and grid styling match dark theme
|
|
13384
|
+
ax3d.xaxis.pane.fill = False
|
|
13385
|
+
ax3d.yaxis.pane.fill = False
|
|
13386
|
+
ax3d.zaxis.pane.fill = False
|
|
13387
|
+
ax3d.xaxis.pane.set_edgecolor('gray')
|
|
13388
|
+
ax3d.yaxis.pane.set_edgecolor('gray')
|
|
13389
|
+
ax3d.zaxis.pane.set_edgecolor('gray')
|
|
13390
|
+
ax3d.grid(True, alpha=0.3, color='gray')
|
|
13391
|
+
|
|
13392
|
+
# Add channel label
|
|
13393
|
+
ax3d.set_title(f'Ch{ci}', fontsize=8, color='white', pad=-5)
|
|
13394
|
+
|
|
13395
|
+
# Set viewing angle for nice perspective
|
|
13396
|
+
ax3d.view_init(elev=25, azim=-45)
|
|
13397
|
+
|
|
13294
13398
|
fig.tight_layout()
|
|
13295
13399
|
|
|
13296
13400
|
# Add thin white frame border to main image
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microlive
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.17
|
|
4
4
|
Summary: Live-cell microscopy image analysis and single-molecule measurements
|
|
5
5
|
Project-URL: Homepage, https://github.com/ningzhaoAnschutz/microlive
|
|
6
6
|
Project-URL: Documentation, https://github.com/ningzhaoAnschutz/microlive/blob/main/docs/user_guide.md
|
|
@@ -67,6 +67,7 @@ Requires-Dist: scipy==1.13.1
|
|
|
67
67
|
Requires-Dist: seaborn==0.13.2
|
|
68
68
|
Requires-Dist: snapgene-reader>=0.1
|
|
69
69
|
Requires-Dist: statsmodels==0.14.4
|
|
70
|
+
Requires-Dist: tasep-models
|
|
70
71
|
Requires-Dist: tifffile>=2024.1
|
|
71
72
|
Requires-Dist: tqdm>=4.66
|
|
72
73
|
Requires-Dist: trackpy>=0.6
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
microlive/__init__.py,sha256=
|
|
1
|
+
microlive/__init__.py,sha256=HO7GUobQMGh4QyPHlad9Z_SEaPNvhz2pdH-YABr7szc,1385
|
|
2
2
|
microlive/imports.py,sha256=VAAMavSLIKO0LooadTXfCdZiv8LQbV_wITeIv8IHwxM,7531
|
|
3
3
|
microlive/microscopy.py,sha256=OFqf0JXJW4-2cLHvXnwwp_SfMFsUXwp5lDKbkCRR4ok,710841
|
|
4
4
|
microlive/ml_spot_detection.py,sha256=pVbOSGNJ0WWMuPRML42rFwvjKVZ0B1fJux1179OIbAg,10603
|
|
@@ -7,7 +7,7 @@ microlive/data/icons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
7
7
|
microlive/data/icons/icon_micro.png,sha256=b5tFv4E6vUmLwYmYeM4PJuxLV_XqEzN14ueolekTFW0,370236
|
|
8
8
|
microlive/data/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
microlive/gui/__init__.py,sha256=tB-CdDC7x5OwYFAQxLOUvfVnUThaXKXVRsB68YP0Y6Q,28
|
|
10
|
-
microlive/gui/app.py,sha256=
|
|
10
|
+
microlive/gui/app.py,sha256=sUz8MBQ4bpiNsZAhyZ7lhneY_hF45KORgepXShCCFYE,805899
|
|
11
11
|
microlive/gui/main.py,sha256=b66W_2V-pclGKOozfs75pwrCGbL_jkVU3kFt8RFMZIc,2520
|
|
12
12
|
microlive/gui/micro_mac.command,sha256=TkxYOO_5A2AiNJMz3_--1geBYfl77THpOLFZnV4J2ac,444
|
|
13
13
|
microlive/gui/micro_windows.bat,sha256=DJUKPhDbCO4HToLwSMT-QTYRe9Kr1wn5A2Ijy2klIrw,773
|
|
@@ -21,8 +21,8 @@ microlive/utils/device.py,sha256=tcPMU8UiXL-DuGwhudUgrbjW1lgIK_EUKIOeOn0U6q4,253
|
|
|
21
21
|
microlive/utils/model_downloader.py,sha256=EruviTEh75YBekpznn1RZ1Nj8lnDmeC4TKEnFLOow6Y,9448
|
|
22
22
|
microlive/utils/resources.py,sha256=Jz7kPI75xMLCBJMyX7Y_3ixKi_UgydfQkF0BlFtLCKs,1753
|
|
23
23
|
microlive/data/models/spot_detection_cnn.pth,sha256=Np7vpPJIbKQmuKY0Hx-4IkeEDsnks_QEgs7TqaYgZmI,8468580
|
|
24
|
-
microlive-1.0.
|
|
25
|
-
microlive-1.0.
|
|
26
|
-
microlive-1.0.
|
|
27
|
-
microlive-1.0.
|
|
28
|
-
microlive-1.0.
|
|
24
|
+
microlive-1.0.17.dist-info/METADATA,sha256=e75rIP8Kpz7nB8DoIEyBHuNqTHm8MV4i6blG9QHgt6c,12462
|
|
25
|
+
microlive-1.0.17.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
26
|
+
microlive-1.0.17.dist-info/entry_points.txt,sha256=Zqp2vixyD8lngcfEmOi8fkCj7vPhesz5xlGBI-EubRw,54
|
|
27
|
+
microlive-1.0.17.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
|
28
|
+
microlive-1.0.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|