import flocpy
import os
import glob
import datetime as dt
import numpy as np
import matplotlib.pyplot as plt
import pandas as pdThe floc identification software operates on a sorted list of images. Each element in the list should be a full path to an image file.
paths = flocpy.testdata_paths()
imgset1_flist = glob.glob(os.path.join(paths[0], '*.bmp')) # image set 1
imgset2_flist = glob.glob(os.path.join(paths[1], '*.png')) # image set 2
print(imgset1_flist[0])c:\users\tashl\onedrive\documents\github\flocpy\flocpy\testdata\imgset1\023.bmp
If the files are conveniently named, you can use
sorted_flist = sorted(flist)
sorted_flist_1 = sorted(imgset1_flist)
for f in sorted_flist_1:
print(os.path.basename(f))023.bmp
024.bmp
025.bmp
026.bmp
027.bmp
028.bmp
029.bmp
030.bmp
Otherwise, it's best to define a custom function to facilitate sorting. In this case, we sort by a date and time encoded in the filename.
def custom_datetime(fpath):
dtprefix = 'img-'
i0 = len(dtprefix)
dtlen = 14
fmt = '%m%d%Y%H%M%S'
return dt.datetime.strptime(os.path.basename(fpath)[i0:i0+dtlen],fmt)
sorted_flist_2 = sorted(imgset2_flist, key=custom_datetime)
for f in sorted_flist_2:
print(os.path.basename(f))img-01272022234452-11395.png
img-01272022234544-11400.png
img-01272022234646-11406.png
To perform the analysis, use the identify_flocs function.
Required arguments:
- flist: A sorted list of flocs (created above)
Optional arguments:
- resolution: A scale factor for the pixels. In these examples, there are 0.95 microns per pixel.
- min_area: Ignores flocs that are smaller than a specified area (in specified units). The default value of zero will save all flocs regardless of size
- max_edgewidth: A threshold parameter for identifying out of focus flocs. Quantifies the distance over which the transition from bacgkround color to floc color is spread averaged over the perimeter of the floc. Shorter edgewidths are more focused. The default value of Inf will save all flocs regardless of focus
- extra_params: A list of parameters to save in addition to area and edgewidth. See the documentation for skimage.measure.regionprops for a list of options.
- index: Choose which images in the list to process. Default value is None, which will process all images. If index is an integer, only one image is processed and the function returns relevant outputs. If index is a list of integers or None, function returns None
- save: Boolean, if true, output is saved as .csv files that are named identically to images
- return_data: Boolean, if True, results are returned as a list of floc objects. Note that this will cause memory issues if analyzing more than a few images
- njobs: int, how many cores to use simultaneously. If njobs>1, parallel processing is performed using joblib
- report_progress: Boolean. True prints progress to console.
# Run on image set 1:
out1 = flocpy.identify_flocs(sorted_flist_1,
resolution=0.95,
min_diameter=0,
max_edgewidth=np.inf,
extra_params=[],
index=None,
save=False,
return_data=True,
n_jobs=2,
report_progress=True)[Parallel(n_jobs=2)]: Using backend LokyBackend with 2 concurrent workers.
[Parallel(n_jobs=2)]: Done 1 tasks | elapsed: 13.0s
[Parallel(n_jobs=2)]: Done 4 tasks | elapsed: 16.0s
[Parallel(n_jobs=2)]: Done 6 out of 8 | elapsed: 18.4s remaining: 6.1s
[Parallel(n_jobs=2)]: Done 8 out of 8 | elapsed: 21.1s remaining: 0.0s
[Parallel(n_jobs=2)]: Done 8 out of 8 | elapsed: 21.1s finished
# Visualize results
fig, ax = plt.subplots(figsize=(14, 8), nrows=2, ncols=2)
ax[0,0].axis('off')
ax[1,0].axis('off')
ax[0,1].axis('off')
ax[1,1].axis('off')
imgix = 4
ax[0,0].set_title('Raw Image')
out1[imgix].plot_segmentation(ax[0,0], use_raw_img=True, show_overlay=False, show_boxes=False,
box_color='k', box_thickness=1)
ax[0,1].set_title('Preprocessed Image')
out1[imgix].plot_segmentation(ax[0,1], use_raw_img=False, show_overlay=False, show_boxes=False,
box_color='k', box_thickness=1)
ax[1,0].set_title('Segmented Image')
out1[imgix].plot_segmentation(ax[1,0], use_raw_img=False, show_overlay=True, show_boxes=False,
box_color='k', box_thickness=1)
ax[1,1].set_title('Flocs that meet area and focus criteria')
out1[imgix].plot_segmentation(ax[1,1], min_diameter=10, max_edgewidth=10, use_raw_img=False, show_overlay=False, show_boxes=True,
box_color='r', box_thickness=1)
fig.patch.set_facecolor('white')
plt.tight_layout()# run on image set 2
out2 = flocpy.identify_flocs(sorted_flist_2,
resolution=0.95,
min_diameter=0,
max_edgewidth=np.inf,
extra_params=[],
index=None,
save=False,
return_data=True,
n_jobs=2,
report_progress=True)[Parallel(n_jobs=2)]: Using backend LokyBackend with 2 concurrent workers.
[Parallel(n_jobs=2)]: Done 3 out of 3 | elapsed: 38.6s remaining: 0.0s
[Parallel(n_jobs=2)]: Done 3 out of 3 | elapsed: 38.6s finished
# visualize results
fig, ax = plt.subplots(figsize=(16, 12), nrows=2, ncols=2)
ax[0,0].axis('off')
ax[1,0].axis('off')
ax[0,1].axis('off')
ax[1,1].axis('off')
ax[0,0].set_title('Raw Image')
out2[2].plot_segmentation(ax[0,0], use_raw_img=True, show_overlay=False, show_boxes=False)
ax[0,1].set_title('Preprocessed Image')
out2[2].plot_segmentation(ax[0,1], use_raw_img=False, show_overlay=False, show_boxes=False)
ax[1,0].set_title('Segmented Image')
out2[2].plot_segmentation(ax[1,0], use_raw_img=False, show_overlay=True, show_boxes=False)
ax[1,1].set_title('Flocs that meet area and focus criteria')
out2[2].plot_segmentation(ax[1,1], min_diameter=10, max_edgewidth=4, use_raw_img=False, show_overlay=False, show_boxes=True,
box_color='r', box_thickness=1)
fig.patch.set_facecolor('white')
plt.tight_layout()timeseries_flist = glob.glob(os.path.join(paths[1], '*.csv'))
sorted_flist_timeseries = sorted(timeseries_flist, key=custom_datetime)
for f in sorted_flist_timeseries[:3]:
print(os.path.basename(f))img-01272022234442-11394.csv
img-01272022234452-11395.csv
img-01272022234503-11396.csv
tlist = [custom_datetime(os.path.basename(f)) for f in sorted_flist_timeseries]
for t in tlist[:3]:
print(t)2022-01-27 23:44:42
2022-01-27 23:44:52
2022-01-27 23:45:03
focal_range = 2000 # microns
w_frame = 4000 * 0.95 # 4000 pixels * 0.95 microns per pixel
l_frame = 3000 * 0.95 # 3000 pixels * 0.95 microns per pixel
a_frame = w_frame * l_frame
rhos_mgl = 2650000 #sediment density in mg/l
flocstats_obj = flocpy.calculate_summarystats(sorted_flist_timeseries, frame_area=a_frame, focal_range=focal_range,
min_floc_diameter=10, max_edgewidth=4)
percentiles = np.array([0.1, 1, 2.5, 10, 16, 25, 36, 50, 64, 75, 84, 90, 97.5, 99, 99.9])
table_dict = flocstats_obj.get_flocstats_table(method='all', percentile=percentiles)
floc_dataframe = pd.DataFrame(table_dict)
floc_dataframe.set_index('index', inplace=True)Loading data
100%|██████████████████████████████████████████████████████████████████████████████████| 19/19 [00:00<00:00, 60.10it/s]
floc_dataframe
<style scoped>
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
</style>
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
| Cs | Df_0.1 | Df_1.0 | Df_2.5 | Df_10.0 | Df_16.0 | Df_25.0 | Df_36.0 | Df_50.0 | Df_64.0 | Df_75.0 | Df_84.0 | Df_90.0 | Df_97.5 | Df_99.0 | Df_99.9 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| index | ||||||||||||||||
| 0 | 244.641986 | 10.281881 | 12.638224 | 15.097269 | 22.73971 | 27.308699 | 33.005235 | 39.426682 | 47.699218 | 56.525374 | 64.710353 | 72.62339 | 78.942068 | 96.876102 | 100.595838 | 116.628579 |
Dfloc = floc_dataframe.iloc[0,1:].to_numpy()
fig, ax = plt.subplots()
ax.plot(Dfloc, percentiles, marker='o')
ax.set_ylim(0, 100)
ax.set_xlim(1e1, 2e2)
ax.set_xscale('log')
ax.set_xticks([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200])
ax.set_xticklabels([10, 20, 30, 40, 50, 60, '', 80, '', 100, 200])
ax.set_ylabel('% finer by mass')
ax.set_xlabel(r'Floc Diameter ($\mu$m)')
fig.patch.set_facecolor('white')table_dict = flocstats_obj.get_flocstats_table(index=tlist, method='frame', percentile=[16,50,84])
floc_dataframe = pd.DataFrame(table_dict)
floc_dataframe.set_index('index', inplace=True)floc_dataframe
<style scoped>
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
</style>
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
| Cs | Df_16 | Df_50 | Df_84 | |
|---|---|---|---|---|
| index | ||||
| 0 | 217.569729 | 25.793956 | 44.578975 | 64.273569 |
| 1 | 253.488955 | 27.497399 | 50.547275 | 74.029477 |
| 2 | 270.600607 | 27.733509 | 47.647773 | 66.883084 |
| 3 | 259.021484 | 26.410717 | 47.432775 | 71.669437 |
| 4 | 240.219236 | 26.762231 | 53.082091 | 82.394202 |
| 5 | 253.839165 | 27.907406 | 47.055850 | 64.351515 |
| 6 | 243.551511 | 27.681302 | 46.573389 | 72.909784 |
| 7 | 255.598728 | 28.096811 | 48.991445 | 67.221932 |
| 8 | 224.657981 | 26.444495 | 44.244575 | 67.828142 |
| 9 | 240.816866 | 25.560399 | 48.158206 | 73.106468 |
| 10 | 258.467996 | 26.796867 | 47.755848 | 81.379993 |
| 11 | 256.799830 | 28.847548 | 49.171886 | 67.951771 |
| 12 | 274.492437 | 27.664109 | 47.503516 | 72.072222 |
| 13 | 184.158931 | 26.323117 | 44.441520 | 60.798018 |
| 14 | 226.819169 | 26.541093 | 45.137240 | 79.294132 |
| 15 | 279.418582 | 28.226286 | 49.958806 | 72.949866 |
| 16 | 238.396805 | 28.302398 | 45.984463 | 78.343901 |
| 17 | 232.289381 | 28.300562 | 48.408588 | 70.510703 |
| 18 | 237.990339 | 26.009581 | 51.142155 | 71.572765 |
fig, ax = plt.subplots()
ax.plot(floc_dataframe.Df_50, marker='o')
ax.set_xlabel('Frame number')
ax.set_xticks(np.arange(0, 19, 3))
ax.set_ylabel(r'Median Floc Diameter ($\mu$m)')
fig.patch.set_facecolor('white')


