Home > Net >  Average diameter of complex shapes from pixels in df, Python
Average diameter of complex shapes from pixels in df, Python

Time:03-09

I have a DataFrame of multiple particles, that have gotten the group numbers (1,2,3,4) like this:

Groups: 
 [[0 0 0 1 1 1 0 0]
 [0 2 0 1 1 1 0 0]
 [0 0 0 1 1 1 0 0]
 [0 0 0 0 1 0 0 0]
 [0 3 3 0 0 4 0 0]
 [0 3 0 0 0 4 0 0]
 [0 0 0 0 0 4 0 0]
 [0 0 0 0 0 4 0 0]]
Number of particles: 4

I have then calculated the areas of the particles and created a DataFrame (assuming 1 pixel = 1 nm):

   Particle #  Size [pixel #]  A [nm2]  
1           1              10       10     
2           2               1        1     
3           3               3        3     
4           4               4        4     

Now I want to calculate the diameter of the particles. However, the shapes of the particles are complex, therefore I am looking for a method to calculate the average diameter (considering the shapes are not perfectly round) and adding another column next to A [nm2] with the average diameter.

Will this be possible?

Here is my full code:

import numpy as np
from skimage import measure
import pandas as pd

final = [
    [0, 0, 0, 255, 255, 255, 0, 0],
    [0, 255, 0, 255, 255, 255, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, ],
    [0, 0, 0, 0, 255, 0, 0, 0],
    [0, 255, 255, 0, 0, 255, 0, 0],
    [0, 255, 0, 0, 0, 255, 0, 0],
    [0, 0, 0, 0, 0, 255, 0, 0],
    [0, 0, 0, 0, 0, 255, 0, 0]
]

final = np.asarray(final)

groups, group_count = measure.label(final > 0, return_num = True, connectivity = 1)
 
print('Groups: \n', groups)
print(f'Number of particles: {group_count}')

df = (pd.DataFrame(dict(zip(['Particle #', 'Size [pixel #]'],
                            np.unique(groups, return_counts=True))))
        .loc[lambda d: d['Particle #'].ne(0)]
     )

pixel_nm_size = 1*1

df['A [nm2]'] = df['Size [pixel #]'] * pixel_nm_size

Any help is appreciated!

CodePudding user response:

I think you are looking for regionprops.

Specifically, either equivalent_diameter, or just perimeter.

props = measure.regionprops_table(groups, properties = ['label', 'equivalent_diameter', 'perimeter'])
df = pd.DataFrame(props)

edit

from the docs:

equivalent_diameter_area: float
The diameter of a circle with the same area as the region.

So, the function takes your labeled region, measures the area and constructs a circle with that area (there is only one such circle for each area). Then it measures the diameter of the circle.

You can also look at major_axis_length and minor_axis_length. These are computed by fitting an ellipse around the object and measuring the long and short axis that define it.

CodePudding user response:

IIUC, you could use a custom function to find the height/width of the bounding box and compute the average of both dimensions:

def get_diameter(g):
    a = (groups==g)
    h = (a.sum(1)!=0).sum()
    w = (a.sum(0)!=0).sum()
    return (h w)/2


df['diameter'] = df['Particle #'].map(get_diameter)

output:

   Particle #  Size [pixel #]  A [nm2]  diameter
1           1              10       10       3.5
2           2               1        1       1.0
3           3               3        3       2.0
4           4               4        4       2.5
  • Related