Skip to content

Commit

Permalink
feat: version 3.3.0
Browse files Browse the repository at this point in the history
feat: version 3.3.0
  • Loading branch information
lgrcia authored Jan 22, 2024
2 parents 454b2cc + d95c43d commit eedfa9f
Show file tree
Hide file tree
Showing 14 changed files with 438 additions and 144 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ _docs
docs/tested_blocks.md
docs/md/all_blocks.rst
docs/md/template.py

## Pycharm
*.xml
.idea

11 changes: 10 additions & 1 deletion docs/md/utils_blocks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ Utils
Get
Calibration
SortSources
CleanBadPixels
CleanBadPixels

.. currentmodule:: prose.blocks.visualization

.. autosummary::
:template: blocksum.rst
:nosignatures:

Video
VideoPlot
29 changes: 21 additions & 8 deletions prose/blocks/detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
from prose.console_utils import info
from prose.core.source import *

__all__ = ["DAOFindStars", "SEDetection", "AutoSourceDetection", "PointSourceDetection"]
__all__ = [
"DAOFindStars",
"SEDetection",
"AutoSourceDetection",
"PointSourceDetection",
"Peaks",
]


class _SourceDetection(Block):
Expand Down Expand Up @@ -268,17 +274,24 @@ def __init__(

# TODO: document
class Peaks(Block):
def __init__(self, cutout=11, **kwargs):
def __init__(self, shape=11, **kwargs):
"""Computation of peak values for the detected stars (in ADUs).
Parameters
----------
shape : int, optional
size of the cutout image within which the peak is calculated, by default 11
"""
super().__init__(**kwargs)
self.cutout = cutout
self.shape = shape

def run(self, image, **kwargs):
idxs, cuts = cutouts(image.data, image.sources.coords, size=self.cutout)
peaks = np.ones(len(image.stars_coords)) * -1
for j, i in enumerate(idxs):
cut = cuts[j]
idxs = np.arange(len(image.sources))
peaks = np.ones(len(idxs)) * -1
for j in idxs:
cut = image.cutout(image.sources.coords[j], shape=self.shape)
if cut is not None:
peaks[i] = np.max(cut.data)
peaks[j] = cut.data.max()
image.peaks = peaks


Expand Down
2 changes: 2 additions & 0 deletions prose/blocks/photometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def run(self, image: Image):

annulus = image.sources.annulus(rin, rout)
annulus_masks = annulus.to_mask(method="center")
annulus_area = np.pi * (rout**2 - rin**2)

bkg_median = []
for mask in annulus_masks:
Expand All @@ -125,4 +126,5 @@ def run(self, image: Image):
"rout": rin,
"median": np.array(bkg_median),
"sigma": self.sigma,
"area": annulus_area,
}
18 changes: 17 additions & 1 deletion prose/blocks/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,12 +540,24 @@ def get_time(im):
def get_aperture(im):
return im.aperture["radii"]

def get_error(im):
_area = np.pi * im.aperture["radii"] ** 2
_signal = im.aperture["fluxes"] - (
im.annulus["median"][:, None] * _area[None, :]
)
# TODO : figure out the correct CCD equation for error computation
_squarred_error = _signal + _area[None, :] * (
im.read_noise**2 + (im.gain / 2) ** 2 + im.annulus["median"][:, None]
)
return np.sqrt(_squarred_error)

super().__init__(
*args,
_time=get_time,
_bkg=get_bkg,
_fluxes=get_fluxes,
_apertures=get_aperture,
_errors=get_error,
name=name,
**kwargs,
)
Expand All @@ -564,7 +576,11 @@ def terminate(self):
data = {"bkg": np.mean(self._bkg, -1)}
data.update({key: value for key, value in self.values.items() if key[0] != "_"})
self.fluxes = Fluxes(
time=time, fluxes=raw_fluxes, data=data, apertures=self._apertures
time=time,
fluxes=raw_fluxes,
data=data,
apertures=self._apertures,
errors=self._errors.T,
)


Expand Down
240 changes: 129 additions & 111 deletions prose/blocks/visualization.py
Original file line number Diff line number Diff line change
@@ -1,145 +1,163 @@
import io
import shutil
import tempfile
import time

import imageio
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg
from skimage.transform import resize

from prose import Block, viz
from prose.visualization import corner_text

__all__ = ["VideoPlot"]


def im_to_255(image, factor=0.25):
if factor != 1:
return (
resize(
image.astype(float),
(np.array(np.shape(image)) * factor).astype(int),
anti_aliasing=False,
)
* 255
).astype("uint8")
else:
data = image.copy().astype(float)
data = data / np.max(data)
data = data * 255
return data.astype("uint8")


class _Video(Block):
"""Base block to build a video"""

def __init__(self, destination, fps=10, **kwargs):
super().__init__(**kwargs)
self.destination = destination
self.images = []
self.fps = fps
self.checked_writer = False

def run(self, image):
if not self.checked_writer:
_ = imageio.get_writer(self.destination, mode="I")
self.checked_writer = True
from prose.core.block import Block
from prose.utils import z_scale

def terminate(self):
imageio.mimsave(self.destination, self.images, fps=self.fps)
__all__ = ["VideoPlot", "Video"]

@property
def citations(self):
return super().citations + ["imageio"]

def im_to_255(image):
data = image.copy().astype(float)
data = data / np.max(data)
data = data * 255
return data.astype("uint8")

class RawVideo(_Video):

class Video(Block):
def __init__(
self, destination, attribute="data", fps=10, function=None, scale=1, **kwargs
self,
destination,
fps=10,
compression=None,
data_function=None,
width=None,
contrast=0.1,
name=None,
):
super().__init__(destination, fps=fps, **kwargs)
if function is None:
"""
A block to create a video from images data (using ffmpeg).
def _donothing(data):
return data
Parameters
----------
destination : str
The path to save the resulting video.
fps : int, optional
The frames per second of the resulting video. Default is 10.
compression : int, optional
The compression rate of the resulting video (-cr value of fmmpeg).
Default is None.
data_function : callable, optional
A function to apply to each image data before adding it to the video.
If none, a z scale is applied to the data with a contrast given by
:code:`contrast`.Default is None.
width : int, optional
The width in pixels of the resulting video.
Default is None (i.e. original image size).
contrast : float, optional
The contrast of the resulting video. Default is 0.1.
Either :code:`contrast` or :code:`data_function` must be provided.
name : str, optional
The name of the block. Default is None.
Attributes
----------
citations : list of str
The citations for the block.
function = _donothing
Methods
-------
run(image)
Adds an image to the video.
terminate()
Closes the video writer.
self.function = function
self.scale = scale
self.attribute = attribute
"""
super().__init__(name=name)
if data_function is None:

def data_function(data):
new_data = data.copy()
new_data = z_scale(new_data, c=contrast)
return new_data

output = []
if compression is not None:
output += ["-crf", f"{compression}"]
if width is not None:
output += ["-vf", f"scale={width}:-1"]
self.writer = imageio.get_writer(
destination,
mode="I",
fps=fps,
output_params=output if len(output) > 0 else None,
)
self.function = data_function

def run(self, image):
super().run(image)
data = self.function(image.__dict__[self.attribute])
self.images.append(im_to_255(data, factor=self.scale))
data = self.function(image.data)
self.writer.append_data(im_to_255(data))

def terminate(self):
self.writer.close()

@property
def citations(self):
return super().citations + ["imageio"]

class VideoPlot(_Video):
def __init__(self, plot_function, destination, fps=10, name=None):
"""Make a video out of a plotting function

class VideoPlot(Video):
def __init__(
self,
plot_function,
destination,
fps=10,
compression=None,
width=None,
name=None,
):
"""
A block to create a video from a matploltib plot (using ffmpeg).
Parameters
----------
plot_function : function
a plotting function taking an :py:class:`prose.Image` as input
destination : str or Path
destination of the video, including extension
plot_function : callable
A function that takes an image as input and produce a plot.
destination : str
The path to save the resulting video.
fps : int, optional
frame per seconds, by default 10
antialias : bool, optional
whether pyplot antialias should be used, by default False
The frames per second of the resulting video. Default is 10.
compression : int, optional
The compression rate of the resulting video (-cr value of fmmpeg).
Default is None.
width : int, optional
The width in pixels of the resulting video.
Default is None (i.e. original image size).
name : str, optional
The name of the block. Default is None.
Attributes
----------
citations : list of str
The citations for the block.
Methods
-------
run(image)
Adds a plot to the video.
terminate()
Closes the video writer.
"""
super().__init__(destination, fps=fps, name=name)
super().__init__(
destination,
fps=fps,
compression=compression,
width=width,
name=name,
)
self.plot_function = plot_function
self.destination = destination
self._temp = tempfile.mkdtemp()
self._images = []

def run(self, image):
self.plot_function(image)
buf = io.BytesIO()
plt.savefig(buf)
self.images.append(imageio.imread(buf))
self.writer.append_data(imageio.imread(buf))
plt.close()

def terminate(self):
super().terminate()
shutil.rmtree(self._temp)


class LivePlot(Block):
def __init__(self, plot_function=None, sleep=0.0, size=None, **kwargs):
super().__init__(**kwargs)
if plot_function is None:
plot_function = lambda im: viz.show_stars(
im.data,
im.stars_coords if hasattr(im, "stars_coords") else None,
size=size,
)

self.plot_function = plot_function
self.sleep = sleep
self.display = None
self.size = size
self.figure_added = False

def run(self, image):
if not self.figure_added:
from IPython import display as disp

self.display = disp
if isinstance(self.size, tuple):
plt.figure(figsize=self.size)
self.figure_added = True

self.plot_function(image)
self.display.clear_output(wait=True)
self.display.display(plt.gcf())
time.sleep(self.sleep)
plt.cla()

def terminate(self):
plt.close()
super().terminate()
Loading

0 comments on commit eedfa9f

Please sign in to comment.