Skip to content

Commit

Permalink
Merge pull request #498 from jungmannlab/development
Browse files Browse the repository at this point in the history
v0.7.1
  • Loading branch information
rafalkowalewski1 authored Sep 19, 2024
2 parents 11711a8 + 31bab59 commit b3f704b
Show file tree
Hide file tree
Showing 17 changed files with 279 additions and 376 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.7.0
current_version = 0.7.1
commit = True
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+)(?P<build>\d+))?
Expand Down
11 changes: 9 additions & 2 deletions changelog.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
Changelog
=========

Last change: 06-JUL-2024 MTS
Last change: 19-SEP-2024 MTS

0.7.1
-----
- SMLM clusterer in picked regions deleted
- Show legend in Render property displayed rounded tick label values
- Pick circular area does not save the area for each pick in localization's metadata
- Other minor bug fixes

0.7.0
-----
- Adaptive Intersection Maximization (AIM, doi: 10.1038/s41592-022-01307-0) implemented
- Z fitting improved by setting bounds on fitted z values to avoid NaNs
- CMD ``clusterfile`` fixed
- Picasso: Render 3D, rectangular and polygonal pick fixed
- picasso.localize.localize fixed
- ``picasso.localize.localize`` fixed
- default MLE fitting uses different sx and sy (CMD only)

0.6.9 - 0.6.11
Expand Down
4 changes: 2 additions & 2 deletions distribution/picasso.iss
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
AppName=Picasso
AppPublisher=Jungmann Lab, Max Planck Institute of Biochemistry

AppVersion=0.7.0
AppVersion=0.7.1
DefaultDirName={commonpf}\Picasso
DefaultGroupName=Picasso
OutputBaseFilename="Picasso-Windows-64bit-0.7.0"
OutputBaseFilename="Picasso-Windows-64bit-0.7.1"
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# The short X.Y version
version = ""
# The full version, including alpha/beta/rc tags
release = "0.7.0"
release = "0.7.1"

# -- General configuration ---------------------------------------------------

Expand Down
6 changes: 3 additions & 3 deletions docs/render.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ Adaptive Intersection Maximization (AIM) drift correction

1. In ``Picasso: Render``, select ``Postprocess > Undrift by AIM``.
2. The dialog asks the user to select:
a. ``Segmentation`` - the number of frames per interval to calculate the drift. The lower the value, the better the temporal resolution of the drift correction, but the higher the computational cost.
b. ``Intersection distance (nm)`` - the maximum distance between two localizations in two consecutive temporal segments to be considered the same molecule. This parameter is robust, 3*NeNA for optimal result is recommended.
c. ``Max. drift in segment (nm)`` - the maximum expected drift between two consecutive temporal segments. If the drift is larger, the algorithm will likely diverge. Setting the parameter up to ``3 * intersection_distance`` will result in fast computation.
| a. ``Segmentation`` - the number of frames per interval to calculate the drift. The lower the value, the better the temporal resolution of the drift correction, but the higher the computational cost.
| b. ``Intersection distance (nm)`` - the maximum distance between two localizations in two consecutive temporal segments to be considered the same molecule. This parameter is robust, 3*NeNA for optimal result is recommended.
| c. ``Max. drift in segment (nm)`` - the maximum expected drift between two consecutive temporal segments. If the drift is larger, the algorithm will likely diverge. Setting the parameter up to ``3 * intersection_distance`` will result in fast computation.
3. After the algorithm finishes, the estimated drift will be displayed in a pop-up window, and the display will show the drift-corrected image.

Marker-based drift correction
Expand Down
2 changes: 1 addition & 1 deletion picasso/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import os.path as _ospath
import yaml as _yaml

__version__ = "0.7.0"
__version__ = "0.7.1"

_this_file = _ospath.abspath(__file__)
_this_dir = _ospath.dirname(_this_file)
Expand Down
36 changes: 19 additions & 17 deletions picasso/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,15 +505,16 @@ def _smlm_clusterer(
paths = glob.glob(files)
if paths:
from . import io, clusterer
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]

params = {
"radius_xy": radius,
"radius_z": radius_z,
"min_locs": min_locs,
"frame_analysis": basic_fa,
}
for path in paths:
print("Loading {} ...".format(path))
locs, info = io.load_locs(path)
locs = clusterer.cluster(locs, params, pixelsize)
locs = clusterer.cluster(locs, **params, pixelsize=pixelsize)
clusters = clusterer.find_cluster_centers(locs, pixelsize)
base, ext = os.path.splitext(path)
smlm_cluster_info = {
Expand Down Expand Up @@ -858,10 +859,10 @@ def prompt_info():
y_min, x_min, y_max, x_max = roi
roi = [[y_min, x_min], [y_max, x_max]]
camera_info = {}
camera_info["baseline"] = args.baseline
camera_info["sensitivity"] = args.sensitivity
camera_info["gain"] = args.gain
camera_info["qe"] = args.qe
camera_info["Baseline"] = args.baseline
camera_info["Sensitivity"] = args.sensitivity
camera_info["Gain"] = args.gain
camera_info["Qe"] = args.qe

if args.fit_method == "mle":
# use default settings
Expand Down Expand Up @@ -892,7 +893,7 @@ def prompt_info():

try:
with open(zpath, "r") as f:
z_calibration = yaml.load(f)
z_calibration = yaml.full_load(f)
except Exception as e:
print(e)
print("Error loading calibration file.")
Expand Down Expand Up @@ -924,7 +925,7 @@ def prompt_info():
elif args.fit_method == "lq-gpu" or args.fit_method == "lq-gpu-3d":
spots = get_spots(movie, ids, box, camera_info)
theta = gausslq.fit_spots_gpufit(spots)
em = camera_info["gain"] > 1
em = camera_info["Gain"] > 1
locs = gausslq.locs_from_fits_gpufit(ids, theta, box, em)
elif args.fit_method == "mle":
current, thetas, CRLBs, likelihoods, iterations = fit_async(
Expand Down Expand Up @@ -955,15 +956,16 @@ def prompt_info():

localize_info = {
"Generated by": "Picasso Localize",
"ROI": None,
"ROI": None, #TODO: change if ROI is given
"Box Size": box,
"Min. Net Gradient": min_net_gradient,
"Convergence Criterion": convergence,
"Max. Iterations": max_iterations,
"Pixelsize": px,
"Fit method": args.fit_method

}
localize_info.update(camera_info)
if args.fit_method == "mle":
localize_info["Convergence Criterion"] = convergence
localize_info["Max. Iterations"] = max_iterations

if args.fit_method == "lq-3d" or args.fit_method == "lq-gpu-3d":
print("------------------------------------------")
Expand Down Expand Up @@ -1120,7 +1122,7 @@ def main():
parser = argparse.ArgumentParser("picasso")
subparsers = parser.add_subparsers(dest="command")

for command in ["toraw", "localize", "filter", "render"]:
for command in ["toraw", "filter"]:
subparsers.add_parser(command)

# link parser
Expand Down
2 changes: 1 addition & 1 deletion picasso/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION_NO = "0.7.0"
VERSION_NO = "0.7.1"
25 changes: 18 additions & 7 deletions picasso/aim.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ def aim(
roi_r=60/130,
progress=None,
):
"""Apply AIM algorithm to the localizations.
"""Apply AIM undrifting to the localizations.
Parameters
----------
Expand All @@ -639,10 +639,21 @@ def aim(
"""

# extract metadata
width = info[0]["Width"]
height = info[0]["Height"]
pixelsize = info[1]["Pixelsize"]
n_frames = info[0]["Frames"]
width = _np.nan
height = _np.nan
pixelsize = _np.nan
n_frames = _np.nan
for inf in info:
if val := inf.get("Width"):
width = val
if val := inf.get("Height"):
height = val
if val := inf.get('Frames'):
n_frames = val
if val := inf.get("Pixelsize"):
pixelsize = val
if _np.isnan(width * height * pixelsize * n_frames):
raise KeyError("Insufficient metadata available.")

# frames should start at 1
frame = locs["frame"] + 1
Expand Down Expand Up @@ -716,9 +727,9 @@ def aim(

new_info = {
"Undrifted by": "AIM",
"Intersect distance (cam. pixels)": intersect_d,
"Intersect distance (nm)": intersect_d * pixelsize,
"Segmentation": segmentation,
"Search regions radius (cam. pixels)": roi_r,
"Search regions radius (nm)": roi_r * pixelsize,
}
new_info = info + [new_info]

Expand Down
35 changes: 21 additions & 14 deletions picasso/clusterer.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,9 @@ def cluster_3D(x, y, z, frame, radius_xy, radius_z, min_locs, fa):
return labels


def cluster(locs, params, pixelsize=None):
def cluster(
locs, radius_xy, min_locs, frame_analysis, radius_z=None, pixelsize=None
):
"""
Clusters localizations given user parameters using KDTree.
Expand All @@ -309,8 +311,15 @@ def cluster(locs, params, pixelsize=None):
----------
locs : np.recarray
Localizations to be clustered
params : tuple
SMLM clustering parameters
radius_xy : float
Clustering radius in xy plane (camera pixels).
min_locs : int
Minimum number of localizations in a cluster.
frame_analysis : bool
If True, performs basic frame analysis.
radius_z : float (default=None)
Clustering radius in z plane (camera pixels). Only used for
3D clustering.
pixelsize : int (default=None)
Camera pixel size in nm. Only needed for 3D clustering.
Expand All @@ -322,12 +331,11 @@ def cluster(locs, params, pixelsize=None):
"""

if hasattr(locs, "z"): # 3D
if pixelsize is None:
if pixelsize is None or radius_z is None:
raise ValueError(
"Camera pixel size must be specified as an integer for 3D"
" clustering."
"Camera pixel size and clustering radius in z must be specified"
" for 3D clustering."
)
radius_xy, radius_z, min_locs, _, fa, _ = params
labels = cluster_3D(
locs.x,
locs.y,
Expand All @@ -336,17 +344,16 @@ def cluster(locs, params, pixelsize=None):
radius_xy,
radius_z,
min_locs,
fa
frame_analysis,
)
else:
radius, min_locs, _, fa, _ = params
labels = cluster_2D(
locs.x,
locs.y,
locs.frame,
radius,
radius_xy,
min_locs,
fa
frame_analysis,
)
locs = extract_valid_labels(locs, labels)
return locs
Expand Down Expand Up @@ -378,7 +385,7 @@ def _dbscan(X, radius, min_density):
return db.labels_.astype(_np.int32)


def dbscan(locs, radius, min_density, pixelsize=None):
def dbscan(locs, radius, min_samples, pixelsize=None):
"""
Performs DBSCAN on localizations.
Expand All @@ -389,7 +396,7 @@ def dbscan(locs, radius, min_density, pixelsize=None):
radius : float
DBSCAN search radius, often referred to as "epsilon". Same units
as locs.
min_density : int
min_samples : int
Number of localizations within radius to consider a given point
a core sample.
pixelsize : int (default=None)
Expand All @@ -411,7 +418,7 @@ def dbscan(locs, radius, min_density, pixelsize=None):
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)
labels = _dbscan(X, radius, min_samples)
locs = extract_valid_labels(locs, labels)
return locs

Expand Down
8 changes: 4 additions & 4 deletions picasso/gui/localize.py
Original file line number Diff line number Diff line change
Expand Up @@ -1221,10 +1221,10 @@ def init_menu_bar(self):
@property
def camera_info(self):
camera_info = {}
camera_info["baseline"] = self.parameters_dialog.baseline.value()
camera_info["gain"] = self.parameters_dialog.gain.value()
camera_info["sensitivity"] = self.parameters_dialog.sensitivity.value()
camera_info["qe"] = self.parameters_dialog.qe.value()
camera_info["Baseline"] = self.parameters_dialog.baseline.value()
camera_info["Gain"] = self.parameters_dialog.gain.value()
camera_info["Sensitivity"] = self.parameters_dialog.sensitivity.value()
camera_info["Qe"] = self.parameters_dialog.qe.value()
return camera_info

def calibrate_z(self):
Expand Down
Loading

0 comments on commit b3f704b

Please sign in to comment.