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
.
.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}
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.')
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.