added an option to allow jumping beads to considered as part of the granular media surface
-> grasslooper v1.1 fixes #1
This commit is contained in:
parent
b7d4966cb9
commit
5cc55990f3
|
@ -9,7 +9,8 @@ import argparse
|
||||||
from improtools.imageprocessing import ImageProcessDebugger, IMovieProcessListener, NullMovieProcessListener, MovieProcessDebugger
|
from improtools.imageprocessing import ImageProcessDebugger, IMovieProcessListener, NullMovieProcessListener, MovieProcessDebugger
|
||||||
from typing import Dict, Tuple
|
from typing import Dict, Tuple
|
||||||
|
|
||||||
GRASSLOPER_VERSION = '1.0'
|
GRASSLOPER_VERSION = '1.1'
|
||||||
|
|
||||||
|
|
||||||
def create_slope_image(image_width: int, image_height: int, p1_angle: float, p1_radius: float, bg_color: float = 0.0, fg_color: float = 1.0):
|
def create_slope_image(image_width: int, image_height: int, p1_angle: float, p1_radius: float, bg_color: float = 0.0, fg_color: float = 1.0):
|
||||||
"""
|
"""
|
||||||
|
@ -91,8 +92,9 @@ def hdf5_to_trac_data(hdf5_file_path: Path):
|
||||||
|
|
||||||
class SlopeFinder():
|
class SlopeFinder():
|
||||||
|
|
||||||
def __init__(self, beads_radius: float):
|
def __init__(self, beads_radius: float, exclude_jumping_beads: bool):
|
||||||
self.beads_radius = beads_radius
|
self.beads_radius = beads_radius
|
||||||
|
self.exclude_jumping_beads = exclude_jumping_beads
|
||||||
|
|
||||||
def find_slope(self, trac_data: TracData, frame_index: int, mo_pro_listener: IMovieProcessListener = NullMovieProcessListener()):
|
def find_slope(self, trac_data: TracData, frame_index: int, mo_pro_listener: IMovieProcessListener = NullMovieProcessListener()):
|
||||||
isbead_image = SlopeFinder.create_isbead_image(trac_data, frame_index, self.beads_radius)
|
isbead_image = SlopeFinder.create_isbead_image(trac_data, frame_index, self.beads_radius)
|
||||||
|
@ -116,9 +118,13 @@ class SlopeFinder():
|
||||||
mo_pro_listener.onImage(labels_image, 'labels')
|
mo_pro_listener.onImage(labels_image, 'labels')
|
||||||
is_non_jumping_bead_image = np.zeros(shape=labels_image.shape, dtype=np.uint8)
|
is_non_jumping_bead_image = np.zeros(shape=labels_image.shape, dtype=np.uint8)
|
||||||
bead_area = math.pi * self.beads_radius * self.beads_radius
|
bead_area = math.pi * self.beads_radius * self.beads_radius
|
||||||
|
min_area = bead_area * 1.1 if self.exclude_jumping_beads else 0.0
|
||||||
|
# print(f'self.exclude_jumping_beads = {self.exclude_jumping_beads}')
|
||||||
|
# print(f'min_area = {min_area}')
|
||||||
for label in range(1, num_labels): # ignore the first label, as it's the background
|
for label in range(1, num_labels): # ignore the first label, as it's the background
|
||||||
area = stats[label][4]
|
area = stats[label][4]
|
||||||
if area > bead_area * 1.1:
|
# print(f'area = {area}')
|
||||||
|
if area > min_area:
|
||||||
is_non_jumping_bead_image[labels_image == label] = 255
|
is_non_jumping_bead_image[labels_image == label] = 255
|
||||||
mo_pro_listener.onImage(is_non_jumping_bead_image, 'non_jumping_beads')
|
mo_pro_listener.onImage(is_non_jumping_bead_image, 'non_jumping_beads')
|
||||||
|
|
||||||
|
@ -127,7 +133,7 @@ class SlopeFinder():
|
||||||
surface_y.fill(is_non_jumping_bead_image.shape[0])
|
surface_y.fill(is_non_jumping_bead_image.shape[0])
|
||||||
contours, hierarchy = cv2.findContours(is_non_jumping_bead_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
contours, hierarchy = cv2.findContours(is_non_jumping_bead_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
||||||
# print('num contours: ', len(contours))
|
# print('num contours: ', len(contours))
|
||||||
assert(len(contours) == 1)
|
assert len(contours) >= 1
|
||||||
contour = contours[0]
|
contour = contours[0]
|
||||||
for point in contour:
|
for point in contour:
|
||||||
x, y = point[0]
|
x, y = point[0]
|
||||||
|
@ -194,12 +200,24 @@ def save_results(results_file_path: Path, fitted_lines: Dict[int, scipy.stats._s
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
def str2bool(v):
|
||||||
|
if isinstance(v, bool):
|
||||||
|
return v
|
||||||
|
if v.lower() in ('yes', 'true', 't', 'y', '1'):
|
||||||
|
return True
|
||||||
|
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise argparse.ArgumentTypeError('Boolean value expected.')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser('grassloper', description='estimates the slope of granular surface', epilog='example: \n\tgrassloper --part-pos-file=./grassloper.git/samples/TracTrac/sample002_track.hdf5 --particle-radius=11.0 --debug-dir=\'./improdebug\' --results-file ./grassloper.git/samples/TracTrac/sample002_track_slopes.hdf5')
|
parser = argparse.ArgumentParser('grassloper', description='estimates the slope of granular surface', epilog='example: \n\tgrassloper --part-pos-file=./grassloper.git/samples/TracTrac/sample002_track.hdf5 --particle-radius=11.0 --exclude-jumping-beads=true --debug-dir=\'./improdebug\' --results-file ./grassloper.git/samples/TracTrac/sample002_track_slopes.hdf5')
|
||||||
parser.add_argument('--part-pos-file', type=Path, default=None, required=True, help='the input file containing the particle positions')
|
parser.add_argument('--part-pos-file', type=Path, default=None, required=True, help='the input file containing the particle positions')
|
||||||
parser.add_argument('--results-file', type=Path, default=None, required=True, help='the output file containing for each frame one line fitting the granular surface (hdf5 file format)')
|
parser.add_argument('--results-file', type=Path, default=None, required=True, help='the output file containing for each frame one line fitting the granular surface (hdf5 file format)')
|
||||||
parser.add_argument('--part-pos-file-format', type=str, choices=['tractrac-hdf5'], default='tractrac-hdf5', help='the file format of the input file that contains particle positions')
|
parser.add_argument('--part-pos-file-format', type=str, choices=['tractrac-hdf5'], default='tractrac-hdf5', help='the file format of the input file that contains particle positions')
|
||||||
parser.add_argument('--particle-radius', type=float, required=True, help='the radius of the particles in pixels (make sure that the radius is big enough to unsure that neighbouring particles touch each other)', default=11)
|
parser.add_argument('--particle-radius', type=float, required=True, help='the radius of the particles in pixels (make sure that the radius is big enough to unsure that neighbouring particles touch each other)', default=11)
|
||||||
|
parser.add_argument('--exclude-jumping-beads', type=str2bool, default=True, required=True, help='if true, the isolated beads (supposedly because they are jumping) are not considered as being part of the granular media surface')
|
||||||
parser.add_argument('--debug-dir', type=Path, default=None, help='where to store image processing debug files')
|
parser.add_argument('--debug-dir', type=Path, default=None, help='where to store image processing debug files')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
# python3 ./tractrac.git/Python/tractrac.py -f ./grassloper.git/samples/sample002.avi --output 1 -a --saveplot
|
# python3 ./tractrac.git/Python/tractrac.py -f ./grassloper.git/samples/sample002.avi --output 1 -a --saveplot
|
||||||
|
@ -208,13 +226,13 @@ def main():
|
||||||
else:
|
else:
|
||||||
mo_pro_listener = NullMovieProcessListener()
|
mo_pro_listener = NullMovieProcessListener()
|
||||||
trac_data = hdf5_to_trac_data(args.part_pos_file)
|
trac_data = hdf5_to_trac_data(args.part_pos_file)
|
||||||
slope_finder = SlopeFinder(beads_radius=args.particle_radius)
|
slope_finder = SlopeFinder(beads_radius=args.particle_radius, exclude_jumping_beads=args.exclude_jumping_beads)
|
||||||
print(trac_data.num_frames)
|
print(f'number of frames: {trac_data.num_frames}')
|
||||||
fitted_lines = {}
|
fitted_lines = {}
|
||||||
for frame_index in range(1, trac_data.num_frames + 1):
|
for frame_index in range(1, trac_data.num_frames + 1):
|
||||||
mo_pro_listener.onImageProcessingStart()
|
mo_pro_listener.onImageProcessingStart()
|
||||||
line = slope_finder.find_slope(trac_data, frame_index=frame_index, mo_pro_listener=mo_pro_listener)
|
line = slope_finder.find_slope(trac_data, frame_index=frame_index, mo_pro_listener=mo_pro_listener)
|
||||||
print('frame %d: %s' % (frame_index, str(line)))
|
print(f'frame {frame_index}: {str(line)}')
|
||||||
fitted_lines[frame_index] = line
|
fitted_lines[frame_index] = line
|
||||||
mo_pro_listener.onImageProcessingEnd()
|
mo_pro_listener.onImageProcessingEnd()
|
||||||
|
|
||||||
|
|
9
setup.py
9
setup.py
|
@ -1,8 +1,11 @@
|
||||||
import setuptools
|
from setuptools import setup
|
||||||
|
|
||||||
setuptools.setup(
|
setup(
|
||||||
name="grassloper",
|
name="grassloper",
|
||||||
version="1.0",
|
description="a tool to estimate the slope of a granular surface",
|
||||||
|
version="1.1",
|
||||||
|
author='Guillaume Raffy',
|
||||||
|
author_email='guillaume.raffy@univ-rennes.fr',
|
||||||
packages=['grassloper'],
|
packages=['grassloper'],
|
||||||
install_requires=['improtools(>=1.0)', 'numpy', 'opencv-python', 'scipy', 'h5py'],
|
install_requires=['improtools(>=1.0)', 'numpy', 'opencv-python', 'scipy', 'h5py'],
|
||||||
entry_points={
|
entry_points={
|
||||||
|
|
Loading…
Reference in New Issue