Process Data from a PushBroom Device With an Object Detector

This example will connect to the virtual pushbroom camera (using ObjectDetectorTest.hdr and its references), measure a white & dark reference. It will then create a processing context for the model object_detector_test.fluxmdl that contains a background masking filter and an object detector.

The input cube of this example looks like this when loaded into fluxTrainer:

_images/example_pushbroom_object_detector_input.png

This example demonstrates how the object detector in fluxEngine can be used even though the data is processed just one line at a time.

All detected objects are output.

C++

The source code of the example can be found in the file example_pushbroom_object_detector.cpp:

  1#if defined(_WIN32) && defined(_MSC_VER)
  2#include <windows.h>
  3#endif
  4
  5#include <iostream>
  6#include <iomanip>
  7#include <string>
  8#include <fstream>
  9#include <streambuf>
 10#include <algorithm>
 11#include <utility>
 12
 13#include <cstddef>
 14
 15#include <fluxEngine/fluxEngine>
 16
 17#include "paths.h"
 18#include "helpers.h"
 19
 20int main()
 21{
 22    try {
 23        std::cout << "fluxEngine version: " << fluxEngine::versionString() << std::endl;
 24        fluxEngine::Handle handle(readFile(g_licenseFileName));
 25        handle.setDriverBaseDirectory(g_driverDirectory);
 26        handle.setDriverIsolationExecutable(g_driverIsolationExecutable);
 27        handle.createProcessingThreads(4);
 28
 29        // Load virtual camera
 30        fluxEngine::EnumerationResult enumeratedDevices = fluxEngine::enumerateDevices(handle, -1, std::chrono::seconds{1});
 31        fluxEngine::EnumeratedDevice* virtualCameraDevice = nullptr;
 32        for (auto const& device : enumeratedDevices.devices) {
 33            if (device->driver->name == "VirtualHyperCamera") {
 34                virtualCameraDevice = device.get();
 35                break;
 36            }
 37        }
 38
 39        if (!virtualCameraDevice)
 40            throw std::runtime_error("Could not find virtual camera driver");
 41
 42        fluxEngine::ConnectionSettings connectionSettings;
 43        connectionSettings.driverName = virtualCameraDevice->driver->name;
 44        connectionSettings.driverType = virtualCameraDevice->driver->type;
 45        connectionSettings.id = virtualCameraDevice->id;
 46        connectionSettings.timeout = std::chrono::seconds{60};
 47        connectionSettings.connectionParameters["Cube"] = encodeFileNameForConnectionParameter(g_odCubeFileName);
 48        connectionSettings.connectionParameters["WhiteReferenceCube"] = encodeFileNameForConnectionParameter(g_odWhiteCubeFileName);
 49        connectionSettings.connectionParameters["DarkReferenceCube"] = encodeFileNameForConnectionParameter(g_odDarkCubeFileName);
 50
 51        std::cout << "Attempting to connect to device...\n" << std::flush;
 52        for (auto const& parameter : connectionSettings.connectionParameters)
 53            std::cout << "  - " << parameter.first << ": " << parameter.second << "\n" << std::flush;
 54        fluxEngine::DeviceGroup deviceGroup = fluxEngine::connectDeviceGroup(handle, connectionSettings);
 55        std::cout << "Connected.\n" << std::flush;
 56        fluxEngine::InstrumentDevice* camera = dynamic_cast<fluxEngine::InstrumentDevice*>(deviceGroup.primaryDevice());
 57        if (!camera) {
 58            deviceGroup.disconnect(std::chrono::seconds{5});
 59            throw std::runtime_error("The device is not an instrument device");
 60        }
 61
 62        camera->setupInternalBuffers(5);
 63
 64        /* Load model
 65         *
 66         * This should be done after connecting with the camera, in
 67         * case the license is tied to a camera serial number. (In
 68         * case the license is tied to a dongle or a mainboard id,
 69         * this may be done beforehand.)
 70         */
 71        fluxEngine::Model model = fluxEngine::Model(handle, fluxEngine::Model::FromFile, g_odModelFileName);
 72
 73        /* NOTE:
 74         * For real devices a this point the user should probably be
 75         * asked to insert a white reference underneath the camera.
 76         *
 77         * For the virtual device this is not required.
 78         */
 79
 80        fluxEngine::InstrumentDevice::AcquisitionParameters acqParams;
 81        std::cout << "Measuring white reference:\n" << std::flush;
 82        fluxEngine::BufferContainer whiteReference = fluxEngine::createRingBufferContainer(camera, 10);
 83        acqParams.referenceName = "WhiteReference";
 84        camera->startAcquisition(acqParams);
 85        for (int i = 0; i < 10; ++i) {
 86            fluxEngine::BufferInfo buffer = camera->retrieveBuffer(std::chrono::seconds{1});
 87            if (buffer.ok) {
 88                whiteReference.add(buffer);
 89                camera->returnBuffer(buffer.id);
 90            }
 91        }
 92        camera->stopAcquisition();
 93        std::cout << "Done.\n" << std::flush;
 94
 95        /* NOTE:
 96         * For real devices a this point the user should probably be
 97         * asked to obscure the optics in front of the camera in order
 98         * for a proper dark reference to be measured.
 99         *
100         * For the virtual device this is not required.
101         *
102         * Some cameras do have an internal shutter, where manual user
103         * intervention is also not required here.
104         */
105
106        std::cout << "Measuring dark reference:\n" << std::flush;
107        fluxEngine::BufferContainer darkReference = fluxEngine::createBufferContainer(camera, 10);
108        acqParams.referenceName = "DarkReference";
109        camera->startAcquisition(acqParams);
110        for (int i = 0; i < 10; ++i) {
111            fluxEngine::BufferInfo buffer = camera->retrieveBuffer(std::chrono::seconds{1});
112            if (buffer.ok) {
113                darkReference.add(buffer);
114                camera->returnBuffer(buffer.id);
115            }
116        }
117        camera->stopAcquisition();
118        std::cout << "Done.\n" << std::flush;
119
120        // Create recording context
121        fluxEngine::ProcessingContext::InstrumentParameters instrumentParameters;
122        instrumentParameters.whiteReference = &whiteReference;
123        instrumentParameters.darkReference = &darkReference;
124        fluxEngine::ProcessingContext ctx = fluxEngine::ProcessingContext::createInstrumentProcessingContext(camera, model, instrumentParameters);
125        ctx.setUseExtendedObjects(true);
126        int const sinkIndex = ctx.findOutputSink(/* outputId = */ 0);
127
128        /* NOTE:
129         * For real devices a this point the user should probably be
130         * asked to position the object to measure underneath the
131         * camera and start the motion of the motion control device
132         * they have.
133         *
134         * For the virtual device this is not required.
135         */
136
137        std::cout << "Starting acquisition:\n" << std::flush;
138        acqParams.referenceName = {};
139        camera->startAcquisition(acqParams);
140        std::cout << "Done.\n" << std::flush;
141
142        std::cout << "Processing the first 100 lines...\n" << std::flush;
143        int y = 0;
144        while (y < 100) {
145            fluxEngine::BufferInfo buffer = camera->retrieveBuffer(std::chrono::milliseconds{100});
146            if (!buffer.ok)
147                continue;
148
149            ctx.setSourceData(buffer);
150            ctx.processNext();
151
152            auto sinkData = ctx.outputSinkData(sinkIndex);
153            auto beginPointer = static_cast<fluxEngine::OutputExtendedObject const*>(sinkData.data);
154            auto endPointer = beginPointer + sinkData.sizes[0];
155            for (auto it = beginPointer; it != endPointer; ++it) {
156                std::cout << "Found object [at line number " << y
157                          << "]: bounding box (" << it->boundingBoxX() << ", " << it->boundingBoxY()
158                          << ") size (" << it->boundingBoxWidth() << ", " << it->boundingBoxHeight()
159                          << "), center of gravity (" << it->gravityCenterX() << ", " << it->gravityCenterY()
160                          << "), area " << it->area() << "\n" << std::flush;
161            }
162
163            camera->returnBuffer(buffer.id);
164            ++y;
165        }
166
167        std::cout << "Stopping acquisition:\n" << std::flush;
168        camera->stopAcquisition();
169        std::cout << "Done.\n" << std::flush;
170    } catch (std::exception& e) {
171        std::cerr << "Error: " << e.what() << std::endl;
172        return 1;
173    } catch (...) {
174        std::cerr << "Unknown error." << std::endl;
175        return 1;
176    }
177
178    return 0;
179}

This source file will compile to the executable ExamplePushBroomObjectDetector.

.NET

The source code of the example can be found in the file ExamplePushBroomObjectDetector\Program.cs.

  1using System;
  2
  3namespace ExamplePushBroomObjectDetector
  4{
  5    class Program
  6    {
  7        static void Main(string[] args)
  8        {
  9            Console.WriteLine("fluxEngine version: " + LuxFlux.fluxEngineNET.Version.String);
 10            var handle = new LuxFlux.fluxEngineNET.Handle(ExampleHelpers.IO.ReadLicenseFile());
 11            handle.SetDriverBaseDirectory(ExampleHelpers.Paths.DriverDirectory);
 12            handle.CreateProcessingThreads(4);
 13
 14            // Load virtual camera
 15            var enumeratedDevices = LuxFlux.fluxEngineNET.DeviceEnumeration.EnumerateDevices(handle, null, TimeSpan.FromSeconds(1));
 16            LuxFlux.fluxEngineNET.EnumeratedDevice virtualCameraDevice = null;
 17            foreach (var device in enumeratedDevices.Devices)
 18            {
 19                if (device.Driver.Name == "VirtualHyperCamera")
 20                {
 21                    virtualCameraDevice = device;
 22                    break;
 23                }
 24            }
 25
 26            if (virtualCameraDevice == null)
 27                throw new Exception("Could not find virtual camera driver");
 28
 29            var connectionSettings = new LuxFlux.fluxEngineNET.ConnectionSettings();
 30            connectionSettings.DriverName = virtualCameraDevice.Driver.Name;
 31            connectionSettings.DriverType = virtualCameraDevice.Driver.Type;
 32            connectionSettings.Id = virtualCameraDevice.Id;
 33            connectionSettings.Timeout = TimeSpan.FromSeconds(60);
 34            connectionSettings.ConnectionParameters = new System.Collections.Generic.Dictionary<string, string>();
 35            connectionSettings.ConnectionParameters["Cube"] = ExampleHelpers.Paths.ExampleDataFileName("ObjectDetectorTest.hdr");
 36            connectionSettings.ConnectionParameters["WhiteReferenceCube"] = ExampleHelpers.Paths.ExampleDataFileName("ObjectDetectorTest_White.hdr");
 37            connectionSettings.ConnectionParameters["DarkReferenceCube"] = ExampleHelpers.Paths.ExampleDataFileName("ObjectDetectorTest_Dark.hdr");
 38
 39            Console.WriteLine("Attempting to connect to device...");
 40            var deviceGroup = LuxFlux.fluxEngineNET.DeviceGroup.Connect(handle, connectionSettings);
 41            Console.WriteLine("Connected.");
 42            if (!(deviceGroup.PrimaryDevice is LuxFlux.fluxEngineNET.InstrumentDevice))
 43            {
 44                deviceGroup.Disconnect(TimeSpan.FromSeconds(5));
 45                throw new Exception("The device is not an instrument device.");
 46            }
 47            var camera = (LuxFlux.fluxEngineNET.InstrumentDevice)deviceGroup.PrimaryDevice;
 48
 49            camera.SetupInternalBuffers(5);
 50
 51            /* Load model
 52             *
 53             * This should be done after connecting with the camera, in
 54             * case the license is tied to a camera serial number. (In
 55             * case the license is tied to a dongle or a mainboard id,
 56             * this may be done beforehand.)
 57             */
 58            var model = LuxFlux.fluxEngineNET.Model.LoadFromFile(handle, ExampleHelpers.Paths.ExampleDataFileName("object_detector_test.fluxmdl"));
 59
 60            /* NOTE:
 61             * For real devices a this point the user should probably be
 62             * asked to insert a white reference underneath the camera.
 63             *
 64             * For the virtual device this is not required.
 65             */
 66
 67            var acqParams = new LuxFlux.fluxEngineNET.InstrumentDevice.AcquisitionParameters();
 68            Console.WriteLine("Measuring white reference:");
 69            var whiteReference = LuxFlux.fluxEngineNET.Util.CreateRingBufferContainer(camera, 10);
 70            acqParams.ReferenceName = "WhiteReference";
 71            camera.StartAcquisition(acqParams);
 72            for (int i = 0; i < 10; ++i)
 73            {
 74                var buffer = camera.RetrieveBuffer(TimeSpan.FromSeconds(1));
 75                if (buffer != null)
 76                {
 77                    try
 78                    {
 79                        whiteReference.Add(buffer);
 80                    }
 81                    finally
 82                    {
 83                        camera.ReturnBuffer(buffer);
 84                    }
 85                }
 86            }
 87            camera.StopAcquisition();
 88            Console.WriteLine("Done.");
 89
 90            /* NOTE:
 91             * For real devices a this point the user should probably be
 92             * asked to obscure the optics in front of the camera in order
 93             * for a proper dark reference to be measured.
 94             *
 95             * For the virtual device this is not required.
 96             *
 97             * Some cameras do have an internal shutter, where manual user
 98             * intervention is also not required here.
 99             */
100
101            Console.WriteLine("Measuring dark reference:");
102            var darkReference = LuxFlux.fluxEngineNET.Util.CreateRingBufferContainer(camera, 10);
103            acqParams.ReferenceName = "DarkReference";
104            camera.StartAcquisition(acqParams);
105            for (int i = 0; i < 10; ++i)
106            {
107                var buffer = camera.RetrieveBuffer(TimeSpan.FromSeconds(1));
108                if (buffer != null)
109                {
110                    try
111                    {
112                        darkReference.Add(buffer);
113                    }
114                    finally
115                    {
116                        camera.ReturnBuffer(buffer);
117                    }
118                }
119            }
120            camera.StopAcquisition();
121            Console.WriteLine("Done.");
122
123            // Create processing context
124            var instrumentReferences = new LuxFlux.fluxEngineNET.ProcessingContext.BufferReferenceInput();
125            instrumentReferences.WhiteReference = whiteReference;
126            instrumentReferences.DarkReference = darkReference;
127            var instrumentParameters = new LuxFlux.fluxEngineNET.ProcessingContext.InstrumentParameters();
128            instrumentParameters.ReferenceInput = instrumentReferences;
129            var ctx = LuxFlux.fluxEngineNET.ProcessingContext.CreateForInstrumentProcessing(camera, model, instrumentParameters);
130            int sinkIndex = ctx.OutputSinkInfoById(/* outputId = */ 0).Index;
131
132            /* NOTE:
133             * For real devices a this point the user should probably be
134             * asked to position the object to measure underneath the
135             * camera and start the motion of the motion control device
136             * they have.
137             *
138             * For the virtual device this is not required.
139             */
140
141            Console.WriteLine("Starting acquisition:");
142            acqParams.ReferenceName = null;
143            camera.StartAcquisition(acqParams);
144            Console.WriteLine("Done.");
145
146            Console.WriteLine("Processing the first 100 lines...");
147            int y = 0;
148            while (y < 100)
149            {
150                var buffer = camera.RetrieveBuffer(TimeSpan.FromMilliseconds(100));
151                if (buffer == null)
152                    continue;
153
154                try
155                {
156                    ctx.SetSourceData(buffer);
157                    ctx.ProcessNext();
158
159                    var data = ctx.OutputSinkData(sinkIndex).AsObjectList;
160                    foreach (var obj in data)
161                    {
162                        Console.WriteLine($"Found object [at line number {y}]: bounding box ({obj.BoundingBoxX}, {obj.BoundingBoxY}), size ({obj.BoundingBoxWidth}, {obj.BoundingBoxHeight}), center of gravity ({obj.GravityCenterX}, {obj.GravityCenterY}), area {obj.Area}");
163                    }
164                }
165                finally
166                {
167                    camera.ReturnBuffer(buffer);
168                }
169                ++y;
170            }
171            Console.WriteLine("Done.");
172
173            Console.WriteLine("Stopping acquisition:");
174            camera.StopAcquisition();
175            Console.WriteLine("Done.");
176
177            Console.WriteLine("Disconnecting from device...");
178            deviceGroup.Disconnect(TimeSpan.FromSeconds(5));
179            Console.WriteLine("Done.");
180            ctx.Dispose();
181            handle.Dispose();
182        }
183    }
184}

Python

The source code of the example can be found in the file example_pushbroom_object_detector.py:

  1#!/usr/bin/env python3
  2
  3import fluxEngine
  4import os, sys
  5
  6import fluxEngine_example_paths as paths
  7data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'data')
  8
  9print('fluxEngine version {}'.format(fluxEngine.versionString()))
 10with open(paths.licenseFileName, 'rb') as f:
 11    handle = fluxEngine.Handle(f.read())
 12handle.setDriverBaseDirectory(paths.driverDirectory)
 13handle.createProcessingThreads(4)
 14
 15enumeratedDevices = fluxEngine.enumerateDevices(handle, -1, 5000)
 16virtualCameraDevice = None
 17for device in enumeratedDevices.devices:
 18    if device.driver.name == 'VirtualHyperCamera':
 19        virtualCameraDevice = device
 20
 21if not virtualCameraDevice:
 22    raise Exception('Could not find virtual camera driver')
 23
 24connectionSettings = fluxEngine.ConnectionSettings(virtualCameraDevice.driver.name, virtualCameraDevice.driver.type, virtualCameraDevice.id)
 25connectionSettings.timeout = 60000
 26connectionSettings.connectionParameters['Cube'] = os.path.join(data_dir, 'ObjectDetectorTest.hdr')
 27connectionSettings.connectionParameters['WhiteReferenceCube'] = os.path.join(data_dir, 'ObjectDetectorTest_White.hdr')
 28connectionSettings.connectionParameters['DarkReferenceCube'] = os.path.join(data_dir, 'ObjectDetectorTest_Dark.hdr')
 29
 30print("Attempting to connect to device...")
 31deviceGroup = fluxEngine.DeviceGroup(handle, connectionSettings)
 32print("Connected.")
 33
 34camera = deviceGroup.primaryDevice()
 35if not isinstance(camera, fluxEngine.InstrumentDevice):
 36    deviceGroup.disconnect(5000)
 37    raise Exception('The device is not an instrument device')
 38
 39camera.setupInternalBuffers(5)
 40
 41# Load model
 42#
 43# This should be done after connecting with the camera, in
 44# case the license is tied to a camera serial number. (In
 45# case the license is tied to a dongle or a mainboard id,
 46# this may be done beforehand.)
 47with open(os.path.join(data_dir, 'object_detector_test.fluxmdl'), 'rb') as f:
 48    model = fluxEngine.Model(handle, f.read())
 49
 50# NOTE:
 51# For real devices a this point the user should probably be
 52# asked to insert a white reference underneath the camera.
 53#
 54# For the virtual device this is not required.
 55
 56acqParams = fluxEngine.InstrumentDevice.AcquisitionParameters()
 57
 58print('Measuring white reference:')
 59whiteReference = fluxEngine.BufferContainer(camera, 10)
 60acqParams.referenceName = "WhiteReference"
 61camera.startAcquisition(acqParams)
 62for i in range(10):
 63    buffer = camera.retrieveBuffer(1000)
 64    if buffer:
 65        whiteReference.add(buffer)
 66        camera.returnBuffer(buffer)
 67camera.stopAcquisition()
 68print('Done.')
 69
 70# NOTE:
 71# For real devices a this point the user should probably be
 72# asked to obscure the optics in front of the camera in order
 73# for a proper dark reference to be measured.
 74#
 75# For the virtual device this is not required.
 76#
 77# Some cameras do have an internal shutter, where manual user
 78# intervention is also not required here.
 79#
 80
 81print('Measuring dark reference:')
 82darkReference = fluxEngine.BufferContainer(camera, 10)
 83acqParams.referenceName = "DarkReference"
 84camera.startAcquisition(acqParams)
 85for i in range(10):
 86    buffer = camera.retrieveBuffer(1000)
 87    if buffer:
 88        darkReference.add(buffer)
 89        camera.returnBuffer(buffer)
 90camera.stopAcquisition()
 91print('Done.')
 92
 93# Create recording contect
 94instrumentParameters = fluxEngine.InstrumentParameters()
 95instrumentParameters.whiteReference = whiteReference
 96instrumentParameters.darkReference = darkReference
 97
 98context = fluxEngine.ProcessingContext(model, fluxEngine.ProcessingContext.InstrumentProcessing,
 99                                       device=camera,
100                                       instrumentParameters=instrumentParameters)
101
102sinkIndex = context.findOutputSink(0)
103
104# NOTE:
105# For real devices a this point the user should probably be
106# asked to position the object to measure underneath the
107# camera and start the motion of the motion control device
108# they have.
109#
110# For the virtual device this is not required.
111#
112
113print('Starting acquisition:')
114acqParams.referenceName = None
115camera.startAcquisition(acqParams)
116print('Done.')
117
118print('Processing the first 100 lines...')
119y = 0
120while y < 100:
121    buffer = camera.retrieveBuffer(100)
122    if not buffer:
123        continue
124
125    context.setSourceData(buffer)
126    context.processNext()
127    data = context.outputSinkData(0)
128    for obj in data:
129        print("Found object [at line number {}]: bounding box ({}, {}) size ({}, {}), center of gravity ({}, {}), area {}".
130              format(y, obj.boundingBox[0], obj.boundingBox[1], obj.boundingBox[2], obj.boundingBox[3],
131                     obj.gravityCenter[0], obj.gravityCenter[1], obj.area))
132    camera.returnBuffer(buffer)
133    y += 1
134print('Done.')
135
136print('Stopping acquisition:')
137camera.stopAcquisition()
138print('Done.')

Expected Output

The output should look like the following:

fluxEngine version: [...]
Attempting to connect to device...
- DarkReferenceCube: examples/data/ObjectDetectorTest_Dark.hdr
- Cube: examples/data/ObjectDetectorTest.hdr
- WhiteReferenceCube: examples/data/ObjectDetectorTest_White.hdr
Connected.
Measuring white reference:
Done.
Measuring dark reference:
Done.
Starting acquisition:
Done.
Processing the first 100 lines...
Found object [at line number 39]: bounding box (14, 12) size (29, 27), center of gravity (28.3425, 24.7256), area 616
Found object [at line number 39]: bounding box (59, 15) size (35, 24), center of gravity (76.2283, 26.875), area 552
Found object [at line number 86]: bounding box (44, 54) size (50, 32), center of gravity (73.5509, 70.309), area 1042
Found object [at line number 93]: bounding box (16, 43) size (45, 50), center of gravity (35.6035, 64.5995), area 1251
Stopping acquisition:
Done.