Skip to content

Commit 65425c0

Browse files
authored
Merge pull request #9 from iteal/develop
Develop
2 parents c19d3dc + 8c9793f commit 65425c0

17 files changed

+80
-17
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![PyPI status](https://img.shields.io/pypi/status/wormpose.svg)](https://pypi.python.org/pypi/wormpose/)
44
[![PyPI version](https://badge.fury.io/py/wormpose.svg)](https://pypi.python.org/pypi/wormpose/)
5-
[![Build Status](https://travis-ci.org/iteal/wormpose.svg?branch=main)](https://travis-ci.org/iteal/wormpose)
5+
[![Build Status](https://travis-ci.com/iteal/wormpose.svg?branch=main)](https://travis-ci.com/iteal/wormpose)
66
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/wormpose.svg)](https://pypi.python.org/pypi/wormpose/)
77
[![DOI](https://zenodo.org/badge/246706178.svg)](https://zenodo.org/badge/latestdoi/246706178)
88
[![Maintainability](https://img.shields.io/codeclimate/maintainability/iteal/wormpose)](https://codeclimate.com/github/iteal/wormpose/maintainability)

docs/source/install.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,15 @@ or directly call the Python functions.
3737
wormpose COMMAND -h
3838
3939
40-
4140
**Troubleshooting**
4241

4342
If the `wormpose` command is not found, maybe you installed WormPose with the pip option ``--user``. Please check
4443
that the local user folder where pip installs the script is in your PATH
45-
variable.
44+
variable.
45+
46+
47+
**Limitations**
48+
49+
WormPose doesn't support at the moment multiple worms in one image.
50+
Only grayscale images (1 channel) are supported.
51+
The resolution of the worm should not be too low, we have tested it successfully with images where the worm length was 90 pixels, but having a smaller resolution will degrade the accuracy rapidly.

docs/source/tierpsy.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ For using `Open Worm Movement Database <http://movement.openworm.org/>`__ videos
2727

2828
**Limitations**
2929

30-
For Tierpsy files with several worms per file, WormPose will only load one worm, the one with the smallest index.
30+
Please use the option "extract timestamp" in Tierpsy so that the field "timestamp/raw" is set in the hdf5 file. The configuration without the timestamp is not currently supported in WormPose.
31+
For Tierpsy files with several worm indexes per file, WormPose will only load one worm, the one with the smallest index.
3132

3233
**Troubleshooting**
3334

wormpose/commands/calibrate_dataset.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from wormpose.commands import _log_parameters
1717
from wormpose.config import default_paths
1818
from wormpose.dataset import Dataset
19+
from wormpose.dataset.image_processing.options import add_image_processing_arguments
1920
from wormpose.dataset.loader import get_dataset_name
2021
from wormpose.dataset.loader import load_dataset
2122
from wormpose.images.scoring.centerline_accuracy_check import CenterlineAccuracyCheck
@@ -155,7 +156,10 @@ def calibrate(dataset_loader: str, dataset_path: str, **kwargs):
155156
os.makedirs(calibration_results_dir, exist_ok=True)
156157

157158
dataset = load_dataset(
158-
dataset_loader, dataset_path, resize_options=args.resize_options, selected_video_names=args.video_names
159+
dataset_loader,
160+
dataset_path,
161+
selected_video_names=args.video_names,
162+
**vars(args),
159163
)
160164

161165
calibrator = _Calibrator(
@@ -178,10 +182,8 @@ def main():
178182

179183
parser = argparse.ArgumentParser()
180184

181-
# inputs
182185
parser.add_argument("dataset_loader", type=str)
183186
parser.add_argument("dataset_path", type=str)
184-
add_resizing_arguments(parser)
185187
parser.add_argument(
186188
"--video_names",
187189
type=str,
@@ -199,6 +201,8 @@ def main():
199201
"--save_images", default=False, action="store_true", help="Also save the images used for the calibration"
200202
)
201203
parser.add_argument("--random_seed", type=int, help="Optional random seed for deterministic results")
204+
add_resizing_arguments(parser)
205+
add_image_processing_arguments(parser)
202206

203207
args = parser.parse_args()
204208

wormpose/commands/evaluate_model.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from wormpose.config.default_paths import CONFIG_FILENAME
2424
from wormpose.config.experiment_config import load_config, add_config_argument
2525
from wormpose.dataset import Dataset
26+
from wormpose.dataset.image_processing.options import WORM_IS_LIGHTER
2627
from wormpose.dataset.loader import load_dataset, get_dataset_name
2728
from wormpose.dataset.loaders.resizer import ResizeOptions
2829
from wormpose.images.scoring import ResultsScoring, BaseScoringDataManager
@@ -172,6 +173,7 @@ def evaluate(dataset_path: str, **kwargs):
172173
dataset_path=dataset_path,
173174
selected_video_names=args.video_names,
174175
resize_options=ResizeOptions(resize_factor=config.resize_factor),
176+
**{WORM_IS_LIGHTER: config.worm_is_lighter},
175177
)
176178

177179
pkl_filenames = _generate_synthetic_data(

wormpose/commands/generate_training_data.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
CONFIG_FILENAME,
2424
)
2525
from wormpose.config.experiment_config import save_config, ExperimentConfig
26+
from wormpose.dataset.image_processing.options import (
27+
add_image_processing_arguments,
28+
WORM_IS_LIGHTER,
29+
)
2630
from wormpose.dataset.loader import get_dataset_name
2731
from wormpose.dataset.loader import load_dataset
2832
from wormpose.dataset.loaders.resizer import add_resizing_arguments, ResizeOptions
@@ -53,6 +57,8 @@ def _parse_arguments(kwargs: dict):
5357
kwargs["video_names"] = None
5458
if kwargs.get("random_seed") is None:
5559
kwargs["random_seed"] = None
60+
if kwargs.get(WORM_IS_LIGHTER) is None:
61+
kwargs[WORM_IS_LIGHTER] = False
5662
kwargs["temp_dir"] = tempfile.mkdtemp(dir=kwargs["temp_dir"])
5763
kwargs["resize_options"] = ResizeOptions(**kwargs)
5864

@@ -89,8 +95,8 @@ def generate(dataset_loader: str, dataset_path: str, **kwargs):
8995
dataset = load_dataset(
9096
dataset_loader=dataset_loader,
9197
dataset_path=dataset_path,
92-
resize_options=args.resize_options,
9398
selected_video_names=args.video_names,
99+
**vars(args),
94100
)
95101

96102
start = time.time()
@@ -129,6 +135,7 @@ def generate(dataset_loader: str, dataset_path: str, **kwargs):
129135
num_eval_samples=num_eval_samples,
130136
resize_factor=args.resize_options.resize_factor,
131137
video_names=dataset.video_names,
138+
worm_is_lighter=getattr(args, WORM_IS_LIGHTER),
132139
),
133140
os.path.join(experiment_dir, CONFIG_FILENAME),
134141
)
@@ -157,6 +164,7 @@ def main():
157164
parser.add_argument("--num_process", type=int, help="How many worker processes")
158165
parser.add_argument("--random_seed", type=int, help="Optional random seed for deterministic results")
159166
add_resizing_arguments(parser)
167+
add_image_processing_arguments(parser)
160168
args = parser.parse_args()
161169

162170
last_progress = None

wormpose/commands/predict_dataset.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from wormpose.config.default_paths import RESULTS_FILENAME, CONFIG_FILENAME
2525
from wormpose.config.experiment_config import load_config, add_config_argument
2626
from wormpose.dataset.features import Features
27+
from wormpose.dataset.image_processing.options import WORM_IS_LIGHTER
2728
from wormpose.dataset.loader import get_dataset_name
2829
from wormpose.dataset.loader import load_dataset
2930
from wormpose.dataset.loaders.resizer import ResizeOptions
@@ -171,6 +172,7 @@ def predict(dataset_path: str, **kwargs):
171172
dataset_path=dataset_path,
172173
selected_video_names=args.video_names,
173174
resize_options=ResizeOptions(resize_factor=config.resize_factor),
175+
**{WORM_IS_LIGHTER: config.worm_is_lighter},
174176
)
175177

176178
keras_model = tf.keras.models.load_model(args.model_path, compile=False)

wormpose/config/experiment_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def __init__(
3434
theta_dimensions: int = None,
3535
resize_factor: float = None,
3636
video_names: List[str] = None,
37+
worm_is_lighter: bool = None,
3738
):
3839
self.num_train_samples = num_train_samples
3940
self.num_eval_samples = num_eval_samples
@@ -42,6 +43,7 @@ def __init__(
4243
self.theta_dimensions = theta_dimensions
4344
self.resize_factor = resize_factor
4445
self.video_names = video_names
46+
self.worm_is_lighter = worm_is_lighter
4547

4648

4749
def save_config(experiment_config: ExperimentConfig, config_filepath: str):

wormpose/dataset/base_dataset.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ class BaseFramePreprocessing(ABC):
1313
This object must be pickable (no inner functions for example)
1414
"""
1515

16+
def __init__(self, *args, **kwargs):
17+
pass
18+
1619
@abstractmethod
1720
def process(self, frame: np.ndarray) -> Tuple[np.ndarray, int]:
1821
"""

wormpose/dataset/image_processing/image_utils.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,17 @@ def segment_foreground(
5555
foreground_close_struct_element,
5656
foreground_dilate_struct_element,
5757
threshold_fn: Callable[[np.ndarray], int],
58+
is_foreground_lighter_than_background: bool,
5859
):
5960
"""
6061
Processes a frame to isolate the object of interest (worm) from the background
6162
6263
:param frame: image to process
6364
:param foreground_close_struct_element: morphological element to close holes in the foreground mask
6465
:param foreground_dilate_struct_element: morphological element to expand the foreground mask
65-
:param threshold_fn: function that will return the threshold to separate forefround from background in a frame
66+
:param threshold_fn: function that will return the threshold to separate foreground from background in a frame
67+
:param is_foreground_lighter_than_background: set to True if the foreground object of interest is lighter
68+
(pixel values are on average higher) than the background
6669
:return: segmentation mask with values of 1 for the worm object and 0 for the background,
6770
and average value of the background pixels
6871
"""
@@ -73,10 +76,14 @@ def segment_foreground(
7376
# use the threshold to deduce background and foreground masks, fill in holes
7477
foreground_mask = (frame > 0).astype(np.uint8) * (frame < background_threshold).astype(np.uint8)
7578
foreground_mask = cv2.morphologyEx(foreground_mask, cv2.MORPH_CLOSE, foreground_close_struct_element)
76-
background_mask = ((frame > 0).astype(np.uint8) - foreground_mask) > 0
79+
background_mask = (((frame > 0).astype(np.uint8) - foreground_mask) > 0).astype(np.uint8)
80+
81+
# invert foreground and background masks if the foreground is lighter than the background
82+
if is_foreground_lighter_than_background:
83+
foreground_mask, background_mask = background_mask, foreground_mask
7784

7885
# calculate the average background color
79-
background_values = frame[background_mask]
86+
background_values = frame[background_mask.astype(bool)]
8087
background_color = int(np.mean(background_values)) if len(background_values) > 0 else 0
8188
background_color = frame.dtype.type(background_color)
8289

0 commit comments

Comments
 (0)