zamba 2.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zamba/__init__.py +15 -0
- zamba/__main__.py +3 -0
- zamba/cli.py +643 -0
- zamba/data/__init__.py +0 -0
- zamba/data/metadata.py +97 -0
- zamba/data/video.py +587 -0
- zamba/exceptions.py +7 -0
- zamba/image_cli.py +364 -0
- zamba/images/__init__.py +1 -0
- zamba/images/bbox.py +179 -0
- zamba/images/classifier.py +224 -0
- zamba/images/config.py +497 -0
- zamba/images/data.py +216 -0
- zamba/images/dataset/__init__.py +0 -0
- zamba/images/dataset/dataset.py +217 -0
- zamba/images/evaluate.py +113 -0
- zamba/images/manager.py +499 -0
- zamba/images/result.py +76 -0
- zamba/metrics.py +35 -0
- zamba/models/__init__.py +2 -0
- zamba/models/config.py +800 -0
- zamba/models/config_common.py +264 -0
- zamba/models/densepose/__init__.py +2 -0
- zamba/models/densepose/assets/Base-DensePose-RCNN-FPN.yaml +60 -0
- zamba/models/densepose/assets/chimp_5029_parts.csv +41 -0
- zamba/models/densepose/assets/chimp_texture_colors_flipped.tif +0 -0
- zamba/models/densepose/assets/densepose_rcnn_R_50_FPN_soft_animals_I0_finetune_16k.yaml +119 -0
- zamba/models/densepose/assets/densepose_rcnn_R_50_FPN_soft_chimps_finetune_4k.yaml +29 -0
- zamba/models/densepose/config.py +137 -0
- zamba/models/densepose/densepose_manager.py +535 -0
- zamba/models/depth_estimation/__init__.py +2 -0
- zamba/models/depth_estimation/config.py +128 -0
- zamba/models/depth_estimation/depth_manager.py +255 -0
- zamba/models/efficientnet_models.py +63 -0
- zamba/models/instantiation.py +132 -0
- zamba/models/model_manager.py +328 -0
- zamba/models/official_models/blank_nonblank/config.yaml +49 -0
- zamba/models/official_models/blank_nonblank/hparams.yaml +11 -0
- zamba/models/official_models/blank_nonblank/predict_configuration.yaml +48 -0
- zamba/models/official_models/blank_nonblank/train_configuration.yaml +71 -0
- zamba/models/official_models/blank_nonblank/val_metrics.json +9 -0
- zamba/models/official_models/european/config.yaml +42 -0
- zamba/models/official_models/european/hparams.yaml +21 -0
- zamba/models/official_models/european/predict_configuration.yaml +57 -0
- zamba/models/official_models/european/train_configuration.yaml +74 -0
- zamba/models/official_models/european/val_metrics.json +52 -0
- zamba/models/official_models/lila.science/config.yaml +3 -0
- zamba/models/official_models/slowfast/config.yaml +48 -0
- zamba/models/official_models/slowfast/hparams.yaml +46 -0
- zamba/models/official_models/slowfast/predict_configuration.yaml +78 -0
- zamba/models/official_models/slowfast/train_configuration.yaml +101 -0
- zamba/models/official_models/slowfast/val_metrics.json +136 -0
- zamba/models/official_models/speciesnet/config.yaml +3 -0
- zamba/models/official_models/time_distributed/config.yaml +49 -0
- zamba/models/official_models/time_distributed/hparams.yaml +42 -0
- zamba/models/official_models/time_distributed/predict_configuration.yaml +79 -0
- zamba/models/official_models/time_distributed/train_configuration.yaml +102 -0
- zamba/models/official_models/time_distributed/val_metrics.json +136 -0
- zamba/models/publish_models.py +186 -0
- zamba/models/registry.py +42 -0
- zamba/models/slowfast_models.py +116 -0
- zamba/models/utils.py +90 -0
- zamba/object_detection/__init__.py +3 -0
- zamba/object_detection/yolox/__init__.py +0 -0
- zamba/object_detection/yolox/assets/yolox_tiny_640_20220528.pth +0 -0
- zamba/object_detection/yolox/assets/yolox_tiny_640_20220528_model_kwargs.json +5 -0
- zamba/object_detection/yolox/megadetector_lite_yolox.py +351 -0
- zamba/object_detection/yolox/yolox_model.py +175 -0
- zamba/pytorch/__init__.py +0 -0
- zamba/pytorch/dataloaders.py +151 -0
- zamba/pytorch/finetuning.py +74 -0
- zamba/pytorch/layers.py +63 -0
- zamba/pytorch/transforms.py +168 -0
- zamba/pytorch/utils.py +89 -0
- zamba/pytorch_lightning/__init__.py +0 -0
- zamba/pytorch_lightning/base_module.py +126 -0
- zamba/pytorch_lightning/video_modules.py +218 -0
- zamba/settings.py +30 -0
- zamba/utils_cli.py +60 -0
- zamba/version.py +9 -0
- zamba-2.7.0.dist-info/METADATA +230 -0
- zamba-2.7.0.dist-info/RECORD +85 -0
- zamba-2.7.0.dist-info/WHEEL +4 -0
- zamba-2.7.0.dist-info/entry_points.txt +3 -0
- zamba-2.7.0.dist-info/licenses/LICENSE +21 -0
zamba/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
|
|
7
|
+
from zamba.version import __version__
|
|
8
|
+
|
|
9
|
+
__version__
|
|
10
|
+
|
|
11
|
+
logger.remove()
|
|
12
|
+
log_level = os.getenv("LOG_LEVEL", "INFO")
|
|
13
|
+
logger.add(sys.stderr, level=log_level)
|
|
14
|
+
|
|
15
|
+
MODELS_DIRECTORY = Path(__file__).parents[1] / "zamba" / "models" / "official_models"
|
zamba/__main__.py
ADDED
zamba/cli.py
ADDED
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from pydantic.error_wrappers import ValidationError
|
|
7
|
+
from tqdm import tqdm
|
|
8
|
+
import typer
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
from zamba import MODELS_DIRECTORY
|
|
12
|
+
from zamba.models.config_common import ModelEnum, RegionEnum
|
|
13
|
+
from zamba.version import __version__
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from zamba.models.densepose import DensePoseOutputEnum
|
|
17
|
+
except ImportError:
|
|
18
|
+
DensePoseOutputEnum = str # graceful fallback when densepose extra not installed
|
|
19
|
+
|
|
20
|
+
# make logger work with tqdm
|
|
21
|
+
logger.remove()
|
|
22
|
+
logger.add(lambda msg: tqdm.write(msg, end=""), colorize=True)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
app = typer.Typer(pretty_exceptions_show_locals=False)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _register_sub_apps():
|
|
29
|
+
"""Lazily add image and utils sub-apps so their deps are only imported when used."""
|
|
30
|
+
try:
|
|
31
|
+
from zamba.image_cli import app as image_app
|
|
32
|
+
|
|
33
|
+
app.add_typer(
|
|
34
|
+
image_app, name="image", help="Tools for working with images instead of videos."
|
|
35
|
+
)
|
|
36
|
+
except ImportError as exc:
|
|
37
|
+
logger.debug(f"Image CLI unavailable (image extra not installed): {exc}")
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
from zamba.utils_cli import app as utils_app
|
|
41
|
+
|
|
42
|
+
app.add_typer(utils_app, name="utils", help="Utilities")
|
|
43
|
+
except ImportError as exc:
|
|
44
|
+
logger.debug(f"Utils CLI unavailable: {exc}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
_register_sub_apps()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.command()
|
|
51
|
+
def train(
|
|
52
|
+
data_dir: Path = typer.Option(None, exists=True, help="Path to folder containing videos."),
|
|
53
|
+
labels: Path = typer.Option(None, exists=True, help="Path to csv containing video labels."),
|
|
54
|
+
model: ModelEnum = typer.Option(
|
|
55
|
+
"time_distributed",
|
|
56
|
+
help="Model to train. Model will be superseded by checkpoint if provided.",
|
|
57
|
+
),
|
|
58
|
+
checkpoint: Path = typer.Option(
|
|
59
|
+
None,
|
|
60
|
+
exists=True,
|
|
61
|
+
help="Model checkpoint path to use for training. If provided, model is not required.",
|
|
62
|
+
),
|
|
63
|
+
config: Path = typer.Option(
|
|
64
|
+
None,
|
|
65
|
+
exists=True,
|
|
66
|
+
help="Specify options using yaml configuration file instead of through command line options.",
|
|
67
|
+
),
|
|
68
|
+
batch_size: int = typer.Option(None, help="Batch size to use for training."),
|
|
69
|
+
gpus: int = typer.Option(
|
|
70
|
+
None,
|
|
71
|
+
help="Number of GPUs to use for training. If not specifiied, will use all GPUs found on machine.",
|
|
72
|
+
),
|
|
73
|
+
dry_run: bool = typer.Option(
|
|
74
|
+
None,
|
|
75
|
+
help="Runs one batch of train and validation to check for bugs.",
|
|
76
|
+
),
|
|
77
|
+
save_dir: Path = typer.Option(
|
|
78
|
+
None,
|
|
79
|
+
help="An optional directory in which to save the model checkpoint and configuration file. If not specified, will save to a `version_n` folder in your working directory.",
|
|
80
|
+
),
|
|
81
|
+
num_workers: int = typer.Option(
|
|
82
|
+
None,
|
|
83
|
+
help="Number of subprocesses to use for data loading.",
|
|
84
|
+
),
|
|
85
|
+
weight_download_region: RegionEnum = typer.Option(
|
|
86
|
+
None, help="Server region for downloading weights."
|
|
87
|
+
),
|
|
88
|
+
skip_load_validation: bool = typer.Option(
|
|
89
|
+
None,
|
|
90
|
+
help="Skip check that verifies all videos can be loaded prior to training. Only use if you're very confident all your videos can be loaded.",
|
|
91
|
+
),
|
|
92
|
+
yes: bool = typer.Option(
|
|
93
|
+
False,
|
|
94
|
+
"--yes",
|
|
95
|
+
"-y",
|
|
96
|
+
help="Skip confirmation of configuration and proceed right to training.",
|
|
97
|
+
),
|
|
98
|
+
):
|
|
99
|
+
"""Train a model on your labeled data.
|
|
100
|
+
|
|
101
|
+
If an argument is specified in both the command line and in a yaml file, the command line input will take precedence.
|
|
102
|
+
"""
|
|
103
|
+
from zamba.data.video import VideoLoaderConfig
|
|
104
|
+
from zamba.models.config import ModelConfig, TrainConfig
|
|
105
|
+
from zamba.models.model_manager import ModelManager
|
|
106
|
+
|
|
107
|
+
if config is not None:
|
|
108
|
+
with config.open() as f:
|
|
109
|
+
config_dict = yaml.safe_load(f)
|
|
110
|
+
config_file = config
|
|
111
|
+
else:
|
|
112
|
+
with (MODELS_DIRECTORY / f"{model.value}/config.yaml").open() as f:
|
|
113
|
+
config_dict = yaml.safe_load(f)
|
|
114
|
+
config_file = None
|
|
115
|
+
|
|
116
|
+
if "video_loader_config" in config_dict.keys():
|
|
117
|
+
video_loader_config = VideoLoaderConfig(**config_dict["video_loader_config"])
|
|
118
|
+
else:
|
|
119
|
+
video_loader_config = None
|
|
120
|
+
|
|
121
|
+
train_dict = config_dict["train_config"]
|
|
122
|
+
|
|
123
|
+
# override if any command line arguments are passed
|
|
124
|
+
if data_dir is not None:
|
|
125
|
+
train_dict["data_dir"] = data_dir
|
|
126
|
+
|
|
127
|
+
if labels is not None:
|
|
128
|
+
train_dict["labels"] = labels
|
|
129
|
+
|
|
130
|
+
if model != "time_distributed":
|
|
131
|
+
train_dict["model_name"] = model
|
|
132
|
+
|
|
133
|
+
if checkpoint is not None:
|
|
134
|
+
train_dict["checkpoint"] = checkpoint
|
|
135
|
+
|
|
136
|
+
if batch_size is not None:
|
|
137
|
+
train_dict["batch_size"] = batch_size
|
|
138
|
+
|
|
139
|
+
if gpus is not None:
|
|
140
|
+
train_dict["gpus"] = gpus
|
|
141
|
+
|
|
142
|
+
if dry_run is not None:
|
|
143
|
+
train_dict["dry_run"] = dry_run
|
|
144
|
+
|
|
145
|
+
if save_dir is not None:
|
|
146
|
+
train_dict["save_dir"] = save_dir
|
|
147
|
+
|
|
148
|
+
if num_workers is not None:
|
|
149
|
+
train_dict["num_workers"] = num_workers
|
|
150
|
+
|
|
151
|
+
if weight_download_region is not None:
|
|
152
|
+
train_dict["weight_download_region"] = weight_download_region
|
|
153
|
+
|
|
154
|
+
if skip_load_validation is not None:
|
|
155
|
+
train_dict["skip_load_validation"] = skip_load_validation
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
manager = ModelManager(
|
|
159
|
+
ModelConfig(
|
|
160
|
+
video_loader_config=video_loader_config,
|
|
161
|
+
train_config=TrainConfig(**train_dict),
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
except ValidationError as ex:
|
|
165
|
+
logger.error("Invalid configuration.")
|
|
166
|
+
raise typer.Exit(ex)
|
|
167
|
+
|
|
168
|
+
config = manager.config
|
|
169
|
+
|
|
170
|
+
# get species to confirm
|
|
171
|
+
spacer = "\n\t- "
|
|
172
|
+
species = spacer + spacer.join(
|
|
173
|
+
sorted(
|
|
174
|
+
[
|
|
175
|
+
c.split("species_", 1)[1]
|
|
176
|
+
for c in config.train_config.labels.filter(regex="species").columns
|
|
177
|
+
]
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
msg = f"""The following configuration will be used for training:
|
|
182
|
+
|
|
183
|
+
Config file: {config_file}
|
|
184
|
+
Data directory: {data_dir if data_dir is not None else config_dict["train_config"].get("data_dir")}
|
|
185
|
+
Labels csv: {labels if labels is not None else config_dict["train_config"].get("labels")}
|
|
186
|
+
Species: {species}
|
|
187
|
+
Model name: {config.train_config.model_name}
|
|
188
|
+
Checkpoint: {checkpoint if checkpoint is not None else config_dict["train_config"].get("checkpoint")}
|
|
189
|
+
Batch size: {config.train_config.batch_size}
|
|
190
|
+
Number of workers: {config.train_config.num_workers}
|
|
191
|
+
GPUs: {config.train_config.gpus}
|
|
192
|
+
Dry run: {config.train_config.dry_run}
|
|
193
|
+
Save directory: {config.train_config.save_dir}
|
|
194
|
+
Weight download region: {config.train_config.weight_download_region}
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
if yes:
|
|
198
|
+
typer.echo(f"{msg}\n\nSkipping confirmation and proceeding to train.")
|
|
199
|
+
else:
|
|
200
|
+
yes = typer.confirm(
|
|
201
|
+
f"{msg}\n\nIs this correct?",
|
|
202
|
+
abort=False,
|
|
203
|
+
default=True,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if yes:
|
|
207
|
+
# kick off training
|
|
208
|
+
manager.train()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@app.command()
|
|
212
|
+
def predict(
|
|
213
|
+
data_dir: Path = typer.Option(None, exists=True, help="Path to folder containing videos."),
|
|
214
|
+
filepaths: Path = typer.Option(
|
|
215
|
+
None, exists=True, help="Path to csv containing `filepath` column with videos."
|
|
216
|
+
),
|
|
217
|
+
model: ModelEnum = typer.Option(
|
|
218
|
+
"time_distributed",
|
|
219
|
+
help="Model to use for inference. Model will be superseded by checkpoint if provided.",
|
|
220
|
+
),
|
|
221
|
+
checkpoint: Path = typer.Option(
|
|
222
|
+
None,
|
|
223
|
+
exists=True,
|
|
224
|
+
help="Model checkpoint path to use for inference. If provided, model is not required.",
|
|
225
|
+
),
|
|
226
|
+
gpus: int = typer.Option(
|
|
227
|
+
None,
|
|
228
|
+
help="Number of GPUs to use for inference. If not specifiied, will use all GPUs found on machine.",
|
|
229
|
+
),
|
|
230
|
+
batch_size: int = typer.Option(None, help="Batch size to use for training."),
|
|
231
|
+
save: bool = typer.Option(
|
|
232
|
+
None,
|
|
233
|
+
help="Whether to save out predictions. If you want to specify the output directory, use save_dir instead.",
|
|
234
|
+
),
|
|
235
|
+
save_dir: Path = typer.Option(
|
|
236
|
+
None,
|
|
237
|
+
help="An optional directory in which to save the model predictions and configuration yaml. "
|
|
238
|
+
"Defaults to the current working directory if save is True.",
|
|
239
|
+
),
|
|
240
|
+
dry_run: bool = typer.Option(None, help="Runs one batch of inference to check for bugs."),
|
|
241
|
+
config: Path = typer.Option(
|
|
242
|
+
None,
|
|
243
|
+
exists=True,
|
|
244
|
+
help="Specify options using yaml configuration file instead of through command line options.",
|
|
245
|
+
),
|
|
246
|
+
proba_threshold: float = typer.Option(
|
|
247
|
+
None,
|
|
248
|
+
help="Probability threshold for classification between 0 and 1. If specified binary predictions "
|
|
249
|
+
"are returned with 1 being greater than the threshold, 0 being less than or equal to. If not "
|
|
250
|
+
"specified, probabilities between 0 and 1 are returned.",
|
|
251
|
+
),
|
|
252
|
+
output_class_names: bool = typer.Option(
|
|
253
|
+
None,
|
|
254
|
+
help="If True, we just return a video and the name of the most likely class. If False, "
|
|
255
|
+
"we return a probability or indicator (depending on --proba_threshold) for every "
|
|
256
|
+
"possible class.",
|
|
257
|
+
),
|
|
258
|
+
num_workers: int = typer.Option(
|
|
259
|
+
None,
|
|
260
|
+
help="Number of subprocesses to use for data loading.",
|
|
261
|
+
),
|
|
262
|
+
weight_download_region: RegionEnum = typer.Option(
|
|
263
|
+
None, help="Server region for downloading weights."
|
|
264
|
+
),
|
|
265
|
+
skip_load_validation: bool = typer.Option(
|
|
266
|
+
None,
|
|
267
|
+
help="Skip check that verifies all videos can be loaded prior to inference. Only use if you're very confident all your videos can be loaded.",
|
|
268
|
+
),
|
|
269
|
+
overwrite: bool = typer.Option(
|
|
270
|
+
None, "--overwrite", "-o", help="Overwrite outputs in the save directory if they exist."
|
|
271
|
+
),
|
|
272
|
+
yes: bool = typer.Option(
|
|
273
|
+
False,
|
|
274
|
+
"--yes",
|
|
275
|
+
"-y",
|
|
276
|
+
help="Skip confirmation of configuration and proceed right to prediction.",
|
|
277
|
+
),
|
|
278
|
+
):
|
|
279
|
+
"""Identify species in a video.
|
|
280
|
+
|
|
281
|
+
This is a command line interface for prediction on camera trap footage. Given a path to camera
|
|
282
|
+
trap footage, the predict function use a deep learning model to predict the presence or absense of
|
|
283
|
+
a variety of species of common interest to wildlife researchers working with camera trap data.
|
|
284
|
+
|
|
285
|
+
If an argument is specified in both the command line and in a yaml file, the command line input will take precedence.
|
|
286
|
+
"""
|
|
287
|
+
from zamba.data.video import VideoLoaderConfig
|
|
288
|
+
from zamba.models.config import ModelConfig, PredictConfig
|
|
289
|
+
from zamba.models.model_manager import ModelManager
|
|
290
|
+
|
|
291
|
+
if config is not None:
|
|
292
|
+
with config.open() as f:
|
|
293
|
+
config_dict = yaml.safe_load(f)
|
|
294
|
+
config_file = config
|
|
295
|
+
else:
|
|
296
|
+
with (MODELS_DIRECTORY / f"{model.value}/config.yaml").open() as f:
|
|
297
|
+
config_dict = yaml.safe_load(f)
|
|
298
|
+
config_file = None
|
|
299
|
+
|
|
300
|
+
if "video_loader_config" in config_dict.keys():
|
|
301
|
+
video_loader_config = VideoLoaderConfig(**config_dict["video_loader_config"])
|
|
302
|
+
else:
|
|
303
|
+
video_loader_config = None
|
|
304
|
+
|
|
305
|
+
predict_dict = config_dict["predict_config"]
|
|
306
|
+
|
|
307
|
+
# override if any command line arguments are passed
|
|
308
|
+
if data_dir is not None:
|
|
309
|
+
predict_dict["data_dir"] = data_dir
|
|
310
|
+
|
|
311
|
+
if filepaths is not None:
|
|
312
|
+
predict_dict["filepaths"] = filepaths
|
|
313
|
+
|
|
314
|
+
if model != "time_distributed":
|
|
315
|
+
predict_dict["model_name"] = model
|
|
316
|
+
|
|
317
|
+
if checkpoint is not None:
|
|
318
|
+
predict_dict["checkpoint"] = checkpoint
|
|
319
|
+
|
|
320
|
+
if batch_size is not None:
|
|
321
|
+
predict_dict["batch_size"] = batch_size
|
|
322
|
+
|
|
323
|
+
if gpus is not None:
|
|
324
|
+
predict_dict["gpus"] = gpus
|
|
325
|
+
|
|
326
|
+
if dry_run is not None:
|
|
327
|
+
predict_dict["dry_run"] = dry_run
|
|
328
|
+
|
|
329
|
+
if save is not None:
|
|
330
|
+
predict_dict["save"] = save
|
|
331
|
+
|
|
332
|
+
# save_dir takes precedence over save
|
|
333
|
+
if save_dir is not None:
|
|
334
|
+
predict_dict["save_dir"] = save_dir
|
|
335
|
+
|
|
336
|
+
if proba_threshold is not None:
|
|
337
|
+
predict_dict["proba_threshold"] = proba_threshold
|
|
338
|
+
|
|
339
|
+
if output_class_names is not None:
|
|
340
|
+
predict_dict["output_class_names"] = output_class_names
|
|
341
|
+
|
|
342
|
+
if num_workers is not None:
|
|
343
|
+
predict_dict["num_workers"] = num_workers
|
|
344
|
+
|
|
345
|
+
if weight_download_region is not None:
|
|
346
|
+
predict_dict["weight_download_region"] = weight_download_region
|
|
347
|
+
|
|
348
|
+
if skip_load_validation is not None:
|
|
349
|
+
predict_dict["skip_load_validation"] = skip_load_validation
|
|
350
|
+
|
|
351
|
+
if overwrite is not None:
|
|
352
|
+
predict_dict["overwrite"] = overwrite
|
|
353
|
+
|
|
354
|
+
try:
|
|
355
|
+
manager = ModelManager(
|
|
356
|
+
ModelConfig(
|
|
357
|
+
video_loader_config=video_loader_config,
|
|
358
|
+
predict_config=PredictConfig(**predict_dict),
|
|
359
|
+
)
|
|
360
|
+
)
|
|
361
|
+
except ValidationError as ex:
|
|
362
|
+
logger.error("Invalid configuration.")
|
|
363
|
+
raise typer.Exit(ex)
|
|
364
|
+
|
|
365
|
+
config = manager.config
|
|
366
|
+
|
|
367
|
+
msg = f"""The following configuration will be used for inference:
|
|
368
|
+
|
|
369
|
+
Config file: {config_file}
|
|
370
|
+
Data directory: {data_dir if data_dir is not None else config_dict["predict_config"].get("data_dir")}
|
|
371
|
+
Filepath csv: {filepaths if filepaths is not None else config_dict["predict_config"].get("filepaths")}
|
|
372
|
+
Model: {config.predict_config.model_name}
|
|
373
|
+
Checkpoint: {checkpoint if checkpoint is not None else config_dict["predict_config"].get("checkpoint")}
|
|
374
|
+
Batch size: {config.predict_config.batch_size}
|
|
375
|
+
Number of workers: {config.predict_config.num_workers}
|
|
376
|
+
GPUs: {config.predict_config.gpus}
|
|
377
|
+
Dry run: {config.predict_config.dry_run}
|
|
378
|
+
Save directory: {config.predict_config.save_dir}
|
|
379
|
+
Proba threshold: {config.predict_config.proba_threshold}
|
|
380
|
+
Output class names: {config.predict_config.output_class_names}
|
|
381
|
+
Weight download region: {config.predict_config.weight_download_region}
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
if yes:
|
|
385
|
+
typer.echo(f"{msg}\n\nSkipping confirmation and proceeding to prediction.")
|
|
386
|
+
else:
|
|
387
|
+
yes = typer.confirm(
|
|
388
|
+
f"{msg}\n\nIs this correct?",
|
|
389
|
+
abort=False,
|
|
390
|
+
default=True,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
if yes:
|
|
394
|
+
# kick off prediction
|
|
395
|
+
manager.predict()
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def version_callback(version: bool):
|
|
399
|
+
"""Print zamba package version and exit."""
|
|
400
|
+
if version:
|
|
401
|
+
typer.echo(__version__)
|
|
402
|
+
raise typer.Exit()
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
@app.callback()
|
|
406
|
+
def main(
|
|
407
|
+
version: Optional[bool] = typer.Option(
|
|
408
|
+
None,
|
|
409
|
+
"--version",
|
|
410
|
+
callback=version_callback,
|
|
411
|
+
is_eager=True,
|
|
412
|
+
help="Show zamba version and exit.",
|
|
413
|
+
),
|
|
414
|
+
):
|
|
415
|
+
"""Zamba is a tool built in Python to automatically identify the species seen
|
|
416
|
+
in camera trap videos from sites in Africa and Europe. Visit
|
|
417
|
+
https://zamba.drivendata.org/docs for more in-depth documentation."""
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
@app.command()
|
|
421
|
+
def densepose(
|
|
422
|
+
data_dir: Path = typer.Option(
|
|
423
|
+
None, exists=True, help="Path to video or image file or folder containing images/videos."
|
|
424
|
+
),
|
|
425
|
+
filepaths: Path = typer.Option(
|
|
426
|
+
None, exists=True, help="Path to csv containing `filepath` column with videos."
|
|
427
|
+
),
|
|
428
|
+
save_dir: Path = typer.Option(
|
|
429
|
+
None,
|
|
430
|
+
help="An optional directory for saving the output. Defaults to the current working directory.",
|
|
431
|
+
),
|
|
432
|
+
config: Path = typer.Option(
|
|
433
|
+
None,
|
|
434
|
+
exists=True,
|
|
435
|
+
help="Specify options using yaml configuration file instead of through command line options.",
|
|
436
|
+
),
|
|
437
|
+
fps: float = typer.Option(
|
|
438
|
+
1.0, help="Number of frames per second to process. Defaults to 1.0 (1 frame per second)."
|
|
439
|
+
),
|
|
440
|
+
output_type: DensePoseOutputEnum = typer.Option(
|
|
441
|
+
"chimp_anatomy",
|
|
442
|
+
help="If 'chimp_anatomy' will apply anatomy model from densepose to the rendering and create a CSV with "
|
|
443
|
+
"the anatomy visible in each frame. If 'segmentation', will just output the segmented area where an animal "
|
|
444
|
+
"is identified, which works for more species than chimpanzees.",
|
|
445
|
+
),
|
|
446
|
+
render_output: bool = typer.Option(
|
|
447
|
+
False,
|
|
448
|
+
help="If True, generate an output image or video with either the segmentation or anatomy rendered "
|
|
449
|
+
"depending on the `output_type` that is chosen.",
|
|
450
|
+
),
|
|
451
|
+
weight_download_region: RegionEnum = typer.Option(
|
|
452
|
+
None, help="Server region for downloading weights."
|
|
453
|
+
),
|
|
454
|
+
cache_dir: Path = typer.Option(
|
|
455
|
+
None,
|
|
456
|
+
exists=False,
|
|
457
|
+
help="Path to directory for model weights. Alternatively, specify with environment variable `MODEL_CACHE_DIR`. If not specified, user's cache directory is used.",
|
|
458
|
+
),
|
|
459
|
+
yes: bool = typer.Option(
|
|
460
|
+
False,
|
|
461
|
+
"--yes",
|
|
462
|
+
"-y",
|
|
463
|
+
help="Skip confirmation of configuration and proceed right to prediction.",
|
|
464
|
+
),
|
|
465
|
+
):
|
|
466
|
+
"""Run densepose algorithm on videos.
|
|
467
|
+
|
|
468
|
+
If an argument is specified in both the command line and in a yaml file, the command line input will take precedence.
|
|
469
|
+
"""
|
|
470
|
+
from zamba.models.densepose import DensePoseConfig
|
|
471
|
+
|
|
472
|
+
if config is not None:
|
|
473
|
+
with config.open() as f:
|
|
474
|
+
config_dict = yaml.safe_load(f)
|
|
475
|
+
config_file = config
|
|
476
|
+
else:
|
|
477
|
+
config_dict = {}
|
|
478
|
+
config_file = None
|
|
479
|
+
|
|
480
|
+
if "video_loader_config" not in config_dict.keys():
|
|
481
|
+
config_dict["video_loader_config"] = dict()
|
|
482
|
+
|
|
483
|
+
if fps is not None:
|
|
484
|
+
config_dict["video_loader_config"]["fps"] = fps
|
|
485
|
+
|
|
486
|
+
predict_dict = config_dict
|
|
487
|
+
|
|
488
|
+
# override if any command line arguments are passed
|
|
489
|
+
if data_dir is not None:
|
|
490
|
+
predict_dict["data_dir"] = data_dir
|
|
491
|
+
|
|
492
|
+
if filepaths is not None:
|
|
493
|
+
predict_dict["filepaths"] = filepaths
|
|
494
|
+
|
|
495
|
+
if save_dir is not None:
|
|
496
|
+
predict_dict["save_dir"] = save_dir
|
|
497
|
+
|
|
498
|
+
if weight_download_region is not None:
|
|
499
|
+
predict_dict["weight_download_region"] = weight_download_region
|
|
500
|
+
|
|
501
|
+
if cache_dir is not None:
|
|
502
|
+
predict_dict["cache_dir"] = cache_dir
|
|
503
|
+
|
|
504
|
+
if output_type is not None:
|
|
505
|
+
predict_dict["output_type"] = output_type
|
|
506
|
+
|
|
507
|
+
if render_output is not None:
|
|
508
|
+
predict_dict["render_output"] = render_output
|
|
509
|
+
|
|
510
|
+
try:
|
|
511
|
+
densepose_config = DensePoseConfig(**predict_dict)
|
|
512
|
+
except ValidationError as ex:
|
|
513
|
+
logger.error("Invalid configuration.")
|
|
514
|
+
raise typer.Exit(ex)
|
|
515
|
+
|
|
516
|
+
msg = f"""The following configuration will be used for inference:
|
|
517
|
+
|
|
518
|
+
Config file: {config_file}
|
|
519
|
+
Output type: {densepose_config.output_type}
|
|
520
|
+
Render output: {densepose_config.render_output}
|
|
521
|
+
Data directory: {data_dir if data_dir is not None else config_dict.get("data_dir")}
|
|
522
|
+
Filepath csv: {filepaths if filepaths is not None else config_dict.get("filepaths")}
|
|
523
|
+
Weight download region: {densepose_config.weight_download_region}
|
|
524
|
+
Cache directory: {densepose_config.cache_dir}
|
|
525
|
+
"""
|
|
526
|
+
|
|
527
|
+
if yes:
|
|
528
|
+
typer.echo(f"{msg}\n\nSkipping confirmation and proceeding to prediction.")
|
|
529
|
+
else:
|
|
530
|
+
yes = typer.confirm(
|
|
531
|
+
f"{msg}\n\nIs this correct?",
|
|
532
|
+
abort=False,
|
|
533
|
+
default=True,
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
if yes:
|
|
537
|
+
# kick off prediction
|
|
538
|
+
try:
|
|
539
|
+
with warnings.catch_warnings():
|
|
540
|
+
warnings.filterwarnings("ignore")
|
|
541
|
+
densepose_config.run_model()
|
|
542
|
+
except ImportError as e:
|
|
543
|
+
from zamba.models.densepose.densepose_manager import DENSEPOSE_INSTALL_MESSAGE
|
|
544
|
+
|
|
545
|
+
logger.error(DENSEPOSE_INSTALL_MESSAGE)
|
|
546
|
+
raise typer.Exit(1) from e
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
@app.command()
|
|
550
|
+
def depth(
|
|
551
|
+
filepaths: Path = typer.Option(
|
|
552
|
+
None, exists=True, help="Path to csv containing `filepath` column with videos."
|
|
553
|
+
),
|
|
554
|
+
data_dir: Path = typer.Option(None, exists=True, help="Path to folder containing videos."),
|
|
555
|
+
save_to: Path = typer.Option(
|
|
556
|
+
None,
|
|
557
|
+
help="An optional directory or csv path for saving the output. Defaults to `depth_predictions.csv` in the working directory.",
|
|
558
|
+
),
|
|
559
|
+
overwrite: bool = typer.Option(
|
|
560
|
+
None, "--overwrite", "-o", help="Overwrite output csv if it exists."
|
|
561
|
+
),
|
|
562
|
+
batch_size: int = typer.Option(None, help="Batch size to use for inference."),
|
|
563
|
+
num_workers: int = typer.Option(
|
|
564
|
+
None,
|
|
565
|
+
help="Number of subprocesses to use for data loading.",
|
|
566
|
+
),
|
|
567
|
+
gpus: int = typer.Option(
|
|
568
|
+
None,
|
|
569
|
+
help="Number of GPUs to use for inference. If not specifiied, will use all GPUs found on machine.",
|
|
570
|
+
),
|
|
571
|
+
model_cache_dir: Path = typer.Option(
|
|
572
|
+
None,
|
|
573
|
+
exists=False,
|
|
574
|
+
help="Path to directory for downloading model weights. Alternatively, specify with environment variable `MODEL_CACHE_DIR`. If not specified, user's cache directory is used.",
|
|
575
|
+
),
|
|
576
|
+
weight_download_region: RegionEnum = typer.Option(
|
|
577
|
+
None, help="Server region for downloading weights."
|
|
578
|
+
),
|
|
579
|
+
yes: bool = typer.Option(
|
|
580
|
+
False,
|
|
581
|
+
"--yes",
|
|
582
|
+
"-y",
|
|
583
|
+
help="Skip confirmation of configuration and proceed right to prediction.",
|
|
584
|
+
),
|
|
585
|
+
):
|
|
586
|
+
"""Estimate animal distance at each second in the video."""
|
|
587
|
+
from zamba.models.depth_estimation import DepthEstimationConfig
|
|
588
|
+
|
|
589
|
+
predict_dict = dict(filepaths=filepaths)
|
|
590
|
+
|
|
591
|
+
# override if any command line arguments are passed
|
|
592
|
+
if data_dir is not None:
|
|
593
|
+
predict_dict["data_dir"] = data_dir
|
|
594
|
+
if save_to is not None:
|
|
595
|
+
predict_dict["save_to"] = save_to
|
|
596
|
+
if overwrite is not None:
|
|
597
|
+
predict_dict["overwrite"] = overwrite
|
|
598
|
+
if batch_size is not None:
|
|
599
|
+
predict_dict["batch_size"] = batch_size
|
|
600
|
+
if num_workers is not None:
|
|
601
|
+
predict_dict["num_workers"] = num_workers
|
|
602
|
+
if gpus is not None:
|
|
603
|
+
predict_dict["gpus"] = gpus
|
|
604
|
+
if model_cache_dir is not None:
|
|
605
|
+
predict_dict["model_cache_dir"] = model_cache_dir
|
|
606
|
+
if weight_download_region is not None:
|
|
607
|
+
predict_dict["weight_download_region"] = weight_download_region
|
|
608
|
+
|
|
609
|
+
try:
|
|
610
|
+
depth_config = DepthEstimationConfig(**predict_dict)
|
|
611
|
+
except ValidationError as ex:
|
|
612
|
+
logger.error(f"Invalid configuration: {ex}")
|
|
613
|
+
raise typer.Exit(1)
|
|
614
|
+
|
|
615
|
+
msg = f"""The following configuration will be used for inference:
|
|
616
|
+
|
|
617
|
+
Filepath csv: {predict_dict["filepaths"]}
|
|
618
|
+
Data directory: {depth_config.data_dir}
|
|
619
|
+
Save to: {depth_config.save_to}
|
|
620
|
+
Overwrite: {depth_config.overwrite}
|
|
621
|
+
Batch size: {depth_config.batch_size}
|
|
622
|
+
Number of workers: {depth_config.num_workers}
|
|
623
|
+
GPUs: {depth_config.gpus}
|
|
624
|
+
Model cache: {depth_config.model_cache_dir}
|
|
625
|
+
Weight download region: {depth_config.weight_download_region}
|
|
626
|
+
"""
|
|
627
|
+
|
|
628
|
+
if yes:
|
|
629
|
+
typer.echo(f"{msg}\n\nSkipping confirmation and proceeding to prediction.")
|
|
630
|
+
else:
|
|
631
|
+
yes = typer.confirm(
|
|
632
|
+
f"{msg}\n\nIs this correct?",
|
|
633
|
+
abort=False,
|
|
634
|
+
default=True,
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
if yes:
|
|
638
|
+
# kick off prediction
|
|
639
|
+
depth_config.run_model()
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
if __name__ == "__main__":
|
|
643
|
+
app()
|
zamba/data/__init__.py
ADDED
|
File without changes
|