Skip to content

Commit

Permalink
Merge pull request #423 from rafalkowalewski1/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
rafalkowalewski1 authored Jan 10, 2024
2 parents 767b93a + c2beeec commit 3895ff8
Show file tree
Hide file tree
Showing 18 changed files with 82 additions and 164 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.6.5
current_version = 0.6.6
commit = True
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+)(?P<build>\d+))?
Expand Down
8 changes: 4 additions & 4 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ sphinx:
# Optional but recommended, declare the Python requirements required
# to build your documentation
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
# python:
# install:
# - requirements: requirements.txt
# - method: pip
python:
install:
- requirements: docs/requirements.txt
- method: pip
13 changes: 12 additions & 1 deletion changelog.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
Changelog
=========

Last change: 22-OCT-2023 MTS
Last change: 05-DEC-2023 MTS

0.6.6
-----
- GUI modules display the Picasso version number in the title bar
- Added readthedocs requirements file (only for developers)
- No blur applied when padding in Picasso: Render (increases speed of rendering)
- Camera settings saved in the .yaml file after localization
- Picasso: Design has the speed optimized extension sequences (Strauss and Jungmann, Nature Methods, 2020)
- Change matplotlib backend for macOS (bug fix with some plots being unavailable)
- Build animation does not trigger antivirus, which could delete Picasso (one click installer only)

0.6.3 - 0.6.5
-------------
Expand All @@ -14,6 +24,7 @@ Last change: 22-OCT-2023 MTS
- Fix test clusterer HDBSCAN bug
- Fix .nd2 localized files info loading (full loader changed to unsafe loader)
- Fix rare bug with pick similar zero division error
- Update installation instructions

0.6.2
-----
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.6.5
AppVersion=0.6.6
DefaultDirName={commonpf}\Picasso
DefaultGroupName=Picasso
OutputBaseFilename="Picasso-Windows-64bit-0.6.5"
OutputBaseFilename="Picasso-Windows-64bit-0.6.6"
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.6.5"
release = "0.6.6"

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

Expand Down
3 changes: 3 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sphinx==5.3.0
sphinx_rtd_theme==1.1.1
readthedocs-sphinx-search==0.1.1
4 changes: 2 additions & 2 deletions picasso/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
~~~~~~~~~~~~~~~~~~~~
:authors: Joerg Schnitzbauer, Maximilian Thomas Strauss, Rafal Kowalewski 2016-2023
:copyright: Copyright (c) 2016-2018 Jungmann Lab, MPI of Biochemistry
:copyright: Copyright (c) 2016-2023 Jungmann Lab, MPI of Biochemistry
"""
import os.path as _ospath
import yaml as _yaml

__version__ = "0.6.5"
__version__ = "0.6.6"

_this_file = _ospath.abspath(__file__)
_this_dir = _ospath.dirname(_this_file)
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.6.5"
VERSION_NO = "0.6.6"
11 changes: 6 additions & 5 deletions picasso/clusterer.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,14 +385,15 @@ def dbscan(locs, radius, min_density, pixelsize=None):
Paramters
---------
locs : np.recarray
Localizations to be clustered
Localizations to be clustered.
radius : float
DBSCAN search radius, often referred to as "epsilon"
DBSCAN search radius, often referred to as "epsilon". Same units
as locs.
min_density : int
Number of localizations within radius to consider a given point
a core sample
pixelsize : int
Camera pixel size in nm
a core sample.
pixelsize : int (default=None)
Camera pixel size in nm. Only needed for 3D.
Returns
-------
Expand Down
2 changes: 1 addition & 1 deletion picasso/design_sequences.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,5 @@
["7xR3", "TCCTCTCTCTCTCTCTCTCTC"],
["7xR4", "CAACACACACACACACACACA"],
["5xR5", "CCCTTCTTCTTCTTCTTCTTC"],
["5xR6", "AAAACAACAACAACAACAACAA"]
["5xR6", "AAAACAACAACAACAACAACAA"],
]
20 changes: 12 additions & 8 deletions picasso/gui/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -5698,7 +5698,9 @@ def add(self, path, render=True):
self.window.dataset_dialog.add_entry(path)

self.window.setWindowTitle(
"Picasso: Render. File: {}".format(os.path.basename(path))
"Picasso v{}: Render. File: {}".format(
__version__, os.path.basename(path)
)
)

# fast rendering add channel
Expand Down Expand Up @@ -7009,7 +7011,7 @@ def save_channel(self, title="Choose a channel"):
return None
elif n_channels == 1:
return 0
elif len(self.locs_paths) > 1:
elif n_channels > 1:
pathlist = list(self.locs_paths)
pathlist.append("Apply to all sequentially")
pathlist.append("Combine all channels")
Expand Down Expand Up @@ -7099,9 +7101,12 @@ def get_render_kwargs(self, viewport=None):
"""

# blur method
blur_button = (
self.window.display_settings_dlg.blur_buttongroup.checkedButton()
)
if self._pan: # no blur when panning
blur_method = None
else: # selected method
blur_method = self.window.display_settings_dlg.blur_methods[
self.window.display_settings_dlg.blur_buttongroup.checkedButton()
]

# oversampling
optimal_oversampling = (
Expand Down Expand Up @@ -7140,9 +7145,7 @@ def get_render_kwargs(self, viewport=None):
return {
"oversampling": oversampling,
"viewport": viewport,
"blur_method": self.window.display_settings_dlg.blur_methods[
blur_button
],
"blur_method": blur_method,
"min_blur_width": float(
self.window.display_settings_dlg.min_blur_width.value()
),
Expand Down Expand Up @@ -7411,6 +7414,7 @@ def mouseReleaseEvent(self, event):
self._pan = False
self.setCursor(QtCore.Qt.ArrowCursor)
event.accept()
self.update_scene()
else:
event.ignore()
elif self._mode == "Pick":
Expand Down
131 changes: 24 additions & 107 deletions picasso/gui/rotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,65 +12,26 @@

import os
import colorsys
import re
from functools import partial

import numpy as np
import matplotlib.pyplot as plt
from moviepy.video.io.ImageSequenceClip import ImageSequenceClip
# from moviepy.video.io.ImageSequenceClip import ImageSequenceClip
import imageio
from PyQt5 import QtCore, QtGui, QtWidgets

from numpy.lib.recfunctions import stack_arrays

from .. import io, render
from ..lib import StatusDialog, ProgressDialog
from ..lib import ProgressDialog

# from icecream import ic #TODO: delete

DEFAULT_OVERSAMPLING = 1.0
INITIAL_REL_MAXIMUM = 0.5
N_GROUP_COLORS = 8
SHIFT = 0.1
ZOOM = 9 / 7

def atoi(text):
"""
Converts string to digit if it represents an integer.
Parameters
----------
text : str
String to be converted
Returns
-------
int or str
int if text represents a digit, text otherwise
"""

return int(text) if text.isdigit() else text

def natural_keys(text):
"""
Splits text into three parts:
* str ending with "/frame_"
* int with frame index
* ".png"
Using this key, frames' paths can be sorted by their index
Parameters
----------
text : str
String to be split and converted
Returns
-------
list
Text split into several parts, where strings representing
digits are converted to int
"""

return [atoi(c) for c in re.split('([0-9]+)', text)]

def get_colors(n_channels):
"""
Expand Down Expand Up @@ -651,75 +612,31 @@ def build_animation(self):
if height % 2 == 1:
height += 1

# create temporary folder to store all frames
base = os.path.dirname(self.window.view_rot.paths[0])
path = os.path.join(base, "animation_frames")
# render all frames and save in RAM
video_writer = imageio.get_writer(name, fps=self.fps.value())
progress = ProgressDialog(
"Rendering frames", 0, len(angx), self.window
)
progress.set_value(0)
try:
os.mkdir(path)
for i in range(len(angx)):
qimage = self.window.view_rot.render_scene(
viewport=[(ymin[i], xmin[i]), (ymax[i], xmax[i])],
ang=(angx[i], angy[i], angz[i]),
animation=True,
)
qimage = qimage.scaled(
width,
height,
# QtCore.Qt.KeepAspectRatioByExpanding,
)
qimage.save(path + "/frame_{}.png".format(i+1))
progress.set_value(i+1)
except:
# if folder exists, ask if it should be used or deleted
m = QtWidgets.QMessageBox()
m.setWindowTitle("Frames already exist")
ret = m.question(
self,
"",
"Use the existing frames folder?",
m.Yes | m.No,
for i in range(len(angx)):
qimage = self.window.view_rot.render_scene(
viewport=[(ymin[i], xmin[i]), (ymax[i], xmax[i])],
ang=(angx[i], angy[i], angz[i]),
animation=True,
)
if ret == m.No:
# use new frames (render each one) and save
for file in os.listdir(path):
os.remove(os.path.join(path, file))
for i in range(len(angx)):
qimage = self.window.view_rot.render_scene(
viewport=[(ymin[i], xmin[i]), (ymax[i], xmax[i])],
ang=(angx[i], angy[i], angz[i]),
animation=True,
)
qimage = qimage.scaled(
width,
height,
# QtCore.Qt.KeepAspectRatioByExpanding,
)
qimage.save(path + "/frame_{}.png".format(i+1))
progress.set_value(i+1)
elif ret == m.Yes:
# use old frames
progress.set_value(len(angx))

# build a video and save it
status = StatusDialog("Creating the video...", self.window)
image_files = [
os.path.join(path, img)
for img in os.listdir(path)
if img.endswith(".png")
] # paths to each frame
image_files.sort(key=natural_keys) # sort frames
video = ImageSequenceClip(image_files, fps=self.fps.value())
video.write_videofile(name, logger=None)

# delete animation frames
for file in os.listdir(path):
os.remove(os.path.join(path, file))
os.rmdir(path)
status.close()
qimage = qimage.scaled(width, height)

# convert to a np.array and append
ptr = qimage.bits()
ptr.setsize(height * width * 4)
frame = np.frombuffer(ptr, np.uint8).reshape((width, height, 4))
frame = frame[:, :, :3]
frame = frame[:, :, ::-1] # invert RGB to BGR

video_writer.append_data(frame)
progress.set_value(i+1)
progress.close()
video_writer.close()


class ViewRotation(QtWidgets.QLabel):
Expand Down Expand Up @@ -2287,7 +2204,7 @@ def __init__(self, window):
rotation_action.triggered.connect(self.view_rot.rotation_input)
rotation_action.setShortcut("Ctrl+Shift+R")

delete_rotation_action = view_menu.addAction("Remove rotation")
delete_rotation_action = view_menu.addAction("Reset rotation")
delete_rotation_action.triggered.connect(self.view_rot.delete_rotation)
delete_rotation_action.setShortcut("Ctrl+Shift+W")
fit_in_view_action = view_menu.addAction("Fit image to window")
Expand Down
4 changes: 2 additions & 2 deletions picasso/postprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1038,8 +1038,8 @@ def link_loc_groups(locs, info, link_group, remove_ambiguous_lengths=True):

def localization_precision(photons, s, bg, em):
"""
Calculates the theoretical localization precision
according to Mortensen et al., Nat Meth, 2010
Calculates the theoretical localization precision according to
Mortensen et al., Nat Meth, 2010 for a 2D unweighted Gaussian fit.
"""
s2 = s**2
sa2 = s2 + 1 / 12
Expand Down
Loading

0 comments on commit 3895ff8

Please sign in to comment.