| 
 | 1 | +import os  | 
 | 2 | + | 
 | 3 | +import matplotlib.pyplot as plt  | 
 | 4 | +import numpy as np  | 
 | 5 | +import pandas as pd  | 
 | 6 | + | 
 | 7 | +from flamingo_tools.s3_utils import create_s3_target, BUCKET_NAME  | 
 | 8 | + | 
 | 9 | + | 
 | 10 | +def _gaussian_kernel1d(sigma_pix, truncate=4.0):  | 
 | 11 | +    r = int(truncate * sigma_pix + 0.5)  | 
 | 12 | +    x = np.arange(-r, r + 1, dtype=float)  | 
 | 13 | +    k = np.exp(-(x**2) / (2 * sigma_pix**2))  | 
 | 14 | +    k /= k.sum()  | 
 | 15 | +    return k  | 
 | 16 | + | 
 | 17 | + | 
 | 18 | +def density_1d_simple(x, window):  | 
 | 19 | +    x = np.asarray(x, dtype=float)  | 
 | 20 | +    xmin, xmax = x.min(), x.max()  | 
 | 21 | + | 
 | 22 | +    # Fixed grid (kept internal for simplicity)  | 
 | 23 | +    nbins = 256  | 
 | 24 | +    edges = np.linspace(xmin, xmax, nbins + 1)  | 
 | 25 | +    centers = 0.5 * (edges[:-1] + edges[1:])  | 
 | 26 | +    dx = edges[1] - edges[0]  | 
 | 27 | + | 
 | 28 | +    # Bin counts, smooth with Gaussian, convert to density  | 
 | 29 | +    counts, _ = np.histogram(x, bins=edges, density=False)  | 
 | 30 | +    sigma_pix = max(window / dx, 1e-9)  | 
 | 31 | +    k = _gaussian_kernel1d(sigma_pix)  | 
 | 32 | +    counts_smooth = np.convolve(counts, k, mode="same")  | 
 | 33 | +    density = counts_smooth / dx  | 
 | 34 | + | 
 | 35 | +    return centers, density  | 
 | 36 | + | 
 | 37 | + | 
 | 38 | +def open_tsv(fs, path):  | 
 | 39 | +    s3_path = os.path.join(BUCKET_NAME, path)  | 
 | 40 | +    with fs.open(s3_path, "r") as f:  | 
 | 41 | +        table = pd.read_csv(f, sep="\t")  | 
 | 42 | +    return table  | 
 | 43 | + | 
 | 44 | + | 
 | 45 | +def analyze_cochlea(cochlea, plot=False):  | 
 | 46 | +    fs = create_s3_target()  | 
 | 47 | +    seg_name = "SGN_v2"  | 
 | 48 | + | 
 | 49 | +    table_path = f"tables/{seg_name}/default.tsv"  | 
 | 50 | +    table = open_tsv(fs, os.path.join(cochlea, table_path))  | 
 | 51 | + | 
 | 52 | +    component_ids = [1]  | 
 | 53 | +    table = table[table.component_labels.isin(component_ids)]  | 
 | 54 | + | 
 | 55 | +    window_size = 200.0  | 
 | 56 | +    grid, density = density_1d_simple(table["length[µm]"], window=window_size)  # window in same units as x  | 
 | 57 | + | 
 | 58 | +    if plot:  | 
 | 59 | +        plt.figure(figsize=(6, 3))  | 
 | 60 | +        plt.plot(grid, density, lw=2)  | 
 | 61 | +        plt.xlabel("Length [µm]")  | 
 | 62 | +        plt.ylabel("Density [SGN/µm]")  | 
 | 63 | +        plt.tight_layout()  | 
 | 64 | +        plt.show()  | 
 | 65 | +    else:  | 
 | 66 | +        return grid, density  | 
 | 67 | + | 
 | 68 | + | 
 | 69 | +def check_implementation():  | 
 | 70 | +    cochlea = "G_EK_000049_L"  | 
 | 71 | +    analyze_cochlea(cochlea, plot=True)  | 
 | 72 | + | 
 | 73 | + | 
 | 74 | +def compare_gerbil_cochleae():  | 
 | 75 | +    # We need the tonotopic mapping for G_EK_000233_L  | 
 | 76 | +    # cochleae = ["G_EK_000233_L", "G_EK_000049_L", "G_EK_000049_R"]  | 
 | 77 | +    cochleae = ["G_EK_000049_L", "G_EK_000049_R"]  | 
 | 78 | + | 
 | 79 | +    plt.figure(figsize=(6, 3))  | 
 | 80 | +    for cochlea in cochleae:  | 
 | 81 | +        grid, density = analyze_cochlea(cochlea, plot=False)  | 
 | 82 | +        plt.plot(grid, density, lw=2, label=cochlea)  | 
 | 83 | + | 
 | 84 | +    plt.xlabel("Length [µm]")  | 
 | 85 | +    plt.ylabel("Density [SGN/µm]")  | 
 | 86 | +    plt.legend()  | 
 | 87 | +    plt.tight_layout()  | 
 | 88 | +    plt.show()  | 
 | 89 | + | 
 | 90 | + | 
 | 91 | +def main():  | 
 | 92 | +    # check_implementation()  | 
 | 93 | +    compare_gerbil_cochleae()  | 
 | 94 | + | 
 | 95 | + | 
 | 96 | +if __name__ == "__main__":  | 
 | 97 | +    main()  | 
0 commit comments