1
+ % [out, res] = calcDistFromCellEdgeIF(data, varargin) calculates and plots
2
+ % distance and amplitude distributions from the cell edge for spot signals in
3
+ % all channels selected in the input
4
+ %
5
+ % Input:
6
+ % data : structure with fields
7
+ % .channels : cell array of paths to TIFF files
8
+ % .results : path to results file
9
+ %
10
+ % Options:
11
+ % 'Channels' : channel indexes to analyze. Default: [2 3]
12
+ % 'BinSize' : histogram bin size, in pixels. Default: 5
13
+ %
14
+ % Output:
15
+ % out : structure with fields
16
+ % .dist: distance to cell edge
17
+ % .A: amplitude
18
+ % .dfeHists: distance from edge histogram
19
+ % .ampHists: amplitude histogram (as a function of distance)
20
+ % .dfeHistMean_bc: average of bias-corrected dfeHists
21
+ % .dfeHistSD_bc: s.d. of bias-corrected dfeHists
22
+ % These are the values plotted
23
+ % .binc: histogram bin center coordinates
24
+ % .ampHistMean_bc: average of bias-corrected ampHists
25
+ % .ampHistSD_bc: s.d. of bias-corrected ampHists
26
+ %
27
+ % Note: 'dist' and 'A' are cell arrays of values for each channel
28
+ %
29
+ %
30
+ % res : structure saved by processFramesIF(), with additional field
31
+ % .ex : coordinates (Nx2) of the cell edge
32
+
33
+ % Francois Aguet, 01/2014
34
+
35
+ function [out , res ] = calcDistFromCellEdgeIF(data , varargin )
36
+
37
+ ip = inputParser ;
38
+ ip.CaseSensitive = false ;
39
+ ip .addRequired(' data' );
40
+ ip .addParameter(' BinSize' , 5 , @isposint );
41
+ ip .addParameter(' Channels' , [2 3 ]);
42
+ ip .addParameter(' Display' , true , @islogical );
43
+ ip .addParameter(' Axis' , []);
44
+ ip .addParameter(' Name' , ' ' );
45
+ ip .addParameter(' Hues' , []);
46
+ ip .addParameter(' Names' , []);
47
+ ip .addParameter(' Normalized' , true , @islogical );
48
+ ip .addParameter(' DisplayPlot' ,true , @islogical );
49
+ ip .addParameter(' DisplayMode' , ' screen' );
50
+ ip .addParameter(' PrintFolder' , []);
51
+ ip .addParameter(' PixelSize' , 0.065 , @isscalar );
52
+ ip .parse(data , varargin{: });
53
+ chIdx = ip .Results .Channels ;
54
+ nc = numel(chIdx );
55
+
56
+ nd = numel(data );
57
+
58
+ hues = ip .Results .Hues ;
59
+ if isempty(hues )
60
+ hues = [0.0 0.3 ];
61
+ end
62
+
63
+
64
+ % load data sets
65
+ for i = 1 : nd
66
+ res(i ) = load(data(i ).results);
67
+ end
68
+
69
+ [ny ,nx ] = arrayfun(@(i ) size(i .mask ), res );
70
+ xv = 0 : ip .Results .BinSize : max([nx ny ]/2 );
71
+ binc = ip .Results .BinSize / 2 : ip .Results .BinSize : xv(end );
72
+
73
+ odist = cell(max(chIdx ),nd ); % distance to cell edge
74
+ oA = cell(max(chIdx ),nd ); % distance to cell edge
75
+ odfeHists= cell(max(chIdx ),nd );
76
+ oampHists= cell(max(chIdx ),nd );
77
+
78
+ parfor i = 1 : nd
79
+ [oodist ,ooA ,oodfeHists ,ooampHists ]=compDist(res(i ),i ,chIdx ,ip .Results ,xv );
80
+
81
+ odist(: ,i )=oodist ;
82
+ oA(: ,i )=ooA ;
83
+ odfeHists(: ,i )=oodfeHists ;
84
+ oampHists(: ,i )=ooampHists ;
85
+ end
86
+ out.dist= odist ;
87
+ out.A= oA ;
88
+
89
+ for c = chIdx
90
+
91
+ out.dfeHists{c }=vertcat(odfeHists{c ,: });
92
+ out.ampHists{c }=vertcat(oampHists{c ,: });
93
+
94
+ % correct for observation bias (cell size-dependent), same for all hists/channel
95
+ w = sum(out.dfeHists{c }~=0 ,1 );
96
+
97
+ M = out.dfeHists{c }./repmat(w ,[nd 1 ])*nd ;
98
+ out.dfeHistsNorm{c }=double(out.dfeHists{c }~=0 );
99
+ mu = mean(M ,1 );
100
+ s = std(M ,[],1 )/sqrt(nd );
101
+ nidx = isnan(mu );
102
+ mu(nidx ) = [];
103
+ s(nidx ) = [];
104
+
105
+ out.dfeHistMean_bc{c } = mu ;
106
+ out.s{c } = s ;
107
+ out.binc{c } = binc(~nidx );
108
+
109
+ % amplitude
110
+ M = out.ampHists{c }./repmat(w ,[nd 1 ])*nd ;
111
+ out.ampHistsNorm{c }=M ;
112
+ mu = mean(M ,1 );
113
+ s = std(M ,[],1 )/sqrt(nd );
114
+ nidx = isnan(mu );
115
+ mu(nidx ) = [];
116
+ s(nidx ) = [];
117
+ out.distBins= xv ;
118
+ out.ampHistMean_bc{c } = mu ;
119
+ out.ampHistSD_bc{c } = s ;
120
+
121
+ end
122
+
123
+ %% Save excel file
124
+
125
+
126
+ if ip .Results .Display
127
+ % #objects/cell as a function of distance from cell edge
128
+ setupFigure(' DisplayMode' , ip .Results .DisplayMode , ' Name' , ip .Results .Name );
129
+ if (~ip .Results .DisplayPlot )
130
+ set(gcf(),' Visible' ,' off' )
131
+ end
132
+
133
+ hp = zeros(1 ,nc );
134
+ for c = 1 : nc
135
+ ch = chIdx(c );
136
+ xi = out.binc{ch }*ip .Results .PixelSize ;
137
+ mu = out.dfeHistMean_bc{ch };
138
+ s = out.s{ch };
139
+
140
+ fill([xi xi(end : -1 : 1 )], [mu + s mu(end : -1 : 1 )-s(end : -1 : 1 )], hsv2rgb([hues(c ) 0.5 1 ]),...
141
+ ' EdgeColor' , ' none' );
142
+ hp(c ) = plot(xi , mu , ' Color' , hsv2rgb([hues(c ) 1 0.9 ]), ' LineWidth' , 1.5 );
143
+ end
144
+
145
+ xlabel([' Distance from cell edge (' char(181 ) ' m)' ]);
146
+ if ip .Results .Normalized
147
+ ylabel(' Frequency (normalized)' );
148
+ else
149
+ ylabel(' # objects/cell' );
150
+ end
151
+
152
+ if ~isempty(ip .Results .Axis )
153
+ axis(ip.Results.Axis{1 });
154
+ end
155
+ if ~isempty(ip .Results .Names )
156
+ hl = legend(hp , ip.Results.Names{: }, ' Location' , ' NorthEast' );
157
+ set(hl , ' Box' , ' off' );
158
+ end
159
+ if (~isempty(ip .Results .PrintFolder ))
160
+ printPNGEPSFIG(gcf(),[ip .Results .PrintFolder filesep ], ' AvgObjectCounts' )
161
+ end
162
+
163
+ % Average spot amplitudes as a function of distance from cell edge
164
+ setupFigure(' DisplayMode' , ip .Results .DisplayMode , ' Name' , ip .Results .Name );
165
+ if (~ip .Results .DisplayPlot )
166
+ set(gcf(),' Visible' ,' off' )
167
+ end
168
+ for c = 1 : nc
169
+ ch = chIdx(c );
170
+ xi = out.binc{ch }*ip .Results .PixelSize ;
171
+ mu = out.ampHistMean_bc{ch };
172
+ s = out.ampHistSD_bc{ch };
173
+ fill([xi xi(end : -1 : 1 )], [mu + s mu(end : -1 : 1 )-s(end : -1 : 1 )], hsv2rgb([hues(c ) 0.5 1 ]),...
174
+ ' EdgeColor' , ' none' );
175
+ hp(c ) = plot(xi , mu , ' Color' , hsv2rgb([hues(c ) 1 0.9 ]), ' LineWidth' , 1.5 );
176
+ end
177
+ xlabel([' Distance from cell edge (' char(181 ) ' m)' ]);
178
+ ylabel(' Average intensity (A.U.)' );
179
+ if ~isempty(ip .Results .Axis )
180
+ axis(ip.Results.Axis{2 });
181
+ end
182
+ if ~isempty(ip .Results .Names )
183
+ hl = legend(hp , ip.Results.Names{: }, ' Location' , ' NorthEast' );
184
+ set(hl , ' Box' , ' off' );
185
+ end
186
+ if (~isempty(ip .Results .PrintFolder ))
187
+ printPNGEPSFIG(gcf(),[ip .Results .PrintFolder filesep ], ' AvgIntensities' )
188
+ end
189
+ end
190
+
191
+
192
+ function [oodist ,ooA ,oodfeHists ,ooampHists ]=compDist(resData ,dataIdx ,chIdx ,param ,xv )
193
+ se = strel(' disk' , 1 );
194
+
195
+ i= dataIdx ;
196
+ nc= length(chIdx );
197
+
198
+ [ny , nx ] = size(resData .mask );
199
+
200
+ % cell boundary (including image boundary)
201
+ B = bwboundaries(resData .mask );
202
+ B = vertcat(B{: }); % [x y] coordinates
203
+ bmask = false(ny ,nx );
204
+ bmask(sub2ind([ny nx ], B(: ,1 ), B(: ,2 ))) = true ;
205
+
206
+ % use only detected cell edge (as opposed to image boundary)
207
+ % for distance calculation
208
+ % emask: cell edge
209
+ % bmask: cell edge in image boundary
210
+ borderIdx = [1 : ny 2 * ny : ny : (nx - 1 )*ny nx * ny : -1 : (nx - 1 )*ny + 1 (nx - 2 )*ny + 1 : -ny : ny + 1 ];
211
+ borderMask = false(ny ,nx );
212
+ borderMask(borderIdx ) = true ;
213
+ emask = bmask ;
214
+ emask(borderIdx ) = false ;
215
+ emask = bwmorph(emask , ' skel' );
216
+ emask = emask | (imdilate(emask ,se ) & borderMask ); % add edge pixel(s) at border
217
+ bmask = bmask & ~emask ;
218
+
219
+ [by ,bx ] = find(bmask );
220
+ [ey ,ex ] = find(emask );
221
+ resData.ex = [ex ey ];
222
+
223
+ % distance transform of mask (for normalization)
224
+ D = bwdist(~resData .mask );
225
+ D2 = bwdist(~padarray(resData .mask , [1 1 ],0 ));
226
+ D2 = D2(2 : end - 1 ,2 : end - 1 );
227
+ D(D ~= D2 ) = 0 ;
228
+ D = D - 1 ;
229
+ D(D < 0) = 0 ;
230
+
231
+ % relative probability of observing specific distances from edge
232
+ tmp = D(D ~= 0 );
233
+ dfeNorm = histc(tmp(tmp <= xv(end )), xv );
234
+ dfeNorm = dfeNorm(1 : end - 1 );
235
+
236
+ if param .Normalized
237
+ dfeNorm = dfeNorm / sum(dfeNorm );
238
+ else
239
+ dfeNorm = dfeNorm / max(dfeNorm );
240
+ end
241
+
242
+ % dirty trick to make it //izable -- PR
243
+ nd= 1 ;
244
+ oodist = cell(max(chIdx ),nd ); % distance to cell edge
245
+ ooA = cell(max(chIdx ),nd ); % distance to cell edge
246
+ oodfeHists= cell(max(chIdx ),1 );
247
+ ooampHists= cell(max(chIdx ),1 );
248
+ for c = 1 : nc
249
+ % query cell boundary (free edge + image boundary)
250
+ [idx , dist ] = KDTreeBallQuery([ex ey ; bx by ], [resData .ps(chIdx(c )).x' resData .ps(chIdx(c )).y' ], max(nx ,ny )/2 );
251
+
252
+ % index of points with match;
253
+ % results are sorted by increasing distance -> retain first point only
254
+ edgeMatchIdx = ~cellfun(@isempty , idx );
255
+ idx = cellfun(@(i ) i(1 ), idx(edgeMatchIdx ));
256
+ dist = cellfun(@(i ) i(1 ), dist(edgeMatchIdx ));
257
+
258
+ % retain only points closer to cell edge than image boundary
259
+ dist = dist(idx <= numel(ex ));
260
+
261
+ oodist{chIdx(c ),1 } = dist ; % distance to cell edge
262
+ A = resData .ps(chIdx(c )).A(idx <= numel(ex ))' ;
263
+ ooA{chIdx(c ),1 } = A ; % amplitude of retained points
264
+
265
+ [dfeHist , hidx ] = histc(dist , xv );
266
+ dfeHist = dfeHist(1 : end - 1 )./ dfeNorm ;
267
+
268
+ dfeHist(isnan(dfeHist )) = 0 ;
269
+ if param .Normalized
270
+ dfeHist = dfeHist / sum(dfeHist );
271
+ end
272
+ oodfeHists{chIdx(c )}(1 ,: ) = dfeHist ;
273
+
274
+ % for each bin, calculate average amplitude
275
+ ahist = zeros(size(dfeHist ));
276
+ tmp = arrayfun(@(i ) mean(A(hidx == i )), unique(hidx ));
277
+ ahist(unique(hidx )) = tmp ;
278
+ ooampHists{chIdx(c )}(1 ,: ) = ahist ;
279
+ end
0 commit comments