- save as mp4 or mkv movie

This commit is contained in:
Wim Pomp
2024-06-04 19:11:40 +02:00
parent e257ec326d
commit 1d2cc951fa
4 changed files with 70 additions and 6 deletions

View File

@@ -17,7 +17,15 @@ Currently, it supports imagej tif files, czi files, micromanager tif sequences a
pip install ndbioimage
```
### Installation with option to write mp4 or mkv:
Work in progress!
```
pip install ndbioimage[write]
```
## Usage
### Python
- Reading an image file and plotting the frame at channel=2, time=1
@@ -57,6 +65,12 @@ with Imread('image_file.tif', axes='cztyx') as im:
array = np.asarray(im[0, 0])
```
### Command line
```ndbioimage --help```: show help
```ndbioimage image```: show metadata about image
```ndbioimage image {name}.tif -r```: copy image into image.tif (replacing {name} with image), while registering channels
```ndbioimage image image.mp4 -C cyan lime red``` copy image into image.mp4 (z will be max projected), make channel colors cyan lime and red
## Adding more formats
Readers for image formats subclass AbstractReader. When an image reader is imported, Imread will
automatically recognize it and use it to open the appropriate file format. Image readers

View File

@@ -22,7 +22,7 @@ from numpy.typing import ArrayLike, DTypeLike
from ome_types import OME, from_xml, model, ureg
from pint import set_application_registry
from tiffwrite import IFD, IJTiffFile # noqa
from tqdm.auto import tqdm
from tqdm.auto import tqdm, trange
from .jvm import JVM, JVMException
from .transforms import Transform, Transforms
@@ -959,12 +959,52 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, ABC):
warnings.warn('Imread.new has been deprecated, use Imread.view instead.', DeprecationWarning, 2)
return self.view(*args, **kwargs)
def save_as_movie(self, fname: Path | str = None,
c: int | Sequence[int] = None, z: int | Sequence[int] = None, # noqa
t: int | Sequence[int] = None, # noqa
colors: tuple[str] = None, brightnesses: tuple[float] = None,
scale: int = None) -> None:
""" saves the image as a mp4 or mkv file """
from matplotlib.colors import to_rgb
from skvideo.io import FFmpegWriter
def get_ab(tyx: Imread, p: tuple[float, float] = (1, 99)) -> tuple[float, float]:
s = tyx.flatten()
s = s[s > 0]
a, b = np.percentile(s, p)
if a == b:
a, b = np.min(s), np.max(s)
if a == b:
a, b = 0, 1
return a, b
def cframe(frame: ArrayLike, color: str, a: float, b: float, scale: float = 1) -> np.ndarray: # noqa
color = to_rgb(color)
frame = (frame - a) / (b - a)
frame = np.dstack([255 * frame * i for i in color])
return np.clip(np.round(frame), 0, 255).astype('uint8')
ab = list(zip(*[get_ab(i) for i in self.transpose('cztyx')]))
colors = colors or ('r', 'g', 'b')[:self.shape['c']] + max(0, self.shape['c'] - 3) * ('w',)
brightnesses = brightnesses or (1,) * self.shape['c']
scale = scale or 1
with FFmpegWriter(
str(fname).format(name=self.path.stem, path=str(self.path.parent)),
outputdict={'-vcodec': 'libx264', '-preset': 'veryslow', '-pix_fmt': 'yuv420p', '-r': '7',
'-vf': f'setpts={25 / 7}*PTS,'
f'scale={self.shape["x"] * scale}:{self.shape["y"] * scale}:flags=neighbor'}
) as movie:
im = self.transpose('tzcyx')
for t in trange(self.shape['t'], desc='Saving movie'):
movie.writeFrame(np.max([cframe(yx, c, a, b / s, scale)
for yx, a, b, c, s in zip(im[t].max('z'), *ab, colors, brightnesses)], 0))
def save_as_tiff(self, fname: Path | str = None, c: int | Sequence[int] = None, z: int | Sequence[int] = None,
t: int | Sequence[int] = None, split: bool = False, bar: bool = True, pixel_type: str = 'uint16',
**kwargs: Any) -> None:
""" saves the image as a tif file
split: split channels into different files """
fname = Path(fname)
fname = Path(str(fname).format(name=self.path.stem, path=str(self.path.parent)))
if fname is None:
fname = self.path.with_suffix('.tif')
if fname == self.path:
@@ -1242,7 +1282,7 @@ class AbstractReader(Imread, metaclass=ABCMeta):
def main() -> None:
parser = ArgumentParser(description='Display info and save as tif')
parser.add_argument('file', help='image_file')
parser.add_argument('out', help='path to tif out', type=str, default=None, nargs='?')
parser.add_argument('out', help='path to tif/movie out', type=str, default=None, nargs='?')
parser.add_argument('-o', '--extract_ome', help='extract ome to xml file', action='store_true')
parser.add_argument('-r', '--register', help='register channels', action='store_true')
parser.add_argument('-c', '--channel', help='channel', type=int, default=None)
@@ -1250,6 +1290,10 @@ def main() -> None:
parser.add_argument('-t', '--time', help='time', type=int, default=None)
parser.add_argument('-s', '--split', help='split channels', action='store_true')
parser.add_argument('-f', '--force', help='force overwrite', action='store_true')
parser.add_argument('-C', '--movie-colors', help='colors for channels in movie', type=str, nargs='*')
parser.add_argument('-B', '--movie-brightnesses', help='scale brightness of each channel',
type=float, nargs='*')
parser.add_argument('-S', '--movie-scale', help='upscale movie xy size, int', type=int)
args = parser.parse_args()
with Imread(args.file) as im:
@@ -1261,6 +1305,9 @@ def main() -> None:
out.parent.mkdir(parents=True, exist_ok=True)
if out.exists() and not args.force:
print(f'File {args.out} exists already, add the -f flag if you want to overwrite it.')
elif out.suffix in ('.mkv', '.mp4'):
im.save_as_movie(out, args.channel, args.zslice, args.time,
args.movie_colors, args.movie_brightnesses, args.movie_scale)
else:
im.save_as_tiff(out, args.channel, args.zslice, args.time, args.split)
if args.extract_ome:

View File

@@ -1,12 +1,12 @@
import multiprocessing
from abc import ABC
from multiprocessing import queues
from traceback import format_exc
from pathlib import Path
from traceback import format_exc
import numpy as np
from .. import AbstractReader, JVM, JVMException
from .. import JVM, AbstractReader, JVMException
jars = {'bioformats_package.jar': 'https://downloads.openmicroscopy.org/bio-formats/latest/artifacts/'
'bioformats_package.jar'}

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "ndbioimage"
version = "2024.5.1"
version = "2024.5.2"
description = "Bio image reading, metadata and some affine registration."
authors = ["W. Pomp <w.pomp@nki.nl>"]
license = "GPLv3"
@@ -32,10 +32,13 @@ SimpleITK-SimpleElastix = [
scikit-image = "*"
imagecodecs = "*"
xsdata = "^23" # until pydantic is up-to-date
matplotlib = { version = "*", optional = true }
scikit-video = { version = "*", optional = true }
pytest = { version = "*", optional = true }
[tool.poetry.extras]
test = ["pytest"]
write = ["matplotlib", "scikit-video"]
[tool.poetry.scripts]
ndbioimage = "ndbioimage:main"