diff --git a/lumicks/pylake/tests/test_imaging_confocal/test_kymo_plotting.py b/lumicks/pylake/tests/test_imaging_confocal/test_kymo_plotting.py index c09be8545..fb71a0c4a 100644 --- a/lumicks/pylake/tests/test_imaging_confocal/test_kymo_plotting.py +++ b/lumicks/pylake/tests/test_imaging_confocal/test_kymo_plotting.py @@ -139,3 +139,97 @@ def test_plot_with_lf_force(kymo_h5_file): with pytest.raises(RuntimeError, match="Desired force channel 1x not available in h5 file"): kymo.plot_with_force("1x", "red") + +def test_plotting_with_histograms(test_kymo): + import matplotlib as mpl + + mpl.use("macosx") + import numpy as np + import matplotlib.pyplot as plt + + def get_rectangle_data(): + widths = [p.get_width() for p in plt.gca().patches] + heights = [p.get_height() for p in plt.gca().patches] + return widths, heights + + kymo, ref = test_kymo + image = ref.image[:, :, 0] + n_pos, n_time = image.shape + + pixelsize = ref.metadata.pixelsize_um[0] + linetime = ref.timestamps.line_time_seconds + + # position, no binning + kymo.plot_with_position_histogram(color_channel="red", pixels_per_bin=1) + counts = image.sum(axis=1) + w, h = get_rectangle_data() + + np.testing.assert_allclose(h, pixelsize) + np.testing.assert_equal(w, counts) + plt.close("all") + + # time, no binning + kymo.plot_with_time_histogram(color_channel="red", pixels_per_bin=1) + w, h = get_rectangle_data() + counts = image.sum(axis=0) + np.testing.assert_allclose(w, linetime) + np.testing.assert_equal(h, counts) + plt.close("all") + + px_per_bin = 3 + n_full_bins = n_pos // px_per_bin + with pytest.warns( + UserWarning, + match=( + f"{n_pos} pixels is not divisible by {px_per_bin}, final bin only " + f"contains {(leftover_pixels := n_pos % px_per_bin)} pixels" + ), + ): + kymo.plot_with_position_histogram(color_channel="red", pixels_per_bin=3) + w, h = get_rectangle_data() + + counts_full = ( + image[: (xx := n_full_bins * px_per_bin)].reshape((n_full_bins, -1)).sum(axis=1) + ) + counts_partial = image[xx:].sum() + counts = np.hstack((counts_full, counts_partial)) + + bin_widths = np.hstack( + ([pixelsize * px_per_bin] * n_full_bins, pixelsize * leftover_pixels) + ) + + np.testing.assert_allclose(h, bin_widths) + np.testing.assert_equal(w, counts) + plt.close("all") + + n_full_bins = n_time // px_per_bin + with pytest.warns( + UserWarning, + match=( + f"{n_time} pixels is not divisible by {px_per_bin}, final bin only " + f"contains {(leftover_pixels := n_time % px_per_bin)} pixels" + ), + ): + kymo.plot_with_time_histogram(color_channel="red", pixels_per_bin=3) + w, h = get_rectangle_data() + + counts_full = ( + image[:, : (xx := n_full_bins * px_per_bin)] + .reshape((-1, n_full_bins), order="F") + .sum(axis=0) + ) + counts_partial = image[:, xx:].sum() + counts = np.hstack((counts_full, counts_partial)) + + bin_widths = np.hstack( + ([linetime * px_per_bin] * n_full_bins, linetime * leftover_pixels) + ) + np.testing.assert_allclose(w, bin_widths) + np.testing.assert_equal(h, counts) + plt.close("all") + + with pytest.raises(ValueError, match="bin size is larger than the available pixels"): + kymo.plot_with_position_histogram(color_channel="red", pixels_per_bin=6) + + with pytest.raises(ValueError, match="bin size is larger than the available pixels"): + kymo.plot_with_time_histogram(color_channel="red", pixels_per_bin=12)