-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHRTEMFringeMapping.m
347 lines (295 loc) · 10.9 KB
/
HRTEMFringeMapping.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
% Author: Maria Botero
% The program is developed for the automatic mapping of fringes in
% high resolution transmission electron microscopy (HRTEM) images of soot.
% It applies a series of image transformations to the original TEM image
% to map the fringes. Fringes length, tortuosity, curvature and
% inter-fringe spacing are measured by this code.
% A detailed description can be found in the papers:
% Botero et al., Carbon, 2016, [doi:10.1016/j.carbon.2015.09.077]
% Botero et al., Carbon, 2019, [doi:10.1016/j.carbon.2018.09.063]
clear all;
close all;
clc;
%% FOLDER and IMAGE definitions
fileLoc='.\Example\';
fileName='ExampleImage';
fileExt= '.tif'; %define the extension of images .png or .jpg or .tiff etc
locImage = [fileLoc,fileName,fileExt];
locImageInfo = [fileLoc,fileName,'.txt']; %this is for images that also come with a txt file containing the px to nm ratio
%% INPUT Parameters
%--%Image transformation parameters. Uncomment if you want to load
% %new parameters and comment out lines 106 to 108
bw_fac=1; %Thresholding factor to binarise image
sigma=1;
hsize=2*ceil(3*sigma)+1; %Gaussian filter
bothat=2; %Bottom-hat disk size
%--%Minimun fringe size allowed and Min and Max interfringe spacings
% %allowed
min_len=0.483;
min_spa=0.3354;
max_spa=0.6;
%--$For curvature calculations according to Wang-Mathews 2016
% $http://pubs.acs.org/doi/abs/10.1021/acs.energyfuels.5b02907
n_seg= 2; %segmentate every nth pixel
minAngle=0; %minimum angle accepted between segments
maxAngle=180; %maximum angle accepted between segments
minsize=0.2; %minimum accepted segment size
curvature_params= [n_seg;minAngle;maxAngle;minsize];
%% LOAD FILE
%-%Check if image and info exists
lImage = (exist(locImage) == 2);
lImageInfo = (exist(locImageInfo) == 2);
if (~(lImage))
warning('Image not found! Check the defined file locations.')
return
end
[fc, map]= imread([fileLoc,fileName,fileExt]);
fc=rgb2gray(fc); % uncomment if the image is not in grey scale
imshow(fc)
%% PIXEL to nm DEFINITION
% %Get scale from ImageInfo (SCALE-X)
% %--To obtain from a txt file containing the information of the scale X and
% %scale Y:
% imageInfo = fileread(locImageInfo);
% i = findstr(imageInfo,'SCALE-X');
% j = findstr(imageInfo,'SCALE-Y');
% scaleStr = imageInfo(i+8:j-1);
% scale = str2num(scaleStr);
% px_nm= 1/scale;%pixelTOnanometer(TEM, eKV, mol);
%--%To obtain from the bar scale in the image:
% %the following 3 lines:
%[x y]=getpts; %select a point at each side of the scale bar in the image
%bar_length= str2double(inputdlg('Scale bar length (nm)')); %write the bar length in [nm]
%px_nm=abs(x(2)-x(1))/bar_length
%save(regexprep(locImage,fileExt,strcat('_','px-nm','.mat')), 'px_nm');
%--%For known px_nm ratio type it or load it
% px_nm=18.0194;
load(regexprep(locImage,fileExt,strcat('_','px-nm','.mat')), 'px_nm');
%% ROI
%--%Select the region of interest ROI
% %You can select multiple ROI in the same image. The program saves the ROI
% %in the the same folder of the image and gives ascending number to each
% %ROI. You can then load a pre-selected ROI by using its number
% %Check if there is an existent ROI
NumRoi=1;
if isempty(dir([fileLoc,fileName,'*_roi.mat'])) ==0
button = questdlg('Do you want to select a new ROI?','ROI selection','Yes','No','default');
if isequal(button,'Yes')==1
Rois=dir([fileLoc,fileName,'*_roi.mat']);
NumRoi=str2num(Rois(length(Rois)).name(end-8))+1;
% NumRoi=length(dir([fileLoc,fileName,'*_roi.mat']))+1;
uiwait(msgbox({'Click at different points around the region you want to analyse. Make sure to close the loop and avoid reaching the edges of the Image.';...
'To accept the selected ROI, right click and select "Create Mask".'},...
'Select ROI','help'));
[I,c,r] =roipoly(fc);
if isempty(I)
c=[2;2;size(fc,1)-1;size(fc,1)-1;2];r=[2;size(fc,2)-1;size(fc,2)-1;2;2];
roi=[c,r];
save(regexprep(locImage,fileExt,strcat('_',num2str(NumRoi),'_roi','.mat')), 'roi');
else
roi=[c,r];
save(regexprep(locImage,fileExt,strcat('_',num2str(NumRoi),'_roi','.mat')), 'roi');
end
clear button
else str = inputdlg('Which ROI number you want to analyse');
if isempty(str)
warning('The ROI selected does not exist.')
return
else
load(regexprep(locImage,fileExt,strcat('_',str,'_roi','.mat')), 'roi')
c=roi(:,1); r=roi(:,2);
NumRoi=str2double(str);
clear str
end
end
else
uiwait(msgbox({'Click at different points around the region you want to analyse. Make sure to close the loop and avoid reaching the edges of the Image.';...
'To accept the selected ROI, right click and select "Create Mask".'},...
'Select ROI','help'));
[I,c,r] =roipoly(fc);
if isempty(I)
c=[2;2;size(fc,1)-1;size(fc,1)-1;2];r=[2;size(fc,2)-1;size(fc,2)-1;2;2];
roi=[c,r];
save(regexprep(locImage,fileExt,strcat('_',num2str(NumRoi),'_roi','.mat')), 'roi');
else
roi=[c,r];
save(regexprep(locImage,fileExt,strcat('_',num2str(NumRoi),'_roi','.mat')), 'roi');
end
end
%% IMAGE TRANSFORMATIONS
% %Analysis parameters
%--%load parameters previously used with that ROI
if isempty(dir([fileLoc,fileName,'_',num2str(NumRoi),'_Data.mat'])) ==0
load([fileLoc,fileName,'_',num2str(NumRoi),'_Data']);
bw_fac=Data.params(1);
sigma=Data.params(2);hsize=Data.params(3);bothat=Data.params(4);
else
end
%--%Store Parameters
fringes.params= [bw_fac;sigma;hsize;bothat];
% %Image transformations
%--%Mask ROI
[N,M]=size(fc);
I=(poly2mask(c,r,N,M)); %roi
g=fc;
g(~I)=0;
imshow(g)
clear N M g
%--%Constrast enhancement (histogram equalization)
hgram=sqrt(imhist(fc));
g2=histeq(fc,hgram);
g2(~I)=0;
imshow(g2)
%--%Gaussian lowpass filter
g3=imgaussfilt(g2,sigma,'FilterSize',hsize);
imshow(g3);
%--%Bottom hat transformation
se = strel('disk',bothat);
g4 = imbothat(g3,se);
imshow(g4);
%--%Thresholding to obtain binary images
level = graythresh(g4);
level = level*bw_fac;
g5 = im2bw(g4,level);
imshow(g5);
% %Morphological operations
%--%Eliminate 8-conn of background pixels
g6= bwmorph(g5,'diag');
imshow(g6);
%--%Skeletonization
g7=bwskel(g6);
imshow(g7);
%--%Eliminate single isolated pixels adn break H connections
g8= bwmorph(g7,'hbreak');
g9= bwmorph(g8,'clean');
imshow(g9);
I8=g9;
clear g2 g3 g4 g5 g6 g7 g8 g9
%% BRANCHES ELIMINATION
%--%Branch cleaning algorithm: screens each fringe with branches and
% %eliminates the shorter branch to keep the main bone of the fringe
branchpoints=length(nonzeros(bwmorph(I8,'branchpoints')));
I8=branch_cleaning(I8,px_nm,min_len);
imshow(I8);
export_fig([fileLoc,fileName,'_',num2str(NumRoi),'_mappedfringes.png'],'-transparent')
%% FRINGE LENGTH and TORTUOSITY Calculation
%--%Sort fringes
coordinates=fringe_sorting(I8);
%--%Calculate fringe length and tortuosity, and give feedback on each fringe orientation
[numNM,linlength,orientation,coords,I8]= ...
fringe_length_tortuosity(I8,coordinates,px_nm,min_len);
%--%Store Fringe information
fringes.length = nonzeros(numNM);
fringes.orientation=nonzeros(orientation);
fringes.Linlength = nonzeros(linlength)/px_nm;
fringes.number= length(nonzeros(numNM));
fringes.tortuosity= fringes.length./fringes.Linlength;
fringes.coordinates = coords;
%% Overlaid IMAGES of fringes
%--%Plot fringes on top of the TEM image
figure(2)
imshow(fc);
hold on
for i=1:numel(coords);
c=coords(i).XY;
scatter(c(:,2),c(:,1),2,'r','filled','o')
end
hold off
%--%Export image with overlaid fringes
export_fig([fileLoc,fileName,'_',num2str(NumRoi),'_overlaidfringes.png'],'-transparent')
%--%Plot fringes on white backgroud
figure(3)
blank=255*ones(size(I8));
imshow(blank)
hold on
for i=1:numel(coords);
coor=coords(i).XY;
plot(coor(:,2),coor(:,1),'k')%,'filled','o')
end
hold off
clear coordinates coor
%--%Export image if fringes in white background
export_fig([fileLoc,fileName,'_',num2str(NumRoi),'_clearfringes.png'],'-transparent')
%% CURVATURE Calculation
%--%Using Menger curvature
%[curvatures,rad_curv,avg_curv,avg_rad,infl] = ...
% menger_curvature(fringes.coordinates,px_nm,fringes.orientation);
%--%Using total curvature
%[curv_nm,r_nm] = tot_curvature(fringes.coordinates,px_nm);
%--%Using method of Wang-Mathews 2016
% %http://pubs.acs.org/doi/abs/10.1021/acs.energyfuels.5b02907
%curvature = angle_curvature(fringes.coordinates,px_nm,curvature_params);
%--%[Only for Angle Curvature]Plot fringes segments on white backgroud
% figure(4)
% blank=255*ones(size(I8));
% imshow(blank)
% hold on
% for i=1:numel(coords);
% coor=coords(i).XY;
% plot(coor(:,2),coor(:,1),'k');
% end
% for i=1:numel(curvature);
% segments=curvature(i).seg;
% plot(segments(:,2),segments(:,1),'r',segments(:,4),segments(:,3),'r');
% end
% hold off
%export_fig([fileLoc,fileName,'_',num2str(NumRoi),'_curvaturefringes.png'],'-transparent')
%--%Store Fringe information
%fringes.curvatures = curvature;
%% INTERFRINGE SPACING Calculation
% %For each fringe compare to all the following to see if there
% %is one or more adjacent (or parts of a fringe adjacent)
figure
imshow(fc);
hold on
spacing = interfringe_spacing(fringes,I8,px_nm,min_len,min_spa,max_spa,fc,fileName);
hold off
%--%Export overlaid image of stacked fringes
export_fig([fileLoc,fileName,'_',num2str(NumRoi),'_overlaidstacks.png'],'-transparent')
%--%Store Fringe information
fringes.spacing=nonzeros(spacing)/px_nm;
%% MEAN and MEDIAN
medL=median(fringes.length)
meaL=mean(fringes.length)
stdL= std(fringes.length)
medT=median(fringes.tortuosity)
meaT=mean(fringes.tortuosity)
stdT=std(fringes.tortuosity)
medS=median(fringes.spacing)
meaS=mean(fringes.spacing)
stdS=std(fringes.spacing)
nsl=((fringes.number-length(fringes.spacing))/fringes.number)*100
highFT= (length(nonzeros(fringes.tortuosity > 1.5))/fringes.number)*100
num_stacks=length(fringes.spacing)/2
Stats=[fringes.number num_stacks branchpoints meaL medL meaT medT meaS medS highFT nsl]';
%% Fringes distance to particle CENTER
button = questdlg(['Do you want to caclulate the fringes position in relation', ...
' to the particle centre? (if YES, then select the particle centre and radius',...
', the press right click)']...
,'Map fringes with respect to particle centre','Yes','No','default');
if isequal(button,'Yes')==1
[FL_dist,center,radius] = fringes_position(fc,fringes,spacing,px_nm);
for i=1:length(spacing)-1
n=nonzeros(spacing(i,:));
if isempty(n)
spa_pos(i)=0;
else
spa_pos(i)=min(n);
end
end
add=length(fringes.length)-length(spacing(:,1));
spa_pos(length(spacing(:,1))+add)=0;
spa_pos=spa_pos/px_nm;
clear n i add
else
FL_dist=[]; center=[];radius=[];
spa_pos=[];
end
%--Store Fringe information
fringes.distcenter = FL_dist;
fringes.minspacing= spa_pos;
fringes.radius= radius;
fringes.center= center;
%% SAVE DATA
% % save all fringes structure data
save(regexprep(locImage,fileExt,strcat('_',num2str(NumRoi),'_fringes','.mat')), 'fringes');