Custom Annotators and AggregatorsΒΆ

The Distant Viewing Toolkit provides several annotators and aggregators that we think will be useful to many users. Many analyses, however, will require additional functionality or might be interested in implementing novel bleeding-edge techniques. This short tutorial demonstrates how to build a custom annotator and aggregator.

In order to run the following code, download the video file at: https://github.com/distant-viewing/dvt/raw/master/tests/test-data/video-clip.mp4

In order to build custom annotators and aggregators, we need to load the abstract classes that will be extended. We will also load a data extraction object to test the code with:

from dvt.core import DataExtraction, FrameInput
from dvt.abstract import FrameAnnotator, Aggregator

As a straightforward example of a custom annotator, we will extract the red, green, and blue intensities from one pixel in the image. The specific pixel will be determined by arguments passed to our annotator:

class DemoAnnotator(FrameAnnotator):

    name = 'demo_anno'

    def __init__(self, **kwargs):
        self.row = kwargs.get('row')
        self.col = kwargs.get('col')

    def annotate(self, batch):
        img = batch.get_batch()
        fnames = batch.get_frame_names()

        output = {'frame': fnames,
                  'red': img[:, self.row, self.col, 0],
                  'green': img[:, self.row, self.col, 1],
                  'blue': img[:, self.row, self.col, 2]}

        return output

Running this gives:

dextra = DataExtraction(FrameInput(input_path="video-clip.mp4"))
dextra.run_annotators([DemoAnnotator(row=0, col=0)])

dextra.get_data()['demo_anno'].head()
    frame  red  green  blue
 0      0    4      2     0
 1      1    4      2     0
 2      2    3      3     0
 3      3    3      3     0
 4      4    3      3     0

Our custom aggregator will then simply take the average of the intensities across all of the frames:

class DemoAggregator(Aggregator):

    name = 'demo_agg'

    def aggregate(self, ldframe, **kwargs):
        dframe = ldframe['demo_anno']

        output = {'red': dframe["red"].mean(),
                  'green': dframe["green"].mean(),
                  'blue': dframe["blue"].mean()}

        return output

And running this gives an output with three columns:

dextra.run_aggregator(DemoAggregator())

dextra.get_data()['demo_agg']
red      green       blue
0  14.175781  11.107422  13.236328

For further examples of constructing annotators and aggregators, see the source code the objects pre-constructed in the Distant Viewing Toolkit Python package.