|
187 | 187 | % 'autocrop' method included below.
|
188 | 188 | [~, ~, Aggs0(jj).rect] = autocrop(img, img_binary);
|
189 | 189 |
|
190 |
| - |
| 190 | + % Add cyop here to speed code? |
191 | 191 |
|
192 | 192 | %== Compute aggregate dimensions/parameters ======================%
|
193 | 193 | SE = strel('disk', 1);
|
194 | 194 | img_dilated = imdilate(img_binary,SE);
|
195 | 195 | 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 |
196 | 200 |
|
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. |
198 | 209 | Aggs0(jj).length = max((max(row)-min(row)), (max(col)-min(col))) * pixsize(ii);
|
199 | 210 | 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; |
201 | 226 |
|
| 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. |
202 | 238 | Aggs0(jj).num_pixels = nnz(img_binary); % number of non-zero pixels
|
203 | 239 | Aggs0(jj).da = ((Aggs0(jj).num_pixels/pi)^.5) * ...
|
204 | 240 | 2 * pixsize(ii); % area-equialent diameter [nm]
|
|
207 | 243 | Aggs0(jj).Rg = gyration(img_binary, pixsize(ii)); % calculate radius of gyration [nm]
|
208 | 244 |
|
209 | 245 | %-- 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); |
214 | 250 |
|
215 |
| - %-- Circularity --% |
| 251 | + %-- Compactness / circularity --% |
216 | 252 | % The degree of being far from a circle (1: circle, 0: straight line).
|
217 | 253 | 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)); |
218 | 267 |
|
219 | 268 | %-- Gradient and sharpness --%
|
220 | 269 | bw = 5; % bandwith on border to gather pixels
|
|
224 | 273 | [grad, ds] = binner(img_binary, grad);
|
225 | 274 | Aggs0(jj).sharp = log10(mean(grad(ds <= bw))) - ...
|
226 | 275 | log10(mean((grad(ds > bw) + eps))); % "+eps" avoids div. by zero
|
227 |
| - |
| 276 | + |
228 | 277 | %-- Optical depth --%
|
229 | 278 | agg_grayscale = double(img(Aggs0(jj).binary)); % the selected agg's grayscale pixel values
|
230 | 279 | gray_extent = double(max(max(max(img)), 1) - min(min(img)));
|
|
236 | 285 |
|
237 | 286 | if f_plot==1
|
238 | 287 | 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; |
240 | 289 | end
|
241 | 290 | end
|
242 | 291 | end
|
|
291 | 340 | %== AUTOCROP =============================================================%
|
292 | 341 | % Automatically crops an image based on binary information
|
293 | 342 | % 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) |
295 | 344 |
|
296 | 345 | [x,y] = find(img_binary);
|
297 | 346 |
|
|
325 | 374 |
|
326 | 375 | x_mb = mb{1}(:,2);
|
327 | 376 | y_mb = mb{1}(:,1);
|
328 |
| -edges_mb = ones(n_mb,1); |
329 | 377 |
|
330 | 378 | % [x_mb, y_mb] = poly2cw(x_mb, y_mb);
|
331 | 379 |
|
332 | 380 | % 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))]); |
340 | 383 |
|
341 | 384 | % Connect last pixel back to the first pixel.
|
342 | 385 | if (x_mb(1) == x_mb(end)) || (y_mb(1) == y_mb(end))
|
343 | 386 | edges_mb(edges_mb == edges_mb(end)) = 1;
|
344 | 387 | end
|
345 | 388 |
|
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))); |
354 | 394 |
|
355 | 395 | % Calculate perimeter by connecting midpoints.
|
356 | 396 | p_circ = sum(sqrt((xx_mb(1:end) - xx_mb([2:end,1])).^2 + ...
|
|
363 | 403 |
|
364 | 404 |
|
365 | 405 | %== 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 |
367 | 407 | % function of those bins. Note that statistics will be poor in the center
|
368 | 408 | % of the particle do to a limit number of pixels.
|
369 | 409 | %
|
|
0 commit comments