Skip to content

Commit e1a46bf

Browse files
committed
Update analyze_binary with more metrics
Caused slower processing. - Aspect ratio calculation was modified slightly.
1 parent 377947c commit e1a46bf

File tree

1 file changed

+68
-28
lines changed

1 file changed

+68
-28
lines changed

+agg/analyze_binary.m

+68-28
Original file line numberDiff line numberDiff line change
@@ -187,18 +187,54 @@
187187
% 'autocrop' method included below.
188188
[~, ~, Aggs0(jj).rect] = autocrop(img, img_binary);
189189

190-
190+
% Add cyop here to speed code?
191191

192192
%== Compute aggregate dimensions/parameters ======================%
193193
SE = strel('disk', 1);
194194
img_dilated = imdilate(img_binary,SE);
195195
img_edge = img_dilated - img_binary;
196+
197+
img_erode = imerode(... % erode one pixel after filling holes
198+
imfill(img_binary, 'holes'), SE);
199+
img_edge_in = img_binary - img_erode; % internal edge
196200

197-
[row, col] = find(imcrop(full(Aggs0(jj).binary), Aggs0(jj).rect));
201+
[row, col] = find(img_edge_in); % use internal edge and find max/min-difference
202+
if length(row) > 2e3 % downsample to avoid memory issues/improve speed
203+
ri = randi(length(row), [2e3,1]);
204+
row = row(ri);
205+
col = col(ri);
206+
end
207+
208+
% Aspect ratio in x-y space as imaged.
198209
Aggs0(jj).length = max((max(row)-min(row)), (max(col)-min(col))) * pixsize(ii);
199210
Aggs0(jj).width = min((max(row)-min(row)), (max(col)-min(col))) * pixsize(ii);
200-
Aggs0(jj).aspect_ratio = Aggs0(jj).length / Aggs0(jj).width;
211+
Aggs0(jj).aspect_ratio0 = Aggs0(jj).length / Aggs0(jj).width;
212+
213+
% Aspect ratio, allowing for rotation.
214+
d = sqrt((row - row') .^ 2 + (col - col') .^ 2); % distances in boundary
215+
[dmax, idx1] = max(d); % max in first dimension
216+
[dmax, idx2] = max(dmax); idx1 = idx1(idx2); % max in second dimension
217+
Aggs0(jj).lmax = dmax * pixsize(ii);
218+
219+
dy = col(idx2) - col(idx1); % change in y along primary dim.
220+
dx = row(idx2) - row(idx1); % change in x along primary dim.
221+
d = (dy .* row - dx .* col) ./ sqrt(dx .^ 2 + dy .^ 2); % distance to line
222+
dd = max(d) - min(d);
223+
Aggs0(jj).lmin = dd * pixsize(ii);
224+
225+
Aggs0(jj).aspect_ratio = Aggs0(jj).lmax / Aggs0(jj).lmin;
201226

227+
%{
228+
% Debug plot.
229+
imshow(~img_binary); axis on; axis image;
230+
[~, idx3] = max(d); [~, idx4] = min(d);
231+
hold on;
232+
plot([col(idx1), col(idx2)], [row(idx1), row(idx2)], 'ro-');
233+
plot([col(idx3), col(idx4)], [row(idx3), row(idx4)], 'ro');
234+
hold off;
235+
%}
236+
237+
% Equivalent diameters.
202238
Aggs0(jj).num_pixels = nnz(img_binary); % number of non-zero pixels
203239
Aggs0(jj).da = ((Aggs0(jj).num_pixels/pi)^.5) * ...
204240
2 * pixsize(ii); % area-equialent diameter [nm]
@@ -207,14 +243,27 @@
207243
Aggs0(jj).Rg = gyration(img_binary, pixsize(ii)); % calculate radius of gyration [nm]
208244

209245
%-- Perimeter --%
210-
perimeter1 = pixsize(ii)* sum(sum(img_edge~=0)); % calculate aggregate perimeter
211-
perimeter2 = pixsize(ii) * sum(sum(bwperim(img_binary)));
212-
perimeter3 = pixsize(ii) * get_perimeter2(img_edge); % edge midpoint connected perimeter
213-
Aggs0(jj).perimeter = max(perimeter2, perimeter3);
246+
perimeter1 = sum(sum(img_edge~=0)); % calculate aggregate perimeter
247+
perimeter2 = sum(sum(bwperim(img_binary)));
248+
perimeter3 = get_perimeter2(img_edge); % edge midpoint connected perimeter
249+
Aggs0(jj).perimeter = pixsize(ii) * max(perimeter2, perimeter3);
214250

215-
%-- Circularity --%
251+
%-- Compactness / circularity --%
216252
% The degree of being far from a circle (1: circle, 0: straight line).
217253
Aggs0(jj).circularity = 4 * pi * Aggs0(jj).area / (Aggs0(jj).perimeter ^ 2); % circularity
254+
Aggs0(jj).compact_b = Aggs0(jj).area / (pi * (Aggs0(jj).lmax / 2) ^ 2);
255+
256+
n = nnz(img_binary);
257+
p = sum(sum(abs(img_binary(2:end, :) - img_binary(1:end-1, :)))) + ... % vertical edges
258+
sum(sum(abs(img_binary(:, 2:end) - img_binary(:, 1:end-1)))); % hori
259+
Ld = (4 * nnz(img_binary) - p) / 2;
260+
Ldmin = n - 1;
261+
Ldmax = 2 * (n - sqrt(n));
262+
Aggs0(jj).compact = (Ld - Ldmin) / (Ldmax - Ldmin);
263+
264+
%-- Texture --%
265+
img_e = entropyfilt(img, true(25));
266+
Aggs0(jj).entropy = mean(img(img_binary == 1));
218267

219268
%-- Gradient and sharpness --%
220269
bw = 5; % bandwith on border to gather pixels
@@ -224,7 +273,7 @@
224273
[grad, ds] = binner(img_binary, grad);
225274
Aggs0(jj).sharp = log10(mean(grad(ds <= bw))) - ...
226275
log10(mean((grad(ds > bw) + eps))); % "+eps" avoids div. by zero
227-
276+
228277
%-- Optical depth --%
229278
agg_grayscale = double(img(Aggs0(jj).binary)); % the selected agg's grayscale pixel values
230279
gray_extent = double(max(max(max(img)), 1) - min(min(img)));
@@ -236,7 +285,7 @@
236285

237286
if f_plot==1
238287
if mod(jj, 10) == 0 % show image after every 10 aggregates processed
239-
set(groot,'CurrentFigure',f0); tools.imshow_agg(Aggs0, ii, 0); title(num2str(ii)); drawnow;
288+
set(groot,'CurrentFigure',f0); tools.imshow_agg(Aggs0, jj, 0); title(num2str(ii)); drawnow;
240289
end
241290
end
242291
end
@@ -291,7 +340,7 @@
291340
%== AUTOCROP =============================================================%
292341
% Automatically crops an image based on binary information
293342
% AUTHOR: Yeshun (Samuel) Ma, Timothy Sipkens, 2019-07-23
294-
function [img_cropped,img_binary,rect] = autocrop(img_orig, img_binary)
343+
function [img_cropped, img_binary, rect] = autocrop(img_orig, img_binary)
295344

296345
[x,y] = find(img_binary);
297346

@@ -325,32 +374,23 @@
325374

326375
x_mb = mb{1}(:,2);
327376
y_mb = mb{1}(:,1);
328-
edges_mb = ones(n_mb,1);
329377

330378
% [x_mb, y_mb] = poly2cw(x_mb, y_mb);
331379

332380
% Compile and group edges.
333-
edges = 1;
334-
for i = 2 : n_mb
335-
if (x_mb(i) ~= x_mb(i-1)) && (y_mb(i) ~= y_mb(i-1)) % flag an angle transition
336-
edges = edges + 1; % found a new edge
337-
end
338-
edges_mb(i) = edges;
339-
end
381+
edges_mb = cumsum([1; ...
382+
and(x_mb(2:end) ~= x_mb(1:end-1), y_mb(2:end) ~= y_mb(1:end-1))]);
340383

341384
% Connect last pixel back to the first pixel.
342385
if (x_mb(1) == x_mb(end)) || (y_mb(1) == y_mb(end))
343386
edges_mb(edges_mb == edges_mb(end)) = 1;
344387
end
345388

346-
% Loop through edges and get midpoints.
347-
nn_mb = max(edges_mb);
348-
xx_mb = zeros(nn_mb,1);
349-
yy_mb = zeros(nn_mb,1);
350-
for ii = 1:nn_mb
351-
xx_mb(ii) = mean(x_mb(edges_mb == ii));
352-
yy_mb(ii) = mean(y_mb(edges_mb == ii));
353-
end
389+
% Get midpoints of lines.
390+
xx_mb = accumarray(edges_mb, x_mb) ./ ...
391+
accumarray(edges_mb, ones(size(x_mb)));
392+
yy_mb = accumarray(edges_mb, y_mb) ./ ...
393+
accumarray(edges_mb, ones(size(y_mb)));
354394

355395
% Calculate perimeter by connecting midpoints.
356396
p_circ = sum(sqrt((xx_mb(1:end) - xx_mb([2:end,1])).^2 + ...
@@ -363,7 +403,7 @@
363403

364404

365405
%== BINNER ===============================================================%
366-
% Bin the pixels from the boundary inwards and the report a quantity as a
406+
% Bin the pixels from the boundary inwards and report a quantity as a
367407
% function of those bins. Note that statistics will be poor in the center
368408
% of the particle do to a limit number of pixels.
369409
%

0 commit comments

Comments
 (0)