Read ENVI Cube and Process using Model (Explicit Variant)

The example will read the example HSI cube MiniCube.hdr and process it using the instrument_function.fluxmdl, and return the result of the model for a couple of pixels. The model itself just calculates the spectral average for each pixel.

This version of the example explicitly creates the processing context from the structure of the loaded cube, in contrast to the other variant of this example, which creates the processing context automatically from the loaded measurement.

C++

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

  1#if defined(_WIN32) && defined(_MSC_VER)
  2#include <windows.h>
  3#endif
  4
  5#include <iostream>
  6#include <string>
  7#include <fstream>
  8#include <streambuf>
  9#include <algorithm>
 10#include <utility>
 11
 12#include <cstddef>
 13
 14#include <fluxEngine/fluxEngine>
 15
 16#include "paths.h"
 17#include "helpers.h"
 18
 19fluxEngine::ProcessingContext createCubeContextFor(fluxEngine::MeasurementList const& cube, int64_t maxHeight, fluxEngine::Model& model)
 20{
 21    fluxEngine::ValueType valueType{fluxEngine::ValueType::Intensity};
 22    (void) cube.getValueType(0, valueType);
 23    auto structure = cube.structure(0);
 24    std::vector<double> wavelengths = cube.wavelengths(0);
 25
 26    fluxEngine::ReferenceInfo referenceInfo;
 27    referenceInfo.valueType = valueType;
 28    if (cube.hasReference(0, "WhiteReference")) {
 29        auto referenceStructure = cube.referenceStructure(0, "WhiteReference");
 30        if (referenceStructure.dataType == structure.dataType) {
 31            std::pair<void const*, std::size_t> data = cube.rawReferenceData(0, "WhiteReference");
 32            referenceInfo.whiteReference = data.first;
 33            referenceInfo.whiteReferenceDimensions.push_back(1);
 34            referenceInfo.whiteReferenceDimensions.push_back(referenceStructure.dimensions[0]);
 35            referenceInfo.whiteReferenceDimensions.push_back(referenceStructure.dimensions[1]);
 36            referenceInfo.whiteReferenceDimensions.push_back(referenceStructure.dimensions[2]);
 37        }
 38    }
 39    if (cube.hasReference(0, "DarkReference")) {
 40        auto referenceStructure = cube.referenceStructure(0, "DarkReference");
 41        if (referenceStructure.dataType == structure.dataType) {
 42            std::pair<void const*, std::size_t> data = cube.rawReferenceData(0, "DarkReference");
 43            referenceInfo.darkReference = data.first;
 44            referenceInfo.darkReferenceDimensions.push_back(1);
 45            referenceInfo.darkReferenceDimensions.push_back(referenceStructure.dimensions[0]);
 46            referenceInfo.darkReferenceDimensions.push_back(referenceStructure.dimensions[1]);
 47            referenceInfo.darkReferenceDimensions.push_back(referenceStructure.dimensions[2]);
 48        }
 49    }
 50
 51    if (maxHeight < structure.dimensions[0])
 52        maxHeight = structure.dimensions[0];
 53
 54    int64_t width = structure.dimensions[1];
 55
 56    return fluxEngine::ProcessingContext(model, fluxEngine::ProcessingContext::HSICube,
 57                                         fluxEngine::HSICube_StorageOrder::BIP, structure.dataType,
 58                                         maxHeight, -1, width, width, wavelengths, referenceInfo);
 59}
 60
 61int main()
 62{
 63    try {
 64        std::cout << "fluxEngine version: " << fluxEngine::versionString() << std::endl;
 65        fluxEngine::Handle handle(readFile(g_licenseFileName));
 66        handle.createProcessingThreads(4);
 67
 68        /* NOTE: if the license file is tied to the serial number of a
 69         * camera, one must connect with the camera first (see the
 70         * device examples for details) for the following lines of code
 71         * to work (and not fail with a licensing error).
 72         *
 73         * If the license is tied to a dongle or a mainboard serial
 74         * connecting to a camera is not necessary.
 75         */
 76
 77        // Load data
 78        fluxEngine::MeasurementList cubeList = fluxEngine::loadMeasurementList(handle, "ENVI", g_cubeFileName);
 79        fluxEngine::Model model = fluxEngine::Model(handle, fluxEngine::Model::FromFile, g_modelFileName);
 80
 81        // Create a processing context
 82        int64_t maxLines = 2048;
 83        fluxEngine::ProcessingContext context = createCubeContextFor(cubeList, maxLines, model);
 84        int const sinkIndex = context.findOutputSink(/* outputId = */ 0);
 85
 86        // Process the data
 87        context.setSourceData(cubeList, 0);
 88        context.processNext();
 89        fluxEngine::ProcessingContext::OutputSinkData outputData = context.outputSinkData(sinkIndex);
 90        fluxEngine::TensorData view{outputData};
 91
 92        if (view.order() != 3
 93            || view.dataType() != fluxEngine::DataType::Float64
 94            || view.dimension(2) != 1)
 95            throw std::runtime_error("The model is not expected");
 96
 97        // The cube we are loading is very small (12x14 pixels),
 98        // but if a larger cube is being loaded, this will produce
 99        // a _lot_ of output!
100        for (int64_t y = 0; y < view.dimension(0); ++y) {
101            for (int64_t x = 0; x < view.dimension(1); ++x)
102                std::cout << "Pixel (" << x << ", " << y << ") has average value " << view.at<double>(y, x, 0) << '\n';
103        }
104        std::cout << std::flush;
105    } catch (std::exception& e) {
106        std::cerr << "Error: " << e.what() << std::endl;
107        return 1;
108    } catch (...) {
109        std::cerr << "Unknown error." << std::endl;
110        return 1;
111    }
112
113    return 0;
114}

This source file will compile to the executable ExampleReadENVIAndProcessExplicit.

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 ExampleReadENVIAndProcessExplicit\Program.cs.

 1using System;
 2
 3namespace ExampleReadENVIAndProcessExplicit
 4{
 5    class Program
 6    {
 7        static LuxFlux.fluxEngineNET.ProcessingContext CreateCubeContextFor(LuxFlux.fluxEngineNET.MeasurementList cube, Int64 maxHeight, LuxFlux.fluxEngineNET.Model model)
 8        {
 9            var valueType = cube.GetValueType(0) ?? LuxFlux.fluxEngineNET.ValueType.Intensity;
10            var structure = cube.GetStructure(0);
11            double[] wavelengths = cube.GetWavelengths(0);
12
13            var inputConfiguration = new LuxFlux.fluxEngineNET.ProcessingContext.ExplicitInputConfiguration();
14            inputConfiguration.InputValueType = valueType;
15            inputConfiguration.InputIsIlluminationCorrected = cube.IsIlluminationCorrected(0);
16            inputConfiguration.CalibrationInfo = cube.GetCalibrationInfo(0);
17            var memoryReferenceInfo = new LuxFlux.fluxEngineNET.ProcessingContext.MemoryReferenceInput();
18            if (cube.HasReference(0, "WhiteReference"))
19            {
20                memoryReferenceInfo.WhiteReference = cube.GetReferenceTensorView(0, "WhiteReference").WithInsertedUnityDimension(0);
21            }
22            if (cube.HasReference(0, "IlluminationReference"))
23            {
24                memoryReferenceInfo.IlluminationReference = cube.GetReferenceTensorView(0, "IlluminationReference").WithInsertedUnityDimension(0);
25            }
26            if (cube.HasReference(0, "DarkReference"))
27            {
28                memoryReferenceInfo.DarkReference = cube.GetReferenceTensorView(0, "DarkReference").WithInsertedUnityDimension(0);
29            }
30
31            if (maxHeight < structure.Dimensions[0])
32                maxHeight = structure.Dimensions[0];
33            Int64 width = structure.Dimensions[1];
34
35            inputConfiguration.ReferenceInput = memoryReferenceInfo;
36            return LuxFlux.fluxEngineNET.ProcessingContext.CreateForHSICube(model, LuxFlux.fluxEngineNET.HSICube_StorageOrder.BIP, structure.DataType,
37                maxHeight, -1, width, width, wavelengths, inputConfiguration);
38        }
39
40        static void Main(string[] args)
41        {
42            Console.WriteLine("fluxEngine version: " + LuxFlux.fluxEngineNET.Version.String);
43            var handle = new LuxFlux.fluxEngineNET.Handle(ExampleHelpers.IO.ReadLicenseFile());
44            handle.CreateProcessingThreads(4);
45
46            /* NOTE: if the license file is tied to the serial number of a
47             * camera, one must connect with the camera first (see the
48             * device examples for details) for the following lines of code
49             * to work (and not fail with a licensing error).
50             *
51             * If the license is tied to a dongle or a mainboard serial
52             * connecting to a camera is not necessary.
53             */
54
55            // Load data
56            var cubeList = LuxFlux.fluxEngineNET.IO.LoadMeasurementList(handle, "ENVI", ExampleHelpers.Paths.ExampleDataFileName("MiniCube.hdr"));
57            var model = LuxFlux.fluxEngineNET.Model.LoadFromFile(handle, ExampleHelpers.Paths.ExampleDataFileName("instrument_function.fluxmdl"));
58
59            // Create a processing context
60            Int64 maxLines = 2048;
61            var context = CreateCubeContextFor(cubeList, maxLines, model);
62            int sinkIndex = context.OutputSinkInfoById(/* outputId = */ 0).Index;
63
64            // Process the data
65            context.SetSourceData(cubeList, 0);
66            context.ProcessNext();
67            var outputData = context.OutputSinkData(sinkIndex);
68            var data = outputData.AsTensorCopy();
69
70            if (data.Order != 3 || data.DataType != LuxFlux.fluxEngineNET.DataType.Float64 || data.Dimensions[2] != 1)
71                throw new Exception("The model is not expected");
72
73            // The cube we are loading is very small (12x14 pixels),
74            // but if a larger cube is being loaded, this will produce
75            // a _lot_ of output!
76            for (Int64 y = 0; y < data.Dimensions[0]; ++y)
77            {
78                for (Int64 x = 0; x < data.Dimensions[1]; ++x)
79                {
80                    Console.WriteLine($"Pixel ({x}, {y}) has average value {data.Value<double>(y, x, 0)}");
81                }
82            }
83
84            context.Dispose();
85            model.Dispose();
86            cubeList.Dispose();
87            handle.Dispose();
88        }
89    }
90}

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_read_envi_and_process_explicit.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
15# NOTE: if the license file is tied to the serial number of a
16# camera, one must connect with the camera first (see the
17# device examples for details) for the following lines of code
18# to work (and not fail with a licensing error).
19#
20# If the license is tied to a dongle or a mainboard serial
21# connecting to a camera is not necessary.
22
23# Load model
24with open(os.path.join(data_dir, 'instrument_function.fluxmdl'), 'rb') as f:
25    model = fluxEngine.Model(handle, f.read())
26
27# Load cube
28cubeList = fluxEngine.loadMeasurementList(handle, 'ENVI', os.path.join(data_dir, 'MiniCube.hdr'))
29
30
31def add_front_unity_dimension(data):
32    return np.reshape(data, (1, ) + data.shape)
33
34def createCubeContextFor(cube, maxHeight, model):
35    valueType = cube.valueType(0)
36    data = cube.data(0)
37
38    referenceInfo = fluxEngine.ReferenceInfo()
39    referenceInfo.valueType = valueType
40    if cube.hasReference(0, "WhiteReference"):
41        referenceInfo.whiteReference = add_front_unity_dimension(cube.referenceData(0, "WhiteReference"))
42    if cube.hasReference(0, "DarkReference"):
43        referenceInfo.darkReference = add_front_unity_dimension(cube.referenceData(0, "DarkReference"))
44
45    if maxHeight < data.shape[0]:
46        maxHeight = data.shape[0]
47    if referenceInfo.whiteReference and maxHeight < referenceInfo.whiteReference.shape[1]:
48        maxHeight = referenceInfo.whiteReference.shape[1]
49    if referenceInfo.darkReference and maxHeight < referenceInfo.darkReference.shape[1]:
50        maxHeight = referenceInfo.darkReference.shape[1]
51    width = data.shape[1]
52
53    return fluxEngine.ProcessingContext(model, fluxEngine.ProcessingContext.HSICube,
54                                        storageOrder=fluxEngine.HSICube_StorageOrder.BIP,
55                                        dataType=data.type,
56                                        maxHeight=maxHeight,
57                                        height=-1,
58                                        maxWidth=width,
59                                        width=width,
60                                        wavelengths=cube.wavelengths(0),
61                                        referenceInfo=referenceInfo)
62
63# Create a processing context
64context = fluxEngine.ProcessingContext(model, fluxEngine.ProcessingContext.MeasurementProcessing,
65                                       measurementList=cubeList, index=0)
66
67sinkIndex = context.findOutputSink(0)
68
69context.setSourceData(cubeList, 0)
70context.processNext()
71outputData = context.outputSinkData(sinkIndex)
72
73# The cube we are loading is very small (12x14 pixels),
74# but if a larger cube is being loaded, this will produce
75# a _lot_ of output!
76print(outputData[:, :, 0])

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

Expected Output

The output should look like the following:

fluxEngine version: [...]
Pixel (0, 0) has average value 0.652092
Pixel (1, 0) has average value 0.641689
Pixel (2, 0) has average value 0.633773
Pixel (3, 0) has average value 0.636609
Pixel (4, 0) has average value 0.633638
Pixel (5, 0) has average value 0.648021
Pixel (6, 0) has average value 0.652871
Pixel (7, 0) has average value 0.661865
Pixel (8, 0) has average value 0.655668
Pixel (9, 0) has average value 0.656452
Pixel (10, 0) has average value 0.661276
Pixel (11, 0) has average value 0.671736
Pixel (12, 0) has average value 0.663536
Pixel (13, 0) has average value 0.666235
Pixel (0, 1) has average value 0.664377
Pixel (1, 1) has average value 0.656757
Pixel (2, 1) has average value 0.648442
[...]
Pixel (10, 11) has average value 0.713334
Pixel (11, 11) has average value 0.728156
Pixel (12, 11) has average value 0.725411
Pixel (13, 11) has average value 0.745923