From fb19e52fc5ce72854947e7fdf28af719d9600d24 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 5 Dec 2022 15:27:55 +0100 Subject: [PATCH 01/39] nena recalculate --- changelog.rst | 5 +++-- picasso/gui/render.py | 34 ++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/changelog.rst b/changelog.rst index 7a5971d0..5593d932 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,13 +1,14 @@ Changelog ========= -Last change: 01-DEC-2022 MTS +Last change: 05-DEC-2022 MTS -0.5.5 - 0.5.6 +0.5.5 - 0.5.7 ------------- - Cluster info is saved in ``_cluster_centers.hdf5`` files which are created when ``Save cluster centers`` box is ticked - Cluster centers contain info about group, mean frame (saved as ``frame``), standard deviation frame, area/volume and convex hull - ``gist_rainbow`` is used for rendering properties +- NeNA can be calculated many times - Bug fixes 0.5.1 - 0.5.4 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 01a7ce88..45121722 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -3106,6 +3106,7 @@ def __init__(self, window): self.setWindowTitle("Info") self.setModal(False) self.lp = None + self.nena_calculated = False self.change_fov = ChangeFOV(self.window) vbox = QtWidgets.QVBoxLayout(self) # Display @@ -3251,27 +3252,43 @@ def calculate_nena_lp(self): locs = self.window.view.locs[channel] info = self.window.view.infos[channel] - # modify the movie grid - self.nena_button.setParent(None) - self.movie_grid.removeWidget(self.nena_button) + # calculate nena progress = lib.ProgressDialog( "Calculating NeNA precision", 0, 100, self ) result_lp = postprocess.nena(locs, info, progress.set_value) + + # modify the movie grid + if not self.nena_calculated: # if nena calculated first time + self.nena_button.setParent(None) + self.movie_grid.removeWidget(self.nena_button) + else: + self.movie_grid.removeWidget(self.nena_label) + self.movie_grid.removeWidget(self.show_plot_button) + self.nena_label = QtWidgets.QLabel() self.movie_grid.addWidget(self.nena_label, 1, 1) self.nena_result, self.lp = result_lp self.lp *= self.window.display_settings_dlg.pixelsize.value() self.nena_label.setText("{:.3} nm".format(self.lp)) - show_plot_button = QtWidgets.QPushButton("Show plot") - self.movie_grid.addWidget( - show_plot_button, self.movie_grid.rowCount() - 1, 2 - ) # Nena plot self.nena_window = NenaPlotWindow(self) self.nena_window.plot(self.nena_result) - show_plot_button.clicked.connect(self.nena_window.show) + + self.show_plot_button = QtWidgets.QPushButton("Show plot") + self.show_plot_button.clicked.connect(self.nena_window.show) + self.movie_grid.addWidget(self.show_plot_button, 0, 2) + + if not self.nena_calculated: + # add recalculate nena + recalculate_nena = QtWidgets.QPushButton("Recalculate NeNA") + recalculate_nena.clicked.connect(self.calculate_nena_lp) + recalculate_nena.setDefault(False) + recalculate_nena.setAutoDefault(False) + self.movie_grid.addWidget(recalculate_nena, 1, 2) + + self.nena_calculated = True def calibrate_influx(self): """ Calculates influx rate (1/frames). """ @@ -10216,6 +10233,7 @@ def initUI(self, plugins_loaded): self.display_settings_dlg, self.dataset_dialog, self.info_dialog, + self.info_dialog.change_fov, self.mask_settings_dialog, self.tools_settings_dialog, self.slicer_dialog, From 4e9a14b9a78e4666651f30e3ed271c7fb249e4b3 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 5 Dec 2022 15:43:32 +0100 Subject: [PATCH 02/39] select picks z units in nm --- picasso/gui/render.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 45121722..0491db3d 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -1010,7 +1010,7 @@ def getParams(all_picked_locs, current, length, mode, color_sys): ) ax.set_xlabel("X [Px]") ax.set_ylabel("Y [Px]") - ax.set_zlabel("Z [Px]") + ax.set_zlabel("Z [nm]") ax.set_xlim( np.mean(locs["x"]) - 3 * np.std(locs["x"]), np.mean(locs["x"]) + 3 * np.std(locs["x"]), @@ -1049,7 +1049,7 @@ def getParams(all_picked_locs, current, length, mode, color_sys): ax.set_xlabel("X [Px]") ax.set_ylabel("Y [Px]") - ax.set_zlabel("Z [Px]") + ax.set_zlabel("Z [nm]") plt.gca().patch.set_facecolor("black") ax.w_xaxis.set_pane_color((0, 0, 0, 1.0)) @@ -1151,7 +1151,7 @@ def getParams(all_picked_locs, current, length, mode, color_sys): ) ax.set_xlabel("X [Px]") ax.set_ylabel("Y [Px]") - ax.set_zlabel("Z [Px]") + ax.set_zlabel("Z [nm]") ax.set_xlim( np.mean(locs["x"]) - 3 * np.std(locs["x"]), np.mean(locs["x"]) + 3 * np.std(locs["x"]), @@ -1188,7 +1188,7 @@ def getParams(all_picked_locs, current, length, mode, color_sys): # AXES 3 ax3.scatter(locs["x"], locs["z"], c=colors, cmap="jet", s=2) ax3.set_xlabel("X [Px]") - ax3.set_ylabel("Z [Px]") + ax3.set_ylabel("Z [nm]") ax3.set_xlim( np.mean(locs["x"]) - 3 * np.std(locs["x"]), np.mean(locs["x"]) + 3 * np.std(locs["x"]), @@ -1203,7 +1203,7 @@ def getParams(all_picked_locs, current, length, mode, color_sys): # AXES 4 ax4.scatter(locs["y"], locs["z"], c=colors, cmap="jet", s=2) ax4.set_xlabel("Y [Px]") - ax4.set_ylabel("Z [Px]") + ax4.set_ylabel("Z [nm]") ax4.set_xlim( np.mean(locs["y"]) - 3 * np.std(locs["y"]), np.mean(locs["y"]) + 3 * np.std(locs["y"]), @@ -1240,7 +1240,7 @@ def getParams(all_picked_locs, current, length, mode, color_sys): ax.set_xlabel("X [Px]") ax.set_ylabel("Y [Px]") - ax.set_zlabel("Z [Px]") + ax.set_zlabel("Z [nm]") ax.w_xaxis.set_pane_color((0, 0, 0, 1.0)) ax.w_yaxis.set_pane_color((0, 0, 0, 1.0)) @@ -1262,7 +1262,7 @@ def getParams(all_picked_locs, current, length, mode, color_sys): # AXES 3 ax3.set_xlabel("X [Px]") - ax3.set_ylabel("Z [Px]") + ax3.set_ylabel("Z [nm]") ax3.set_xlim( np.mean(locs["x"]) - 3 * np.std(locs["x"]), np.mean(locs["x"]) + 3 * np.std(locs["x"]), @@ -1276,7 +1276,7 @@ def getParams(all_picked_locs, current, length, mode, color_sys): # AXES 4 ax4.set_xlabel("Y [Px]") - ax4.set_ylabel("Z [Px]") + ax4.set_ylabel("Z [nm]") ax4.set_xlim( np.mean(locs["y"]) - 3 * np.std(locs["y"]), np.mean(locs["y"]) + 3 * np.std(locs["y"]), @@ -7316,7 +7316,7 @@ def pick_message_box(self, params): "Keep pick No: {} of {} ?\n" "Picks removed: {} Picks kept: {} Keep Ratio: {:.2f} % \n" "Time elapsed: {:.2f} Minutes, " - "Picks per Minute: {:.2f}" + " Picks per Minute: {:.2f}" ).format( params["i"] + 1, params["n_total"], @@ -7351,7 +7351,7 @@ def pick_message_box(self, params): def select_traces(self): """ - Lets user to select picks based on their traces. + Lets the user to select picks based on their traces. Opens self.pick_message_box to display information. """ @@ -7410,6 +7410,7 @@ def select_traces(self): ax3.set_title("Localizations") ax3.set_xlabel("Frames") ax3.set_ylabel("ON") + ax3.set_yticks([0, 1]) fig.canvas.draw() width, height = fig.canvas.get_width_height() From bdf48280bc5ea0cadb150ec12d805d2ca671f692 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 5 Dec 2022 15:51:35 +0100 Subject: [PATCH 03/39] Select traces layout corrected --- picasso/gui/render.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 0491db3d..490b4ce6 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -7258,6 +7258,7 @@ def show_trace(self): ax3.set_title("Localizations") ax3.set_xlabel("Frames") ax3.set_ylabel("ON") + ax3.set_yticks([0, 1]) ax3.set_ylim([-0.1, 1.1]) self.export_trace_button = QtWidgets.QPushButton("Export (*.csv)") @@ -7349,6 +7350,7 @@ def pick_message_box(self, params): return msgBox + @check_pick def select_traces(self): """ Lets the user to select picks based on their traces. @@ -7366,16 +7368,18 @@ def select_traces(self): i = 0 # index of the currently shown pick n_frames = self.infos[channel][0]["Frames"] while i < len(self._picks): - fig = plt.figure(figsize=(5, 5), constrained_layout=True) + fig, (ax1, ax2, ax3) = plt.subplots( + 3, 1, figsize=(5, 5), constrained_layout=True + ) fig.canvas.set_window_title("Trace") pick = self._picks[i] locs = all_picked_locs[i] locs = stack_arrays(locs, asrecarray=True, usemask=False) # essentialy the same plotting as in self.show_trace - ax1 = fig.add_subplot(311) - ax2 = fig.add_subplot(312, sharex=ax1) - ax3 = fig.add_subplot(313, sharex=ax1) + # ax1 = fig.add_subplot(311) + # ax2 = fig.add_subplot(312, sharex=ax1) + # ax3 = fig.add_subplot(313, sharex=ax1) xvec = np.arange(n_frames) yvec = xvec[:] * 0 From 03ef28f7a12935d22df34ce3bad9f087124a543a Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 5 Dec 2022 16:12:16 +0100 Subject: [PATCH 04/39] remove dsstore --- picasso/.DS_Store | Bin 6148 -> 0 bytes picasso/gui/.DS_Store | Bin 6148 -> 0 bytes picasso/gui/plugins/.DS_Store | Bin 6148 -> 0 bytes release/.DS_Store | Bin 6148 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 picasso/.DS_Store delete mode 100644 picasso/gui/.DS_Store delete mode 100644 picasso/gui/plugins/.DS_Store delete mode 100644 release/.DS_Store diff --git a/picasso/.DS_Store b/picasso/.DS_Store deleted file mode 100644 index fd07e879d88dd4f8d1e700b9c143e2aa2c497974..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKO-chX6n<%|OmWkt%YYuhuH24gJV7rYwNjN1Q`CyfIf3BHO%ZX?g}CqnLIuG? z2;w2S75rY3b`qy`DI)Jd@@3x7e91S=WI{x$HS9Kt8blPKF;?p678u95m8{}CT&(yW z&Gvql4oZ3A_`3@5v#ZhpUC>#-=Rd!#yCRZiNz&+zGmdfD-#-5;WZL;tQZ z%D~s7Asq#L8`H}o;`42PPG0AHrY_A2zH&6++q`AI6-p_iE>_tOI7j!7Vbte6etVd$ z)2sKn7iR?gqY3k`(kZx4=$!jt26V%3OAzH82p+Cz}R7K5gwTQ N2pAcp69s-%fiHmShFJgr diff --git a/picasso/gui/.DS_Store b/picasso/gui/.DS_Store deleted file mode 100644 index d3c04c3cb37557fe7f39884fdc7f7cd3bf903af4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKyG{c!5FCd9L7_+$h0ZM%3O~RlI$8?y0ePYXDd8faNYGtN!{aOX3M%+8W^F6E z%Oyx@5JJ1sdVKb-JvWwa&j3t&F=_%e02R7m>4?n+lX>ximBPp|(YQ5+=wpZpZqO}6 z>%d=BK+dkp)9PZ5N1om4{JPU|volO)b4UB&?0I|SEQ#a6G#N0W^6)u0xp?_%{S-ag zA$mT^vgmPH?BkY96WrlG$FY8Oo}bO~x;}mXuy2l6CV!T!xNMHd?O%VD{ST?t6bp>W zW1RyUZQKTof?%=+j}s<->%X zO}L>Lw>#IjE*vKEsI4lX3gi{o^N%&D|CihE|M?_+QUz3jf2Dva$DO#%R|>VY@N!aX sExkt2u(t!oti?Olvt@`-~Xw`s(kPt$) zbocpu_Qm<4obQN;XWQkJXhK94njniZAYvYzI&{|-BUBL&|fr3?qg)z zP)|?D{UiTo{V<)k-KO`nUq1KTbalO0cMEt_yNjFV`SJDYEAQnuy!+K*7V@1!f`MQl z7zhS}fgdq|nk`a|4WkbRf`MS*odMY&5}IIkEQY#upwlY=P>#_m(DjT8Okx0L$6^Qz zge??kq3l--ws6>!`(?*sXyL^AWUS*)e!IM|pO87JJ8?FQJ{SlFju|+&;Y8~HHGZYR zOa5_4jDmq+;GZ$Tqk3M?@KRn|PhL-IZ9=<66A`~63Iw{AVxX2{3}hd<)JdC9qGLWg W7DHJ@>>3V?gMbneT`=$q4D11Y$uds> diff --git a/release/.DS_Store b/release/.DS_Store deleted file mode 100644 index 3f6021e16cd0730f9ae4b05a8abc6551597ae7bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5T1<@iapqiD0tbEXZr@R#G41951_V{T1=W?EyY`|9=-Vr9()83g3z>8p;SafW?=T)?9A-Wd?CA8B2uk!uSwJ(q8gMj+(NTJ*v>j9fg9mqW#$+q z{bswHrTLt%3D>5RI_WPtV>{Rw(Q>cKXl5uW=rrh&h|Y~QaJUCsYJeVw}e*H}Nz zzUuql3|{@Dx&AukbZ4p^(Jftog9L2c__gkTEcdL|^{C7DvSi+B4XeHVd=EL_(BzxX zzKvkFYOCfnJt%W3W2;##vM*Sd*B1FX25@Gx)an+c6$XR>VW4Dy_XiDSj2spg&DMd& zTmgU~hK-=lzZ4v!9Yzidi?BeH4F%dz<*pdYhGSeizsO->(T0<9mk;GuR_=zP%<7n5 z8+THXMQMcrVZdi#!#+0n{6AiP|M!FBNf;0Y{uKi%jN5SwCAqV8t~fqxHS`?H!hVHC k6N1Ja$EL$a@gCF&Y??d3$YEg-BM|u!Ff>Rb4E!nspLdL?S^xk5 From c42bf617f7a783b7e36448da278da28a28c88a86 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 5 Dec 2022 16:22:26 +0100 Subject: [PATCH 05/39] ignore ds_store, config.yaml and plugins --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7f4e0c3e..20c147a4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,12 @@ wheels/ .installed.cfg *.egg +# Plugins +picasso/gui/plugins/ + +# Camera configuration +picasso/config.yaml + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. @@ -69,7 +75,7 @@ docs/_build/ target/ # Mac Desktop Service Store -.DS_Store +**/.DS_Store # Jupyter Notebook .ipynb_checkpoints From a0add10de05652e037d4bbe98687700a0c7d790c Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 13 Dec 2022 10:26:55 +0100 Subject: [PATCH 06/39] config template new camera --- picasso/config_template.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/picasso/config_template.yaml b/picasso/config_template.yaml index 1f5ac2bc..a68ae13f 100644 --- a/picasso/config_template.yaml +++ b/picasso/config_template.yaml @@ -1,4 +1,30 @@ Cameras: + Andor Zyla 4.2 Plus: + Pixelsize: 130 + Baseline: 100 + Quantum Efficiency: + 525: 0.8 + 595: 0.82 + 700: 0.74 + Sensitivity Categories: + - PixelReadoutRate + - Sensitivity/DynamicRange + Sensitivity: + 540 MHz - fastest readout: + 12-bit (high well capacity): 7.18 + 12-bit (low noise): 0.29 + 16-bit (low noise & high well capacity): 0.46 + 200 MHz - lowest noise: + 12-bit (high well capacity): 7.0 + 12-bit (low noise): 0.26 + 16-bit (low noise & high well capacity): 0.45 + Channel Device: + # This information is used to automatically set the emission wavelength based on MicroManager metadata + Name: FilterTurret1-Label + Emission Wavelengths: + 1-TIRF 488: 525 + 2-TIRF 560: 595 + 3-TIRF 640: 700 Andor iXon X-9603: Baseline: 200 Gain Property Name: Gain From 5e8b9a4fe1a7d2440588a8a076d7e45e1fb86aed Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 13 Dec 2022 11:04:00 +0100 Subject: [PATCH 07/39] test clusterer one pixel blur option --- picasso/gui/render.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 490b4ce6..0425bbef 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -2149,7 +2149,7 @@ def __init__(self, window): layout.addWidget( QtWidgets.QLabel( "Pick a region of interest and test different clustering\n" - "parameters.\n\n" + "algorithms and parameters.\n\n" "Use shortcuts Alt + {W, A, S, D, -, =} to change FOV.\n" ), 0, 0 ) @@ -2178,24 +2178,29 @@ def __init__(self, window): self.test_smlm_params = TestSMLMParams(self) parameters_stack.addWidget(self.test_smlm_params) - # parameters - display mode + # parameters - display modes + self.one_pixel_blur = QtWidgets.QCheckBox("One pixel blur") + self.one_pixel_blur.setChecked(False) + self.one_pixel_blur.stateChanged.connect(self.view.update_scene) + parameters_grid.addWidget(self.one_pixel_blur, 2, 0, 1, 2) + self.display_all_locs = QtWidgets.QCheckBox( "Display non-clustered localizations" ) self.display_all_locs.setChecked(False) self.display_all_locs.stateChanged.connect(self.view.update_scene) - parameters_grid.addWidget(self.display_all_locs, 2, 0, 1, 2) + parameters_grid.addWidget(self.display_all_locs, 3, 0, 1, 2) # parameters - test test_button = QtWidgets.QPushButton("Test") test_button.clicked.connect(self.test_clusterer) test_button.setDefault(True) - parameters_grid.addWidget(test_button, 3, 0) + parameters_grid.addWidget(test_button, 4, 0) # display settings - return to full FOV full_fov = QtWidgets.QPushButton("Full FOV") full_fov.clicked.connect(self.get_full_fov) - parameters_grid.addWidget(full_fov, 3, 1) + parameters_grid.addWidget(full_fov, 4, 1) # view view_box = QtWidgets.QGroupBox("View") @@ -2679,10 +2684,14 @@ def update_scene(self): locs = self.split_locs() # render kwargs + if self.dialog.one_pixel_blur.isChecked(): + blur_method = 'smooth' + else: + blur_method = 'convolve' kwargs = { 'oversampling': self.get_optimal_oversampling(), 'viewport': self.viewport, - 'blur_method': 'convolve', + 'blur_method': blur_method, 'min_blur_width': 0, } From 7c6e6944e27b00f49c87d6bd78f11e74cd36e348 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 13 Dec 2022 11:07:44 +0100 Subject: [PATCH 08/39] cluster center lpz = std(grouplocs.z) --- picasso/clusterer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picasso/clusterer.py b/picasso/clusterer.py index 27d23354..6e82c7e8 100644 --- a/picasso/clusterer.py +++ b/picasso/clusterer.py @@ -518,7 +518,7 @@ def cluster_center(grouplocs, pixelsize, separate_lp=False): weights=1/((grouplocs.lpx+grouplocs.lpy)**2), ) std_z = grouplocs.z.std() / pixelsize - lpz = 2 * lpx + lpz = std_z volume = _np.power((std_x + std_y + std_z) / 3 * 2, 3) * 4.18879 try: X = _np.stack( From 22df951d91d2ae3f2f2e3e0fadbe9cd10909ae66 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 13 Dec 2022 12:11:15 +0100 Subject: [PATCH 09/39] test clusterer 3d projections --- picasso/gui/render.py | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 0425bbef..df510141 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -15,9 +15,10 @@ import importlib, pkgutil from glob import glob from math import ceil -# from icecream import ic from functools import partial +# from icecream import ic + import lmfit import matplotlib import matplotlib.pyplot as plt @@ -2191,16 +2192,29 @@ def __init__(self, window): self.display_all_locs.stateChanged.connect(self.view.update_scene) parameters_grid.addWidget(self.display_all_locs, 3, 0, 1, 2) + # parameters - xy, xz, yz projections + xy_proj = QtWidgets.QPushButton("XY projection") + xy_proj.clicked.connect(self.on_xy_proj) + parameters_grid.addWidget(xy_proj, 4, 0, 1, 2) + + xz_proj = QtWidgets.QPushButton("XZ projection") + xz_proj.clicked.connect(self.on_xz_proj) + parameters_grid.addWidget(xz_proj, 5, 0) + + yz_proj = QtWidgets.QPushButton("YZ projection") + yz_proj.clicked.connect(self.on_yz_proj) + parameters_grid.addWidget(yz_proj, 5, 1) + # parameters - test test_button = QtWidgets.QPushButton("Test") test_button.clicked.connect(self.test_clusterer) test_button.setDefault(True) - parameters_grid.addWidget(test_button, 4, 0) + parameters_grid.addWidget(test_button, 6, 0) # display settings - return to full FOV full_fov = QtWidgets.QPushButton("Full FOV") full_fov.clicked.connect(self.get_full_fov) - parameters_grid.addWidget(full_fov, 4, 1) + parameters_grid.addWidget(full_fov, 6, 1) # view view_box = QtWidgets.QGroupBox("View") @@ -2241,6 +2255,24 @@ def __init__(self, window): zoomout_action.triggered.connect(self.view.zoom_out) self.addAction(zoomout_action) + def on_xy_proj(self): + self.view.ang = None + self.view.update_scene() + + def on_xz_proj(self): + if self.view.locs is not None and hasattr(self.view.locs, "z"): + self.view.ang = [1.5708, 0, 0] # 90 deg rotation + else: + self.view.ang = None + self.view.update_scene() + + def on_yz_proj(self): + if self.view.locs is not None and hasattr(self.view.locs, "z"): + self.view.ang = [0, 1.5708, 0] # 90 deg rotation + else: + self.view.ang = None + self.view.update_scene() + def cluster(self, locs, params): """ Clusters locs using the chosen clusterer. @@ -2382,6 +2414,8 @@ def test_clusterer(self): # extract picked locs self.channel = self.window.view.get_channel("Test clusterer") locs = self.window.view.picked_locs(self.channel)[0] + if hasattr(locs, "z"): + locs.z /= self.window.display_settings_dlg.pixelsize.value() # cluster picked locs self.view.locs = self.cluster(locs, params) # update viewport if pick has changed @@ -2586,6 +2620,7 @@ def __init__(self, dialog): self.view = dialog.window.view self.viewport = None self.locs = None + self.ang = None self._size = 500 self.setMinimumSize(self._size, self._size) self.setMaximumSize(self._size, self._size) @@ -2693,6 +2728,7 @@ def update_scene(self): 'viewport': self.viewport, 'blur_method': blur_method, 'min_blur_width': 0, + 'ang': self.ang, } # render images for all channels From d6fc96add2e6c60262e09a04ea7bf14a98a96549 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 13 Jan 2023 13:38:04 +0100 Subject: [PATCH 10/39] cluster centers save std_x, std_y, std_z --- changelog.rst | 6 ++--- picasso/clusterer.py | 55 ++++++++++++++++++++++++++----------------- picasso/gui/render.py | 2 +- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/changelog.rst b/changelog.rst index 5593d932..9b0738ee 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,12 +1,12 @@ Changelog ========= -Last change: 05-DEC-2022 MTS +Last change: 13-JAN-2023 MTS -0.5.5 - 0.5.7 +0.5.5 - 0.5.8 ------------- - Cluster info is saved in ``_cluster_centers.hdf5`` files which are created when ``Save cluster centers`` box is ticked -- Cluster centers contain info about group, mean frame (saved as ``frame``), standard deviation frame, area/volume and convex hull +- Cluster centers contain info about group, mean frame (saved as ``frame``), standard deviation frame, std in x,y and z, area/volume and convex hull - ``gist_rainbow`` is used for rendering properties - NeNA can be calculated many times - Bug fixes diff --git a/picasso/clusterer.py b/picasso/clusterer.py index 6e82c7e8..662b409d 100644 --- a/picasso/clusterer.py +++ b/picasso/clusterer.py @@ -4,7 +4,6 @@ Clusterer optimized for DNA PAINT in CPU and GPU versions. - Based on the work of Thomas Schlichthaerle and Susanne Reinhardt. :authors: Thomas Schlichthaerle, Susanne Reinhardt, Rafal Kowalewski, 2020-2022 @@ -30,6 +29,8 @@ ("std_frame", "f4"), ("x", "f4"), ("y", "f4"), + ("std_x", "f4"), + ("std_y", "f4"), ("photons", "f4"), ("sx", "f4"), ("sy", "f4"), @@ -48,6 +49,8 @@ ("std_frame", "f4"), ("x", "f4"), ("y", "f4"), + ("std_x", "f4"), + ("std_y", "f4"), ("z", "f4"), ("photons", "f4"), ("sx", "f4"), @@ -55,7 +58,7 @@ ("bg", "f4"), ("lpx", "f4"), ("lpy", "f4"), - ("lpz", "f4"), + ("std_z", "f4"), ("ellipticity", "f4"), ("net_gradient", "f4"), ("n", "u4"), @@ -388,21 +391,23 @@ def find_cluster_centers(locs, pixelsize): std_frame = _np.array([_[1] for _ in centers_]) x = _np.array([_[2] for _ in centers_]) y = _np.array([_[3] for _ in centers_]) - photons = _np.array([_[4] for _ in centers_]) - sx = _np.array([_[5] for _ in centers_]) - sy = _np.array([_[6] for _ in centers_]) - bg = _np.array([_[7] for _ in centers_]) - lpx = _np.array([_[8] for _ in centers_]) - lpy = _np.array([_[9] for _ in centers_]) - ellipticity = _np.array([_[10] for _ in centers_]) - net_gradient = _np.array([_[11] for _ in centers_]) - n = _np.array([_[12] for _ in centers_]) + std_x = _np.array([_[4] for _ in centers_]) + std_y = _np.array([_[5] for _ in centers_]) + photons = _np.array([_[6] for _ in centers_]) + sx = _np.array([_[7] for _ in centers_]) + sy = _np.array([_[8] for _ in centers_]) + bg = _np.array([_[9] for _ in centers_]) + lpx = _np.array([_[10] for _ in centers_]) + lpy = _np.array([_[11] for _ in centers_]) + ellipticity = _np.array([_[12] for _ in centers_]) + net_gradient = _np.array([_[13] for _ in centers_]) + n = _np.array([_[14] for _ in centers_]) if hasattr(locs, "z"): - z = _np.array([_[13] for _ in centers_]) - lpz = _np.array([_[14] for _ in centers_]) - volume = _np.array([_[15] for _ in centers_]) - convexhull = _np.array([_[16] for _ in centers_]) + z = _np.array([_[15] for _ in centers_]) + std_z = _np.array([_[16] for _ in centers_]) + volume = _np.array([_[17] for _ in centers_]) + convexhull = _np.array([_[18] for _ in centers_]) centers = _np.rec.array( ( res.index.values, # group id @@ -410,6 +415,8 @@ def find_cluster_centers(locs, pixelsize): std_frame, x, y, + std_x, + std_y, z, photons, sx, @@ -417,7 +424,7 @@ def find_cluster_centers(locs, pixelsize): bg, lpx, lpy, - lpz, + std_z, ellipticity, net_gradient, n, @@ -436,6 +443,8 @@ def find_cluster_centers(locs, pixelsize): std_frame, x, y, + std_x, + std_y, photons, sx, sy, @@ -512,13 +521,12 @@ def cluster_center(grouplocs, pixelsize, separate_lp=False): # n_locs in cluster n = len(grouplocs) if hasattr(grouplocs, "z"): - # take lpz = 2 * mean(lpx, lpy) z = _np.average( grouplocs.z, weights=1/((grouplocs.lpx+grouplocs.lpy)**2), - ) + ) # take lpz = 2 * mean(lpx, lpy) std_z = grouplocs.z.std() / pixelsize - lpz = std_z + # lpz = std_z volume = _np.power((std_x + std_y + std_z) / 3 * 2, 3) * 4.18879 try: X = _np.stack( @@ -534,6 +542,8 @@ def cluster_center(grouplocs, pixelsize, separate_lp=False): std_frame, x, y, + std_x, + std_y, photons, sx, sy, @@ -543,8 +553,9 @@ def cluster_center(grouplocs, pixelsize, separate_lp=False): ellipticity, net_gradient, n, - z, - lpz, + z, + std_z, + # lpz, volume, convexhull, ] @@ -561,6 +572,8 @@ def cluster_center(grouplocs, pixelsize, separate_lp=False): std_frame, x, y, + std_x, + std_y, photons, sx, sy, diff --git a/picasso/gui/render.py b/picasso/gui/render.py index df510141..5d9b095d 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -4203,7 +4203,7 @@ def __init__(self, window): # Camera_parameters camera_groupbox = QtWidgets.QGroupBox("Camera") self.camera_grid = QtWidgets.QGridLayout(camera_groupbox) - self.camera_grid.addWidget(QtWidgets.QLabel("Pixel Size:"), 0, 0) + self.camera_grid.addWidget(QtWidgets.QLabel("Pixel Size (nm):"), 0, 0) self.pixelsize = QtWidgets.QDoubleSpinBox() self.pixelsize.setRange(1, 100000) self.pixelsize.setValue(130) From df0ee7cb85db0fae1b0786afdb39c25eb378ab0e Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 13 Jan 2023 13:58:07 +0100 Subject: [PATCH 11/39] test clusterer display cluster centers --- changelog.rst | 1 + picasso/gui/render.py | 54 ++++++++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/changelog.rst b/changelog.rst index 9b0738ee..3943f17f 100644 --- a/changelog.rst +++ b/changelog.rst @@ -9,6 +9,7 @@ Last change: 13-JAN-2023 MTS - Cluster centers contain info about group, mean frame (saved as ``frame``), standard deviation frame, std in x,y and z, area/volume and convex hull - ``gist_rainbow`` is used for rendering properties - NeNA can be calculated many times +- Cluster centers display in Test Clusterer window (Render) - Bug fixes 0.5.1 - 0.5.4 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 5d9b095d..507b577d 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -2192,29 +2192,34 @@ def __init__(self, window): self.display_all_locs.stateChanged.connect(self.view.update_scene) parameters_grid.addWidget(self.display_all_locs, 3, 0, 1, 2) + self.display_centers = QtWidgets.QCheckBox("Display cluster centers") + self.display_centers.setChecked(False) + self.display_centers.stateChanged.connect(self.view.update_scene) + parameters_grid.addWidget(self.display_centers, 4, 0, 1, 2) + # parameters - xy, xz, yz projections xy_proj = QtWidgets.QPushButton("XY projection") xy_proj.clicked.connect(self.on_xy_proj) - parameters_grid.addWidget(xy_proj, 4, 0, 1, 2) + parameters_grid.addWidget(xy_proj, 5, 0, 1, 2) xz_proj = QtWidgets.QPushButton("XZ projection") xz_proj.clicked.connect(self.on_xz_proj) - parameters_grid.addWidget(xz_proj, 5, 0) + parameters_grid.addWidget(xz_proj, 6, 0) yz_proj = QtWidgets.QPushButton("YZ projection") yz_proj.clicked.connect(self.on_yz_proj) - parameters_grid.addWidget(yz_proj, 5, 1) + parameters_grid.addWidget(yz_proj, 6, 1) # parameters - test test_button = QtWidgets.QPushButton("Test") test_button.clicked.connect(self.test_clusterer) test_button.setDefault(True) - parameters_grid.addWidget(test_button, 6, 0) + parameters_grid.addWidget(test_button, 7, 0) # display settings - return to full FOV full_fov = QtWidgets.QPushButton("Full FOV") full_fov.clicked.connect(self.get_full_fov) - parameters_grid.addWidget(full_fov, 6, 1) + parameters_grid.addWidget(full_fov, 7, 1) # view view_box = QtWidgets.QGroupBox("View") @@ -2418,6 +2423,11 @@ def test_clusterer(self): locs.z /= self.window.display_settings_dlg.pixelsize.value() # cluster picked locs self.view.locs = self.cluster(locs, params) + # calculate cluster centers + self.view.centers = clusterer.find_cluster_centers( + self.view.locs, + self.window.display_settings_dlg.pixelsize.value(), + ) # update viewport if pick has changed if self.pick_changed(): self.view.viewport = self.view.get_full_fov() @@ -2589,9 +2599,8 @@ class TestClustererView(QtWidgets.QLabel): shift_viewport(dx, dy) Moves viewport by a specified amount split_locs() - Splits self.locs into a list. It has either two (all - clustered locs and all picked locs) or N_GROUP_COLORS elements - (each one for a group color) + Splits self.locs into a list. It has either two, three or + N_GROUP_COLORS elements (each for one group color). to_down() Shifts viewport downwards to_left() @@ -2759,17 +2768,36 @@ def update_scene(self): def split_locs(self): """ - Splits self.locs into a list. It has either two (all - clustered locs and all picked locs) or N_GROUP_COLORS elements - (each one for a group color). + Splits self.locs into a list. It has either two, three or + N_GROUP_COLORS elements (each for one group color). """ - if self.dialog.display_all_locs.isChecked(): - # two channels, all locs and clustered locs + if ( + self.dialog.display_all_locs.isChecked() + and not self.dialog.display_centers.isChecked() + ): # two channels, all locs and clustered locs + channel = self.dialog.channel + locs = [ + self.dialog.window.view.picked_locs(channel)[0], + self.locs, + ] + elif ( + not self.dialog.display_all_locs.isChecked() + and self.dialog.display_centers.isChecked() + ): # two channels, clustered locs and cluster centers + locs = [ + self.locs, + self.centers, + ] + elif( + self.dialog.display_all_locs.isChecked() + and self.dialog.display_centers.isChecked() + ): # three channels, all locs, clustered locs and cluster centers channel = self.dialog.channel locs = [ self.dialog.window.view.picked_locs(channel)[0], self.locs, + self.centers, ] else: # multiple channels, each for one group color From 845e56e4f095a0742ce584bf9ff26c9adb3e0c94 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 20 Jan 2023 11:53:34 +0100 Subject: [PATCH 12/39] CLEAN --- changelog.rst | 4 ++-- picasso/clusterer.py | 4 ---- picasso/render.py | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/changelog.rst b/changelog.rst index 3943f17f..bdc48476 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,9 +1,9 @@ Changelog ========= -Last change: 13-JAN-2023 MTS +Last change: 20-JAN-2023 MTS -0.5.5 - 0.5.8 +0.5.5 - 0.5.7 ------------- - Cluster info is saved in ``_cluster_centers.hdf5`` files which are created when ``Save cluster centers`` box is ticked - Cluster centers contain info about group, mean frame (saved as ``frame``), standard deviation frame, std in x,y and z, area/volume and convex hull diff --git a/picasso/clusterer.py b/picasso/clusterer.py index 662b409d..ebfd14d5 100644 --- a/picasso/clusterer.py +++ b/picasso/clusterer.py @@ -10,11 +10,7 @@ :copyright: Copyright (c) 2022 Jungmann Lab, MPI of Biochemistry """ -import os as _os - import numpy as _np -import math as _math -import yaml as _yaml import pandas as _pd from scipy.spatial import cKDTree as _cKDTree from scipy.spatial import ConvexHull as _ConvexHull diff --git a/picasso/render.py b/picasso/render.py index db80f62f..5d3088a0 100755 --- a/picasso/render.py +++ b/picasso/render.py @@ -970,7 +970,7 @@ def rotation_matrix(angx, angy, angz): Returns ------- scipy.spatial.transform.Rotation - Scipy method that can be applied to rotate an Nx3 np.array + Scipy class that can be applied to rotate an Nx3 np.array """ rot_mat_x = _np.array( From 166ede3bc77ea6311624f9f7e1e4b042168dfc9e Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 20 Jan 2023 16:48:45 +0100 Subject: [PATCH 13/39] render locs.group much faster --- changelog.rst | 1 + picasso/gui/render.py | 35 +---------------------------------- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/changelog.rst b/changelog.rst index bdc48476..38f00504 100644 --- a/changelog.rst +++ b/changelog.rst @@ -10,6 +10,7 @@ Last change: 20-JAN-2023 MTS - ``gist_rainbow`` is used for rendering properties - NeNA can be calculated many times - Cluster centers display in Test Clusterer window (Render) +- Render group information is faster (e.g., clustered data) - Bug fixes 0.5.1 - 0.5.4 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 507b577d..aee42a34 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -5272,40 +5272,7 @@ def get_group_color(self, locs): Array with int group color index for each loc """ - groups = np.unique(locs.group) - groupcopy = locs.group.copy() - - # check if groups are consecutive - if set(groups) == set(range(min(groups), max(groups) + 1)): - if len(groups) > 5000: - choice = QtWidgets.QMessageBox.question( - self, - "Group question", - ( - "Groups are not consecutive" - " and more than 5000 groups detected." - " Re-Index groups? This may take a while." - ), - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - ) - if choice == QtWidgets.QMessageBox.Yes: - pb = lib.ProgressDialog( - "Re-Indexing groups", 0, len(groups), self - ) - pb.set_value(0) - for i in tqdm(range(len(groups))): - groupcopy[locs.group == groups[i]] = i - pb.set_value(i) - pb.close() - else: - for i in tqdm(range(len(groups))): - groupcopy[locs.group == groups[i]] = i - else: - for i in range(len(groups)): - groupcopy[locs.group == groups[i]] = i - np.random.shuffle(groups) - groups %= N_GROUP_COLORS - return groups[groupcopy] + return locs.group.astype(int) % N_GROUP_COLORS def add(self, path, render=True): """ From 306d6036a8006d003a3dbe5fcb35f87380249f76 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 20 Jan 2023 20:59:30 +0100 Subject: [PATCH 14/39] clarify readme --- readme.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/readme.rst b/readme.rst index 52a6ec22..b42add55 100644 --- a/readme.rst +++ b/readme.rst @@ -39,7 +39,7 @@ Check out the `Picasso release page Date: Thu, 26 Jan 2023 13:45:51 +0100 Subject: [PATCH 15/39] installation fix --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index abcf399d..9238e495 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ PyQt5 -numpy>=1.19.2 +numpy==1.19.2 h5py>=2.10.0 matplotlib==3.3.2 numba>=0.51.2 From ba8b5d7c683a56cdfc19e619d1b040fcb950e8f8 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 26 Jan 2023 16:29:26 +0100 Subject: [PATCH 16/39] update readme for clearer installation instrcutions --- changelog.rst | 15 ++++++--- readme.rst | 85 +++++++++++++++++++++++---------------------------- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/changelog.rst b/changelog.rst index 38f00504..8543f3e0 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,16 +1,21 @@ Changelog ========= -Last change: 20-JAN-2023 MTS +Last change: 26-JAN-2023 MTS -0.5.5 - 0.5.7 +0.5.7 +----- +- Updated installation instructions +- Render group information is faster (e.g., clustered data) +- Cluster centers display in Test Clusterer window (Render) +- Cluster centers contain info about std in x,y and z + +0.5.5 - 0.5.6 ------------- - Cluster info is saved in ``_cluster_centers.hdf5`` files which are created when ``Save cluster centers`` box is ticked -- Cluster centers contain info about group, mean frame (saved as ``frame``), standard deviation frame, std in x,y and z, area/volume and convex hull +- Cluster centers contain info about group, mean frame (saved as ``frame``), standard deviation frame, area/volume and convex hull - ``gist_rainbow`` is used for rendering properties - NeNA can be calculated many times -- Cluster centers display in Test Clusterer window (Render) -- Render group information is faster (e.g., clustered data) - Bug fixes 0.5.1 - 0.5.4 diff --git a/readme.rst b/readme.rst index b42add55..01143e40 100644 --- a/readme.rst +++ b/readme.rst @@ -34,57 +34,52 @@ Picasso now has a server-based workflow management-system. Check out `here `__ to download and run the latest compiled one-click installer for Windows. Here you will also find the Nature Protocols legacy version. For the platform-independent usage of Picasso (e.g., with Linux and Mac Os X), please follow the advanced installation instructions below. +Check out the `Picasso release page `__ to download and run the latest compiled one-click installer for Windows. Here you will also find the Nature Protocols legacy version. -Advanced installation for Python programmers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For the platform-independent usage of Picasso (e.g., with Linux and Mac Os X), please follow the advanced installation instructions below. -As an alternative to the stand-alone program for end-users, Picasso can be installed as a Python package. This is the preferred option to use Picasso’s internal routines in custom Python programs. For windows, it is still possible to use Picasso as an end-user by creating the respective shortcuts. This allows Picasso to be used on the same system by both programmers and end-users. +Other installation modes (Python 3.8) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Requirements -^^^^^^^^^^^^ +As an alternative to the stand-alone program for end-users, Picasso can be installed as a Python package. This is the preferred option to use Picasso’s internal routines in custom Python programs. Those can be imported by running, for example, ``from picasso import io`` to use input/output functions from Picasso. For windows, it is still possible to use Picasso as an end-user by creating the respective shortcuts. This allows Picasso to be used on the same system by both programmers and end-users. -Python 3.8 (Tested on Windows 10) -''''''''''''''''''''''''''''''''' +Via PyPI +'''''''' -We highly recommend the `Anaconda or Miniconda `__ Python distribution which comes with a powerful package manager. +1. Open the console/terminal and create a new conda environment: ``conda create --name picasso python=3.8`` +2. Activate the environment: ``conda activate picasso``. +3. Install Picasso package using: ``pip install picassosr``. +4. You can now run any Picasso function directly from the console/terminal by running: ``picasso render``, ``picasso localize``, etc. -Setting up the environment with conda -''''''''''''''''''''''''''''''''''''' +For Developers +'''''''''''''' -Sample instructions to create an environment and installation of packages with conda are as follows: +If you wish to use your local version of Picasso with your own modifications: -1. Open the console and create a new conda environment: ``conda create --name picasso python=3.8`` -2. Activate the environment: ``source activate picasso`` for Linux / Mac Os X or ``activate picasso`` for Windows. -3. (Optional) For Mac systems (e.g. M1) install PyQt via conda: ``conda install -c anaconda pyqt``. -4. (Optional) If you want to use hdbscan install using pip: ``pip install hdbscan``. -5. (Optional) If you plan to compile your own installer additionally install Pyinstaller: ``pip install pyinstaller`` -6. Continue with the installation of Picasso (see the **Instalation (continued)** tab below) +1. Open the console/terminal and create a new conda environment: ``conda create --name picasso python=3.8`` +2. Activate the environment: ``conda activate picasso``. +3. Change to the directory of choice using ``cd``. +4. Clone this GitHub repository by running ``git clone https://github.com/jungmannlab/picasso``. Alternatively, `download `__ the zip file and unzip it. +5. Open the Picasso directory: ``cd picasso``. +6. You can modify Picasso code from here. -Troubleshooting: In case installing via ``pip`` fails, try to install the failing packages via conda. +*Windows* -Note that sometimes outdated packages can cause problems. As of version 0.3.0, Picasso switched from PyQt4 to PyQt5, so make sure to update PyQt. If you experience errors, please check whether your packages have the correct version (e.g., see issue #4). When using conda, make sure that you have the default package channel (e.g., see issue #30). +7. If you wish to create a *local* Picasso package to use it in other Python scripts (that includes your changes), run ``python setup.py install``. +8. You can now run any Picasso function directly from the console/terminal by running: ``picasso render``, ``picasso localize``, etc. +9. Remember that in order to update changes in Picasso code, you need to repeat step 7. -.. _installation-1: +*Mac* -Installation (continued) -^^^^^^^^^^^^^^^^^^^^^^^^ +Currently, Picasso does not support package creation on Mac OS. If you wish to run your modified Picasso code, simply go to your ``picasso`` directory and run ``python -m picasso render``, ``python -m picasso localize``, etc. -There are two approaches to installing Picasso. Firstly, ``pip`` can be used to download Picasso release from `PyPI `_. Alternatively, the GitHub repo can be directly cloned to your computer. Please see the instructions below for details. +Optional packages +''''''''''''''''' -Via PyPI: -''''''''' +Regardless of whether Picasso was installed via PyPI or by cloning the GitHub repository, some packages may be additionally installed to allow extra functionality: -1. Install using pip: ``pip install picassosr``. -2. Launch via calling one of the modules, e.g. ``picasso localize``. - -Via GitHub: -''''''''''' - -1. Open the console, ``cd`` to the directory where you want to install and run ``git clone https://github.com/jungmannlab/picasso``. Alternatively, `download `__ the zip file and unzip it. -2. Change to the downloaded directory ``cd picasso`` -3. Run installation ``python setup.py install``. -4. Launch via calling one of the modules, e.g. ``picasso localize``. +- ``pip install hdbscan`` for clustering with `HDBSCAN `_`. +- ``pip install pyinstaller`` if you plan to additionally compile your own installer with `Pyinstaller _`. Updating ^^^^^^^^ @@ -96,10 +91,10 @@ If Picasso was installed from PyPI, run the following command: If Picasso was cloned from the GitHub repo, use the following commands: ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -1. Move to the Picasso folder with the terminal, activate environment. +1. Move to the ``picasso`` folder with the terminal, activate environment. 2. Update with git: ``git pull``. 3. Update the environment: ``pip install --upgrade -r requirements.txt``. -4. Run installation ``python setup.py install``. +4. (*Windows only*)Run installation ``python setup.py install``. Creating shortcuts on Windows (optional) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,12 +102,12 @@ Creating shortcuts on Windows (optional) Run the PowerShell script “createShortcuts.ps1” in the gui directory. This should be doable by right-clicking on the script and choosing “Run with PowerShell”. Alternatively, run the command ``powershell ./createShortcuts.ps1`` in the command line. Use the generated shortcuts in the top level directory to start GUI components. Users can drag these shortcuts to their Desktop, Start Menu or Task Bar. -Using Picasso as a module -^^^^^^^^^^^^^^^^^^^^^^^^^ +.. Using Picasso as a module +.. ^^^^^^^^^^^^^^^^^^^^^^^^^ -The individual modules of picasso can be started as follows: -1. Open the console, activate the environment: ``source activate picasso`` for Linux / Mac Os X or ``activate picasso`` for Windows. -2. Start the picasso modules via ``python -m picasso ..``, e.g. ``python -m picasso render`` for the render module +.. The individual modules of picasso can be started as follows: +.. 1. Open the console, activate the environment: ``source activate picasso`` for Linux / Mac Os X or ``activate picasso`` for Windows. +.. 2. Start the picasso modules via ``python -m picasso ..``, e.g. ``python -m picasso render`` for the render module Using GPU for Fitting (optional) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -141,15 +136,11 @@ Jupyter Notebooks Check picasso/samples/ for Jupyter Notebooks that show how to interact with the Picasso codebase. - Contributing ------------ If you have a feature request or a bug report, please post it as an issue on the GitHub issue tracker. If you want to contribute, put a PR for it. You can find more guidelines for contributing `here `__. I will gladly guide you through the codebase and credit you accordingly. Additionally, you can check out the ``Projects``-page on GitHub. You can also contact me via picasso@jungmannlab.org. - - - Contributions & Copyright ------------------------- From 8916fcef1de3b80e8e7a99bcae43528d7e05b48e Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 26 Jan 2023 16:33:59 +0100 Subject: [PATCH 17/39] readme fix --- readme.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/readme.rst b/readme.rst index 01143e40..b720c932 100644 --- a/readme.rst +++ b/readme.rst @@ -44,7 +44,7 @@ Other installation modes (Python 3.8) As an alternative to the stand-alone program for end-users, Picasso can be installed as a Python package. This is the preferred option to use Picasso’s internal routines in custom Python programs. Those can be imported by running, for example, ``from picasso import io`` to use input/output functions from Picasso. For windows, it is still possible to use Picasso as an end-user by creating the respective shortcuts. This allows Picasso to be used on the same system by both programmers and end-users. Via PyPI -'''''''' +^^^^^^^^ 1. Open the console/terminal and create a new conda environment: ``conda create --name picasso python=3.8`` 2. Activate the environment: ``conda activate picasso``. @@ -52,7 +52,7 @@ Via PyPI 4. You can now run any Picasso function directly from the console/terminal by running: ``picasso render``, ``picasso localize``, etc. For Developers -'''''''''''''' +^^^^^^^^^^^^^^ If you wish to use your local version of Picasso with your own modifications: @@ -74,21 +74,22 @@ If you wish to use your local version of Picasso with your own modifications: Currently, Picasso does not support package creation on Mac OS. If you wish to run your modified Picasso code, simply go to your ``picasso`` directory and run ``python -m picasso render``, ``python -m picasso localize``, etc. Optional packages -''''''''''''''''' +^^^^^^^^^^^^^^^^^ Regardless of whether Picasso was installed via PyPI or by cloning the GitHub repository, some packages may be additionally installed to allow extra functionality: -- ``pip install hdbscan`` for clustering with `HDBSCAN `_`. -- ``pip install pyinstaller`` if you plan to additionally compile your own installer with `Pyinstaller _`. +- ``pip install hdbscan`` for clustering with `HDBSCAN `__. +- ``pip install pyinstaller`` if you plan to additionally compile your own installer with `Pyinstaller __`. Updating ^^^^^^^^ + If Picasso was installed from PyPI, run the following command: '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ``pip install --upgrade picassosr`` -If Picasso was cloned from the GitHub repo, use the following commands: +If Picasso was cloned from the GitHub repository, use the following commands: ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 1. Move to the ``picasso`` folder with the terminal, activate environment. @@ -96,8 +97,8 @@ If Picasso was cloned from the GitHub repo, use the following commands: 3. Update the environment: ``pip install --upgrade -r requirements.txt``. 4. (*Windows only*)Run installation ``python setup.py install``. -Creating shortcuts on Windows (optional) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Creating shortcuts on Windows (*optional*) +---------------------------------------- Run the PowerShell script “createShortcuts.ps1” in the gui directory. This should be doable by right-clicking on the script and choosing “Run with PowerShell”. Alternatively, run the command ``powershell ./createShortcuts.ps1`` in the command line. Use the generated shortcuts in the top level directory to start GUI components. Users can drag these shortcuts to their Desktop, Start Menu or Task Bar. From ba6e55d7f131af9435b09437aae13387aed8c5e3 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 26 Jan 2023 16:35:41 +0100 Subject: [PATCH 18/39] readme fix --- readme.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.rst b/readme.rst index b720c932..a95ad6c5 100644 --- a/readme.rst +++ b/readme.rst @@ -78,8 +78,8 @@ Optional packages Regardless of whether Picasso was installed via PyPI or by cloning the GitHub repository, some packages may be additionally installed to allow extra functionality: -- ``pip install hdbscan`` for clustering with `HDBSCAN `__. -- ``pip install pyinstaller`` if you plan to additionally compile your own installer with `Pyinstaller __`. +- ``pip install hdbscan`` for clustering with `HDBSCAN `_. +- ``pip install pyinstaller`` if you plan to additionally compile your own installer with `Pyinstaller `_. Updating ^^^^^^^^ From 29a57e9d78cab712783f69724b6280312183193a Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 27 Jan 2023 11:14:26 +0100 Subject: [PATCH 19/39] readme fix --- readme.rst | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/readme.rst b/readme.rst index a95ad6c5..743b7d93 100644 --- a/readme.rst +++ b/readme.rst @@ -13,7 +13,7 @@ Picasso :alt: CI .. image:: https://static.pepy.tech/personalized-badge/picassosr?period=total&units=international_system&left_color=black&right_color=brightgreen&left_text=Downloads - :target: https://pepy.tech/project/picassosr + :target: https://pepy.tech/project/picassosr .. image:: main_render.png :scale: 100 % @@ -103,13 +103,6 @@ Creating shortcuts on Windows (*optional*) Run the PowerShell script “createShortcuts.ps1” in the gui directory. This should be doable by right-clicking on the script and choosing “Run with PowerShell”. Alternatively, run the command ``powershell ./createShortcuts.ps1`` in the command line. Use the generated shortcuts in the top level directory to start GUI components. Users can drag these shortcuts to their Desktop, Start Menu or Task Bar. -.. Using Picasso as a module -.. ^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. The individual modules of picasso can be started as follows: -.. 1. Open the console, activate the environment: ``source activate picasso`` for Linux / Mac Os X or ``activate picasso`` for Windows. -.. 2. Start the picasso modules via ``python -m picasso ..``, e.g. ``python -m picasso render`` for the render module - Using GPU for Fitting (optional) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From f85eb115b82c2f6c3baafbd9150989550aad2bdc Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 27 Jan 2023 11:25:03 +0100 Subject: [PATCH 20/39] readme fix --- readme.rst | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/readme.rst b/readme.rst index 743b7d93..a9b5ef60 100644 --- a/readme.rst +++ b/readme.rst @@ -13,7 +13,7 @@ Picasso :alt: CI .. image:: https://static.pepy.tech/personalized-badge/picassosr?period=total&units=international_system&left_color=black&right_color=brightgreen&left_text=Downloads - :target: https://pepy.tech/project/picassosr + :target: https://pepy.tech/project/picassosr .. image:: main_render.png :scale: 100 % @@ -64,12 +64,14 @@ If you wish to use your local version of Picasso with your own modifications: 6. You can modify Picasso code from here. *Windows* +''''''''' 7. If you wish to create a *local* Picasso package to use it in other Python scripts (that includes your changes), run ``python setup.py install``. 8. You can now run any Picasso function directly from the console/terminal by running: ``picasso render``, ``picasso localize``, etc. 9. Remember that in order to update changes in Picasso code, you need to repeat step 7. *Mac* +''''' Currently, Picasso does not support package creation on Mac OS. If you wish to run your modified Picasso code, simply go to your ``picasso`` directory and run ``python -m picasso render``, ``python -m picasso localize``, etc. @@ -78,19 +80,19 @@ Optional packages Regardless of whether Picasso was installed via PyPI or by cloning the GitHub repository, some packages may be additionally installed to allow extra functionality: -- ``pip install hdbscan`` for clustering with `HDBSCAN `_. -- ``pip install pyinstaller`` if you plan to additionally compile your own installer with `Pyinstaller `_. +- ``pip install hdbscan`` for clustering with `HDBSCAN `__. +- ``pip install pyinstaller`` if you plan to additionally compile your own installer with `Pyinstaller `__. + +To enable GPU fitting, follow instructions on `Gpufit `__ to install the Gpufit python library in your conda environment. In practice, this means downloading the zipfile and installing the Python wheel. Picasso Localize will automatically import the library if present and enables a checkbox for GPU fitting when selecting the LQ-Method. Updating ^^^^^^^^ If Picasso was installed from PyPI, run the following command: -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ``pip install --upgrade picassosr`` If Picasso was cloned from the GitHub repository, use the following commands: -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 1. Move to the ``picasso`` folder with the terminal, activate environment. 2. Update with git: ``git pull``. @@ -98,18 +100,14 @@ If Picasso was cloned from the GitHub repository, use the following commands: 4. (*Windows only*)Run installation ``python setup.py install``. Creating shortcuts on Windows (*optional*) ----------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Run the PowerShell script “createShortcuts.ps1” in the gui directory. This should be doable by right-clicking on the script and choosing “Run with PowerShell”. Alternatively, run the command ``powershell ./createShortcuts.ps1`` in the command line. Use the generated shortcuts in the top level directory to start GUI components. Users can drag these shortcuts to their Desktop, Start Menu or Task Bar. -Using GPU for Fitting (optional) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To enable GPU fitting, follow instructions on `Gpufit `__ to install the Gpufit python library in your conda environment. In practice, this means downloading the zipfile and installing the Python wheel. Picasso Localize will automatically import the library if present and enables a checkbox for GPU fitting when selecting the LQ-Method. - Example Usage ------------- + Besides using the GUI, you can use picasso like any other Python module. Consider the following example::: from picasso import io, postprocess @@ -138,9 +136,8 @@ If you have a feature request or a bug report, please post it as an issue on the Contributions & Copyright ------------------------- -| Contributors: Joerg Schnitzbauer, Maximilian Strauss, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm -| Copyright (c) 2015-2019 Jungmann Lab, Max Planck Institute of - Biochemistry +| Contributors: Joerg Schnitzbauer, Maximilian Strauss, Rafal Kowalewski, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm +| Copyright (c) 2015-2019 Jungmann Lab, Max Planck Institute of Biochemistry | Copyright (c) 2020-2021 Maximilian Strauss | Copyright (c) 2022 Rafal Kowalewski From b824881bb427d5fd893a65f70d695d8f4570529d Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 27 Jan 2023 14:05:31 +0100 Subject: [PATCH 21/39] (h)dbscan in cmd fix --- changelog.rst | 3 +- picasso/__main__.py | 49 +++++------- picasso/clusterer.py | 167 +++++++++++++++++++++++++++++++++++++++-- picasso/gui/render.py | 105 +++++++++----------------- picasso/postprocess.py | 30 -------- readme.rst | 2 +- 6 files changed, 217 insertions(+), 139 deletions(-) diff --git a/changelog.rst b/changelog.rst index 8543f3e0..e68ec42f 100644 --- a/changelog.rst +++ b/changelog.rst @@ -6,8 +6,9 @@ Last change: 26-JAN-2023 MTS 0.5.7 ----- - Updated installation instructions +- (H)DBSCAN available from cmd (bug fix) - Render group information is faster (e.g., clustered data) -- Cluster centers display in Test Clusterer window (Render) +- Test Clusterer window (Render) has multiple updates, e.g., different projections, cluster centers display - Cluster centers contain info about std in x,y and z 0.5.5 - 0.5.6 diff --git a/picasso/__main__.py b/picasso/__main__.py index f0c0d91e..83664fbf 100644 --- a/picasso/__main__.py +++ b/picasso/__main__.py @@ -445,14 +445,15 @@ def _dbscan(files, radius, min_density): paths = glob.glob(files) if paths: - from . import io, postprocess - from h5py import File + from . import io, clusterer + pixelsize = int(input("Enter the camera pixelsize in nm: ")) for path in paths: print("Loading {} ...".format(path)) locs, info = io.load_locs(path) - clusters, locs = postprocess.dbscan(locs, radius, min_density) - base, ext = os.path.splitext(path) + locs = clusterer.dbscan(locs, radius, min_density, pixelsize) + clusters = clusterer.find_cluster_centers(locs, pixelsize) + base, _ = os.path.splitext(path) dbscan_info = { "Generated by": "Picasso DBSCAN", "Radius": radius, @@ -460,15 +461,11 @@ def _dbscan(files, radius, min_density): } info.append(dbscan_info) io.save_locs(base + "_dbscan.hdf5", locs, info) - with File(base + "_dbclusters.hdf5", "w") as clusters_file: - clusters_file.create_dataset("clusters", data=clusters) + io.save_locs(base + "_dbclusters.hdf5", clusters, info) print( "Clustering executed. Results are saved in: \n" - + base - + "_dbscan.hdf5" - + "\n" - + base - + "_dbclusters.hdf5" + f"{base}_dbscan.hdf5\n" + f"{base}_dbclusters.hdf5" ) @@ -477,20 +474,14 @@ def _hdbscan(files, min_cluster, min_samples): paths = glob.glob(files) if paths: - from . import io, postprocess - from h5py import File + from . import io, clusterer + pixelsize = int(input("Enter the camera pixelsize in nm: ")) for path in paths: print("Loading {} ...".format(path)) locs, info = io.load_locs(path) - if hasattr(locs, "z"): - pixelsize = input("Camera pixelsize in nm: ") - pixelsize = float(pixelsize) - else: - pixelsize = None - locs = postprocess.hdbscan( - locs, min_cluster, min_samples, pixelsize - ) + locs = clusterer.hdbscan(locs, min_cluster, min_samples, pixelsize) + clusters = clusterer.find_cluster_centers(locs, pixelsize) base, ext = os.path.splitext(path) hdbscan_info = { "Generated by": "Picasso HDBSCAN", @@ -499,15 +490,11 @@ def _hdbscan(files, min_cluster, min_samples): } info.append(hdbscan_info) io.save_locs(base + "_hdbscan.hdf5", locs, info) - # with File(base + "_hdbclusters.hdf5", "w") as clusters_file: - # clusters_file.create_dataset("clusters", data=clusters) + io.save_locs(base + "_hdbclusters.hdf5", clusters, info) print( "Clustering executed. Results are saved in: \n" - + base - + "_hdbscan.hdf5" - + "\n" - + base - + "_hdbclusters.hdf5" + f"{base}_hdbscan.hdf5\n" + f"{base}_hdbclusters.hdf5" ) @@ -545,8 +532,8 @@ def _dark(files): locs, info = io.load_locs(path) locs = postprocess.compute_dark_times(locs) base, ext = os.path.splitext(path) - dbscan_info = {"Generated by": "Picasso Dark"} - info.append(dbscan_info) + d_info = {"Generated by": "Picasso Dark"} + info.append(d_info) io.save_locs(base + "_dark.hdf5", locs, info) @@ -1242,7 +1229,7 @@ def main(): dbscan_parser.add_argument( "radius", type=float, - help=("maximal distance between to localizations" " to be considered local"), + help=("maximal distance (camera pixels) between to localizations" " to be considered local"), ) dbscan_parser.add_argument( "density", diff --git a/picasso/clusterer.py b/picasso/clusterer.py index ebfd14d5..55aaf4f6 100644 --- a/picasso/clusterer.py +++ b/picasso/clusterer.py @@ -2,7 +2,7 @@ picasso.clusterer ~~~~~~~~~~~~~~~~~ - Clusterer optimized for DNA PAINT in CPU and GPU versions. + Clusterer optimized for DNA PAINT, as well as DBSCAN and HDBSCAN. Based on the work of Thomas Schlichthaerle and Susanne Reinhardt. :authors: Thomas Schlichthaerle, Susanne Reinhardt, @@ -14,6 +14,7 @@ import pandas as _pd from scipy.spatial import cKDTree as _cKDTree from scipy.spatial import ConvexHull as _ConvexHull +from sklearn.cluster import DBSCAN as _DBSCAN from . import lib as _lib @@ -147,7 +148,7 @@ def frame_analysis(labels, frame): return labels -def _cluster(X, radius, min_locs, frame): +def _cluster(X, radius, min_locs, frame=None): """ Clusters points given by X with a given clustering radius and minimum number of localizaitons withing that radius using KDTree @@ -160,7 +161,7 @@ def _cluster(X, radius, min_locs, frame): Clustering radius min_locs : int Minimum number of localizations in a cluster - frame : np.array + frame : np.array (default=None) Frame number of each localization. If None, no frame analysis is performed @@ -333,7 +334,159 @@ def cluster(locs, params, pixelsize): min_locs, fa ) - return labels + locs = extract_valid_labels(locs, labels) + return locs + +def _dbscan(X, radius, min_density): + """ + Finds DBSCAN cluster labels, given data points and parameters. + + Parameters + ---------- + X : np.array + Array of shape (N, D), with N being the number of data points + and D the number of dimensions. + radius : float + DBSCAN search radius, often referred to as "epsilon" + min_density : int + Number of points within radius to consider a given point a core + sample + + Returns + ------- + labels : np.array + Cluster labels for each point. Shape: (N,). -1 means no cluster + assigned. + """ + + db = _DBSCAN(eps=radius, min_samples=min_density).fit(X) + return db.labels_.astype(_np.int32) + +def dbscan(locs, radius, min_density, pixelsize): + """ + Performs DBSCAN on localizations. + + Paramters + --------- + locs : np.recarray + Localizations to be clustered + radius : float + DBSCAN search radius, often referred to as "epsilon" + min_density : int + Number of localizations within radius to consider a given point + a core sample + pixelsize : int + Camera pixel size in nm + + Returns + ------- + locs : np.recarray + Clustered localizations; cluster labels are assigned to the + "group" column + """ + + if hasattr(locs, "z"): + X = _np.vstack((locs.x, locs.y, locs.z / pixelsize)).T + else: + X = _np.vstack((locs.x, locs.y)).T + labels = _dbscan(X, radius, min_density) + locs = extract_valid_labels(locs, labels) + return locs + +def _hdbscan(X, min_cluster_size, min_samples, cluster_eps=0): + """ + Finds HDBSCAN cluster labels, given data points and parameters. + + Parameters + ---------- + X : np.array + Array of shape (N, D), with N being the number of data points + and D the number of dimensions. + min_cluster_size : int + Minimum number of points in cluster + min_samples : int + Number of points within radius to consider a given point a core + sample + cluster_eps : float (default=0.) + Distance threshold. Clusters below this value will be merged + + Returns + ------- + labels : np.array + Cluster labels for each point. Shape: (N,). -1 means no cluster + assigned. + """ + + from hdbscan import HDBSCAN as _HDBSCAN + hdb = _HDBSCAN( + min_samples=min_samples, + min_cluster_size=min_cluster_size, + cluster_selection_epsilon=cluster_eps, + ).fit(X) + return hdb.labels_.astype(_np.int32) + +def hdbscan(locs, min_cluster_size, min_samples, pixelsize, cluster_eps=0.): + """ + Performs HDBSCAN on localizations. + + Paramters + --------- + locs : np.recarray + Localizations to be clustered + min_cluster_size : int + Minimum number of localizations in cluster + min_samples : int + Number of localizations within radius to consider a given point + a core sample + pixelsize : int + Camera pixel size in nm + cluster_eps : float (default=0.) + Distance threshold. Clusters below this value will be merged + + Returns + ------- + locs : np.recarray + Clustered localizations; cluster labels are assigned to the + "group" column + """ + + if hasattr(locs, "z"): + X = _np.vstack((locs.x, locs.y, locs.z / pixelsize)).T + else: + X = _np.vstack((locs.x, locs.y)).T + labels = _hdbscan( + X, min_cluster_size, min_samples, cluster_eps=cluster_eps + ) + locs = extract_valid_labels(locs, labels) + return locs + +def extract_valid_labels(locs, labels): + """ + Extracts localizations based on clustering results. + + Localizations that were not clustered are excluded. + + Parameters + ---------- + locs : np.recarray + Localizations used for clustering + labels : np.array + Array of cluster labels for each localization. -1 means no + cluster assignment. + + Returns + ------- + locs : np.recarray + Localization list with "group" column appended, providing + cluster label. + """ + + # add cluster id to locs, as "group" + locs = _lib.append_to_rec(locs, labels, "group") + + # -1 means no cluster assigned to a loc + locs = locs[locs.group != -1] + return locs def error_sums_wtd(x, w): """ @@ -521,9 +674,11 @@ def cluster_center(grouplocs, pixelsize, separate_lp=False): grouplocs.z, weights=1/((grouplocs.lpx+grouplocs.lpy)**2), ) # take lpz = 2 * mean(lpx, lpy) - std_z = grouplocs.z.std() / pixelsize + std_z = grouplocs.z.std() # lpz = std_z - volume = _np.power((std_x + std_y + std_z) / 3 * 2, 3) * 4.18879 + volume = _np.power( + (std_x + std_y + std_z / pixelsize) / 3 * 2, 3 + ) * 4.18879 try: X = _np.stack( (grouplocs.x, grouplocs.y, grouplocs.z / pixelsize), diff --git a/picasso/gui/render.py b/picasso/gui/render.py index aee42a34..c1d7aae9 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -35,7 +35,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets from sklearn.metrics.pairwise import euclidean_distances -from sklearn.cluster import KMeans, DBSCAN +from sklearn.cluster import KMeans from collections import Counter from tqdm import tqdm @@ -2299,25 +2299,23 @@ def cluster(self, locs, params): # for converting z coordinates pixelsize = self.window.display_settings_dlg.pixelsize.value() - if hasattr(locs, "z"): - X = np.vstack((locs.x, locs.y, locs.z / pixelsize)).T - else: - X = np.vstack((locs.x, locs.y)).T clusterer_name = self.clusterer_name.currentText() if clusterer_name == "DBSCAN": - clusterer_ = DBSCAN( - eps=params["radius"], - min_samples=params["min_samples"], - ).fit(X) - labels = clusterer_.labels_ + locs = clusterer.dbscan( + locs, + params["radius"], + params["min_samples"], + pixelsize, + ) elif clusterer_name == "HDBSCAN": if HDBSCAN_IMPORTED: - clusterer_ = HDBSCAN( - min_samples=params["min_samples"], - min_cluster_size=params["min_cluster_size"], - cluster_selection_epsilon=params["intercluster_radius"], - ).fit(X) - labels = clusterer_.labels_ + locs = clusterer.hdbscan( + locs, + params["min_cluster_size"], + params["min_samples"], + pixelsize, + params["intercluster_radius"] + ) else: return None elif clusterer_name == "SMLM": @@ -2327,49 +2325,29 @@ def cluster(self, locs, params): frame = None if hasattr(locs, "z"): - X[:, 2] = X[:, 2] * params["radius_xy"] / params["radius_z"] - labels = clusterer._cluster( - X, - params["radius_xy"], - params["min_cluster_size"], - frame, - ) + params_c = [ + params["radius_xy"], + params["radius_z"], + params["min_cluster_size"], + None, + params["frame_analysis"], + None, + ] else: - labels = clusterer._cluster( - X, - params["radius_xy"], + params_c = [ + params["radius_xy"], params["min_cluster_size"], + None, frame, - ) - locs = self.assign_groups(locs, labels) + None, + ] + + locs = clusterer.cluster(locs, params_c, pixelsize) + if len(locs): self.view.group_color = self.window.view.get_group_color(locs) return locs - def assign_groups(self, locs, labels): - """ - Filters out non-clustered locs and adds group column to locs. - - Parameters - ---------- - locs : np.recarray - Contains all picked localizations from a given channel - labels : np.array - Contains cluster indeces in scikit-learn format, i.e. - -1 means no cluster, other integers are cluster ids. - - Returns - ------- - np.recarray - Contains localizations that were clustered, with "group" - dtype specifying cluster indeces - """ - - group = np.int32(labels) - locs = lib.append_to_rec(locs, group, "group") - locs = locs[locs.group != -1] - return locs - def get_cluster_params(self): """ Extracts clustering parameters for a given clusterer into a @@ -5715,7 +5693,7 @@ def _dbscan(self, channel, path, params): pixelsize = self.window.display_settings_dlg.pixelsize.value() # perform DBSCAN in a channel - locs = postprocess.dbscan( + locs = clusterer.dbscan( locs, radius, min_density, @@ -5822,7 +5800,7 @@ def _hdbscan(self, channel, path, params): pixelsize = self.window.display_settings_dlg.pixelsize.value() # perform HDBSCAN for each channel - locs = postprocess.hdbscan( + locs = clusterer.hdbscan( locs, min_cluster, min_samples, @@ -5928,19 +5906,13 @@ def _smlm_clusterer(self, channel, path, params): ) if len(locs) > 0: - labels = clusterer.cluster(locs, params, pixelsize) + temp_locs = clusterer.cluster(locs, params, pixelsize) - temp_locs = lib.append_to_rec( - locs, labels, "group" - ) # add cluster id to locs - - # -1 means no cluster assigned to a loc - temp_locs = temp_locs[temp_locs.group != -1] if len(temp_locs) > 0: # make sure each picks produces unique cluster ids temp_locs.group += group_offset clustered_locs.append(temp_locs) - group_offset += np.max(labels) + 1 + group_offset += np.max(temp_locs.group) + 1 pd.set_value(i + 1) clustered_locs = stack_arrays( clustered_locs, asrecarray=True, usemask=False @@ -5959,14 +5931,7 @@ def _smlm_clusterer(self, channel, path, params): else: locs = self.all_locs[channel] - labels = clusterer.cluster(locs, params, pixelsize) - - clustered_locs = lib.append_to_rec( - locs, labels, "group" - ) # add cluster id to locs - - # -1 means no cluster assigned to a loc - clustered_locs = clustered_locs[clustered_locs.group != -1] + clustered_locs = clusterer.cluster(locs, params, pixelsize) status.close() # saving diff --git a/picasso/postprocess.py b/picasso/postprocess.py index b44f00bd..e207fa7d 100644 --- a/picasso/postprocess.py +++ b/picasso/postprocess.py @@ -405,35 +405,6 @@ def pair_correlation(locs, info, bin_size, r_max): area = _np.pi * bin_size * (2 * bins_lower + bin_size) return bins_lower, dh / area - -def dbscan(locs, radius, min_density, pixelsize): - if hasattr(locs, "z"): - X = _np.vstack((locs.x, locs.y, locs.z / pixelsize)).T - else: - X = _np.vstack((locs.x, locs.y)).T - db = _DBSCAN(eps=radius, min_samples=min_density).fit(X) - group = _np.int32(db.labels_) # int32 for Origin compatiblity - locs = _lib.append_to_rec(locs, group, "group") - locs = locs[locs.group != -1] - return locs - -def hdbscan(locs, min_cluster_size, min_samples, pixelsize, cluster_eps=0): - from hdbscan import HDBSCAN as _HDBSCAN - - if hasattr(locs, "z"): - X = _np.vstack((locs.x, locs.y, locs.z / pixelsize)).T - else: - X = _np.vstack((locs.x, locs.y)).T - hdb = _HDBSCAN( - min_samples=min_samples, - min_cluster_size=min_cluster_size, - cluster_selection_epsilon=cluster_eps, - ).fit(X) - group = _np.int32(hdb.labels_) # int32 for Origin compatiblity - locs = _lib.append_to_rec(locs, group, "group") - locs = locs[locs.group != -1] - return locs - @_numba.jit(nopython=True, nogil=True) def _local_density( locs, radius, x_index, y_index, block_starts, block_ends, start, chunk @@ -465,7 +436,6 @@ def _local_density( density[i] = di return density - def compute_local_density(locs, info, radius): locs, x_index, y_index, block_starts, block_ends, K, L = get_index_blocks( locs, info, radius diff --git a/readme.rst b/readme.rst index a9b5ef60..2b8d21c0 100644 --- a/readme.rst +++ b/readme.rst @@ -41,7 +41,7 @@ For the platform-independent usage of Picasso (e.g., with Linux and Mac Os X), p Other installation modes (Python 3.8) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -As an alternative to the stand-alone program for end-users, Picasso can be installed as a Python package. This is the preferred option to use Picasso’s internal routines in custom Python programs. Those can be imported by running, for example, ``from picasso import io`` to use input/output functions from Picasso. For windows, it is still possible to use Picasso as an end-user by creating the respective shortcuts. This allows Picasso to be used on the same system by both programmers and end-users. +As an alternative to the stand-alone program for end-users, Picasso can be installed as a Python package. This is the preferred option to use Picasso’s internal routines in custom Python programs. Those can be imported by running, for example, ``from picasso import io`` (see the "Example usage" tab below) to use input/output functions from Picasso. For windows, it is still possible to use Picasso as an end-user by creating the respective shortcuts. This allows Picasso to be used on the same system by both programmers and end-users. Via PyPI ^^^^^^^^ From 8cc7af39207e9fed46a8872fdbfe90c3d34b6040 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 27 Jan 2023 14:24:41 +0100 Subject: [PATCH 22/39] add smlm clusterer to cmd --- picasso/__main__.py | 79 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/picasso/__main__.py b/picasso/__main__.py index 83664fbf..b63c7460 100644 --- a/picasso/__main__.py +++ b/picasso/__main__.py @@ -497,6 +497,40 @@ def _hdbscan(files, min_cluster, min_samples): f"{base}_hdbclusters.hdf5" ) +def _smlm_clusterer(files, radius, min_locs, basic_fa=False, radius_z=None): + import glob + + paths = glob.glob(files) + if paths: + from . import io, clusterer + pixelsize = int(input("Enter the camera pixelsize in nm: ")) + if radius_z is not None: # 3D + params = [radius, radius_z, min_locs, 0, basic_fa, 0] + else: # 2D + params = [radius, min_locs, 0, basic_fa, 0] + + for path in paths: + print("Loading {} ...".format(path)) + locs, info = io.load_locs(path) + locs = clusterer.cluster(locs, params, pixelsize) + clusters = clusterer.find_cluster_centers(locs, pixelsize) + base, ext = os.path.splitext(path) + smlm_cluster_info = { + "Generated by": "Picasso SMLM clusterer", + "Radius_xy": radius, + "Radius_z": radius_z, + "Min locs": min_locs, + "Basic frame analysis": basic_fa, + } + info.append(smlm_cluster_info) + io.save_locs(base + "_clusters.hdf5", locs, info) + io.save_locs(base + "_cluster_centers.hdf5", clusters, info) + print( + "Clustering executed. Results are saved in: \n" + f"{base}_clusters.hdf5\n" + f"{base}_cluster_centers.hdf5" + ) + def _nneighbor(files): import glob @@ -1260,6 +1294,43 @@ def main(): help=("the higher the more points are considered noise"), ) + # SMLM clusterer + smlm_cluster_parser = subparsers.add_parser( + "smlm_cluster", + help="cluster localizations with the custom SMLM clustering algorithm", + ) + smlm_cluster_parser.add_argument( + "files", + help=( + "one or multiple hdf5 localization files" + " specified by a unix style path pattern" + ), + ) + smlm_cluster_parser.add_argument( + "radius", + type=float, + help=("clustering radius (in camera pixels)"), + ) + smlm_cluster_parser.add_argument( + "min_locs", + type=int, + help=("minimum number of localizations in a cluster"), + ) + smlm_cluster_parser.add_argument( + "basic_fa", + type=bool, + help=("whether or not perform basic frame analysis (sticking event removal)"), + default=False, + ) + smlm_cluster_parser.add_argument( + "radius_z", + type=float, + help=( + "clustering radius in axial direction (MUST BE SET FOR 3D!!!)" + ), + default=None, + ) + # Dark time dark_parser = subparsers.add_parser( "dark", help="compute the dark time for grouped localizations" @@ -1606,6 +1677,14 @@ def main(): _dbscan(args.files, args.radius, args.density) elif args.command == "hdbscan": _hdbscan(args.files, args.min_cluster, args.min_samples) + elif args.command == "smlm_cluster": + _smlm_clusterer( + args.files, + args.radius, + args.min_locs, + args.basic_fa, + args.radius_z, + ) elif args.command == "nneighbor": _nneighbor(args.files) elif args.command == "dark": From 6ddc1f50b5d7c6792718258a643a317b262de54c Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 27 Jan 2023 14:34:17 +0100 Subject: [PATCH 23/39] cmd smlm clustering --- docs/cmd.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/cmd.rst b/docs/cmd.rst index 621b42a5..11bbc5bb 100644 --- a/docs/cmd.rst +++ b/docs/cmd.rst @@ -89,6 +89,12 @@ hdbscan ------- Cluster localizations with the hdbscan clustering algorithm. +smlm_cluster +------------ +Cluster localizations with the custom SMLM clustering algorithm. + +The algorithm finds localizations with the most neighbors within a specified radius and finds clusters based on such "local maxima". + dark ---- Compute the dark time for grouped localizations. From fa142a2e1bd00bda20c16f83f908f351afc9c5c0 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Sun, 5 Feb 2023 09:48:19 +0100 Subject: [PATCH 24/39] CLEAN --- picasso/gui/rotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picasso/gui/rotation.py b/picasso/gui/rotation.py index 6e355a09..3adcac8e 100644 --- a/picasso/gui/rotation.py +++ b/picasso/gui/rotation.py @@ -2100,7 +2100,7 @@ def get_render_kwargs(self, viewport=None, animation=False): self.window.window.display_settings_dlg.pixelsize.value() / optimal_oversampling ) - else: + else: # if animating, the message box may appear oversampling = float( self.window.window.display_settings_dlg.pixelsize.value() / self.window.display_settings_dlg.disp_px_size.value() From e41f6cf348a50ef09b260eea5090364839401aa3 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Sun, 5 Feb 2023 09:56:45 +0100 Subject: [PATCH 25/39] multiprocessing, limit CPU cores to 60 --- changelog.rst | 1 + picasso/avgroi.py | 4 +++- picasso/gausslq.py | 4 +++- picasso/gaussmle.py | 4 +++- picasso/gui/average.py | 8 ++++++-- picasso/gui/nanotron.py | 4 +++- picasso/localize.py | 4 +++- picasso/postprocess.py | 8 ++++++-- picasso/zfit.py | 4 +++- 9 files changed, 31 insertions(+), 10 deletions(-) diff --git a/changelog.rst b/changelog.rst index e68ec42f..755c060f 100644 --- a/changelog.rst +++ b/changelog.rst @@ -10,6 +10,7 @@ Last change: 26-JAN-2023 MTS - Render group information is faster (e.g., clustered data) - Test Clusterer window (Render) has multiple updates, e.g., different projections, cluster centers display - Cluster centers contain info about std in x,y and z +- Number of CPU cores used in multiprocessing limited at 60 0.5.5 - 0.5.6 ------------- diff --git a/picasso/avgroi.py b/picasso/avgroi.py index 16cf846f..940ad59b 100644 --- a/picasso/avgroi.py +++ b/picasso/avgroi.py @@ -44,7 +44,9 @@ def fit_spots(spots): def fit_spots_parallel(spots, asynch=False): - n_workers = max(1, int(0.75 * _multiprocessing.cpu_count())) + n_workers = min( + 60, max(1, int(0.75 * _multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores n_spots = len(spots) n_tasks = 100 * n_workers spots_per_task = [ diff --git a/picasso/gausslq.py b/picasso/gausslq.py index b8468381..a1cbbe39 100644 --- a/picasso/gausslq.py +++ b/picasso/gausslq.py @@ -162,7 +162,9 @@ def fit_spots(spots): def fit_spots_parallel(spots, asynch=False): - n_workers = max(1, int(0.75 * _multiprocessing.cpu_count())) + n_workers = min( + 60, max(1, int(0.75 * _multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores n_spots = len(spots) n_tasks = 100 * n_workers spots_per_task = [ diff --git a/picasso/gaussmle.py b/picasso/gaussmle.py index dbf9c061..051c7961 100644 --- a/picasso/gaussmle.py +++ b/picasso/gaussmle.py @@ -283,7 +283,9 @@ def gaussmle_async(spots, eps, max_it, method="sigma"): CRLBs = _np.inf * _np.ones((N, 6), dtype=_np.float32) likelihoods = _np.zeros(N, dtype=_np.float32) iterations = _np.zeros(N, dtype=_np.int32) - n_workers = max(1, int(0.75 * _multiprocessing.cpu_count())) + n_workers = min( + 60, max(1, int(0.75 * _multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores lock = _threading.Lock() current = [0] if method == "sigma": diff --git a/picasso/gui/average.py b/picasso/gui/average.py index ab2c86d7..20e687b3 100644 --- a/picasso/gui/average.py +++ b/picasso/gui/average.py @@ -107,7 +107,9 @@ def run(self): n_groups = self.group_index.shape[0] a_step = np.arcsin(1 / (self.oversampling * self.r)) angles = np.arange(0, 2 * np.pi, a_step) - n_workers = max(1, int(0.75 * multiprocessing.cpu_count())) + n_workers = min( + 60, max(1, int(0.75 * multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores manager = multiprocessing.Manager() counter = manager.Value("d", 0) lock = manager.Lock() @@ -269,7 +271,9 @@ def open(self, path): pass x = sharedctypes.RawArray("f", self.locs.x) y = sharedctypes.RawArray("f", self.locs.y) - n_workers = max(1, int(0.75 * multiprocessing.cpu_count())) + n_workers = min( + 60, max(1, int(0.75 * multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores pool = multiprocessing.Pool(n_workers, init_pool, (x, y, self.group_index)) self.window.status_bar.showMessage("Ready for processing!") status.close() diff --git a/picasso/gui/nanotron.py b/picasso/gui/nanotron.py index c40ce3fc..e0e0f954 100644 --- a/picasso/gui/nanotron.py +++ b/picasso/gui/nanotron.py @@ -270,7 +270,9 @@ def predict_async(self, model, locs, picks, pick_radius, oversampling): lock = threading.Lock() - n_workers = multiprocessing.cpu_count() + n_workers = min( + 60, max(1, int(0.75 * multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores current = [0] finished = [0] diff --git a/picasso/localize.py b/picasso/localize.py index def45a9b..abaadde6 100755 --- a/picasso/localize.py +++ b/picasso/localize.py @@ -208,7 +208,9 @@ def identify_async(movie, minimum_ng, box, roi=None): settings["Localize"]["cpu_utilization"] = cpu_utilization _io.save_user_settings(settings) - n_workers = max(1, int(cpu_utilization * _multiprocessing.cpu_count())) + n_workers = min( + 60, max(1, int(0.75 * _multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores lock = _threading.Lock() current = [0] diff --git a/picasso/postprocess.py b/picasso/postprocess.py index e207fa7d..f3cbba22 100644 --- a/picasso/postprocess.py +++ b/picasso/postprocess.py @@ -290,7 +290,9 @@ def distance_histogram(locs, info, bin_size, r_max): locs, info, r_max ) N = len(locs) - n_threads = _multiprocessing.cpu_count() + n_threads = min( + 60, max(1, int(0.75 * _multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores chunk = int(N / n_threads) starts = range(0, N, chunk) args = [ @@ -441,7 +443,9 @@ def compute_local_density(locs, info, radius): locs, info, radius ) N = len(locs) - n_threads = _multiprocessing.cpu_count() + n_threads = min( + 60, max(1, int(0.75 * _multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores chunk = int(N / n_threads) starts = range(0, N, chunk) args = [ diff --git a/picasso/zfit.py b/picasso/zfit.py index 8ee8fae2..b96457a1 100644 --- a/picasso/zfit.py +++ b/picasso/zfit.py @@ -236,7 +236,9 @@ def fit_z_parallel( filter=2, asynch=False, ): - n_workers = max(1, int(0.75 * _multiprocessing.cpu_count())) + n_workers = min( + 60, max(1, int(0.75 * _multiprocessing.cpu_count())) + ) # Python crashes when using >64 cores n_locs = len(locs) n_tasks = 100 * n_workers spots_per_task = [ From aa43b88260fadffd79d99079613d16808ec5b1e5 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Sun, 5 Feb 2023 10:01:21 +0100 Subject: [PATCH 26/39] add localized data to tests --- tests/data/testdata_locs.hdf5 | Bin 0 -> 30100 bytes tests/data/testdata_locs.yaml | 72 ++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/data/testdata_locs.hdf5 create mode 100644 tests/data/testdata_locs.yaml diff --git a/tests/data/testdata_locs.hdf5 b/tests/data/testdata_locs.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..b3f0af0a2568359836f7b12d37550f39e118eb8b GIT binary patch literal 30100 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 literal 0 HcmV?d00001 diff --git a/tests/data/testdata_locs.yaml b/tests/data/testdata_locs.yaml new file mode 100644 index 00000000..c4db63ef --- /dev/null +++ b/tests/data/testdata_locs.yaml @@ -0,0 +1,72 @@ +Byte Order: < +Camera: Simulation +Camera.Frames: 1000 +Camera.Image Size: 32 +Camera.Integration Time: 300 +Camera.Pixelsize: 160 +Data Type: uint16 +Frames: 1000 +Generated by: Picasso simulate +Height: 32 +Imager.BackgroundLevel: 1 +Imager.Constant Photonrate Std: 0 +Imager.Laserpower: 1.5 +Imager.PSF: 0.82 +Imager.Photonbudget: 1500000.0 +Imager.Photonrate: 53.0 +Imager.Photonrate Std: 29.0 +Imager.Photonslope: 35 +Imager.PhotonslopeStd: 19.23076923076923 +Noise.BackgroundOff: 0 +Noise.BackgroundStdOff: 0 +Noise.EquationA: -0.002866 +Noise.EquationB: 0.259038 +Noise.EquationC: 13.085473 +Noise.Imagercoefficient: 0.003195 +Noise.Lasercoefficient: 0.012063 +PAINT.imager: 5.0 +PAINT.k_on: 1600000.0 +PAINT.taub: 500.0 +Spotkinetics.Localizations: 11.000000,1.000000,4.000000,3.000000,8.000000,7.000000,6.000000,0.000000,5.000000,10.000000,5.000000,6.000000,8.000000,9.000000,0.000000,7.000000,0.000000,12.000000,10.000000,3.000000,8.000000,20.000000,11.000000,7.000000,9.000000,0.000000,28.000000,6.000000,5.000000,4.000000,19.000000,4.000000,13.000000,13.000000,11.000000,4.000000,1.000000,6.000000,4.000000,1.000000,3.000000,5.000000,9.000000,12.000000,3.000000,0.000000,8.000000,12.000000,8.000000,5.000000,12.000000,0.000000,2.000000,5.000000,2.000000,2.000000,1.000000,4.000000,0.000000,2.000000,5.000000,1.000000,11.000000,6.000000,7.000000,12.000000,14.000000,10.000000,5.000000,7.000000,5.000000,3.000000,4.000000,4.000000,7.000000,19.000000,9.000000,10.000000,13.000000,4.000000,1.000000,0.000000,7.000000,8.000000,1.000000,13.000000,6.000000,18.000000,11.000000,8.000000,8.000000,7.000000,4.000000 +Spotkinetics.MEAN_BRIGHT: 595.948173,145.517166,237.027203,671.220265,776.620485,767.604309,715.177289,0.000000,481.940355,461.976049,382.809919,577.185423,601.657376,648.600798,0.000000,515.305702,0.000000,426.537392,471.653611,542.502364,836.861047,650.552142,424.742204,296.225236,444.969969,0.000000,1732.897764,651.875378,1172.853101,406.773851,891.752827,327.995010,263.505183,962.263570,824.544258,835.737581,75.999607,312.760209,405.905569,197.339731,518.987782,127.675120,570.572288,826.685879,685.340104,0.000000,466.486630,478.943643,484.269094,488.082168,754.574910,0.000000,208.847139,324.906536,273.219358,293.101441,258.838011,293.499640,0.000000,406.169487,401.155867,90.321306,803.845247,531.874619,1820.804677,551.336970,1100.381982,388.324942,1075.210187,377.122295,447.586972,534.178912,171.505310,402.016221,727.948831,1104.775637,365.278119,530.818496,936.269602,202.213864,64.049096,0.000000,340.903926,271.900219,116.208159,990.212681,444.756107,612.507443,778.437825,402.488961,526.252443,385.259944,852.835221 +Spotkinetics.MEAN_DARK: 72530.054301,148315.294599,135966.250244,294791.741753,120214.843110,109064.217635,88937.505263,0.000000,136495.256558,42920.363337,49792.697280,92199.436214,98374.859798,95196.960351,0.000000,47464.810491,0.000000,57803.241074,69338.615492,16134.522432,66719.954003,47854.761459,70905.026647,94182.629072,47369.521135,0.000000,41395.145921,92000.171802,194443.031946,85385.677565,57074.746602,138961.539833,38806.672553,34740.143910,71789.192305,149455.044458,150100.499013,94559.751046,90768.843315,291052.921411,295127.809348,76624.343703,90859.027716,43525.346468,126151.484214,0.000000,90070.633140,37372.904201,83313.581502,64827.191638,69164.907977,0.000000,224568.366284,54965.966044,182385.453512,69366.374891,162040.176613,83690.517796,0.000000,118388.328022,128277.986521,202369.326136,68106.771712,129877.710572,62027.996373,43901.864844,93587.420182,34567.031428,261434.597796,70737.723695,73097.374589,3743.359726,91583.078542,98507.092618,120779.145854,44121.378299,69854.182255,56276.823448,86141.979131,109141.695072,65487.350419,0.000000,71419.971635,68881.254484,244320.393025,93342.490052,83805.374176,47052.990614,56033.109102,71603.487889,95339.691767,73483.023611,281951.231468 +Spotkinetics.ON_Events: 4.000000,1.000000,2.000000,1.000000,2.000000,2.000000,3.000000,0.000000,2.000000,4.000000,3.000000,2.000000,3.000000,3.000000,0.000000,3.000000,0.000000,5.000000,4.000000,1.000000,2.000000,6.000000,4.000000,3.000000,3.000000,0.000000,4.000000,2.000000,1.000000,2.000000,5.000000,2.000000,7.000000,3.000000,3.000000,1.000000,1.000000,3.000000,2.000000,1.000000,1.000000,3.000000,3.000000,3.000000,1.000000,0.000000,3.000000,6.000000,3.000000,2.000000,4.000000,0.000000,1.000000,3.000000,1.000000,1.000000,1.000000,3.000000,0.000000,1.000000,2.000000,1.000000,3.000000,2.000000,1.000000,5.000000,3.000000,4.000000,1.000000,3.000000,2.000000,1.000000,3.000000,2.000000,2.000000,4.000000,4.000000,4.000000,3.000000,2.000000,1.000000,0.000000,3.000000,4.000000,1.000000,3.000000,3.000000,6.000000,3.000000,3.000000,3.000000,3.000000,1.000000 +Structure.3D: 0 +Structure.Arrangement: 0 +Structure.CX: +- 3.1638306844743706e-17 +- -2.2103661248660896e-14 +- -9.775815406044296e-12 +- 8.2178622893072e-09 +- 4.91181990105529e-06 +- -0.0028759382006135654 +- 1.1756537760039398 +Structure.CY: +- 1.710907877866197e-17 +- -2.4986657766862576e-15 +- -8.405284979510355e-12 +- 1.1548322314075128e-11 +- 5.4270591055277476e-06 +- 0.0018155881468011011 +- 1.011468185618154 +Structure.Frame: 6 +Structure.Handle3d: 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000 +Structure.HandleEx: 1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000 +Structure.HandleStruct: 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,1.000000,2.000000,2.000000,2.000000,2.000000,2.000000,2.000000,2.000000,2.000000,2.000000,2.000000,2.000000,2.000000,3.000000,3.000000,3.000000,3.000000,3.000000,3.000000,3.000000,3.000000,3.000000,3.000000,4.000000,4.000000,4.000000,4.000000,4.000000,4.000000,4.000000,4.000000,4.000000,5.000000,5.000000,5.000000,5.000000,5.000000,5.000000,5.000000,5.000000,5.000000,5.000000,5.000000,6.000000,6.000000,6.000000,6.000000,6.000000,6.000000,6.000000,6.000000,6.000000,6.000000,6.000000,6.000000,7.000000,7.000000,7.000000,7.000000,7.000000,7.000000,7.000000,7.000000,7.000000,7.000000,7.000000,8.000000,8.000000,8.000000,8.000000,8.000000,8.000000,8.000000,8.000000,8.000000 +Structure.HandleX: 5.875000,6.125000,5.875000,6.000000,6.125000,5.875000,6.000000,6.125000,5.875000,6.000000,6.125000,15.875000,16.000000,16.000000,16.000000,16.125000,15.875000,16.000000,16.125000,25.875000,26.000000,26.125000,25.875000,26.000000,26.125000,25.875000,26.000000,26.125000,25.875000,26.000000,26.125000,5.875000,6.000000,6.125000,5.875000,6.000000,6.125000,6.000000,5.875000,6.000000,6.125000,15.875000,16.000000,16.125000,15.875000,15.875000,16.125000,15.875000,16.000000,16.125000,25.875000,26.000000,26.125000,25.875000,26.000000,25.875000,26.000000,26.125000,25.875000,26.000000,26.125000,5.875000,6.000000,6.125000,5.875000,6.000000,6.125000,5.875000,6.000000,6.125000,5.875000,6.000000,6.125000,15.875000,16.000000,16.125000,15.875000,16.125000,15.875000,16.000000,16.125000,15.875000,16.000000,16.125000,26.000000,26.125000,25.875000,26.000000,25.875000,26.000000,26.125000,25.875000,26.125000 +Structure.HandleY: 5.812500,6.062500,6.187500,5.812500,5.937500,6.062500,6.187500,5.812500,5.937500,6.062500,6.187500,5.812500,5.937500,5.812500,6.187500,5.812500,5.937500,6.062500,6.187500,5.812500,5.937500,6.062500,6.187500,5.812500,5.937500,6.062500,6.187500,5.812500,5.937500,6.062500,6.187500,15.812500,15.937500,16.062500,16.187500,15.812500,15.937500,16.187500,15.937500,16.062500,16.187500,15.812500,15.937500,16.062500,16.187500,16.062500,15.812500,15.937500,16.062500,16.187500,15.812500,15.937500,16.062500,16.187500,15.812500,16.062500,16.187500,15.812500,15.937500,16.062500,16.187500,25.812500,25.937500,26.062500,26.187500,25.812500,25.937500,26.062500,26.187500,25.812500,25.937500,26.062500,26.187500,25.812500,25.937500,26.062500,26.187500,25.937500,26.062500,26.187500,25.812500,25.937500,26.062500,26.187500,25.937500,26.062500,26.187500,25.812500,26.062500,26.187500,25.812500,25.937500,26.187500 +Structure.Incorporation: 85.0 +Structure.Number: 9 +Structure.Orientation: 0 +Structure.Structure3D: 0,0,0,0,0,0,0,0,0,0,0,0 +Structure.StructureEx: 1,1,1,1,1,1,1,1,1,1,1,1 +Structure.StructureX: 0,20,40,0,20,40,0,20,40,0,20,40 +Structure.StructureY: 0,20,40,60,0,20,40,60,0,20,40,60 +Width: 32 +--- +Box Size: 7 +Fit method: LQ, Gaussian +Generated by: Picasso Localize +Min. Net Gradient: 3000 +Pixelsize: 130 +ROI: null From d892dd5bbab06bafd3140a0628c0b8f4b7aa605d Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Sun, 5 Feb 2023 10:20:57 +0100 Subject: [PATCH 27/39] render documentation update --- changelog.rst | 3 ++- docs/render.rst | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/changelog.rst b/changelog.rst index 755c060f..212ce574 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,7 +1,7 @@ Changelog ========= -Last change: 26-JAN-2023 MTS +Last change: 08-FEB-2023 MTS 0.5.7 ----- @@ -11,6 +11,7 @@ Last change: 26-JAN-2023 MTS - Test Clusterer window (Render) has multiple updates, e.g., different projections, cluster centers display - Cluster centers contain info about std in x,y and z - Number of CPU cores used in multiprocessing limited at 60 +- Updated 3D rendering and clustering documentation 0.5.5 - 0.5.6 ------------- diff --git a/docs/render.rst b/docs/render.rst index cffbe9ff..52a1546c 100644 --- a/docs/render.rst +++ b/docs/render.rst @@ -50,6 +50,8 @@ The 3D rotation window allows the user to render 3D localization data. To use it The user may perform multiple actions in the rotation window, including: saving rotated localizations, building animations (.mp4 format), rotating by a specified angle, etc. +Rotation around z-axis is available by pressing Ctrl/Command. Rotation axis can be frozen by pressing x/y/z to freeze around the corresponding axes (to freeze around the z-axis, Ctrl/Command must be pressed as well). + There are several things to keep in mind when using the rotation window. Firstly, using individual localization precision is very slow and is not recommended as a default blur method. Also, the size of the rotation window can be altered, however, if it becomes too large, rendering may start to lag. Dialogs @@ -373,7 +375,10 @@ Combines all localizations in each pick to one. Apply expressions to localizations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -This tool allows you to apply expressions to localizations. +This tool allows you to apply expressions to localizations, for example: + +- ``x +=1`` will shift all localization by one to the right +- ``x +=1;y+=1`` will shift all localization by one to the right and one up. DBSCAN ^^^^^^ @@ -385,6 +390,7 @@ Cluster localizations with the hdbscan clustering algorithm. SMLM clusterer ^^^^^^^^^^^^^^ +Cluster localizations with the custom algorithm designed for SMLM. In short, localizations with the maximum number of neighboring localizations within a user-defined radius are chosen as cluster centers, around which all localizations withing the given radius belong to one cluster. If two or more such clusters overlap, they are combined. Test clusterer ^^^^^^^^^^^^^^ @@ -394,11 +400,6 @@ Nearest Neighbor Analysis ^^^^^^^^^^^^^^^^^^^^^^^^^ Calculates distances to the ``k``-th nearest neighbors between two channels (can be the same channel). ``k`` is defined by the user. The distances are stored in nm as a .csv file. -Examples -++++++++ -- ``x +=1`` will shift all localization by one to the right -- ``x +=1;y+=1`` will shift all localization by one to the right and one up. - Notes +++++ Using two variables in one statement is not supported (e.g. ``x = y``) To filter localizations use picasso filter. From ba66af8513c85b0a5b0dc03f605be3a84d8f5663 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Sun, 5 Feb 2023 10:26:19 +0100 Subject: [PATCH 28/39] lpz can be rendered when provided --- changelog.rst | 1 + picasso/render.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/changelog.rst b/changelog.rst index 212ce574..24dde7a8 100644 --- a/changelog.rst +++ b/changelog.rst @@ -10,6 +10,7 @@ Last change: 08-FEB-2023 MTS - Render group information is faster (e.g., clustered data) - Test Clusterer window (Render) has multiple updates, e.g., different projections, cluster centers display - Cluster centers contain info about std in x,y and z +- If localization precision in z-axis is provided, it will be rendered when using ``Individual localization precision`` and ``Individual localization precision (iso)``. *NOTE:* the column must be named ``lpz`` and have the same units as ``lpx`` and ``lpy``. - Number of CPU cores used in multiprocessing limited at 60 - Updated 3D rendering and clustering documentation diff --git a/picasso/render.py b/picasso/render.py index 5d3088a0..54163a97 100755 --- a/picasso/render.py +++ b/picasso/render.py @@ -714,8 +714,11 @@ def render_gaussian( ) blur_width = oversampling * _np.maximum(locs.lpx, min_blur_width) blur_height = oversampling * _np.maximum(locs.lpy, min_blur_width) - # for now, let lpz be twice the mean of lpx and lpy (todo): - lpz = 2 * _np.mean(_np.stack((locs.lpx, locs.lpy)), axis=0) + # for now, let lpz be twice the mean of lpx and lpy (TODO): + if hasattr(locs, "lpz"): + lpz = locs.lpz #NOTE: lpz must be in the same units as lpx + else: + lpz = 2 * _np.mean(_np.stack((locs.lpx, locs.lpy)), axis=0) blur_depth = oversampling * _np.maximum(lpz, min_blur_width) sy = blur_height[in_view] @@ -790,8 +793,11 @@ def render_gaussian_iso( ) blur_width = oversampling * _np.maximum(locs.lpx, min_blur_width) blur_height = oversampling * _np.maximum(locs.lpy, min_blur_width) - # for now, let lpz be twice the mean of lpx and lpy - lpz = 2 * _np.mean(_np.stack((locs.lpx, locs.lpy)), axis=0) + # for now, let lpz be twice the mean of lpx and lpy (TODO): + if hasattr(locs, "lpz"): + lpz = locs.lpz #NOTE: lpz must be in the same units as lpx + else: + lpz = 2 * _np.mean(_np.stack((locs.lpx, locs.lpy)), axis=0) blur_depth = oversampling * _np.maximum(lpz, min_blur_width) sy = (blur_height[in_view] + blur_width[in_view]) / 2 From 29fc6d67fc3fdbc1d2b8c250e1a18b6ff6f4b6d3 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Sun, 5 Feb 2023 10:28:21 +0100 Subject: [PATCH 29/39] doc CLEAN --- docs/render.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/render.rst b/docs/render.rst index 52a1546c..746f4fde 100644 --- a/docs/render.rst +++ b/docs/render.rst @@ -390,7 +390,9 @@ Cluster localizations with the hdbscan clustering algorithm. SMLM clusterer ^^^^^^^^^^^^^^ -Cluster localizations with the custom algorithm designed for SMLM. In short, localizations with the maximum number of neighboring localizations within a user-defined radius are chosen as cluster centers, around which all localizations withing the given radius belong to one cluster. If two or more such clusters overlap, they are combined. +Cluster localizations with the custom algorithm designed for SMLM. In short, localizations with the maximum number of neighboring localizations within a user-defined radius are chosen as cluster centers, around which all localizations withing the given radius belong to one cluster. If two or more such clusters overlap, they are combined. + +*NOTE:* it is highly recommended to remove any fiducial markers before clustering, to lower clustering time, given they are of no interest to the user. To do that, the markers can be picked and removed using ``Tools > Remove localizations in picks``. Test clusterer ^^^^^^^^^^^^^^ From 5a66127a3c4840efac707fb836d52e27d9ece6a5 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Sun, 5 Feb 2023 12:39:50 +0100 Subject: [PATCH 30/39] FIX --- changelog.rst | 1 + picasso/gui/render.py | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/changelog.rst b/changelog.rst index 24dde7a8..ab6a7d86 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,7 @@ Last change: 08-FEB-2023 MTS - If localization precision in z-axis is provided, it will be rendered when using ``Individual localization precision`` and ``Individual localization precision (iso)``. *NOTE:* the column must be named ``lpz`` and have the same units as ``lpx`` and ``lpy``. - Number of CPU cores used in multiprocessing limited at 60 - Updated 3D rendering and clustering documentation +- Other bug fixes 0.5.5 - 0.5.6 ------------- diff --git a/picasso/gui/render.py b/picasso/gui/render.py index c1d7aae9..339ac869 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -17,8 +17,6 @@ from math import ceil from functools import partial -# from icecream import ic - import lmfit import matplotlib import matplotlib.pyplot as plt @@ -45,19 +43,16 @@ from .rotation import RotationWindow # PyImarisWrite works on windows only -if sys.platform == "win32": - from .. ext.bitplane import IMSWRITER - if IMSWRITER: - from .. ext.bitplane import numpy_to_imaris - from PyImarisWriter.ImarisWriterCtypes import * - from PyImarisWriter import PyImarisWriter as PW -else: - IMSWRITER = False +from ..ext.bitplane import IMSWRITER +if IMSWRITER: + from .. ext.bitplane import numpy_to_imaris + from PyImarisWriter.ImarisWriterCtypes import * + from PyImarisWriter import PyImarisWriter as PW try: from hdbscan import HDBSCAN HDBSCAN_IMPORTED = True -except: +except ModuleNotFoundError: HDBSCAN_IMPORTED = False if sys.platform == "darwin": # plots do not work on mac os @@ -2755,8 +2750,12 @@ def split_locs(self): and not self.dialog.display_centers.isChecked() ): # two channels, all locs and clustered locs channel = self.dialog.channel + all_locs = self.dialog.window.view.picked_locs(channel)[0] + all_locs.z /= ( + self.dialog.window.display_settings_dlg.pixelsize.value() + ) locs = [ - self.dialog.window.view.picked_locs(channel)[0], + all_locs, self.locs, ] elif ( @@ -2767,16 +2766,20 @@ def split_locs(self): self.locs, self.centers, ] - elif( + elif ( self.dialog.display_all_locs.isChecked() and self.dialog.display_centers.isChecked() ): # three channels, all locs, clustered locs and cluster centers channel = self.dialog.channel + all_locs = self.dialog.window.view.picked_locs(channel)[0] + all_locs.z /= ( + self.dialog.window.display_settings_dlg.pixelsize.value() + ) locs = [ - self.dialog.window.view.picked_locs(channel)[0], + all_locs, self.locs, self.centers, - ] + ] else: # multiple channels, each for one group color locs = [ From ecf71294e90807de5ea983ab672a50c0764635bf Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Sun, 5 Feb 2023 15:36:06 +0100 Subject: [PATCH 31/39] =?UTF-8?q?Bump=20version:=200.5.6=20=E2=86=92=200.5?= =?UTF-8?q?.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- distribution/picasso.iss | 4 ++-- docs/conf.py | 2 +- picasso/__version__.py | 2 +- release/one_click_windows_gui/create_installer_windows.bat | 2 +- release/one_click_windows_gui/picasso_innoinstaller.iss | 4 ++-- setup.py | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index c4672481..3dd5de58 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.5.6 +current_version = 0.5.7 commit = True tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+)(?P\d+))? diff --git a/distribution/picasso.iss b/distribution/picasso.iss index 3c1f2e5c..d9b60b4d 100644 --- a/distribution/picasso.iss +++ b/distribution/picasso.iss @@ -2,10 +2,10 @@ AppName=Picasso AppPublisher=Jungmann Lab, Max Planck Institute of Biochemistry -AppVersion=0.5.6 +AppVersion=0.5.7 DefaultDirName={pf}\Picasso DefaultGroupName=Picasso -OutputBaseFilename="Picasso-Windows-64bit-0.5.6" +OutputBaseFilename="Picasso-Windows-64bit-0.5.7" ArchitecturesAllowed=x64 ArchitecturesInstallIn64BitMode=x64 diff --git a/docs/conf.py b/docs/conf.py index 57137330..49e6c504 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ # The short X.Y version version = "" # The full version, including alpha/beta/rc tags -release = "0.5.6" +release = "0.5.7" # -- General configuration --------------------------------------------------- diff --git a/picasso/__version__.py b/picasso/__version__.py index 20ce9145..ebc7d661 100644 --- a/picasso/__version__.py +++ b/picasso/__version__.py @@ -1 +1 @@ -VERSION_NO = "0.5.6" +VERSION_NO = "0.5.7" diff --git a/release/one_click_windows_gui/create_installer_windows.bat b/release/one_click_windows_gui/create_installer_windows.bat index 43133d94..73e9beae 100644 --- a/release/one_click_windows_gui/create_installer_windows.bat +++ b/release/one_click_windows_gui/create_installer_windows.bat @@ -11,7 +11,7 @@ call conda activate picasso_installer call python setup.py sdist bdist_wheel call cd release/one_click_windows_gui -call pip install "../../dist/picassosr-0.5.6-py3-none-any.whl" +call pip install "../../dist/picassosr-0.5.7-py3-none-any.whl" call pip install pyinstaller==5.1 call pyinstaller ../pyinstaller/picasso.spec -y --clean diff --git a/release/one_click_windows_gui/picasso_innoinstaller.iss b/release/one_click_windows_gui/picasso_innoinstaller.iss index e2703b06..41e59d0c 100644 --- a/release/one_click_windows_gui/picasso_innoinstaller.iss +++ b/release/one_click_windows_gui/picasso_innoinstaller.iss @@ -1,10 +1,10 @@ [Setup] AppName=Picasso AppPublisher=Jungmann Lab, Max Planck Institute of Biochemistry -AppVersion=0.5.6 +AppVersion=0.5.7 DefaultDirName={pf}\Picasso DefaultGroupName=Picasso -OutputBaseFilename="Picasso-Windows-64bit-0.5.6" +OutputBaseFilename="Picasso-Windows-64bit-0.5.7" ArchitecturesAllowed=x64 ArchitecturesInstallIn64BitMode=x64 diff --git a/setup.py b/setup.py index a7fbb15e..ea7580f3 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name="picassosr", - version="0.5.6", + version="0.5.7", author="Joerg Schnitzbauer, Maximilian T. Strauss, Rafal Kowalewski", author_email=("joschnitzbauer@gmail.com, straussmaximilian@gmail.com, rafalkowalewski998@gmail.com"), url="https://github.com/jungmannlab/picasso", From 3e49d8bfac167fd6050a4d5224242a627220dbb4 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 6 Feb 2023 15:38:53 +0100 Subject: [PATCH 32/39] qe deletion warning --- changelog.rst | 6 +++--- readme.rst | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/changelog.rst b/changelog.rst index ab6a7d86..c877d478 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,7 +1,7 @@ Changelog ========= -Last change: 08-FEB-2023 MTS +Last change: 07-FEB-2023 MTS 0.5.7 ----- @@ -10,10 +10,10 @@ Last change: 08-FEB-2023 MTS - Render group information is faster (e.g., clustered data) - Test Clusterer window (Render) has multiple updates, e.g., different projections, cluster centers display - Cluster centers contain info about std in x,y and z -- If localization precision in z-axis is provided, it will be rendered when using ``Individual localization precision`` and ``Individual localization precision (iso)``. *NOTE:* the column must be named ``lpz`` and have the same units as ``lpx`` and ``lpy``. +- If localization precision in z-axis is provided, it will be rendered when using ``Individual localization precision`` and ``Individual localization precision (iso)``. **NOTE:** the column must be named ``lpz`` and have the same units as ``lpx`` and ``lpy``. - Number of CPU cores used in multiprocessing limited at 60 - Updated 3D rendering and clustering documentation -- Other bug fixes +- Bug fixes 0.5.5 - 0.5.6 ------------- diff --git a/readme.rst b/readme.rst index 2b8d21c0..3adfcd53 100644 --- a/readme.rst +++ b/readme.rst @@ -22,6 +22,20 @@ Picasso A collection of tools for painting super-resolution images. The Picasso software is complemented by our `Nature Protocols publication `__. A comprehensive documentation can be found here: `Read the Docs `__. +Localization precision update +----------------------------- +In the next Picasso update **(0.5.8)** the formula for conversion of raw data to photons will be changed and this will affect the calculate localization precision as the number of photons changes. + +Until version *0.5.7*, the formula was: :math:`\frac{(raw_data - baseline) * sensitivity}{gain * QE}`, where :math:`QE` is quantum efficiency of the camera. In the new version it will be changed too: + +.. math:: + + \frac{(raw_data - baseline) * sensitivity}{gain} + +**that is, quantum effiency will be ignored.** + +For backward compatibility, quantum efficiency will be kept in Picasso Localize, however, it will have no effect on the new photon conversion formula. + Picasso 0.5.0 ------------- Picasso has introduced many changes, including 3D rotation window and a new clustering algorithm in Render and reading of .nd2 files in Localize. Please check the `changelog `_ to see all modifications. From b1372647fcf88e8d3de49e4dc4edf918463592e2 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 6 Feb 2023 15:40:36 +0100 Subject: [PATCH 33/39] FIX --- readme.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.rst b/readme.rst index 3adfcd53..7ef833cb 100644 --- a/readme.rst +++ b/readme.rst @@ -22,15 +22,15 @@ Picasso A collection of tools for painting super-resolution images. The Picasso software is complemented by our `Nature Protocols publication `__. A comprehensive documentation can be found here: `Read the Docs `__. -Localization precision update +Photon conversion update ----------------------------- -In the next Picasso update **(0.5.8)** the formula for conversion of raw data to photons will be changed and this will affect the calculate localization precision as the number of photons changes. +In the next Picasso update **(0.5.8)** the formula for conversion of raw data to photons will be changed and **this will affect the calculate localization precision** as the number of photons changes. -Until version *0.5.7*, the formula was: :math:`\frac{(raw_data - baseline) * sensitivity}{gain * QE}`, where :math:`QE` is quantum efficiency of the camera. In the new version it will be changed too: +Until version *0.5.7*, the formula was: :math:`\\frac{(raw_data - baseline) * sensitivity}{gain * QE}`, where :math:`QE` is quantum efficiency of the camera. In the new version it will be changed too: .. math:: - \frac{(raw_data - baseline) * sensitivity}{gain} + \\frac{(raw_data - baseline) * sensitivity}{gain} **that is, quantum effiency will be ignored.** From 4a594a18556d8826b339255406d8a539284f76de Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 6 Feb 2023 15:49:43 +0100 Subject: [PATCH 34/39] fix --- readme.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.rst b/readme.rst index 7ef833cb..d16cb8c7 100644 --- a/readme.rst +++ b/readme.rst @@ -26,13 +26,13 @@ Photon conversion update ----------------------------- In the next Picasso update **(0.5.8)** the formula for conversion of raw data to photons will be changed and **this will affect the calculate localization precision** as the number of photons changes. -Until version *0.5.7*, the formula was: :math:`\\frac{(raw_data - baseline) * sensitivity}{gain * QE}`, where :math:`QE` is quantum efficiency of the camera. In the new version it will be changed too: +Until version *0.5.7*, the formula was: :math:`\frac{(\text{raw_data} - \text{baseline}) * \text{sensitivity}}{\text{gain} * \text{QE}}`, where :math:`\text{QE}` is quantum efficiency of the camera. In the new version it will be changed too: .. math:: - \\frac{(raw_data - baseline) * sensitivity}{gain} + \frac{(raw_data - baseline) * sensitivity}{gain} -**that is, quantum effiency will be ignored.** +**i.e., quantum effiency will be ignored.** For backward compatibility, quantum efficiency will be kept in Picasso Localize, however, it will have no effect on the new photon conversion formula. From f093d506cf86725467162f71a17ef26b7b3a7998 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 6 Feb 2023 15:51:01 +0100 Subject: [PATCH 35/39] fix --- readme.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.rst b/readme.rst index d16cb8c7..7fc005ce 100644 --- a/readme.rst +++ b/readme.rst @@ -26,7 +26,7 @@ Photon conversion update ----------------------------- In the next Picasso update **(0.5.8)** the formula for conversion of raw data to photons will be changed and **this will affect the calculate localization precision** as the number of photons changes. -Until version *0.5.7*, the formula was: :math:`\frac{(\text{raw_data} - \text{baseline}) * \text{sensitivity}}{\text{gain} * \text{QE}}`, where :math:`\text{QE}` is quantum efficiency of the camera. In the new version it will be changed too: +Until version *0.5.7*, the formula was: :math:`\\frac{(\\text{raw_data} - \\text{baseline}) * \\text{sensitivity}}{\\text{gain} * \\text{QE}}`, where :math:`\\text{QE}` is quantum efficiency of the camera. In the new version it will be changed too: .. math:: From d5281597145713a033e3238639216c2e6fe4395a Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 6 Feb 2023 15:52:44 +0100 Subject: [PATCH 36/39] fix --- readme.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.rst b/readme.rst index 7fc005ce..1c10f4c7 100644 --- a/readme.rst +++ b/readme.rst @@ -24,11 +24,11 @@ A comprehensive documentation can be found here: `Read the Docs Date: Tue, 7 Feb 2023 10:58:09 +0100 Subject: [PATCH 37/39] FIX --- readme.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.rst b/readme.rst index 1c10f4c7..028b5438 100644 --- a/readme.rst +++ b/readme.rst @@ -26,11 +26,11 @@ Photon conversion update ----------------------------- In the next Picasso update **(0.5.8)** the formula for conversion of raw data to photons will be changed and **this will affect the calculated localization precision** as the number of photons changes. -Until version *0.5.7*, the formula was: :latex:`\\frac{(\\text{raw_data} - \\text{baseline}) * \\text{sensitivity}}{\\text{gain} * \\text{QE}}`, where :math:`\\text{QE}` is quantum efficiency of the camera. In the new version it will be changed too: +Until version *0.5.7*, the formula was: -.. latex:: +*(RAW_DATA - BASELINE) x SENSITIVITY / (GAIN x QE)*, where QE is quantum efficiency of the camera. In the new version it will be changed too: - \frac{(raw_data - baseline) * sensitivity}{gain} +*(RAW_DATA - BASELINE) x SENSITIVITY / GAIN* **i.e., quantum effiency will be ignored.** From 633c093f7ddef50f3102233cd4205630a4b85ad8 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 7 Feb 2023 10:59:18 +0100 Subject: [PATCH 38/39] FIX --- readme.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.rst b/readme.rst index 028b5438..4cde274b 100644 --- a/readme.rst +++ b/readme.rst @@ -24,7 +24,7 @@ A comprehensive documentation can be found here: `Read the Docs Date: Tue, 7 Feb 2023 11:20:55 +0100 Subject: [PATCH 39/39] FIX --- readme.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.rst b/readme.rst index 4cde274b..7549fda3 100644 --- a/readme.rst +++ b/readme.rst @@ -32,7 +32,7 @@ Until version *0.5.7*, the formula was: *(RAW_DATA - BASELINE) x SENSITIVITY / GAIN* -**i.e., quantum effiency will be ignored.** +**i.e., quantum effiency will be removed.** For backward compatibility, quantum efficiency will be kept in Picasso Localize, however, it will have no effect on the new photon conversion formula.