# Frames Export
Get frame rate information
videoname=T-L\ _\ 1-50\ tip-tip.avi
ffmpeg -i $videoname 2>&1 |grep -o '[0-9]\+ fps'
The output is 30 fps
ffmpeg -i $videoname -r 30 output_%04d.png
# Edges Detection
The Canny edge detector is used in this step.
from capillary import edge, fitting, display
from importlib import reload
reload(fitting);
reload(display);
# Read the 72nd Frame
im=edge.R[0](72)
# Extract coordinates of edges
points=edge.G[0](72)
x, y=points
imshow(im);
plot(x, y, 'o', markersize=1)
xlabel('Row/px')
ylabel('Column/px')
axis('equal');
title('Detected Edges');
# Use Major Axis to split particles
Theoretically, if a particle has symmetry for rotate $2\pi/3$, the moment of inertia $I_0$ is independent on the direction of axis as long as it passes the centroid. More general, for arbitrary axis, we can use parallel axis theorem to find $I=I_0+md^2$.
Lemma For two particles the major axis $\hat n$ of least moment of inertia must go through both centroid.
So the major axis is used to estimate the line connecting both centroid. The information of another major axis $\hat m$ with greatest moment of inertia can also be used to estimate the separation $r$ between the centroids.
As $I_n\approx 2I_0, I_m\approx 2\left[I_0+m\left(\dfrac{r}{2}\right)^2\right]$, we have $$\frac{r}{2}\approx\sqrt{\frac{I_M-I_m}{2m}}$$
If the two particles are separated far enough, we can separate them into two particles based on the major axis $\hat n$ and centroids $O$ of two particles by criteria $$\mathrm{sgn}\left[(\vec r_i-\vec r_O)\cdot \hat n\right]$$
display.show_split(points)
display.show_axis(points)
axis('equal');
title('Partition of Particles');
When two particles are too close, the partition and the estimation of centroids may be inaccurate. But it will nevertheless give a good starting point. We will discuss this later.
# Use Triangles to Fit Particles
Triangles are used to Fit Particles. Fitting parameters of Triangle $T$ are centroid position $C(x, y)$ and angle location $(\rho, \theta)$ with respect to centroid $C$. The idea is like https://link.springer.com/article/10.1007%2FBF00939613 :
$$\mathrm{minimize}\left[\sum_i|\vec r_i-\vec r_C|^2d^2(\vec r_i, T)\right]$$The initial estimation of centroid and angle should be given to ensure a good global minimum.
display.show_frame(points)
axis('equal');
title('Hulls and Centroids');
# Deal with close case
pts=edge.G[2](1196)
pos, neg = fitting.split(pts)
display.show_split(pos, neg)
display.show_axis(pts)
axis('equal');
title('Naive partition using major axes fails when close');
If we fit triangles on the wrong split, the result is still good.
display.show_frame(pts)
axis('equal');
title('Hulls and Centroids');
This is a good starting point for our iteration:
- Find a new split $S'$ based on the fitting triangles $T$.
- Find new fitting triangle $T'$ based on $S'$
The $T\to S$ is given in last section. For the $S\to T$ step, the criteria for each point are:
$$d(\vec r_i, T)=\mathrm{distance}(\vec r_i, \mathrm{triangle}),$$which is defined to be positive if point is inside a triangle and negative if outside. A point $P$ belongs to $T_1$ iff
$$d(P, T_1) < d(P, T_2)+\Delta$$The $\Delta$ is controlling the contiguous points which belongs to both. we are setting it to be 1 pixel.
Even one iteration could give fair result:
display.show_frame(pts, iterate=1)
axis('equal');
title('Hulls and Centroids');
Three iterations makes it perfect:
display.show_frame(pts, iterate=3)
axis('equal');
title('Hulls and Centroids');
# Deal with Touching thick border?
Solved by Fix the radius of triangle to 30
Comments
comments powered by Disqus