Process Data from a PushBroom Device in a Model

This example will connect to the virtual pushbroom camera (using MiniCube.hdr and its references), measure a white & dark reference, create a processing context for the model instrument_function.fluxmdl. The first 10 lines returned from the device will be processed, and the spectral average at the 4 left-most pixels for each of these lines will be shown.

C++

The source code of the example can be found in the file example_pushbroom_process_model.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_cubeFileName);
 48        connectionSettings.connectionParameters["WhiteReferenceCube"] = encodeFileNameForConnectionParameter(g_whiteCubeFileName);
 49        connectionSettings.connectionParameters["DarkReferenceCube"] = encodeFileNameForConnectionParameter(g_darkCubeFileName);
 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_modelFileName);
 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        int const sinkIndex = ctx.findOutputSink(/* outputId = */ 0);
126
127        /* NOTE:
128         * For real devices a this point the user should probably be
129         * asked to position the object to measure underneath the
130         * camera and start the motion of the motion control device
131         * they have.
132         *
133         * For the virtual device this is not required.
134         */
135
136        std::cout << "Starting acquisition:\n" << std::flush;
137        acqParams.referenceName = {};
138        camera->startAcquisition(acqParams);
139        std::cout << "Done.\n" << std::flush;
140
141        std::cout << "Processing the first 10 lines...\n" << std::flush;
142        int y = 0;
143        while (y < 10) {
144            fluxEngine::BufferInfo buffer = camera->retrieveBuffer(std::chrono::milliseconds{100});
145            if (!buffer.ok)
146                continue;
147
148            ctx.setSourceData(buffer);
149            ctx.processNext();
150            fluxEngine::TensorData view{ctx.outputSinkData(sinkIndex)};
151
152            // Has the correct structure
153            if (view.order() == 3
154                && view.dimension(2) == 1
155                && view.dataType() == fluxEngine::DataType::Float64) {
156                for (int x = 0; x < 4; ++x)
157                    std::cout << "Spectral Average @(" << x << ", " << y << ") = " << view.at<double>(0, x, 0) << '\n';
158                std::cout << std::flush;
159            }
160
161            camera->returnBuffer(buffer.id);
162            ++y;
163        }
164
165        std::cout << "Stopping acquisition:\n" << std::flush;
166        camera->stopAcquisition();
167        std::cout << "Done.\n" << std::flush;
168    } catch (std::exception& e) {
169        std::cerr << "Error: " << e.what() << std::endl;
170        return 1;
171    } catch (...) {
172        std::cerr << "Unknown error." << std::endl;
173        return 1;
174    }
175
176    return 0;
177}

This source file will compile to the executable ExamplePushBroomProcessModel.

.NET

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

  1using System;
  2
  3namespace ExamplePushBroomProcessModel
  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("MiniCube.hdr");
 36            connectionSettings.ConnectionParameters["WhiteReferenceCube"] = ExampleHelpers.Paths.ExampleDataFileName("MiniCube_White.hdr");
 37            connectionSettings.ConnectionParameters["DarkReferenceCube"] = ExampleHelpers.Paths.ExampleDataFileName("MiniCube_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("instrument_function.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 10 lines...");
147            int y = 0;
148            while (y < 10)
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).AsTensorCopy();
160                    // Has the correct structure
161                    if (data.Order == 3 && data.Dimensions[2] == 1 && data.DataType == LuxFlux.fluxEngineNET.DataType.Float64)
162                    {
163                        for (int x = 0; x < 4; ++x)
164                            Console.WriteLine($"Spectral Average @({x}, {y}) = {data.Value<double>(0, x, 0)}");
165                    }
166                }
167                finally
168                {
169                    camera.ReturnBuffer(buffer);
170                }
171                ++y;
172            }
173            Console.WriteLine("Done.");
174
175            Console.WriteLine("Stopping acquisition:");
176            camera.StopAcquisition();
177            Console.WriteLine("Done.");
178
179            Console.WriteLine("Disconnecting from device...");
180            deviceGroup.Disconnect(TimeSpan.FromSeconds(5));
181            Console.WriteLine("Done.");
182            ctx.Dispose();
183            handle.Dispose();
184        }
185    }
186}

Python

The source code of the example can be found in the file example_pushbroom_process_model.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, 'MiniCube.hdr')
 27connectionSettings.connectionParameters['WhiteReferenceCube'] = os.path.join(data_dir, 'MiniCube_White.hdr')
 28connectionSettings.connectionParameters['DarkReferenceCube'] = os.path.join(data_dir, 'MiniCube_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, 'instrument_function.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 10 lines...')
119y = 0
120while y < 10:
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 x in range(4):
129        print('Spectral Average @({}, {}) = {}'.format(x, y, data[0, x, 0]))
130    camera.returnBuffer(buffer)
131    y += 1
132print('Done.')
133
134print('Stopping acquisition:')
135camera.stopAcquisition()
136print('Done.')

Expected Output

The output should look like the following:

fluxEngine version: [...]
Attempting to connect to device...
- DarkReferenceCube: examples/data/MiniCube_Dark.hdr
- Cube: examples/data/MiniCube.hdr
- WhiteReferenceCube: examples/data/MiniCube_White.hdr
Connected.
Measuring white reference:
Done.
Measuring dark reference:
Done.
Starting acquisition:
Done.
Processing the first 10 lines...
Spectral Average @(0, 0) = 0.652092
Spectral Average @(1, 0) = 0.641689
Spectral Average @(2, 0) = 0.633773
Spectral Average @(3, 0) = 0.636609
Spectral Average @(0, 1) = 0.664377
Spectral Average @(1, 1) = 0.656757
[...]
Spectral Average @(2, 8) = 0.67596
Spectral Average @(3, 8) = 0.674892
Spectral Average @(0, 9) = 0.691204
Spectral Average @(1, 9) = 0.68223
Spectral Average @(2, 9) = 0.678032
Spectral Average @(3, 9) = 0.678329
Stopping acquisition:
Done.