4D Object By Change - Customizing the algorithm
The 4D-OBC implementation provided by py4dgeo
can be customized similarly to how M3C2 is customized. The fundamental idea is to create your own algorithm class derived from py4dgeo
’s RegionGrowingAlgorithm
class and override only those parts of the algorithm that you want to change. This notebook introduces the currently existing customization points.
[1]:
import py4dgeo
import numpy as np
import _py4dgeo # The C++ bindings module for py4dgeo
[2]:
# Load test data
analysis = py4dgeo.SpatiotemporalAnalysis("synthetic.zip")
[3]:
# We are disabling log messages on this tutorial to increase the readability of the output
import logging
logging.disable()
Distance Measure
By default, 4D-OBC uses a normalized Dynamic Time Warping distance measure. You can provide your own Python function, although all the same warnings as with M3C2 apply: Python code will be significantly slower compared to C++ implementations and will be run sequentially even if you are using OpenMP.
[4]:
def custom_distance(params: _py4dgeo.TimeseriesDistanceFunctionData):
mask = ~np.isnan(params.ts1) & ~np.isnan(params.ts2)
if not np.any(mask):
return np.nan
# Mask the two input arrays
masked_ts1 = params.ts1[mask]
masked_ts2 = params.ts2[mask]
return np.sum(np.abs(masked_ts1 - masked_ts2)) / np.sum(
np.abs(np.maximum(masked_ts1, masked_ts2))
)
[5]:
class CustomDistance4DOBC(py4dgeo.RegionGrowingAlgorithm):
def distance_measure(self):
return custom_distance
[6]:
analysis.invalidate_results()
objects = CustomDistance4DOBC(
neighborhood_radius=2.0,
seed_subsampling=20,
).run(analysis)
The params
data structure passed to the distance function contains the following fields: * ts1
and ts2
are the time series to compare which may include NaN
values * norm1
and norm2
which are normalization factors
Prioritizing seeds
The 4D-OBC algorithm finds a number of seed locations for region growing and then prioritizes these seeds by sorting them according to a criterion. You can pass your own criterium like this:
[7]:
def sorting_criterion(seed):
# Choose a random score, resulting in random seed order
return np.random.rand()
[8]:
class CustomSeedSorting(py4dgeo.RegionGrowingAlgorithm):
def seed_sorting_scorefunction(self):
return sorting_criterion
[9]:
analysis.invalidate_results()
objects = CustomSeedSorting(
neighborhood_radius=2.0,
seed_subsampling=20,
).run(analysis)
Rejecting grown objects
After the region growing is done, the algorithm class calls it method filter_objects
to check whether the object should be used or discarded. The method must return True
(keep) or False
(discard):
[10]:
class RejectSmallObjects(py4dgeo.RegionGrowingAlgorithm):
def filter_objects(self, obj):
return len(obj.indices) > 10
[11]:
analysis.invalidate_results()
objects = RejectSmallObjects(
neighborhood_radius=2.0,
seed_subsampling=20,
).run(analysis)
Seed point detection
If you would like to run an entirely different algorithm to determine the seeds for region growing, you can do so by overriding find_seedpoints
:
[12]:
from py4dgeo.segmentation import RegionGrowingSeed
[13]:
class DifferentSeeds(py4dgeo.RegionGrowingAlgorithm):
def find_seedpoints(self):
# Use one seed for corepoint 0 and the entire time interval
return [RegionGrowingSeed(0, 0, self.analysis.distances.shape[1] - 1)]
[14]:
analysis.invalidate_results()
objects = DifferentSeeds(
neighborhood_radius=2.0,
seed_subsampling=20,
).run(analysis)