Source code for sarcasm.structure_modules.kymograph

# -*- coding: utf-8 -*-
# Copyright (c) 2025 University Medical Center Göttingen, Germany.
# All rights reserved.
#
# Patent Pending: DE 10 2024 112 939.5
# SPDX-License-Identifier: LicenseRef-Proprietary-See-LICENSE
#
# This software is licensed under a custom license. See the LICENSE file
# in the root directory for full details.
#
# **Commercial use is prohibited without a separate license.**
# Contact MBM ScienceBridge GmbH (https://sciencebridge.de/en/) for licensing.

"""Kymograph generation module."""

from multiprocessing import Pool
import numpy as np
from scipy import ndimage


[docs] def kymograph_movie(movie: np.ndarray, line: np.ndarray, linewidth: int = 10, order: int = 0): """ Generate a kymograph using multiprocessing. Parameters -------- movie : np.ndarray, shape (N, H, W) The movie. line : np.ndarray, shape (N, 2) The coordinates of the segmented line (N>1) linewidth : int, optional Width of the scan in pixels, perpendicular to the line order : int in {0, 1, 2, 3, 4, 5}, optional The order of the spline interpolation, default is 0 if image.dtype is bool and 1 otherwise. The order has to be in the range 0-5. See `skimage.transform.warp` for detail. Return --------- return_value : ndarray Kymograph along segmented line Notes ------- Adapted from scikit-image (https://scikit-image.org/docs/0.22.x/api/skimage.measure.html#skimage.measure.profile_line). """ # prepare coordinates of segmented line perp_lines = curved_line_profile_coordinates(points=line, linewidth=linewidth) # Prepare arguments for each frame args = [(movie[frame], perp_lines, linewidth, order) for frame in range(movie.shape[0])] # Create a Pool and map process_frame to each frame with Pool() as pool: results = pool.map(process_frame, args) # Convert list of results to a numpy array kymograph = np.array(results) return kymograph
[docs] def process_frame(args): """Process a single frame for kymograph generation.""" frame, perp_lines, linewidth, order = args pixels = ndimage.map_coordinates(frame, perp_lines, prefilter=order > 1, order=order, mode='reflect', cval=0.0) pixels = np.flip(pixels, axis=1) intensities = np.mean(pixels, axis=1) return intensities
[docs] def curved_line_profile_coordinates(points: np.ndarray, linewidth: int = 10): """ Calculate the coordinates of a curved line profile composed of multiple segments with specified linewidth. Parameters ---------- points : np.ndarray A list of points (y, x) defining the segments of the curved line. linewidth : int, optional The width of the line in pixels. Returns ------- coords : ndarray The coordinates of the curved line profile. Shape is (2, N, linewidth), where N is the total number of points in the line. """ all_perp_rows = [] all_perp_cols = [] for i in range(len(points) - 1): src, dst = np.asarray(points[i], dtype=float), np.asarray(points[i + 1], dtype=float) d_row, d_col = dst - src theta = np.arctan2(d_row, d_col) length = int(np.ceil(np.hypot(d_row, d_col) + 1)) line_col = np.linspace(src[1], dst[1], length) line_row = np.linspace(src[0], dst[0], length) col_width, row_width = (linewidth - 1) * np.sin(-theta) / 2, (linewidth - 1) * np.cos(theta) / 2 perp_rows = np.stack([np.linspace(row - row_width, row + row_width, linewidth) for row in line_row]) perp_cols = np.stack([np.linspace(col - col_width, col + col_width, linewidth) for col in line_col]) all_perp_rows.append(perp_rows) all_perp_cols.append(perp_cols) # Concatenate all segments final_perp_rows = np.concatenate(all_perp_rows, axis=0) final_perp_cols = np.concatenate(all_perp_cols, axis=0) return np.stack([final_perp_rows, final_perp_cols])