Simple-Track 2.0.3__tar.gz → 2.0.5__tar.gz
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.
- {simple_track-2.0.3 → simple_track-2.0.5}/PKG-INFO +9 -7
- {simple_track-2.0.3 → simple_track-2.0.5}/README.md +8 -6
- {simple_track-2.0.3 → simple_track-2.0.5}/pyproject.toml +1 -1
- {simple_track-2.0.3 → simple_track-2.0.5}/src/Simple_Track.egg-info/PKG-INFO +9 -7
- {simple_track-2.0.3 → simple_track-2.0.5}/src/run_simple_track.py +7 -10
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/feature.py +22 -10
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/flow_solver.py +2 -3
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/frame.py +6 -3
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/frame_output.py +1 -2
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/frame_tracker.py +2 -4
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/load.py +1 -2
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/track.py +3 -4
- {simple_track-2.0.3 → simple_track-2.0.5}/tests/test_frame.py +28 -3
- {simple_track-2.0.3 → simple_track-2.0.5}/LICENSE +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/setup.cfg +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/src/Simple_Track.egg-info/SOURCES.txt +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/src/Simple_Track.egg-info/dependency_links.txt +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/src/Simple_Track.egg-info/entry_points.txt +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/src/Simple_Track.egg-info/requires.txt +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/src/Simple_Track.egg-info/top_level.txt +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/__init__.py +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/exceptions.py +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/src/simpletrack/utils.py +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/tests/test_feature.py +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/tests/test_flow_solver.py +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/tests/test_frame_tracker.py +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/tests/test_mwe_output.py +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/tests/test_simple_track_and_load.py +0 -0
- {simple_track-2.0.3 → simple_track-2.0.5}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Simple-Track
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: Threshold-based object tracking algorithm for 2D data
|
|
5
5
|
Author-email: Adam Gainford <adam.gainford@reading.ac.uk>, Thorwald Stein <t.h.m.stein@reading.ac.uk>
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -33,13 +33,15 @@ Features are tracked between consecutive frames of data by projecting feature fi
|
|
|
33
33
|
|
|
34
34
|
# Installation
|
|
35
35
|
|
|
36
|
-
Simple-Track can be installed using PyPi:
|
|
36
|
+
Simple-Track can be installed using PyPi or conda-forge:
|
|
37
37
|
|
|
38
38
|
```
|
|
39
39
|
python3 -m pip install simple-track
|
|
40
40
|
```
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
```
|
|
42
|
+
conda install conda-forge::simple-track
|
|
43
|
+
```
|
|
44
|
+
Coming soon to uv
|
|
43
45
|
|
|
44
46
|
# User Guide
|
|
45
47
|
|
|
@@ -177,10 +179,10 @@ Fields (`.field` files):
|
|
|
177
179
|
|
|
178
180
|
Features (`.csv` or `.txt` files):
|
|
179
181
|
* ID: Unique feature identifier that persists between frames (i.e., a feature retains the same id across all frames that it is tracked).
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
+
* centroid: (y, x) tuple containing central location of feature.
|
|
183
|
+
* size: Number of pixels spanned by the feature.
|
|
182
184
|
* dydx: (dy, dx) tuple containing motion vector that translated feature to its location in the current frame from the previous frame.
|
|
183
|
-
*
|
|
185
|
+
* max: Maximum value contained within the feature in the input data.
|
|
184
186
|
* lifetime: Number of timesteps the feature has existed for.
|
|
185
187
|
* accreted: List of IDs of features that were accreted by this feature, if applicable.
|
|
186
188
|
* parent: ID of parent feature that this feature split from, if applicable
|
|
@@ -11,13 +11,15 @@ Features are tracked between consecutive frames of data by projecting feature fi
|
|
|
11
11
|
|
|
12
12
|
# Installation
|
|
13
13
|
|
|
14
|
-
Simple-Track can be installed using PyPi:
|
|
14
|
+
Simple-Track can be installed using PyPi or conda-forge:
|
|
15
15
|
|
|
16
16
|
```
|
|
17
17
|
python3 -m pip install simple-track
|
|
18
18
|
```
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
```
|
|
20
|
+
conda install conda-forge::simple-track
|
|
21
|
+
```
|
|
22
|
+
Coming soon to uv
|
|
21
23
|
|
|
22
24
|
# User Guide
|
|
23
25
|
|
|
@@ -155,10 +157,10 @@ Fields (`.field` files):
|
|
|
155
157
|
|
|
156
158
|
Features (`.csv` or `.txt` files):
|
|
157
159
|
* ID: Unique feature identifier that persists between frames (i.e., a feature retains the same id across all frames that it is tracked).
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
+
* centroid: (y, x) tuple containing central location of feature.
|
|
161
|
+
* size: Number of pixels spanned by the feature.
|
|
160
162
|
* dydx: (dy, dx) tuple containing motion vector that translated feature to its location in the current frame from the previous frame.
|
|
161
|
-
*
|
|
163
|
+
* max: Maximum value contained within the feature in the input data.
|
|
162
164
|
* lifetime: Number of timesteps the feature has existed for.
|
|
163
165
|
* accreted: List of IDs of features that were accreted by this feature, if applicable.
|
|
164
166
|
* parent: ID of parent feature that this feature split from, if applicable
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Simple-Track
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: Threshold-based object tracking algorithm for 2D data
|
|
5
5
|
Author-email: Adam Gainford <adam.gainford@reading.ac.uk>, Thorwald Stein <t.h.m.stein@reading.ac.uk>
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -33,13 +33,15 @@ Features are tracked between consecutive frames of data by projecting feature fi
|
|
|
33
33
|
|
|
34
34
|
# Installation
|
|
35
35
|
|
|
36
|
-
Simple-Track can be installed using PyPi:
|
|
36
|
+
Simple-Track can be installed using PyPi or conda-forge:
|
|
37
37
|
|
|
38
38
|
```
|
|
39
39
|
python3 -m pip install simple-track
|
|
40
40
|
```
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
```
|
|
42
|
+
conda install conda-forge::simple-track
|
|
43
|
+
```
|
|
44
|
+
Coming soon to uv
|
|
43
45
|
|
|
44
46
|
# User Guide
|
|
45
47
|
|
|
@@ -177,10 +179,10 @@ Fields (`.field` files):
|
|
|
177
179
|
|
|
178
180
|
Features (`.csv` or `.txt` files):
|
|
179
181
|
* ID: Unique feature identifier that persists between frames (i.e., a feature retains the same id across all frames that it is tracked).
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
+
* centroid: (y, x) tuple containing central location of feature.
|
|
183
|
+
* size: Number of pixels spanned by the feature.
|
|
182
184
|
* dydx: (dy, dx) tuple containing motion vector that translated feature to its location in the current frame from the previous frame.
|
|
183
|
-
*
|
|
185
|
+
* max: Maximum value contained within the feature in the input data.
|
|
184
186
|
* lifetime: Number of timesteps the feature has existed for.
|
|
185
187
|
* accreted: List of IDs of features that were accreted by this feature, if applicable.
|
|
186
188
|
* parent: ID of parent feature that this feature split from, if applicable
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
import sys
|
|
3
2
|
|
|
4
3
|
from simpletrack import Tracker
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
def run_tracking():
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
# TODO: make argparser default way of handling inputs, including configs and loaders
|
|
8
|
+
# Need to make sure that changes don't affect pyproject.toml entry points
|
|
9
|
+
# easiest just to pass the parser in to run_tracking.
|
|
10
|
+
parser = argparse.ArgumentParser(description="Run Simple-Track")
|
|
11
|
+
parser.add_argument("configs", help="Path to one or more yaml config files.")
|
|
12
|
+
args = parser.parse_args()
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
for config_path in config_paths:
|
|
14
|
+
for config_path in args.configs:
|
|
13
15
|
# With None passed into run method, uses input path in config
|
|
14
16
|
Tracker(config_path).run()
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
if __name__ == "__main__":
|
|
18
|
-
# TODO: make argparser default way of handling inputs, including configs and loaders
|
|
19
|
-
# Need to make sure that changes don't affect pyproject.toml entry points
|
|
20
|
-
# easiest just to pass the parser in to run_tracking.
|
|
21
|
-
msg = "Run Simple-Track. Requires path to at least one yaml config file"
|
|
22
|
-
parser = argparse.ArgumentParser(description=msg)
|
|
23
20
|
run_tracking()
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
|
-
from typing import Union
|
|
3
2
|
|
|
4
3
|
import numpy as np
|
|
5
4
|
from numpy.typing import NDArray
|
|
@@ -10,7 +9,7 @@ from simpletrack.utils import check_arrays, check_valid_ids, native
|
|
|
10
9
|
class Feature:
|
|
11
10
|
"""
|
|
12
11
|
Object containing details about a specific feature, including its id, time,
|
|
13
|
-
centroid,
|
|
12
|
+
centroid, maximum value, lifetime, and whether it has undergone any
|
|
14
13
|
mergers of splits in the current timestep.
|
|
15
14
|
"""
|
|
16
15
|
|
|
@@ -31,7 +30,8 @@ class Feature:
|
|
|
31
30
|
self._parent = None
|
|
32
31
|
self._children = []
|
|
33
32
|
self._dydx = ()
|
|
34
|
-
self.
|
|
33
|
+
self._max = None
|
|
34
|
+
self._mean = None
|
|
35
35
|
|
|
36
36
|
def __repr__(self) -> str:
|
|
37
37
|
repr_str = f"Feature id: {self._id} (provisionally {self._provisional_id}), "
|
|
@@ -141,11 +141,18 @@ class Feature:
|
|
|
141
141
|
return native(self._dydx)
|
|
142
142
|
|
|
143
143
|
@property
|
|
144
|
-
def
|
|
144
|
+
def max(self) -> float:
|
|
145
145
|
"""
|
|
146
146
|
Maximum value of the Feature in the raw input data
|
|
147
147
|
"""
|
|
148
|
-
return self.
|
|
148
|
+
return self._max
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def mean(self) -> float:
|
|
152
|
+
"""
|
|
153
|
+
Mean value of the Feature in the raw input data
|
|
154
|
+
"""
|
|
155
|
+
return self._mean
|
|
149
156
|
|
|
150
157
|
@coords.setter
|
|
151
158
|
def coords(self, new_coords: NDArray[np.integer]) -> None:
|
|
@@ -185,9 +192,13 @@ class Feature:
|
|
|
185
192
|
id_of_accreting_feature = check_valid_ids(id_of_accreting_feature)
|
|
186
193
|
self._accreted_in_next_frame_by = id_of_accreting_feature
|
|
187
194
|
|
|
188
|
-
@
|
|
189
|
-
def
|
|
190
|
-
self.
|
|
195
|
+
@max.setter
|
|
196
|
+
def max(self, max_val: float) -> None:
|
|
197
|
+
self._max = max_val
|
|
198
|
+
|
|
199
|
+
@mean.setter
|
|
200
|
+
def mean(self, mean_val: float) -> None:
|
|
201
|
+
self._mean = mean_val
|
|
191
202
|
|
|
192
203
|
def calculate_centroid(self) -> tuple:
|
|
193
204
|
"""
|
|
@@ -261,7 +272,7 @@ class Feature:
|
|
|
261
272
|
|
|
262
273
|
def summarise(
|
|
263
274
|
self, output_type: str = "str", headers_only: bool = False
|
|
264
|
-
) ->
|
|
275
|
+
) -> str | dict | list:
|
|
265
276
|
"""
|
|
266
277
|
Return a summary of the Feature properties
|
|
267
278
|
|
|
@@ -280,7 +291,8 @@ class Feature:
|
|
|
280
291
|
"size": self.get_size(),
|
|
281
292
|
# native() does not convert dydx to python type for some reason
|
|
282
293
|
"dydx": tuple([val.item() for val in self._dydx]),
|
|
283
|
-
"
|
|
294
|
+
"max": self._max,
|
|
295
|
+
"mean": self._mean,
|
|
284
296
|
"lifetime": self._lifetime,
|
|
285
297
|
"accreted": self._accreted,
|
|
286
298
|
# This will not be output properly in the current workflow, since each
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
import warnings
|
|
3
3
|
from collections.abc import Iterable
|
|
4
|
-
from typing import Union
|
|
5
4
|
|
|
6
5
|
import numpy as np
|
|
7
6
|
import scipy.ndimage as ndimage
|
|
@@ -69,7 +68,7 @@ class FlowSolver:
|
|
|
69
68
|
self.apply_tukey_filtering = apply_tukey_filtering
|
|
70
69
|
|
|
71
70
|
def analyse_flow(
|
|
72
|
-
self, prev_field:
|
|
71
|
+
self, prev_field: Frame | NDArray, current_field: Frame | NDArray
|
|
73
72
|
) -> list[NDArray, NDArray]:
|
|
74
73
|
"""
|
|
75
74
|
Analyses previous field and current field to identify flow field. Uses phase
|
|
@@ -330,7 +329,7 @@ class FlowSolver:
|
|
|
330
329
|
|
|
331
330
|
subdomain_check = [
|
|
332
331
|
dim % sd_shape / 2
|
|
333
|
-
for dim, sd_shape in zip(feature_field_shape, subdomain_shape)
|
|
332
|
+
for dim, sd_shape in zip(feature_field_shape, subdomain_shape, strict=True)
|
|
334
333
|
]
|
|
335
334
|
return not any([remainder != 0 for remainder in subdomain_check])
|
|
336
335
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
|
-
from typing import Union
|
|
3
2
|
|
|
4
3
|
import numpy as np
|
|
5
4
|
import scipy.ndimage as ndimage
|
|
@@ -123,7 +122,7 @@ class Frame:
|
|
|
123
122
|
else:
|
|
124
123
|
return None
|
|
125
124
|
|
|
126
|
-
def get_flow(self) ->
|
|
125
|
+
def get_flow(self) -> NDArray | None:
|
|
127
126
|
"""
|
|
128
127
|
Get a list of the y-flow and x-flow fields derived by comparing features between
|
|
129
128
|
this frame and a frame from a previous timestep. Flow fields are both numpy
|
|
@@ -212,7 +211,8 @@ class Frame:
|
|
|
212
211
|
)
|
|
213
212
|
# If raw field is not None, use this to find max value within Feature
|
|
214
213
|
if self.raw_field is not None:
|
|
215
|
-
feature.
|
|
214
|
+
feature.max = np.max(self.raw_field[feature_mask])
|
|
215
|
+
feature.mean = np.mean(self.raw_field[feature_mask])
|
|
216
216
|
self._features[feature_id] = feature
|
|
217
217
|
|
|
218
218
|
def assign_displacements(self, y_flow: NDArray, x_flow: NDArray) -> None:
|
|
@@ -388,6 +388,9 @@ class Timeline:
|
|
|
388
388
|
def __init__(self):
|
|
389
389
|
self.timeline = {}
|
|
390
390
|
|
|
391
|
+
def __len__(self) -> int:
|
|
392
|
+
return len(self.timeline)
|
|
393
|
+
|
|
391
394
|
def add_to_timelime(self, frame: Frame) -> None:
|
|
392
395
|
"""
|
|
393
396
|
Add the input frame to the timeline, using the frame.get_time() to
|
|
@@ -2,7 +2,6 @@ import csv
|
|
|
2
2
|
import datetime
|
|
3
3
|
from ast import literal_eval
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Union
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
7
|
|
|
@@ -152,7 +151,7 @@ class LoadOutput:
|
|
|
152
151
|
(contanining Frames of field and Feature data) for further inspection and analysis.
|
|
153
152
|
"""
|
|
154
153
|
|
|
155
|
-
def __init__(self, st_data_path:
|
|
154
|
+
def __init__(self, st_data_path: str | Path):
|
|
156
155
|
self.path = Path(st_data_path)
|
|
157
156
|
self.strftime = "%Y%m%d_%H%M"
|
|
158
157
|
# Links field type names in outputs to attribute names in Frame
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Union
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
from numpy.typing import NDArray
|
|
5
3
|
|
|
@@ -493,7 +491,7 @@ class FrameTracker:
|
|
|
493
491
|
advected_feature_field: NDArray,
|
|
494
492
|
current_feature_field: NDArray,
|
|
495
493
|
current_feature_id: int,
|
|
496
|
-
) -> list[
|
|
494
|
+
) -> list[int | None, NDArray | None]:
|
|
497
495
|
"""
|
|
498
496
|
Use overlap histogram to find the closest matching feature id in the advected
|
|
499
497
|
field for the current_feature_id in the current_field. Any other ids that are
|
|
@@ -865,7 +863,7 @@ def advect_field_using_motion_vectors(
|
|
|
865
863
|
dx = np.mean(x_flow[feature_mask], dtype=int)
|
|
866
864
|
|
|
867
865
|
# Now, advect the feature to the new position
|
|
868
|
-
for y_coord, x_coord in zip(*feature_mask):
|
|
866
|
+
for y_coord, x_coord in zip(*feature_mask, strict=True):
|
|
869
867
|
advected_y_coord = y_coord + dy
|
|
870
868
|
advected_x_coord = x_coord + dx
|
|
871
869
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
|
-
from typing import Union
|
|
3
2
|
|
|
4
3
|
from numpy.typing import NDArray
|
|
5
4
|
|
|
@@ -39,7 +38,7 @@ class BaseLoader:
|
|
|
39
38
|
Loaders should be
|
|
40
39
|
"""
|
|
41
40
|
|
|
42
|
-
def __init__(self, input_data:
|
|
41
|
+
def __init__(self, input_data: list[str] | dict) -> None:
|
|
43
42
|
self.domain_shape = None
|
|
44
43
|
self.input_data = input_data
|
|
45
44
|
# Set the iterating list
|
|
@@ -3,7 +3,6 @@ Run the SimpleTrack algorithm to track objects through a sequence of images
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Union
|
|
7
6
|
|
|
8
7
|
from yaml import safe_load
|
|
9
8
|
|
|
@@ -19,7 +18,7 @@ class Tracker:
|
|
|
19
18
|
Simple-Track manager controlling inputs, processing, outputs
|
|
20
19
|
"""
|
|
21
20
|
|
|
22
|
-
def __init__(self, config_input:
|
|
21
|
+
def __init__(self, config_input: str | dict) -> None:
|
|
23
22
|
"""
|
|
24
23
|
Initialize SimpleTrack with configuration file
|
|
25
24
|
|
|
@@ -59,7 +58,7 @@ class Tracker:
|
|
|
59
58
|
self.frame_tracker = FrameTracker()
|
|
60
59
|
|
|
61
60
|
if "OUTPUT" in self.config:
|
|
62
|
-
self.skip_tracking = self.config["
|
|
61
|
+
self.skip_tracking = self.config["OUTPUT"].get("skip_tracking", False)
|
|
63
62
|
output_path = self.config["OUTPUT"].get("path", "./output")
|
|
64
63
|
expt_name = self.config["OUTPUT"].get(
|
|
65
64
|
"experiment_name", "Simple-Track Experiment"
|
|
@@ -80,7 +79,7 @@ class Tracker:
|
|
|
80
79
|
config_path,
|
|
81
80
|
)
|
|
82
81
|
|
|
83
|
-
def run(self, input_data:
|
|
82
|
+
def run(self, input_data: list[str] | dict = None) -> Timeline:
|
|
84
83
|
"""
|
|
85
84
|
Runs SimpleTrack using the designated config options.
|
|
86
85
|
|
|
@@ -103,7 +103,7 @@ def test_populate_features_valid_feature_field():
|
|
|
103
103
|
assert test_frame.features == expected_dict
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def
|
|
106
|
+
def test_populate_features_sets_max_property():
|
|
107
107
|
test_time = dt.datetime.now()
|
|
108
108
|
test_frame = Frame()
|
|
109
109
|
test_frame.time = test_time
|
|
@@ -123,8 +123,33 @@ def test_populate_features_sets_extreme_property():
|
|
|
123
123
|
test_frame.raw_field = test_raw_field
|
|
124
124
|
test_frame.populate_features()
|
|
125
125
|
|
|
126
|
-
assert test_frame.get_feature(1).
|
|
127
|
-
assert test_frame.get_feature(2).
|
|
126
|
+
assert test_frame.get_feature(1).max == 10
|
|
127
|
+
assert test_frame.get_feature(2).max == 20
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_populate_features_sets_mean_property():
|
|
131
|
+
test_time = dt.datetime.now()
|
|
132
|
+
test_frame = Frame()
|
|
133
|
+
test_frame.time = test_time
|
|
134
|
+
|
|
135
|
+
test_feature_field = test_field.copy()
|
|
136
|
+
test_feature_field[2:6, 2:6] = 1
|
|
137
|
+
test_feature_field[6:9, 6:9] = 2
|
|
138
|
+
|
|
139
|
+
test_raw_field = test_field.copy()
|
|
140
|
+
test_raw_field[2:6, 2:6] = 10
|
|
141
|
+
test_raw_field[2:6, 4:6] = 50
|
|
142
|
+
test_raw_field[6:9, 6:9] = 20
|
|
143
|
+
# Set another higher maximum within the field to check mean
|
|
144
|
+
# only picks up the values within the feature mask
|
|
145
|
+
test_raw_field[0:2, 0:2] = 100
|
|
146
|
+
|
|
147
|
+
test_frame.feature_field = test_feature_field
|
|
148
|
+
test_frame.raw_field = test_raw_field
|
|
149
|
+
test_frame.populate_features()
|
|
150
|
+
|
|
151
|
+
assert test_frame.get_feature(1).mean == 30
|
|
152
|
+
assert test_frame.get_feature(2).mean == 20
|
|
128
153
|
|
|
129
154
|
|
|
130
155
|
def test_populate_features_invalid_negative_features():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|