File size: 5,285 Bytes
b53e5c3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158


import lxml.etree as ET
import gzip
import tifffile
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image, ImageDraw
import pandas as pd


def get_paths_from_traces_file(traces_file):
    tree = ET.parse(traces_file)

    root = tree.getroot()
    all_paths = []
    path_lengths = []
    for path in root.findall('path'):
        length=path.get('reallength')
        path_points = []
        for point in path:
            path_points.append((int(point.get('x')), int(point.get('y')), int(point.get('z'))))
        all_paths.append(path_points)
        path_lengths.append(length)
    return all_paths, path_lengths

def visualise_ordering(points_list, dim):
    rdim, cdim, _ = dim
    vis = np.zeros((rdim, cdim, 3), dtype=np.uint8)

    def get_col(i):
        r = int(255 * i/len(points_list))
        g = 255 - r
        return r, g, 0

    for n, p in enumerate(points_list):
        c, r, _ = p
        wr, wc = 5, 5
        vis[max(0,r-wr):min(rdim,r+wr),max(0,c-wc):min(cdim,c+wc)] = get_col(n)

    return vis

col_map = [(255,0,0), (0,255,0), (0,0,255), (255,255,0), (255,0,255), (0,255,255)]

def draw_paths(all_paths, foci_stack):
    im = np.max(foci_stack, axis=0)
    im = (im/np.max(im)*255).astype(np.uint8)
    im = np.dstack((im,)*3)
    im = Image.fromarray(im) #.convert('RGB')
    draw = ImageDraw.Draw(im)
    for i, (p, col) in enumerate(zip(all_paths, col_map)):
        draw.line([(u[0], u[1]) for u in p], fill=col)
        draw.text((p[0][0], p[0][1]), str(i+1), fill=col)
    return im
    

# Sum of measure_stack over regin where mask==1
def measure_from_mask(mask, measure_stack):
    return np.sum(mask * measure_stack)

# Max of measure_stack over region where mask==1
def max_from_mask(mask, measure_stack):
    return np.max(mask * measure_stack)


# Translate mask to point p, treating makss near stack edges correctly
def make_mask_s(p, melem, measure_stack):
    mask = melem
    
    R = melem.shape[0] // 2
    r, c, z = p

    m_data = np.zeros(melem.shape)
    s = measure_stack.shape
    o_1, o_2, o_3 = max(R-r, 0), max(R-c, 0), max(R-z,0)
    e_1, e_2, e_3 = min(R-r+s[0], 2*R), min(R-c+s[1], 2*R), min(R-z+s[2], 2*R)
    m_data[o_1:e_1,o_2:e_2,o_3:e_3] = measure_stack[max(r-R,0):min(r+R,s[0]),max(c-R,0):min(c+R,s[1]),max(z-R,0):min(z+R, s[2])]
    return mask, m_data

# Measure the (mean/max) value of measure_stack about the point p, using
# the structuring element melem. op indicates the appropriate measurement (mean/max)
def measure_at_point(p, melem, measure_stack, op='mean'):
    if op=='mean':
        mask, m_data = make_mask_s(p, melem, measure_stack)
        melem_size = np.sum(melem)
        return float(measure_from_mask(mask, m_data) / melem_size)
    else:
        mask, m_data = make_mask_s(p, melem, measure_stack)
        return float(max_from_mask(mask, m_data))

# Generate spherical region
def make_sphere(R=5, z_scale_ratio=2.3):
    x, y, z = np.ogrid[-R:R, -R:R, -R:R]
    sphere = x**2 + y**2 + (z_scale_ratio * z)**2 < R**2
    return sphere

# Measure the values of measure_stack at each of the points of points_list in turn.
# Measurement is the mean / max (specified by op) on the spherical region about each point
def measure_all_with_sphere(points_list, measure_stack, op='mean'):
    melem = make_sphere()
    measure_func = lambda p: measure_at_point(p, melem, measure_stack, op)
    return list(map(measure_func, points_list))


# Measure fluorescence levels along ordered skeleton
def measure_chrom2(path, hei10):
    # single chrom - structure containing skeleton (single_chrom.skel) and
    # fluorecence levels (single_chrom.hei10) as Image3D objects (equivalent to ndarray)
    # Returns list of coordinates in skeleton, the ordered path 
    vis = visualise_ordering(path, dim=hei10.shape)
 
    measurements = measure_all_with_sphere(path, hei10, op='mean')
    measurements_max = measure_all_with_sphere(path, hei10, op='max')

    return vis, measurements, measurements_max

def extract_peaks(cell_id, all_paths, path_lengths, measured_traces):

    n = len(all_paths)
    
    
    #headers = ['Cell_ID', 'Trace', 'Trace_length(um)', 'detection_sphere_radius(um)', 'Foci_ID_threshold', 'Foci_per_trace']
    #for i in range(max_n):
    #    headers += [f'Foci{i}_relative_intensity', f'Foci_{i}_position(um)']

    data_dict = {}
    data_dict['Cell_ID'] = [cell_id]*n
    data_dict['Trace'] = range(1, n+1)
    data_dict['Trace_length(um)'] = path_lengths
    data_dict['Detection_sphere_radius(um)'] = [0.2]*n
    data_dict['Foci_ID_threshold'] = [0.4]*n

    
        
    return pd.DataFrame(data_dict)


def analyse_paths(cell_id, foci_file, traces_file):
    foci_stack = tifffile.imread(foci_file)
    all_paths, path_lengths = get_paths_from_traces_file(traces_file)

    all_trace_vis = []
    all_m = []
    for p in all_paths:
        vis, m, _ = measure_chrom2(p,foci_stack.transpose(2,1,0))
        all_trace_vis.append(vis)
        all_m.append(m)

    trace_overlay = draw_paths(all_paths, foci_stack)

    fig, ax = plt.subplots(len(all_paths),1)
    for i, m in enumerate(all_m):
        ax[i].plot(m)

    extracted_peaks = extract_peaks(cell_id, all_paths, path_lengths, all_m)
    
    return trace_overlay, all_trace_vis, fig, extracted_peaks