Skip to content

Inference_CSharp

Inference_CSharp.cs shows how to perform the following: - Upload custom inference neural networks to the camera (DDR or Flash) - Inject sample test image - Enable/Configure chunk data - Enable/Configure trigger inference ready sync - Acquire images - Display inference data from acquired image chunk data - Disable previously configured camera configurations


//=============================================================================
// Copyright (c) 2026 FLIR Integrated Imaging Solutions, Inc. All Rights Reserved.
//
// This software is the confidential and proprietary information of FLIR
// Integrated Imaging Solutions, Inc. ("Confidential Information"). You
// shall not disclose such Confidential Information and shall use it only in
// accordance with the terms of the license agreement you entered into
// with FLIR Integrated Imaging Solutions, Inc. (FLIR).
//
// FLIR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
// SOFTWARE, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE, OR NON-INFRINGEMENT. FLIR SHALL NOT BE LIABLE FOR ANY DAMAGES
// SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
// THIS SOFTWARE OR ITS DERIVATIVES.
//=============================================================================

/**
 *  @example Inference_CSharp.cs
 *
 *  @brief Inference_CSharp.cs shows how to perform the following:
 *  - Upload custom inference neural networks to the camera (DDR or Flash)
 *  - Inject sample test image
 *  - Enable/Configure chunk data
 *  - Enable/Configure trigger inference ready sync
 *  - Acquire images
 *  - Display inference data from acquired image chunk data
 *  - Disable previously configured camera configurations
 *
 *  Inference is only available for Firefly deep learning cameras.
 *  See the related content section on the Firefly DL product page for relevant
 *  documentation.
 *
 *  https://www.teledynevisionsolutions.com/products/firefly-dl/
 *
 *  It can also be helpful to familiarize yourself with the ChunkData example.
 *
 *  Please leave us feedback at: https://www.surveymonkey.com/r/TDYMVAPI
 *  More source code examples at: https://github.com/Teledyne-MV/Spinnaker-Examples
 *  Need help? Check out our forum at: https://teledynevisionsolutions.zendesk.com/hc/en-us/community/topics
 */

using SpinnakerNET;
using SpinnakerNET.GenApi;
using System;
using System.IO;

namespace Inference_CSharp
{
    class Program
    {
        // Use the following enum and global constant to select whether inference network
        // type is Detection or Classification.
        enum InferenceNetworkType
        {
            /**
             * This network determines the  most likely class given a set of predetermined,
             * trained options. Object detection can also provide a location within the
             * image (in the form of a "bounding box" surrounding the class), and can
             * detect multiple objects.
             */
            Detection,
            /**
             * This network determines the best option from a list of predetermined options;
             * the camera gives a percentage that determines the likelihood of the currently
             * perceived image being one of the classes it has been trained to recognize.
             */
            Classification
        }

        static InferenceNetworkType chosenInferenceNetworkType = InferenceNetworkType.Detection;

        // Use the following enum and global constant to select whether uploaded inference
        // network and injected image should be written to camera flash or DDR
        enum FileUploadPersistence
        {
            FLASH, // Slower upload but data persists after power cycling the camera
            DDR    // Faster upload but data clears after power cycling the camera
        }
        ;

        const FileUploadPersistence chosenFileUploadPersistence = FileUploadPersistence.DDR;

        // The example provides two existing custom networks that can be uploaded
        // on to the camera to demonstrate classification and detection capabilities.
        // "Network_Classification" file is created with Tensorflow using a mobilenet
        // neural network for classifying flowers.
        // "Network_Detection" file is created with Caffe using mobilenet SSD network
        // for people object detection.
        // Note: Make sure these files exist on the system and are accessible by the example
        string networkFilePath =
            (chosenInferenceNetworkType == InferenceNetworkType.Classification ? "Network_Classification"
             : "Network_Detection");

        // The example provides two raw images that can be injected into the camera
        // to demonstrate camera inference classification and detection capabilities. Jpeg
        // representation of the raw images can be found packaged with the example with
        // the names "Injected_Image_Classification_Daisy.jpg" and "Injected_Image_Detection_Aeroplane.jpg".
        // Note: Make sure these files exist on the system and are accessible by the example
        string injectedImageFilePath =
            (chosenInferenceNetworkType == InferenceNetworkType.Classification ? "Injected_Image_Classification.raw"
             : "Injected_Image_Detection.raw");

        // The injected images have different ROI sizes so the camera needs to be
        // configured to the appropriate width and height to match the injected image
        static long injectedImageWidth = (chosenInferenceNetworkType == InferenceNetworkType.Classification ? 640 : 720);
        static long injectedImageHeight =
            (chosenInferenceNetworkType == InferenceNetworkType.Classification ? 400 : 540);

        // The sample classification inference network file was trained with the following
        // data set labels
        // Note: This list should match the list of labels used during the training
        //       stage of the network file
        static string[] labelClassification = {"daisy", "dandelion", "roses", "sunflowers", "tulips"};

        // The sample detection inference network file was trained with the following
        // data set labels
        // Note: This list should match the list of labels used during the training
        //       stage of the network file
        static string[] labelDetection = {
            "background", "aeroplane", "bicycle",     "bird",  "boat",        "bottle", "bus",
            "car",        "cat",       "chair",       "cow",   "diningtable", "dog",    "horse",
            "motorbike",  "person",    "pottedplant", "sheep", "sofa",        "train",  "monitor"};

        // This function prints the device information of the camera from the
        // transport layer; please see NodeMapInfo_CSharp example for more
        // in-depth comments on printing device information from the nodemap.
        static int PrintDeviceInfo(INodeMap nodeMap)
        {
            int result = 0;

            try
            {
                Console.WriteLine("\n*** DEVICE INFORMATION ***\n");

                ICategory category = nodeMap.GetNode<ICategory>("DeviceInformation");
                if (category != null && category.IsReadable)
                {
                    for (int i = 0; i < category.Children.Length; i++)
                    {
                        Console.WriteLine(
                            "{0}: {1}",
                            category.Children[i].Name,
                            (category.Children[i].IsReadable ? category.Children[i].ToString()
                             : "Node not available"));
                    }
                    Console.WriteLine();
                }
                else
                {
                    Console.WriteLine("Device control information not available.");
                }
            }
            catch (SpinnakerException ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                result = -1;
            }

            return result;
        }

        // This function executes a file delete operation on the camera.
        static int CameraDeleteFile(INodeMap nodeMap)
        {
            int result = 0;

            IInteger iFileSize = nodeMap.GetNode<IInteger>("FileSize");
            if (iFileSize == null || !iFileSize.IsReadable)
            {
                Console.WriteLine("Unable to query FileSize. Aborting...");
                return -1;
            }

            if (iFileSize.Value == 0)
            {
                // No file uploaded yet, skip delete
                Console.WriteLine("No files found, skipping file deletion.");
                return 0;
            }

            Console.WriteLine("Deleting file...");
            try
            {
                // Get FileOperationSelector
                IEnum iFileSelectorNode = nodeMap.GetNode<IEnum>("FileOperationSelector");
                if (iFileSelectorNode == null || !iFileSelectorNode.IsWritable || !iFileSelectorNode.IsReadable)
                {
                    Console.WriteLine("Unable to configure FileOperationSelector. Aborting...");
                    return -1;
                }

                IEnumEntry iFileOperationDelete = iFileSelectorNode.GetEntryByName("Delete");
                if (iFileOperationDelete == null || !iFileOperationDelete.IsReadable)
                {
                    Console.WriteLine("Unable to configure FileOperationSelector Delete. Aborting...");
                    return -1;
                }

                iFileSelectorNode.Value = iFileOperationDelete.Symbolic;

                ICommand iFileOperationExecute = nodeMap.GetNode<ICommand>("FileOperationExecute");
                if (iFileOperationExecute == null || !iFileOperationExecute.IsWritable)
                {
                    Console.WriteLine("Unable to configure FileOperationExecute. Aborting...");
                    return -1;
                }

                iFileOperationExecute.Execute();

                IEnum iFileOperationStatus = nodeMap.GetNode<IEnum>("FileOperationStatus");
                if (iFileOperationStatus == null || !iFileOperationStatus.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationStatus. Aborting...");
                    return -1;
                }

                IEnumEntry iFileOperationStatusSuccess = iFileOperationStatus.GetEntryByName("Success");
                if (iFileOperationStatusSuccess == null || !iFileOperationStatusSuccess.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationStatus Success. Aborting...");
                    return -1;
                }

                if (iFileOperationStatus.Value != iFileOperationStatusSuccess.Value)
                {
                    Console.WriteLine(
                        "Failed to delete file!  File Operation Status : {0}.",
                        iFileOperationStatus.Symbolics[iFileOperationStatus.Value]);
                    return -1;
                }
            }
            catch (SpinnakerException e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.Message);
                result = -1;
            }

            return result;
        }

        // This function executes file open/write on the camera, sets the uploaded file persistence
        // and attempts to set FileAccessLength to FileAccessBufferNode length to speed up the write.
        static int CameraOpenFile(INodeMap nodeMap)
        {
            int result = 0;

            Console.WriteLine("Opening file for writing...");
            try
            {
                IEnum iFileOperationSelector = nodeMap.GetNode<IEnum>("FileOperationSelector");
                if (iFileOperationSelector == null || !iFileOperationSelector.IsWritable ||
                    !iFileOperationSelector.IsReadable)
                {
                    Console.WriteLine("Unable to configure FileOperationSelector. Aborting...");
                    return -1;
                }

                IEnumEntry iFileOperationOpen = iFileOperationSelector.GetEntryByName("Open");
                if (iFileOperationOpen == null || !iFileOperationOpen.IsReadable)
                {
                    Console.WriteLine("Unable to configure FileOperationSelector Open. Aborting...");
                    return -1;
                }

                iFileOperationSelector.Value = iFileOperationOpen.Symbolic;

                IEnum iFileOpenMode = nodeMap.GetNode<IEnum>("FileOpenMode");
                if (iFileOpenMode == null || !iFileOpenMode.IsWritable || !iFileOpenMode.IsReadable)
                {
                    Console.WriteLine("Unable to configure FileOpenMode. Aborting...");
                    return -1;
                }

                IEnumEntry iFileFileOpenModeWrite = iFileOpenMode.GetEntryByName("Write");
                if (iFileFileOpenModeWrite == null || !iFileFileOpenModeWrite.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationSelector Write. Aborting...");
                    return -1;
                }

                iFileOpenMode.Value = iFileFileOpenModeWrite.Symbolic;

                ICommand iFileOperationExecute = nodeMap.GetNode<ICommand>("FileOperationExecute");
                if (iFileOperationExecute == null || !iFileOperationExecute.IsWritable)
                {
                    Console.WriteLine("Unable to execute FileOperationExecute - Write. Aborting...");
                    return -1;
                }

                iFileOperationExecute.Execute();

                IEnum iFileOperationStatus = nodeMap.GetNode<IEnum>("FileOperationStatus");
                if (iFileOperationStatus == null || !iFileOperationStatus.IsReadable)
                {
                    Console.WriteLine("Unable to query iFileOperationStatus. Aborting...");
                    return -1;
                }

                IEnumEntry iFileOperationStatusSuccess = iFileOperationStatus.GetEntryByName("Success");
                if (iFileOperationStatusSuccess == null || !iFileOperationStatusSuccess.IsReadable)
                {
                    Console.WriteLine("Unable to query iFileOperationStatus Success. Aborting...");
                    return -1;
                }

                if (iFileOperationStatus.Value != iFileOperationStatusSuccess.Value)
                {
                    Console.WriteLine("Failed to open file for writing!");
                    return -1;
                }

                IBool iFileWriteToFlash = nodeMap.GetNode<IBool>("FileWriteToFlash");
                if (iFileWriteToFlash == null || !iFileWriteToFlash.IsWritable || !iFileWriteToFlash.IsReadable)
                {
                    Console.WriteLine("Unable to configure FileWriteToFlash. Aborting...");
                    return -1;
                }

                iFileWriteToFlash.Value = (chosenFileUploadPersistence == FileUploadPersistence.FLASH);
                Console.WriteLine("FileWriteToFlash is set to {0}.", iFileWriteToFlash.Value);

                IInteger iFileAccessLength = nodeMap.GetNode<IInteger>("FileAccessLength");
                if (iFileAccessLength == null || !iFileAccessLength.IsWritable)
                {
                    Console.WriteLine("Unable to configure FileAccessLength. Aborting...");
                    return -1;
                }

                IRegister iFileAccessBuffer = nodeMap.GetNode<IRegister>("FileAccessBuffer");
                if (iFileAccessBuffer == null || !iFileAccessBuffer.IsReadable)
                {
                    Console.WriteLine("Unable to query iFileAccessBuffer. Aborting...");
                    return -1;
                }

                if (iFileAccessLength.Value < iFileAccessBuffer.Length)
                {
                    try
                    {
                        iFileAccessLength.Value = iFileAccessBuffer.Length;
                    }
                    catch (SpinnakerException e)
                    {
                        Console.WriteLine("Unable to set FileAccessLength to FileAccessBuffer length: {0}.", e.Message);
                    }
                }

                // Set File Access Offset to zero
                IInteger iFileAccessOffset = nodeMap.GetNode<IInteger>("FileAccessOffset");
                if (iFileAccessOffset == null || !iFileAccessOffset.IsWritable)
                {
                    Console.WriteLine("Unable to configure FileWriteToFlash. Aborting...");
                    return -1;
                }

                iFileAccessOffset.Value = 0;
            }
            catch (SpinnakerException e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.Message);
                result = -1;
            }

            return result;
        }

        // This function executes a file close operation on the camera.
        static int CameraCloseFile(INodeMap nodeMap)
        {
            int result = 0;

            Console.WriteLine("Closing file...");
            try
            {
                IEnum iFileOperationSelector = nodeMap.GetNode<IEnum>("FileOperationSelector");
                if (iFileOperationSelector == null || !iFileOperationSelector.IsWritable ||
                    !iFileOperationSelector.IsReadable)
                {
                    Console.WriteLine("Unable to configure FileOperationSelector. Aborting...");
                    return -1;
                }

                IEnumEntry iFileOperationClose = iFileOperationSelector.GetEntryByName("Close");
                if (iFileOperationClose == null || !iFileOperationClose.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationSelector Close. Aborting...");
                    return -1;
                }

                iFileOperationSelector.Value = iFileOperationClose.Symbolic;

                ICommand iFileOperationExecute = nodeMap.GetNode<ICommand>("FileOperationExecute");
                if (iFileOperationExecute == null || !iFileOperationExecute.IsWritable)
                {
                    Console.WriteLine("Unable to execute File Close. Aborting...");
                    return -1;
                }

                iFileOperationExecute.Execute();

                IEnum iFileOperationStatus = nodeMap.GetNode<IEnum>("FileOperationStatus");
                if (iFileOperationStatus == null || !iFileOperationStatus.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationStatus. Aborting...");
                    return -1;
                }

                IEnumEntry iFileOperationStatusSuccess = iFileOperationStatus.GetEntryByName("Success");
                if (iFileOperationStatusSuccess == null || !iFileOperationStatus.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationStatus - Success. Aborting...");
                    return -1;
                }

                if (iFileOperationStatus.Value != iFileOperationStatusSuccess.Value)
                {
                    Console.Write("Failed to write to file!");
                    return -1;
                }
            }
            catch (SpinnakerException e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.Message);
                result = -1;
            }

            return result;
        }

        static int UploadFileToCamera(INodeMap nodeMap, string fileSelectorEntryName, string filePath)
        {
            int result = 0;
            Console.WriteLine("\n*** CONFIGURING FILE SELECTOR ***");

            try
            {
                IEnum iFileSelector = nodeMap.GetNode<IEnum>("FileSelector");
                if (iFileSelector == null || !iFileSelector.IsWritable || !iFileSelector.IsReadable)
                {
                    Console.WriteLine("Unable to configure FileSelector. Aborting...");
                    return -1;
                }

                IEnumEntry iInferenceSelectorEntry = iFileSelector.GetEntryByName(fileSelectorEntryName);
                if (iInferenceSelectorEntry == null || !iInferenceSelectorEntry.IsReadable)
                {
                    Console.WriteLine("Unable to query FileSelector entry {0}. Aborting...", fileSelectorEntryName);
                    return -1;
                }

                // Set file selector
                Console.WriteLine("Setting FileSelector to {0}...", iInferenceSelectorEntry.Symbolic);
                iFileSelector.Value = iInferenceSelectorEntry.Symbolic;

                // Delete file on camera before writing in case camera runs out of space
                if (CameraDeleteFile(nodeMap) != 0)
                {
                    Console.WriteLine(
                        "Failed to delete existing file for selector entry {0}. Aborting...", iInferenceSelectorEntry);
                    return -1;
                }

                // Open file on camera for write.
                if (CameraOpenFile(nodeMap) != 0)
                {
                    // File may not be closed properly last time
                    // Close and re-open again
                    if (CameraCloseFile(nodeMap) != 0)
                    {
                        // It fails to close the file.  Abort!
                        Console.WriteLine("Problem opening file node.  Aborting...");
                        return -1;
                    }

                    // File was closed.  Retry writing the file again.
                    if (CameraOpenFile(nodeMap) != 0)
                    {
                        // Fails again.  Abort!
                        Console.WriteLine("Problem opening file node.  Aborting...");
                        return -1;
                    }
                }

                IInteger iFileAccessLength = nodeMap.GetNode<IInteger>("FileAccessLength");
                if (iFileAccessLength == null || !iFileAccessLength.IsWritable)
                {
                    Console.WriteLine("Unable to query FileAccessLength. Aborting...");
                    return -1;
                }

                IInteger iFileAccessOffset = nodeMap.GetNode<IInteger>("FileAccessOffset");
                if (iFileAccessOffset == null || !iFileAccessOffset.IsReadable)
                {
                    Console.WriteLine("Unable to query FileAccessOffset. Aborting...");
                    return -1;
                }

                IInteger iFileOperationResult = nodeMap.GetNode<IInteger>("FileOperationResult");
                if (iFileOperationResult == null || !iFileOperationResult.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationResult. Aborting...");
                    return -1;
                }

                IRegister iFileAccessBuffer = nodeMap.GetNode<IRegister>("FileAccessBuffer");
                if (iFileAccessBuffer == null || !iFileAccessBuffer.IsWritable)
                {
                    Console.WriteLine("Unable to configure FileAccessBuffer. Aborting...");
                    return -1;
                }

                // Read file to memory
                byte[] fileBytes = File.ReadAllBytes(filePath);

                // Compute number of write operations required
                long totalBytesToWrite = fileBytes.LongLength;
                long intermediateBufferSize = iFileAccessLength.Value;
                long writeIterations = (totalBytesToWrite / intermediateBufferSize) +
                                       (totalBytesToWrite % intermediateBufferSize == 0 ? 0 : 1);

                if (totalBytesToWrite == 0)
                {
                    Console.WriteLine("Empty Image. No data will be written to camera. Aborting...");
                    return -1;
                }

                Console.WriteLine("Starting uploading \"{0}\" to device", filePath);
                Console.WriteLine("Total Bytes to write : {0}", totalBytesToWrite);
                Console.WriteLine("FileAccessLength : {0}", intermediateBufferSize);
                Console.WriteLine("Write Iterations : {0}", writeIterations);

                long index = 0;
                long bytesLeftToWrite = totalBytesToWrite;
                long totalBytesWritten = 0;
                bool paddingRequired = false;
                long numPaddings = 0;

                IEnum iFileOperationSelector = nodeMap.GetNode<IEnum>("FileOperationSelector");
                if (iFileOperationSelector == null || !iFileOperationSelector.IsWritable)
                {
                    Console.WriteLine("Unable to configure FileOperationSelector. Aborting...");
                    return -1;
                }

                ICommand iFileOperationExecute = nodeMap.GetNode<ICommand>("FileOperationExecute");
                if (iFileOperationExecute == null || !iFileOperationExecute.IsWritable)
                {
                    Console.WriteLine("Unable to configure FileOperationExecute. Aborting...");
                    return -1;
                }

                IEnum iFileOperationStatus = nodeMap.GetNode<IEnum>("FileOperationStatus");
                if (iFileOperationStatus == null || !iFileOperationStatus.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationStatus. Aborting...");
                    return -1;
                }

                IEnumEntry iFileOperationSuccess = iFileOperationStatus.GetEntryByName("Success");
                if (iFileOperationSuccess == null || !iFileOperationSuccess.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperationStatus Success. Aborting...");
                    return -1;
                }

                for (long i = 0; i < writeIterations; i++)
                {
                    if (intermediateBufferSize > bytesLeftToWrite)
                    {
                        // Check for multiple of 4 bytes
                        long remainder = bytesLeftToWrite % 4;
                        if (remainder != 0)
                        {
                            paddingRequired = true;
                            numPaddings = 4 - remainder;
                        }
                    }

                    // Extract bytes from data array
                    byte[] tempData = new byte
                        [intermediateBufferSize <= bytesLeftToWrite ?(int)
                            intermediateBufferSize:(int)(bytesLeftToWrite + numPaddings)];

                    Array.Copy(fileBytes,
                               index,
                               tempData,
                               0,
                               intermediateBufferSize <= bytesLeftToWrite ?(int) intermediateBufferSize
                               : (int) bytesLeftToWrite);

                    if (paddingRequired)
                    {
                        // Fill padded bytes
                        for (long j = 0; j < numPaddings; j++)
                        {
                            tempData[bytesLeftToWrite + j] = 0xFF;
                        }
                    }

                    // Update index for next write iteration
                    index = index + (intermediateBufferSize <= bytesLeftToWrite ? intermediateBufferSize
                                     : bytesLeftToWrite);

                    // Write to AccessBufferNode
                    iFileAccessBuffer.Write(tempData);

                    // Do Write command
                    IEnumEntry iFileOperationWrite = iFileOperationSelector.GetEntryByName("Write");
                    if (iFileOperationWrite == null || !iFileOperationWrite.IsReadable)
                    {
                        Console.WriteLine("Unable to configure FileOperationSelector Write. Aborting...");
                        return -1;
                    }

                    // Enforce Selector -> Offset -> Length -> Execute sequence.
                    // Some cameras reset Offset/Length when Selector changes.
                    iFileOperationSelector.Value = iFileOperationWrite.Symbolic;

                    // Determine length to write
                    long requestedLength = (intermediateBufferSize <= bytesLeftToWrite) ? intermediateBufferSize
                                                                                        : bytesLeftToWrite;

                     if (!iFileAccessOffset.IsWritable ||
                         !iFileAccessLength.IsWritable)
                    {
                        return -1;
                    }
                    iFileAccessOffset.Value = totalBytesWritten;
                    iFileAccessLength.Value = requestedLength;

                    iFileOperationExecute.Execute();

                    if (iFileOperationStatus.Value != iFileOperationSuccess.Value)
                    {
                        Console.WriteLine(
                            "Failed to upload file!  File Operation Status : {0}.",
                            iFileOperationStatus.Symbolics[iFileOperationStatus.Value]);
                        return -1;
                    }

                    // Verify size of bytes written
                    long sizeWritten = iFileOperationResult.Value;

                    // Fallback: If hardware returns 0, but status was success and we have data left,
                    // assume the requested block was written. This handles cameras that don't reliably update ResultNode.
                    if (sizeWritten == 0 && bytesLeftToWrite > 0)
                    {
                        sizeWritten = requestedLength;
                        Console.WriteLine("Write returned 0 bytes at offset {0}. Falling back to assumed length: {1}", totalBytesWritten, sizeWritten);
                    }

                    // Clamp: Ensure sizeWritten does not cause totalBytesWritten to exceed totalBytesToWrite
                    if (totalBytesWritten + sizeWritten > totalBytesToWrite)
                    {
                        sizeWritten = totalBytesToWrite - totalBytesWritten;
                    }

                    // Log current file access offset
                    // Keep track of total bytes written
                    totalBytesWritten += sizeWritten;

                    // Keep track of bytes left to write
                    bytesLeftToWrite = fileBytes.Length - totalBytesWritten;

                    Console.Write("Progress: {0}%\r", (i * 100 / writeIterations));
                }

                Console.Write("Writing complete");

                // Validate upload completion
                if (totalBytesWritten < totalBytesToWrite)
                {
                    Console.WriteLine("Incomplete upload. Wrote {0} bytes to device, but expected {1}.", totalBytesWritten, totalBytesToWrite);
                    return -1;
                }

                // Clear progress message from console
                Console.WriteLine("\r");

                // Close the file
                Console.WriteLine("Closing file...");
                IEnumEntry iFileOperationClose = iFileOperationSelector.GetEntryByName("Close");
                if (iFileOperationClose == null || !iFileOperationClose.IsReadable)
                {
                    Console.WriteLine("Unable to query FileOperation - Close. Aborting...");
                    return -1;
                }

                iFileOperationSelector.Value = iFileOperationClose.Symbolic;
                iFileOperationExecute.Execute();

                if (iFileOperationStatus.Value != iFileOperationSuccess.Value)
                {
                    Console.WriteLine(
                        "Failed to write file!  File Operation Status : {0}.",
                        iFileOperationStatus.Symbolics[iFileOperationStatus.Value]);
                    return -1;
                }

                StringReg iInterfaceNetworkName = nodeMap.GetNode<StringReg>("InferenceNetworkName");
                if (iInterfaceNetworkName == null || !iInterfaceNetworkName.IsWritable)
                {
                    Console.WriteLine("Unable to write to InferenceNetworkName. Aborting...");
                    return -1;
                }

                iInterfaceNetworkName.Value = chosenInferenceNetworkType == InferenceNetworkType.Detection ?
                                                                            "Network_Detection"
                    : "Network_Classification";
            }
            catch (SpinnakerException e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.Message);
                result = -1;
            }

            return result;
        }

        // This function deletes the file uploaded to the camera given the selected
        // file selector entry.
        static int DeleteFileOnCamera(INodeMap nodeMap, string fileSelectorEntryName)
        {
            Console.WriteLine("\n*** CLEANING UP FILE SELECTOR ***");

            IEnum ptrFileSelector = nodeMap.GetNode<IEnum>("FileSelector");
            if (!ptrFileSelector.IsWritable || !ptrFileSelector.IsReadable)
            {
                Console.WriteLine("Unable to configure FileSelector. Aborting...");
                return -1;
            }

            IEnumEntry ptrInferenceSelectorEntry = ptrFileSelector.GetEntryByName(fileSelectorEntryName);
            if (!ptrInferenceSelectorEntry.IsReadable)
            {
                Console.WriteLine("Unable to query FileSelector entry {0}. Aborting...", fileSelectorEntryName);
                return -1;
            }

            // Set file selector to entry
            Console.WriteLine("Setting FileSelector to  {0}", ptrInferenceSelectorEntry.Symbolic);

            ptrFileSelector.Value = ptrInferenceSelectorEntry.Value;

            // Delete file on camera before writing in case camera runs out of space
            if (CameraDeleteFile(nodeMap) != 0)
            {
                Console.WriteLine(
                    "Failed to delete existing file for selector entry {0}. Aborting...", ptrInferenceSelectorEntry);
                return -1;
            }

            return 0;
        }

        // This function enables or disables the given chunk data type based on
        // the specified entry name.
        static int SetChunkEnable(INodeMap nodeMap, string entryName, bool enable)
        {
            int result = 0;
            IEnum iChunkSelector = nodeMap.GetNode<IEnum>("ChunkSelector");

            IEnumEntry iEntry = iChunkSelector.GetEntryByName(entryName);
            if (iEntry == null || !iEntry.IsReadable)
            {
                return -1;
            }

            IBool iChunkEnable = nodeMap.GetNode<IBool>("ChunkEnable");
            if (iChunkEnable == null)
            {
                Console.WriteLine("{0} not available", entryName);
                return -1;
            }
            if (enable)
            {
                if (iChunkEnable.Value)
                {
                    Console.WriteLine("{0} enabled", entryName);
                }
                else if (iChunkEnable.IsWritable)
                {
                    iChunkEnable.Value = true;
                    Console.WriteLine("{0} enabled", entryName);
                }
                else
                {
                    Console.WriteLine("{0} not writable", entryName);
                    result = -1;
                }
            }
            else
            {
                if (!iChunkEnable.Value)
                {
                    Console.WriteLine("{0} disabled", entryName);
                }
                else if (iChunkEnable.IsWritable)
                {
                    iChunkEnable.Value = false;
                    Console.WriteLine("{0} disabled", entryName);
                }
                else
                {
                    Console.WriteLine("{0} not writable", entryName);
                    result = -1;
                }
            }

            return result;
        }

        // This function configures the camera to add chunk data to each image.
        // It does this by enabling each type of chunk data after enabling
        // chunk data mode. When chunk data mode is turned on, the data is made
        // available in both the nodemap and each image.
        static int ConfigureChunkData(INodeMap nodeMap)
        {
            int result = 0;

            Console.WriteLine("\n*** CONFIGURING CHUNK DATA ***");

            try
            {

                // Activate chunk mode
                //
                // *** NOTES ***
                // Once enabled, chunk data will be available at the end of the
                // payload of every image captured until it is disabled. Chunk
                // data can also be retrieved from the nodemap.
                //
                IBool iChunkModeActive = nodeMap.GetNode<IBool>("ChunkModeActive");
                if (iChunkModeActive == null || !iChunkModeActive.IsWritable)
                {
                    Console.WriteLine("Cannot active chunk mode. Aborting...");
                    return -1;
                }

                iChunkModeActive.Value = true;

                Console.WriteLine("Chunk mode activated...");

                // Enable inference related chunks in chunk data

                // Enable chunk data inference Frame Id
                result = SetChunkEnable(nodeMap, "InferenceFrameId", true);
                if (result == -1)
                {
                    Console.WriteLine("Unable to enable Inference Frame Id chunk data.  Aborting...");
                    return result;
                }

                if (chosenInferenceNetworkType == InferenceNetworkType.Detection)
                {
                    // Enable chunk data inference bounding box for Detection
                    result = SetChunkEnable(nodeMap, "InferenceBoundingBoxResult", true);
                    if (result == -1)
                    {
                        Console.WriteLine("Unable to enable Inference Bounding Box chunk data.  Aborting...");
                        return result;
                    }
                }
                else
                {
                    // Enable chunk data inference result for Classification
                    result = SetChunkEnable(nodeMap, "InferenceResult", true);
                    if (result == -1)
                    {
                        Console.WriteLine("Unable to enable Inference Result chunk data.  Aborting...");
                        return result;
                    }

                    // Enable chunk data inference confidence for Classification
                    result = SetChunkEnable(nodeMap, "InferenceConfidence", true);
                    if (result == -1)
                    {
                        Console.WriteLine("Unable to enable Inference Confidence chunk data.  Aborting...");
                        return result;
                    }
                }

                Console.WriteLine();
            }
            catch (SpinnakerException ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                result = -1;
            }

            return result;
        }

        // This function disables each type of chunk data before disabling chunk data mode.
        static int DisableChunkData(INodeMap nodeMap)
        {
            Console.WriteLine("*** DISABLING CHUNK DATA ***");

            int result = 0;

            try
            {
                result = SetChunkEnable(nodeMap, "InferenceFrameId", false);
                if (result == -1)
                {
                    Console.WriteLine("Unable to disable Inference Frame Id chunk data. Aborting...");
                    return result;
                }

                if (chosenInferenceNetworkType == InferenceNetworkType.Detection)
                {
                    // Disable chunk data for Detection
                    result = SetChunkEnable(nodeMap, "InferenceBoundingBoxResult", false);
                    if (result == -1)
                    {
                        Console.WriteLine("Unable to disable Inference Bounding Box chunk data.  Aborting...");
                        return result;
                    }
                }
                else
                {
                    // Disable chunk data for Classification
                    result = SetChunkEnable(nodeMap, "InferenceResult", false);
                    if (result == -1)
                    {
                        Console.WriteLine("Unable to disable Inference Result chunk data.  Aborting...");
                        return result;
                    }

                    result = SetChunkEnable(nodeMap, "InferenceConfidence", false);
                    if (result == -1)
                    {
                        Console.WriteLine("Unable to disable Inference Confidence chunk data.  Aborting...");
                        return result;
                    }
                }

                // Deactivate ChunkMode
                IBool iChunkModeActive = nodeMap.GetNode<IBool>("ChunkModeActive");
                if (iChunkModeActive == null || !iChunkModeActive.IsWritable)
                {
                    Console.WriteLine("Cannot deactivate chunk mode. Aborting...");
                    result = -1;
                }

                iChunkModeActive.Value = false;

                Console.WriteLine("Chunk mode deactivated...");
            }
            catch (SpinnakerException ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                result = -1;
            }

            return result;
        }

        // This function displays the inference-related chunk data from the image.
        static int DisplayChunkData(IManagedImage managedImage)
        {
            int result = 0;

            Console.WriteLine("Printing chunk data from image...");

            try
            {
                //
                // Retrieve chunk data from image
                //
                // *** NOTES ***
                // When retrieving chunk data from an image, the data is stored
                // in a a ChunkData object and accessed with getter functions.
                //
                ManagedChunkData managedChunkData = managedImage.ChunkData;

                long inferenceFrameID = managedChunkData.FrameID;
                Console.WriteLine("\tInference frame ID: {0}", inferenceFrameID);

                if (chosenInferenceNetworkType == InferenceNetworkType.Detection)
                {
                    ManagedInferenceBoundingBoxResult boxResult = managedChunkData.InferenceBoundingBoxResult;
                    Console.WriteLine("\tInference bounding box result");

                    short boxCount = boxResult.BoxCount;
                    if (boxCount == 0)
                    {
                        Console.WriteLine("\tNo bounding box");
                    }

                    for (short i = 0; i < boxResult.BoxCount; ++i)
                    {
                        ManagedInferenceBoundingBox box = boxResult.get_BoxAt(i);
                        switch (box.BoxType)
                        {
                            case InferenceBoundingBoxType.RECTANGLE:
                                Console.WriteLine(
                                    "\t\tBox[{0}]: Class {1} ({2}) - {3}% - Rectangle (X={4}, Y={5}, W={6}, H={7})",
                                    i + 1,
                                    box.ClassId,
                                    (box.ClassId < labelDetection.Length ? labelDetection[box.ClassId]
                                     : "N/A"),
                                    box.Confidence * 100,
                                    box.RectangleTopLeftX,
                                    box.RectangleTopLeftY,
                                    box.RectangleBottomRightX - box.RectangleTopLeftX,
                                    box.RectangleBottomRightY - box.RectangleTopLeftY);
                                break;
                            case InferenceBoundingBoxType.CIRCLE:
                                Console.WriteLine(
                                    "\t\tBox[{0}]: Class {1} ({2}) - {3}% - Rectangle (X={4}, Y={5}, W={6}, H={7})",
                                    i + 1,
                                    box.ClassId,
                                    box.Confidence * 100,
                                    box.CircleCenterX,
                                    box.CircleCenterY,
                                    box.CircleRadius);
                                break;
                            case InferenceBoundingBoxType.ROTATED_RECTANGLE:
                                Console.WriteLine(
                                    "\t\tBox[{0}]: Class {1} ({2}) - {3}% - Rectangle (X={4}, Y={5}, W={6}, H={7})",
                                    i + 1,
                                    box.ClassId,
                                    (box.ClassId < labelDetection.Length ? labelDetection[box.ClassId]
                                     : "N/A"),
                                    box.Confidence * 100,
                                    box.RotatedRectangleTopLeftX,
                                    box.RotatedRectangleTopLeftY,
                                    box.RotatedRectangleBottomRightX,
                                    box.RotatedRectangleBottomRightY,
                                    box.RotatedRectangleRotationAngle);
                                break;
                            default:
                                Console.WriteLine(
                                    "\t\tBox[{0}]: Class {1} - {2} Unknown bounding box type (not supported)",
                                    i,
                                    box.ClassId,
                                    box.Confidence * 100);
                                break;
                        }
                    }
                }
                else
                {
                    long inferenceResult = managedChunkData.InferenceResult;
                    Console.WriteLine(
                        "\tInference result: {0} ({1})",
                        inferenceResult,
                        (inferenceResult < labelClassification.Length? labelClassification[(int) inferenceResult]
                         : "N/A"));

                    double inferenceConfidence = managedChunkData.InferenceConfidence;
                    Console.WriteLine("\tInference confidence: {0} %", inferenceConfidence * 100);
                }
            }
            catch (SpinnakerException ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                result = -1;
            }

            return result;
        }

        // This function disables trigger mode on the camera.
        int DisableTrigger(INodeMap nodeMap)
        {
            int result = 0;
            Console.WriteLine("\n*** DISABLING TRIGGER ***");

            try
            {
                // Configure TriggerMode
                IEnum iTriggerMode = nodeMap.GetNode<IEnum>("TriggerMode");
                if (iTriggerMode == null || !iTriggerMode.IsWritable || !iTriggerMode.IsReadable)
                {
                    Console.WriteLine("Unable to disable trigger mode (enum retrieval). Aborting...");
                    return -1;
                }

                IEnumEntry iTriggerModeOff = iTriggerMode.GetEntryByName("Off");
                if (iTriggerModeOff == null || !iTriggerModeOff.IsReadable)
                {
                    Console.WriteLine("Unable to disable trigger mode (entry retrieval). Aborting...");
                    return -1;
                }

                Console.WriteLine("Configure TriggerMode to {0}", iTriggerModeOff.Symbolic);
                iTriggerMode.Value = iTriggerModeOff.Symbolic;
            }
            catch (SpinnakerException ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                result = -1;
            }

            return result;
        }

        // This function configures camera to run in "inference sync" trigger mode.
        // of the chosen trigger.
        int ConfigureTrigger(INodeMap nodeMap)
        {
            int result = 0;

            Console.WriteLine("\n*** CONFIGURING TRIGGER ***");

            try
            {
                // Set TriggerSelector to FrameStart
                IEnum iTriggerSelector = nodeMap.GetNode<IEnum>("TriggerSelector");
                if (iTriggerSelector == null || !iTriggerSelector.IsWritable || !iTriggerSelector.IsReadable)
                {
                    Console.WriteLine("Unable to get or set trigger selector (enum retrieval). Aborting...");
                    return -1;
                }

                // Set trigger mode to software
                IEnumEntry iTriggerSelectorFrameStarts = iTriggerSelector.GetEntryByName("FrameStart");
                if (iTriggerSelectorFrameStarts == null || !iTriggerSelectorFrameStarts.IsReadable)
                {
                    Console.WriteLine("Unable to get software trigger selector (entry retrieval). Aborting...");
                    return -1;
                }

                iTriggerSelector.Value = iTriggerSelectorFrameStarts.Symbolic;

                Console.WriteLine("Trigger selector set to frame start...");

                // Configure TriggerSource
                IEnum iTriggerSource = nodeMap.GetNode<IEnum>("TriggerSource");
                if (iTriggerSource == null || !iTriggerSource.IsWritable || iTriggerSource.IsReadable)
                {
                    Console.WriteLine("Unable to get or set trigger mode (enum retrieval). Aborting...");
                    return -1;
                }

                IEnumEntry iTriggerSourceSoftware = iTriggerSource.GetEntryByName("InferenceReady");
                if (iTriggerSourceSoftware == null || !iTriggerSourceSoftware.IsReadable)
                {
                    Console.WriteLine("Unable to get software trigger mode (entry retrieval). Aborting...");
                    return -1;
                }

                iTriggerSource.Value = iTriggerSourceSoftware.Symbolic;

                Console.WriteLine("Configuring TriggerSource to {0}", iTriggerSourceSoftware.Symbolic);

                // Configure TriggerMode
                IEnum iTriggerMode = nodeMap.GetNode<IEnum>("TriggerMode");
                if (!iTriggerMode.IsWritable || !iTriggerMode.IsReadable)
                {
                    Console.WriteLine("Unable to configure TriggerMode. Aborting...");
                    return -1;
                }

                IEnumEntry iTriggerModeOn = iTriggerMode.GetEntryByName("On");
                if (iTriggerModeOn == null || !iTriggerModeOn.IsReadable)
                {
                    Console.WriteLine("Unable to enable trigger mode (entry retrieval). Aborting...");
                    return -1;
                }

                Console.WriteLine("Configuring TriggerMode to {0}", iTriggerModeOn.Symbolic);
                iTriggerMode.Value = iTriggerModeOn.Symbolic;
            }
            catch (SpinnakerException ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                result = -1;
            }

            return result;
        }

        // This function enables/disables inference on the camera and configures the inference network type
        static int ConfigureInference(INodeMap nodeMap, bool isEnabled)
        {
            int result = 0;

            if (isEnabled)
            {
                Console.WriteLine(
                    "\n*** CONFIGURING INFERENCE ( {0} ) ***",
                    ((chosenInferenceNetworkType == InferenceNetworkType.Detection) ? "DETECTION" : "CLASSIFICATION"));
            }
            else
            {
                Console.WriteLine("\n*** DISABLING INFERENCE ***");
            }

            try
            {
                if (isEnabled)
                {
                    // Set Network Type to Detection
                    IEnum ptrInferenceNetworkTypeSelector = nodeMap.GetNode<IEnum>("InferenceNetworkTypeSelector");
                    if (!ptrInferenceNetworkTypeSelector.IsWritable || !ptrInferenceNetworkTypeSelector.IsReadable)
                    {
                        Console.WriteLine("Unable to query InferenceNetworkTypeSelector. Aborting...");
                        return -1;
                    }

                    string networkTypeString =
                        (chosenInferenceNetworkType == InferenceNetworkType.Detection) ? "Detection" : "Classification";

                    // Retrieve entry node from enumeration node
                    IEnumEntry ptrInferenceNetworkType =
                        ptrInferenceNetworkTypeSelector.GetEntryByName(networkTypeString);
                    if (!ptrInferenceNetworkType.IsReadable)
                    {
                        Console.WriteLine(
                            "Unable to set inference network type to {0}(entry retrieval). Aborting...",
                            networkTypeString);
                        return -1;
                    }

                    ptrInferenceNetworkTypeSelector.Value = ptrInferenceNetworkType.Symbolic;

                    Console.WriteLine("Inference network type set to {0}...", networkTypeString);
                }

                // Enable/Disable inference
                Console.WriteLine("{0} inference...", isEnabled ? "Enabling" : "Disabling");
                IBool ptrInferenceEnable = nodeMap.GetNode<IBool>("InferenceEnable");
                if (!ptrInferenceEnable.IsWritable)
                {
                    Console.WriteLine("Unable to enable inference. Aborting...");
                    return -1;
                }

                ptrInferenceEnable.Value = isEnabled;
                Console.WriteLine("Inference {0}", (isEnabled ? "enabled..." : "disabled..."));
            }
            catch (SpinnakerException e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.Message);
                result = -1;
            }

            return result;
        }

        // This function configures camera test pattern to make use of the injected test image for inference
        static int ConfigureTestPattern(INodeMap nodeMap, bool isEnabled)
        {
            int result = 0;

            if (isEnabled)
            {
                Console.WriteLine("\n*** CONFIGURING TEST PATTERN ***");
            }
            else
            {
                Console.WriteLine("\n*** DISABLING TEST PATTERN ***");
            }

            try
            {
                // Set TestPatternGeneratorSelector to PipelineStart
                IEnum iTestPatternGeneratorSelector = nodeMap.GetNode<IEnum>("TestPatternGeneratorSelector");

                if (isEnabled)
                {
                    IEnumEntry iTestPatternGeneratorPipelineStart =
                        iTestPatternGeneratorSelector.GetEntryByName("PipelineStart");
                    iTestPatternGeneratorSelector.Value = iTestPatternGeneratorPipelineStart.Symbolic;
                    Console.WriteLine(
                        "TestPatternGeneratorSelector set to {0}...", iTestPatternGeneratorPipelineStart.Symbolic);
                }
                else
                {
                    IEnumEntry iTestPatternGeneratorSensor = iTestPatternGeneratorSelector.GetEntryByName("Sensor");
                    iTestPatternGeneratorSelector.Value = iTestPatternGeneratorSensor.Symbolic;
                    Console.WriteLine(
                        "TestPatternGeneratorSelector set to {0}...", iTestPatternGeneratorSensor.Symbolic);
                }

                // Set TestPattern to InjectedImage
                IEnum iTestPattern = nodeMap.GetNode<IEnum>("TestPattern");

                if (isEnabled)
                {
                    IEnumEntry iTestPatternInjectedImage = iTestPattern.GetEntryByName("InjectedImage");
                    iTestPattern.Value = iTestPatternInjectedImage.Symbolic;
                    Console.WriteLine("TestPattern set to {0}...", iTestPatternInjectedImage.Symbolic);
                }
                else
                {
                    IEnumEntry iTestPatternOff = iTestPattern.GetEntryByName("Off");
                    iTestPattern.Value = iTestPatternOff.Symbolic;
                    Console.WriteLine("TestPattern set to {0}...", iTestPatternOff.Symbolic);
                }

                if (isEnabled)
                {
                    // The inject images have different ROI sizes so camera needs to be configured to the appropriate
                    // injected width and height
                    IInteger iInjectedWidth = nodeMap.GetNode<IInteger>("InjectedWidth");
                    iInjectedWidth.Value = injectedImageWidth;

                    IInteger iInjectedHeight = nodeMap.GetNode<IInteger>("InjectedHeight");
                    iInjectedHeight.Value = injectedImageHeight;
                }
            }
            catch (SpinnakerException e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.Message);
                result = -1;
            }

            return result;
        }

        // This function acquires and saves 10 images from a device; please see
        // Acquisition_CSharp example for more in-depth comments on acquiring images.
        static int AcquireImages(IManagedCamera cam, INodeMap nodeMap, INodeMap nodeMapTLDevice)
        {
            int result = 0;

            Console.WriteLine("*** IMAGE ACQUISITION ***");

            try
            {
                // Set acquisition mode to continuous
                IEnum iAcquisitionMode = nodeMap.GetNode<IEnum>("AcquisitionMode");

                IEnumEntry iAcquisitionModeContinuous = iAcquisitionMode.GetEntryByName("Continuous");

                iAcquisitionMode.Value = iAcquisitionModeContinuous.Symbolic;

                Console.WriteLine("Acquisition mode set to continuous...");

                // Begin acquiring images
                cam.BeginAcquisition();

                Console.WriteLine("Acquiring images...");

                // Retrieve device serial number for filename
                string deviceSerialNumber = "";

                IString iDeviceSerialNumber = nodeMapTLDevice.GetNode<IString>("DeviceSerialNumber");
                if (iDeviceSerialNumber != null && iDeviceSerialNumber.IsReadable)
                {
                    deviceSerialNumber = iDeviceSerialNumber.Value;
                    Console.WriteLine("Device serial number retrieved as {0}...", deviceSerialNumber);
                }
                Console.WriteLine();

                // Retrieve, convert, and save images
                const int NumImages = 10;

                //
                // Create ImageProcessor instance for post processing images
                //
                IManagedImageProcessor processor = new ManagedImageProcessor();

                //
                // Set default image processor color processing method
                //
                // *** NOTES ***
                // By default, if no specific color processing algorithm is set, the image
                // processor will default to NEAREST_NEIGHBOR method.
                //
                processor.SetColorProcessing(ColorProcessingAlgorithm.HQ_LINEAR);

                for (int imageCnt = 0; imageCnt < NumImages; imageCnt++)
                {
                    try
                    {
                        // Retrieve next received image and ensure image completion
                        using(IManagedImage rawImage = cam.GetNextImage(1000))
                        {
                            if (rawImage.IsIncomplete)
                            {
                                Console.WriteLine("Image incomplete with image status {0}...", rawImage.ImageStatus);
                            }
                            else
                            {
                                // Print image information
                                Console.WriteLine(
                                    "Grabbed image {0}, width = {1}, height = {1}",
                                    imageCnt,
                                    rawImage.Width,
                                    rawImage.Height);

                                // Convert image to mono 8
                                using(
                                    IManagedImage convertedImage = processor.Convert(rawImage, PixelFormatEnums.Mono8))
                                {
                                    // Create unique file name
                                    string filename = "Inference-CSharp-";
                                    if (deviceSerialNumber != "")
                                    {
                                        filename = filename + deviceSerialNumber + "-";
                                    }
                                    filename = filename + imageCnt + ".jpg";

                                    // Save image
                                    convertedImage.Save(filename);

                                    Console.WriteLine("Image saved at {0}", filename);

                                    // Display chunk data
                                    result = DisplayChunkData(rawImage);
                                }
                            }
                        }
                        Console.WriteLine();
                    }
                    catch (SpinnakerException ex)
                    {
                        Console.WriteLine("Error: {0}", ex.Message);
                        result = -1;
                    }
                }

                // End acquisition
                cam.EndAcquisition();
            }
            catch (SpinnakerException ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                result = -1;
            }

            return result;
        }

        // This function acts as the body of the example; please see
        // NodeMapInfo_CSharp example for more in-depth comments on setting up
        // cameras.
        int RunSingleCamera(IManagedCamera cam)
        {
            int result = 0;

            try
            {
                // Retrieve TL device nodemap and print device information
                INodeMap nodeMapTLDevice = cam.GetTLDeviceNodeMap();

                result = PrintDeviceInfo(nodeMapTLDevice);

                // Initialize camera
                cam.Init();

                // Retrieve GenICam nodemap
                INodeMap nodeMap = cam.GetNodeMap();

                // Check to make sure camera supports inference
                Console.WriteLine("Checking camera inference support...");
                IBool ptrInferenceEnable = nodeMap.GetNode<IBool>("InferenceEnable");
                if (ptrInferenceEnable == null || !ptrInferenceEnable.IsWritable)
                {
                    Console.WriteLine("Inference is not supported on this camera. Aborting...");
                    return -1;
                }

                // Upload custom inference network onto the camera
                // The inference network file is in a movidius specific neural network format.
                // Uploading the network to the camera allows for "inference on the edge" where
                // camera can apply deep learning on a live stream. Refer to "Getting Started
                // with Firefly-DL" for information on how to create your own custom inference
                // network files using pre-existing neural network.
                result = UploadFileToCamera(nodeMap, "InferenceNetwork", networkFilePath);
                if (result < 0)
                {
                    return result;
                }

                // Upload injected test image
                // Instead of applying deep learning on a live stream, the camera can be
                // tested with an injected test image.
                result = UploadFileToCamera(nodeMap, "InjectedImage", injectedImageFilePath);
                if (result < 0)
                {
                    return result;
                }

                // Configure inference
                result = ConfigureInference(nodeMap, true);
                if (result < 0)
                {
                    return result;
                }

                // Configure test pattern to make use of the injected image
                result = ConfigureTestPattern(nodeMap, true);
                if (result < 0)
                {
                    return result;
                }

                // Configure trigger
                // When enabling inference results via chunk data, the results that accompany a frame
                // will likely not be the frame that inference was run on. In order to guarantee that
                // the chunk inference results always correspond to the frame that they are sent with,
                // the camera needs to be put into the "inference sync" trigger mode.
                // Note: Enabling this setting will limit frame rate so that every frame contains new
                //       inference dataset. To not limit the frame rate, you can enable InferenceFrameID
                //       chunk data to help determine which frame is associated with a particular
                //       inference data.
                result = ConfigureTrigger(nodeMap);
                if (result < 0)
                {
                    return result;
                }

                // Configure chunk data
                result = ConfigureChunkData(nodeMap);
                if (result < 0)
                {
                    return result;
                }

                // Acquire images and display chunk data
                result = result | AcquireImages(cam, nodeMap, nodeMapTLDevice);

                // Disable chunk data
                result = DisableChunkData(nodeMap);
                if (result < 0)
                {
                    return result;
                }

                // Disable trigger
                result = DisableTrigger(nodeMap);
                if (result < 0)
                {
                    return result;
                }

                // Disable test pattern
                result = ConfigureTestPattern(nodeMap, false);
                if (result < 0)
                {
                    return result;
                }

                // Disable inference
                result = ConfigureInference(nodeMap, false);
                if (result < 0)
                {
                    return result;
                }

                // Clear injected test image
                result = DeleteFileOnCamera(nodeMap, "InjectedImage"); //?
                if (result < 0)
                {
                    return result;
                }

                // Clear uploaded inference network
                result = DeleteFileOnCamera(nodeMap, "InferenceNetwork");
                if (result < 0)
                {
                    return result;
                }

                //  Deinitialize camera
                cam.DeInit();
            }
            catch (SpinnakerException ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                result = -1;
            }

            return result;
        }

        private static int Main(string[] args)
        {
            int result = 0;
            Program program = new Program();

            // Since this application saves images in the current folder
            // we must ensure that we have permission to write to this folder.
            // If we do not have permission, fail right away.
            FileStream fileStream;
            try
            {
                fileStream = new FileStream(@"test.txt", FileMode.Create);
                fileStream.Close();
                File.Delete("test.txt");
            }
            catch
            {
                Console.WriteLine("Failed to create file in current folder. Please check permissions.");
                Console.WriteLine("Press enter to exit...");
                Console.ReadLine();
                return -1;
            }

            // Retrieve singleton reference to system object
            ManagedSystem system = new ManagedSystem();

            // Print out current library version
            LibraryVersion spinVersion = system.GetLibraryVersion();
            Console.WriteLine(
                "Spinnaker library version: {0}.{1}.{2}.{3}\n\n",
                spinVersion.major,
                spinVersion.minor,
                spinVersion.type,
                spinVersion.build);

            // Retrieve list of cameras from the system
            ManagedCameraList camList = system.GetCameras();

            int numCameras = camList.Count;

            Console.WriteLine("Number of cameras detected: {0}\n\n", camList.Count);

            // Finish if there are no cameras
            if (camList.Count == 0)
            {
                // Clear camera list before releasing system
                camList.Clear();

                // Release system
                system.Dispose();

                Console.WriteLine("Not enough cameras!");
                Console.WriteLine("Done! Press Enter to exit...");
                Console.ReadLine();

                return -1;
            }

            // Run example on each camera
            int i = 0;
            foreach(IManagedCamera managedCamera in camList) using(managedCamera)
            {
                try
                {
                    // Run example
                    result = result | program.RunSingleCamera(managedCamera);
                }
                catch (SpinnakerException ex)
                {
                    Console.WriteLine("Error: {0}", ex.Message);
                    result = -1;
                }

                Console.WriteLine("Camera {0} example complete...\n", i++);
            }

            // Clear camera list before releasing system
            camList.Clear();

            // Release system
            system.Dispose();

            Console.WriteLine("\nDone! Press Enter to exit...");
            Console.ReadLine();

            return result;
        }
    }
}