Process Data from a PushBroom Device in a Model (Loading a White Reference from disk)

This example will connect to the virtual pushbroom camera (using MiniCube.hdr and its references), load a white reference from disk (instead of measuring it), measure a dark reference, and finally 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        fluxEngine::InstrumentDevice::AcquisitionParameters acqParams;
 74
 75        // Load the white reference
 76        fluxEngine::BufferContainer whiteReference = fluxEngine::importReference(camera, g_whiteRefRawFileName, 0);
 77
 78        /* NOTE:
 79         * For real devices a this point the user should probably be
 80         * asked to obscure the optics in front of the camera in order
 81         * for a proper dark reference to be measured.
 82         *
 83         * For the virtual device this is not required.
 84         *
 85         * Some cameras do have an internal shutter, where manual user
 86         * intervention is also not required here.
 87         */
 88
 89        std::cout << "Measuring dark reference:\n" << std::flush;
 90        fluxEngine::BufferContainer darkReference = fluxEngine::createBufferContainer(camera, 10);
 91        acqParams.referenceName = "DarkReference";
 92        camera->startAcquisition(acqParams);
 93        for (int i = 0; i < 10; ++i) {
 94            fluxEngine::BufferInfo buffer = camera->retrieveBuffer(std::chrono::seconds{1});
 95            if (buffer.ok) {
 96                darkReference.add(buffer);
 97                camera->returnBuffer(buffer.id);
 98            }
 99        }
100        camera->stopAcquisition();
101        std::cout << "Done.\n" << std::flush;
102
103        // Create recording context
104        fluxEngine::ProcessingContext::InstrumentParameters instrumentParameters;
105        instrumentParameters.whiteReference = &whiteReference;
106        instrumentParameters.darkReference = &darkReference;
107        fluxEngine::ProcessingContext ctx = fluxEngine::ProcessingContext::createInstrumentProcessingContext(camera, model, instrumentParameters);
108        int const sinkIndex = ctx.findOutputSink(/* outputId = */ 0);
109
110        /* NOTE:
111         * For real devices a this point the user should probably be
112         * asked to position the object to measure underneath the
113         * camera and start the motion of the motion control device
114         * they have.
115         *
116         * For the virtual device this is not required.
117         */
118
119        std::cout << "Starting acquisition:\n" << std::flush;
120        acqParams.referenceName = {};
121        camera->startAcquisition(acqParams);
122        std::cout << "Done.\n" << std::flush;
123
124        std::cout << "Processing the first 10 lines...\n" << std::flush;
125        int y = 0;
126        while (y < 10) {
127            fluxEngine::BufferInfo buffer = camera->retrieveBuffer(std::chrono::milliseconds{100});
128            if (!buffer.ok)
129                continue;
130
131            ctx.setSourceData(buffer);
132            ctx.processNext();
133            fluxEngine::TensorData view{ctx.outputSinkData(sinkIndex)};
134
135            // Has the correct structure
136            if (view.order() == 3
137                && view.dimension(2) == 1
138                && view.dataType() == fluxEngine::DataType::Float64) {
139                for (int x = 0; x < 4; ++x)
140                    std::cout << "Spectral Average @(" << x << ", " << y << ") = " << view.at<double>(0, x, 0) << '\n';
141                std::cout << std::flush;
142            }
143
144            camera->returnBuffer(buffer.id);
145            ++y;
146        }
147
148        std::cout << "Stopping acquisition:\n" << std::flush;
149        camera->stopAcquisition();
150        std::cout << "Done.\n" << std::flush;
151    } catch (std::exception& e) {
152        std::cerr << "Error: " << e.what() << std::endl;
153        return 1;
154    } catch (...) {
155        std::cerr << "Unknown error." << std::endl;
156        return 1;
157    }
158
159    return 0;
160}

This source file will compile to the executable ExamplePushBroomProcessModelWithLoadedRef.

The following classes and methods are among those used in this example:

.NET

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

  1using System;
  2
  3namespace ExamplePushBroomProcessModelWithLoadedRef
  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            // Load the white reference
 61            var whiteReference = LuxFlux.fluxEngineNET.IO.ImportReference(camera, ExampleHelpers.Paths.ExampleDataFileName("MiniCube_White.fluxref"), 0);
 62
 63            /* NOTE:
 64             * For real devices a this point the user should probably be
 65             * asked to obscure the optics in front of the camera in order
 66             * for a proper dark reference to be measured.
 67             *
 68             * For the virtual device this is not required.
 69             *
 70             * Some cameras do have an internal shutter, where manual user
 71             * intervention is also not required here.
 72             */
 73
 74            var acqParams = new LuxFlux.fluxEngineNET.InstrumentDevice.AcquisitionParameters();
 75            Console.WriteLine("Measuring dark reference:");
 76            var darkReference = LuxFlux.fluxEngineNET.Util.CreateRingBufferContainer(camera, 10);
 77            acqParams.ReferenceName = "DarkReference";
 78            camera.StartAcquisition(acqParams);
 79            for (int i = 0; i < 10; ++i)
 80            {
 81                var buffer = camera.RetrieveBuffer(TimeSpan.FromSeconds(1));
 82                if (buffer != null)
 83                {
 84                    try
 85                    {
 86                        darkReference.Add(buffer);
 87                    }
 88                    finally
 89                    {
 90                        camera.ReturnBuffer(buffer);
 91                    }
 92                }
 93            }
 94            camera.StopAcquisition();
 95            Console.WriteLine("Done.");
 96
 97            // Create processing context
 98            var instrumentReferences = new LuxFlux.fluxEngineNET.ProcessingContext.BufferReferenceInput();
 99            instrumentReferences.WhiteReference = whiteReference;
100            instrumentReferences.DarkReference = darkReference;
101            var instrumentParameters = new LuxFlux.fluxEngineNET.ProcessingContext.InstrumentParameters();
102            instrumentParameters.ReferenceInput = instrumentReferences;
103            var ctx = LuxFlux.fluxEngineNET.ProcessingContext.CreateForInstrumentProcessing(camera, model, instrumentParameters);
104            int sinkIndex = ctx.OutputSinkInfoById(/* outputId = */ 0).Index;
105
106            /* NOTE:
107             * For real devices a this point the user should probably be
108             * asked to position the object to measure underneath the
109             * camera and start the motion of the motion control device
110             * they have.
111             *
112             * For the virtual device this is not required.
113             */
114
115            Console.WriteLine("Starting acquisition:");
116            acqParams.ReferenceName = null;
117            camera.StartAcquisition(acqParams);
118            Console.WriteLine("Done.");
119
120            Console.WriteLine("Processing the first 10 lines...");
121            int y = 0;
122            while (y < 10)
123            {
124                var buffer = camera.RetrieveBuffer(TimeSpan.FromMilliseconds(100));
125                if (buffer == null)
126                    continue;
127
128                try
129                {
130                    ctx.SetSourceData(buffer);
131                    ctx.ProcessNext();
132
133                    var data = ctx.OutputSinkData(sinkIndex).AsTensorCopy();
134                    // Has the correct structure
135                    if (data.Order == 3 && data.Dimensions[2] == 1 && data.DataType == LuxFlux.fluxEngineNET.DataType.Float64)
136                    {
137                        for (int x = 0; x < 4; ++x)
138                            Console.WriteLine($"Spectral Average @({x}, {y}) = {data.Value<double>(0, x, 0)}");
139                    }
140                }
141                finally
142                {
143                    camera.ReturnBuffer(buffer);
144                }
145                ++y;
146            }
147            Console.WriteLine("Done.");
148
149            Console.WriteLine("Stopping acquisition:");
150            camera.StopAcquisition();
151            Console.WriteLine("Done.");
152
153            Console.WriteLine("Disconnecting from device...");
154            deviceGroup.Disconnect(TimeSpan.FromSeconds(5));
155            Console.WriteLine("Done.");
156            ctx.Dispose();
157            handle.Dispose();
158        }
159    }
160}

The following classes and methods are among those used in this example:

Python

The source code of the example can be found in the file example_pushbroom_process_model_with_loaded_ref.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
 58whiteReference = fluxEngine.importReference(camera, os.path.join(data_dir, 'MiniCube_White.fluxref'), 0)
 59
 60# NOTE:
 61# For real devices a this point the user should probably be
 62# asked to obscure the optics in front of the camera in order
 63# for a proper dark reference to be measured.
 64#
 65# For the virtual device this is not required.
 66#
 67# Some cameras do have an internal shutter, where manual user
 68# intervention is also not required here.
 69#
 70
 71print('Measuring dark reference:')
 72darkReference = fluxEngine.BufferContainer(camera, 10)
 73acqParams.referenceName = "DarkReference"
 74camera.startAcquisition(acqParams)
 75for i in range(10):
 76    buffer = camera.retrieveBuffer(1000)
 77    if buffer:
 78        darkReference.add(buffer)
 79        camera.returnBuffer(buffer)
 80camera.stopAcquisition()
 81print('Done.')
 82
 83# Create recording contect
 84instrumentParameters = fluxEngine.InstrumentParameters()
 85instrumentParameters.whiteReference = whiteReference
 86instrumentParameters.darkReference = darkReference
 87
 88context = fluxEngine.ProcessingContext(model, fluxEngine.ProcessingContext.InstrumentProcessing,
 89                                       device=camera,
 90                                       instrumentParameters=instrumentParameters)
 91
 92sinkIndex = context.findOutputSink(0)
 93
 94# NOTE:
 95# For real devices a this point the user should probably be
 96# asked to position the object to measure underneath the
 97# camera and start the motion of the motion control device
 98# they have.
 99#
100# For the virtual device this is not required.
101#
102
103print('Starting acquisition:')
104acqParams.referenceName = None
105camera.startAcquisition(acqParams)
106print('Done.')
107
108print('Processing the first 10 lines...')
109y = 0
110while y < 10:
111    buffer = camera.retrieveBuffer(100)
112    if not buffer:
113        continue
114
115    context.setSourceData(buffer)
116    context.processNext()
117    data = context.outputSinkData(0)
118    for x in range(4):
119        print('Spectral Average @({}, {}) = {}'.format(x, y, data[0, x, 0]))
120    camera.returnBuffer(buffer)
121    y += 1
122print('Done.')
123
124print('Stopping acquisition:')
125camera.stopAcquisition()
126print('Done.')

The following classes and methods are among those used in this example:

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.