Skip to content

Commit

Permalink
Add export_image() function in addition to save_image()
Browse files Browse the repository at this point in the history
  • Loading branch information
sitic committed May 24, 2024
1 parent bf60938 commit e69f63f
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 7 deletions.
4 changes: 4 additions & 0 deletions optimap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
load_mask,
save_image,
save_mask,
export_image,
show_image,
show_mask,
)
Expand All @@ -40,6 +41,7 @@
from .video import (
export_video,
export_videos,
export_video_with_overlay,
load_metadata,
load_video,
save_image_sequence,
Expand Down Expand Up @@ -86,11 +88,13 @@
"show_video_overlay",

"save_image",
"export_image",
"save_image_sequence",
"save_mask",
"save_video",
"export_video",
"export_videos",
"export_video_with_overlay",

"extract_traces",
"compare_traces",
Expand Down
3 changes: 2 additions & 1 deletion optimap/image/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Functions for loading, saving, and displaying images, and for creating masks."""

from ._core import collage, load_image, load_mask, save_image, save_mask, show_image, smooth_gaussian
from ._core import collage, load_image, load_mask, save_image, export_image, save_mask, show_image, smooth_gaussian
from ._edit import normalize, crop, flip_left_right, flip_up_down, pad, resize, rotate_left, rotate_right
from ._mask import (
background_mask,
Expand All @@ -22,6 +22,7 @@
"show_image",
"show_mask",
"save_image",
"export_image",
"save_mask",
"load_image",
"load_mask",
Expand Down
47 changes: 46 additions & 1 deletion optimap/image/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import cv2
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from scipy import ndimage
Expand Down Expand Up @@ -209,7 +210,16 @@ def save_mask(filename, mask, image=None, **kwargs):


def save_image(filename, image: np.ndarray, compat=False, **kwargs):
"""Export an image to a file. The file format is inferred from the filename extension.
"""Save an image to a file. Makes best effort to avoid data precision loss, use {func}`export_image` to export images for publications.
The file format is inferred from the filename extension.
The following file formats and image data types are supported:
* NumPy: .npy, all data types
* PNG: .png, 8-bit or 16-bit unsigned per image channel
* TIFF: .tif/.tiff, 8-bit unsigned, 16-bit unsigned, 32-bit float, or 64-bit float images
* JPEG: .jpeg/.jpg, 8-bit unsigned
* Windows bitmaps: .bmp, 8-bit unsigned
Uses :func:`numpy.save` internally if the file extension is ``.npy`` and :func:`cv2.imwrite` otherwise.
Expand Down Expand Up @@ -240,6 +250,41 @@ def save_image(filename, image: np.ndarray, compat=False, **kwargs):
cv2.imwrite(str(fn), image, **kwargs)


def export_image(filename,
image: np.ndarray,
cmap = "gray",
vmin : float = None,
vmax : float = None):
"""Export an image to a file for publications, use {func}`save_image` to save an image if it will be reimported later.
Images will be converted to uint8, colormap will be applied to grayscale images.
The file format is inferred from the filename extension.
Parameters
----------
filename : str or pathlib.Path
Path to save image to
image : np.ndarray
Image to save HxW or HxWxC
cmap : str or matplotlib.colors.Colormap, optional
Colormap to use for grayscale images, by default "gray"
vmin : float, optional
Minimum value for the colormap, by default None
vmax : float, optional
Maximum value for the colormap, by default None
"""
if isinstance(cmap, str):
cmap = plt.get_cmap(cmap)
norm = Normalize(vmin=vmin, vmax=vmax, clip=True)
if image.ndim == 2:
image = cmap(norm(image))

if image.dtype != np.uint8:
image = (image * 255).astype(np.uint8)
save_image(filename, image)


def smooth_gaussian(image, sigma, **kwargs):
"""Smooth an image or mask using a Gaussian filter.
Expand Down
30 changes: 25 additions & 5 deletions tests/test_image.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
from pathlib import Path

import numpy as np
import matplotlib.pyplot as plt
import pytest

import optimap as om


def test_image_export(tmpdir):
def test_image_save(tmpdir):
img = np.random.rand(100, 100).astype(np.float32)

fn = tmpdir / "test.npy"
om.image.save_image(fn, img)
img2 = om.image.load_image(fn)
assert np.allclose(img, img2)

def test_16bit_export(tmpdir):
"""Test if 16-bit PNG and TIFF export works."""
def test_16bit_save(tmpdir):
"""Test if 16-bit PNG and TIFF save works."""
img = np.random.rand(100, 100).astype(np.float32)
img = (img * 65535).astype(np.uint16)

Expand All @@ -32,7 +33,7 @@ def test_16bit_export(tmpdir):
assert np.allclose(img, img2)


def test_compat_export(tmpdir):
def test_compat_save(tmpdir):
img = np.random.rand(100, 100).astype(np.float32)
img = (img * 65535).astype(np.uint16)

Expand All @@ -43,7 +44,26 @@ def test_compat_export(tmpdir):
assert img2.max() > 100


def test_mask_export(tmpdir):
def test_image_export(tmpdir):
img = np.random.rand(100, 100).astype(np.float32)

fn = tmpdir / "test.png"
om.image.export_image(fn, img)
img2 = om.image.load_image(fn)
assert img2.ndim == 3
assert img2.dtype == np.dtype("uint8")

fn = tmpdir / "test.jpg"
om.image.export_image(fn, img, vmin=0.1, vmax=0.9, cmap=plt.get_cmap("viridis"))

fn = tmpdir / "test.tiff"
om.image.export_image(fn, img, vmin=0.1, vmax=0.9, cmap="viridis")
img2 = om.image.load_image(fn)
assert img2.ndim == 3
assert img2.dtype == np.dtype("uint8")


def test_mask_save(tmpdir):
img = np.random.rand(100, 100).astype(np.float32)
mask = img > 0.5

Expand Down

0 comments on commit e69f63f

Please sign in to comment.