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.