-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscMST_Section1.m
419 lines (359 loc) · 17.7 KB
/
scMST_Section1.m
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
%-------------------------------------------------------------------------%
%-------------------------------------------------------------------------%
%-------------------------------------------------------------------------%
% Maintenance of pluripotency in entire post-gastrulation ectoderm enables neural crest formation
%
% Ceren Pajanoja, 2022
% The following script includes the code used to execute STEP-1 of scMST pipeline
%-------------------------------------------------------------------------%
%-------------------------------------------------------------------------%
%-------------------------------------------------------------------------%
%% 1) read in Ilastik file (for membrane staining, h5 format)
addpath(genpath('\path\to\data\folder\'));
ilastik_filename = 'SampleName_Probabilities.h5';% type membrane ilastik file name here
ilastik_file = h5read(ilastik_filename,'/exported_data/');
pred = squeeze(ilastik_file(2,:,:,:));
pred = permute(pred,[2,1,3]);
figure
imshow(sum(pred,3),[])
title('Ilastik Prediction Map View, z-Projection')
%% 2) read in the original membrane image (tiff format)
imagename_original = 'RGB.tif';
original_img = 0*pred;
for z = 1 : size(pred,3)
temp = imread(imagename_original,z);
original_img(:,:,z) = temp(:,:,1);
end
figure
imshow(sum(original_img,3),[])
title('Membrane Image, z-Projection')
%% 3) Blurred version of the membrane img
im_blurr = imdilate(original_img,strel3D('sphere',3));
figure
imshow(sum(im_blurr,3),[])
title('Blurred Membrane Image, z-Projection')
%% 4) Threshold the Ilastik map pixel value
seg = pred>0.99;
seg = bwareaopen(seg,50); % min size
seg = seg - bwareaopen(seg,3000000); % max size
figure
imshow(sum(single(seg),3),[])
title('Ilastik map pixel value thresholding, z-Projection')
%% 5) Watershed segmentation
seed = imimposemin(im_blurr,seg);
Label = watershed(seed);
%% 6) Size hreshold Label matrix and check in Volume viewer
vol_min = 50;
vol_max = 200000;
Label2 = bwareaopen(Label,vol_min); % min size
Label2 = Label2 - bwareaopen(Label2,vol_max); % max size
Label2 = bwlabeln(Label2);
stats = regionprops3(Label2);
volume = cat(1,stats.Volume);
%% 7) Save Label matrices to disk;
for z = 1 : size(Label2,3)
imwrite(Label2(:,:,z),'Label2_WatershedMatrix.tif','compression','none','WriteMode','append');
end
save('Label2_WatershedMatrix.mat','Label2');
%% Visually check the result of watershed segmentation slice by slice
for z = 1 : size(original_img,3)
temp = zeros(size(original_img,1),size(original_img,2),3,'uint8');
per = Label2(:,:,z) == 0; %
temp(:,:,1) = original_img(:,:,z); % original img on the back
temp(:,:,2) = uint8(per)*10; % label (segmentation lines)
imwrite(temp,'seg_RGB.tif','compression','none','WriteMode','append');
end
%% HYB ROUNDS ANALYSIS
%-------------------------------------------------------------------------%
% Below start analyzing dot (transcript) ilastik files
% and signal processing
%-------------------------------------------------------------------------%
%% 1) Scale image dimensions depending on the scope and objective used
AimedMag = 0.1135; % pixel size in microns used to analyse the data;
nChan = 5; % number of channels used
hybnum = 2; %remember to change here !!
somite = 7; %number of somites for the embryo
sample = 'e5-RightTop-3';
scope = 'dragonfly2';%'olympus';
imageName_allDots = '280621_7som_hyb2_e5-RightTop-3.ome.tif';
scaleFactorX = 1; % or AimedMag/dx;
scaleFactorZ = 1;
% settings of the two different types of images. These values might need to
% be changed, depending on the acquisition used.
if strcmp(scope,'dragonfly')
mag = 63;
pixelSize = 0.184; % in microns;
dx = pixelSize/mag;
dz = 0.356; % in microns
elseif strcmp(scope,'dragonfly2')
mag = 63;
pixelSize = 0.155; % in microns
dx = pixelSize/mag;
dz = 0.356; % in microns
end
stackSize = (length(imfinfo(imageName_allDots)))/nChan;
im_dots = cell(1,nChan); % pre-allocating
for c = 1:nChan
for z = 1:stackSize
if c < 6
im_dots{c}(:,:,z) = (double(imread(imageName_allDots,c+(z-1)*nChan)));
else
im_dots{c}(:,:,z) = double(imread(imageName_allDots,c+(z-1)*nChan));
end
end
end
% scale the image to the Aimed PixelSize;
im_dots_scaled = cell(size(im_dots));
[X,Y] = meshgrid(1:scaleFactorX:size(im_dots{1},1),1:scaleFactorX:size(im_dots{1},2));
newNSlices = round(size(im_dots{1},3)/scaleFactorZ);
for c = 1 : nChan
im_dots_scaled{c} = imresize(im_dots{c},1/scaleFactorX);
scaled = imresize(permute(im_dots_scaled{c},[3,1,2]),[newNSlices,size(permute(im_dots_scaled{c},[3,1,2]),2)]);
im_dots_scaled{c} = ipermute(scaled,[3,1,2]);
end
%% 1.extra) Write scaled images to disc
% separates channels and saves individually
%-----------------------------------------------------------
% IMPORTANT: Run this only ONE TIME for image with multiple channels
for c = 1 : nChan
for z = 1 : size(im_dots_scaled{c},3)
imwrite(uint16(im_dots_scaled{c}(:,:,z)),sprintf('%dsom_hyb%d_%s_chan_%d.tif',somite,hybnum,sample,c),'tiff','compression','none','WriteMode','append');
end
end
%% 2) Read in membrane image (unbinned) this is RGB.
imagename_RGB = 'RGB.tif'; % write there the RGB image name
RGB_StackSize = size(original_img,3);
im_RGB = cell(1,2); % pre-allocating
for z = 1 : RGB_StackSize
temp = imread(imagename_RGB,z);
im_RGB{1}(:,:,z) = temp(:,:,1);
end
%% 3.1) Manual rotation from im_dots_scaled into im_RGB frame of reference
chan = 5; %choose a channel for alignment
angle = 0;
im_dots_rot = imrotate(im_dots_scaled{chan},angle,'crop');
[shift,image1,image2] = xcorr3fft(2*im_RGB{1}(:,:,:),uint8(255*mat2gray(im_dots_rot)));
%% 3.2)takes image2, and shifts it according to shift.
close all;
image2_shift = 0*image2; % pre-allocating
if shift(1)<0 && shift(2)>0
image2_shift(-shift(1)+(1:(size(image2,1)+shift(1))),shift(2)+(1:size(image2,2)-shift(2)),:) = ...
image2(1:(size(image2,1)+shift(1)),1:size(image2,2)-shift(2),:);
elseif shift(1)>0 && shift(2)<0
image2_shift((1:size(image2,1)-shift(1)),1:size(image2,2)+shift(2),:) = ...
image2((shift(1)+1):size(image2,1),-shift(2)+(1:size(image2,2)+shift(2)),:);
elseif shift(1)<=0 && shift(2)<=0
image2_shift(-shift(1)+(1:size(image2,1)+shift(1)),1:size(image2,2)+shift(2),:) = ...
image2((1:size(image2,1)+shift(1)),-shift(2)+(1:size(image2,2)+shift(2)),:);
elseif shift(1)>=0 && shift(2)>=0
image2_shift((1:size(image2,1)-shift(1)),shift(2)+(1:size(image2,2)-shift(2)),:) = ...
image2((shift(1)+1):size(image2,1),1:size(image2,2)-shift(2),:);
elseif shift(1) == 0 && shift(2) == 0
image2_shift = image2;
end
% visualize the result of the shift
tmp = zeros(size(image1,1),size(image1,2),3,'double');
tmp(:,:,1) = double(sum(image1,3))/size(image1,3); % (membrane) mean intensity projection
tmp(:,:,2) = (sum(double(image2_shift)/size(image2,3),3))*5; % green (shifted version of image2)
tmp = uint8(255*mat2gray(tmp));
imshow(tmp);
%type in command window -> shift = [x,y,z] in order to adjust the shift and run this section again
% x (+up, -down) & y (+right, -left)
%%
%---------------------------------------
% If alignment is good, move to step 4
%---------------------------------------
%
%% 4) Summarize the dots in each cell that were found using waterhed label
% First segment the dots using ilastk prediction map.
% Points: summarizes information about cells, such as Volume, centre of mass
% position (centroid), Mean Intensity in the segmentation of the dots,
% total intensity; To get the total number of spots multiply by volume;
points = struct('volume',[],'centroid',[],'MeanIntensity',[],'Intensity',[],...
'NSpots',[],'NSpotsinVol',[],'NSpots_Cleared',[],'NSpotsinVol_Cleared',[]);
%% 5) Run this for each channel
for chan = 1
ilastik_file_dots = h5read(sprintf('%dsom_hyb%d_%s_chan_%d_Probabilities.h5',somite,hybnum,sample,chan),'/exported_data/');
pred = squeeze(ilastik_file_dots(2,:,:,:));
pred = permute(pred,[2,1,3]);
image2 = pred>0.5; % threshold value(can be low typically .5 or .3)
image2 = imrotate(image2,angle,'crop'); % rotates the segmentation
si1 = size(image1);
si2 = size(image1);
desiredSize = max(si1,si2);
padded_image = zeros(desiredSize,'uint8');
padded_image(1:size(image2,1),1:size(image2,2),1:size(image2,3)) = image2;
image2 = padded_image;
%shift the segmentation;
image2_shift = 0*image2;
if shift(1)<0 && shift(2)>0
image2_shift(-shift(1)+(1:(size(image2,1)+shift(1))),shift(2)+(1:size(image2,2)-shift(2)),:) = ...
image2(1:(size(image2,1)+shift(1)),1:size(image2,2)-shift(2),:);
elseif shift(1)>0 && shift(2)<0
image2_shift((1:size(image2,1)-shift(1)),1:size(image2,2)+shift(2),:) = ...
image2((shift(1)+1):size(image2,1),-shift(2)+(1:size(image2,2)+shift(2)),:);
elseif shift(1)<=0 && shift(2)<=0
image2_shift(-shift(1)+(1:size(image2,1)+shift(1)),1:size(image2,2)+shift(2),:) = ...
image2((1:size(image2,1)+shift(1)),-shift(2)+(1:size(image2,2)+shift(2)),:);
elseif shift(1)>=0 && shift(2)>=0
image2_shift((1:size(image2,1)-shift(1)),shift(2)+(1:size(image2,2)-shift(2)),:) = ...
image2((shift(1)+1):size(image2,1),1:size(image2,2)-shift(2),:);
elseif shift(1) == 0 && shift(2) == 0
image2_shift = image2;
end
tmp = zeros(size(image1,1),size(image1,2),3,'double');
tmp(:,:,1) = double(sum(image1,3))/size(image1,3);
tmp(:,:,2) = (sum(double(mat2gray(image2_shift)*255)/size(image2,3),3))*.3;
tmp = uint8(tmp);
figure % visualize the shift
imshow(tmp);
stats1 = regionprops3(Label2(:,:,:),image2_shift,'centroid','volume','MeanIntensity','VoxelValues');
points(chan).volume = cat(1,stats1.Volume);
points(chan).centroid = cat(1,stats1.Centroid);
points(chan).MeanIntensity = cat(1,stats1.MeanIntensity);
points(chan).Intensity = points(chan).MeanIntensity.*points(chan).volume;
stats2 = regionprops3(Label2(:,:,:),im_dots_scaled{chan},'MeanIntensity');
points(chan).MeanDotIntensity = cat(1,stats2.MeanIntensity);
Label_dots = bwlabeln(1-image2_shift); % in the thresholded ilastik prediction map, which was rotated and shifted,
% we now look for connected groups of pixels. Each connected group gets a unique label, which we interpret as a hotspot.
stats1 = regionprops3(Label2(:,:,:),Label_dots,'centroid','volume','MeanIntensity','VoxelValues');
NSpots = zeros(size(stats1(:,1))); % pre-allocating
for z = 1 : height(stats1)
temp = stats1.VoxelValues{z,:}; % what labels are around in the cell k?
temp = unique(temp); % extract the unique label values
NSpots(z) = length(temp); % remove contribution from 0.
end
points(chan).NSpots = NSpots;
points(chan).NSpotsinVol = NSpots./cat(1,stats1.Volume);
tmp = imrotate(im_dots_scaled{chan},angle,'crop'); % rotate the original image
size1 = size(image1);
size2 = size(image1);
desiredSize = max(size1,size2);
padded_image = zeros(desiredSize,'uint16');
padded_image(1:size(image2,1),1:size(image2,2),1:size(image2,3)) = tmp;
tmp = padded_image;
tmp_shift = 0*tmp; % pre-allocating
% shifting the two images:
if shift(1)<0 && shift(2)>0
tmp_shift(-shift(1)+(1:(size(image2,1)+shift(1))),shift(2)+(1:size(image2,2)-shift(2)),:) = ...
tmp(1:(size(image2,1)+shift(1)),1:size(image2,2)-shift(2),:);
elseif shift(1)>0 && shift(2)<0
tmp_shift((1:size(image2,1)-shift(1)),1:size(image2,2)+shift(2),:) = ...
tmp((shift(1)+1):size(image2,1),-shift(2)+(1:size(image2,2)+shift(2)),:);
elseif shift(1)<=0 && shift(2)<=0
tmp_shift(-shift(1)+(1:size(image2,1)+shift(1)),1:size(image2,2)+shift(2),:) = ...
tmp((1:size(image2,1)+shift(1)),-shift(2)+(1:size(image2,2)+shift(2)),:);
elseif shift(1)>=0 && shift(2)>=0
tmp_shift((1:size(image2,1)-shift(1)),shift(2)+(1:size(image2,2)-shift(2)),:) = ...
tmp((shift(1)+1):size(image2,1),1:size(image2,2)-shift(2),:);
elseif shift(1) == 0 && shift(2) == 0
tmp_shift = image2;
end
Label_dots = bwlabeln(1-image2_shift,6); % update the dot label matrix
stats2 = regionprops3(Label_dots,tmp_shift,'volume','MeanIntensity','centroid','VoxelValues');
DotVolumes = cat(1,stats2.Volume);
DotCentre = cat(1,stats2.Centroid);
MeanDotIntensity = cat(1,stats2.MeanIntensity);
for h = 1 : height(stats2)
stats2.StdIntensity{h,:} = std(double(stats2.VoxelValues{h,:}));
end
StdDotIntensity = cell2mat(cat(1,stats2.StdIntensity));
%Kmeans cluster ::::::::::::::::::::::::::::::::::::::::::::::::::::::
% Type number of total clusters below
rng('default')
[assignedClass, clusterCenters]= kmeans(MeanDotIntensity,8);
[sortedDistances, sortOrder] = sort(clusterCenters, 'ascend'); % Sort the clusters according to how far each cluster center is from the origin.
newClassNumbers = zeros(size(stats1(:,1)));
for k = 1 : size(clusterCenters, 1)
currentClassLocations = assignedClass == k; % First find out what points have this current class & where they are
% Now assign all of those locations to their new class.
newClassNumber = find(k == sortOrder); % Find index in sortOrder where this class number appears.
newClassNumbers(currentClassLocations) = newClassNumber; % Do the relabeling right here:
end
group = newClassNumbers;
figure();
datacursormode on
gscatter(MeanDotIntensity,StdDotIntensity,group); %comment this line alone out in case of low signal
xlabel('Mean Dot Intensity')
ylabel('Std Intensity')
points(chan).DotVolumes = DotVolumes;
points(chan).MeanIntensityOfDots = MeanDotIntensity;
points(chan).MeanIntensityOfDotsNormalized = group;
% Change this (below) according to chosen clusters ::::::::::::::::::::
ind = intersect(find(group >6),find(group <9));
% :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
points(chan).index_filter = ind;
figure
imshow(max(tmp_shift,[],3),[])
hold on
plot(DotCentre(:,1),DotCentre(:,2),'b.') % All dots shown in blue
plot(DotCentre(ind,1),DotCentre(ind,2),'r.') % Selected dots shown in red.
% now use the ind to determine which of the dots are in which cells;
% to achieve this, we will remove all the labels of the label_dots matrix, that are
% not member of ind: Make it a label matrix again, call Label_dots2 and
% gets its statistics:
Label_dots2 = bwlabeln(ismember(Label_dots, ind),6);
% update the stats1 to correspond the updated Label_dots2:
stats1 = regionprops3(Label2(:,:,:),Label_dots2,'centroid','volume','MeanIntensity','VoxelValues');
NSpots = zeros(size(stats1(:,1))); % pre-allocating
for z = 1 : height(stats1)
temp = stats1.VoxelValues{z,:}; % labels that are in the cell z
temp = unique(temp); % extract the unique label values
NSpots(z) = length(temp);
end
points(chan).NSpots_Cleared = NSpots;
points(chan).NSpotsinVol_Cleared = NSpots./cat(1,stats1.Volume);
end
%% 6) Run this only after you complete running all 5 channels for that particular hyb
pointssave = sprintf('hyb%d_points.mat',hybnum); %run this after you are done with all channels for this hyb
save(pointssave,'points');
%% the end
%
%
%
%
%% Functions
function se = strel3D(shape, size)
% 3D version of matlabs 2d strel function
% Implements 'sphere' and 'cube'
% strel3d(shap,size)
% Copyright 2015 Idse Heemskerk and Sebastian Streichan
N = size;
if strcmp(shape, 'sphere')
se = false([2*N+1 2*N+1 2*N+1]);
[X,Y,Z] = meshgrid(-N:N, -N:N, -N:N);
se(X.^2 + Y.^2 + Z.^2 <= N^2) = 1;
elseif strcmp(shape, 'cube')
se = true([2*N+1 2*N+1 2*N+1]);
else
error('strel type not recognized');
end
end
function [shift,image1,image2] = xcorr3fft(image1,image2)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% xcorr2fft computes offsets between images image1 and image2 based
% on Phase Correlation method. image1 & image2 are assumed to have
% the same dimensions.
% Written by: Sebastian J Streichan, EMBL, February 29, 2012
% Extended to 3D and bug corrected by: Stefan Gunther, EMBL, March, 20, 2012
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
F = fftn(image1);
Fc = conj(fftn(image2));
R = F.*Fc; % Phase correlation, Compute fft of image1 and conjugate fft of image2, elementwise multiply and normalise.
c = ifftn(R); % Inverse fft of Phase correlation gives cross correlation map.
[~,i] = max(c(:));
[I,J,~] = ind2sub(size(c),i);
if abs(I-1)<abs(size(image1,1)-I+1)
shiftx = -I+1;
else
shiftx = size(image1,1)-I+1;
end
if abs(J-1)<abs(size(image1,2)-J+1)
shifty = -J+1;
else
shifty = size(image1,2)-J+1;
end
shift=[shiftx,shifty,0];
end