//=============================================================================
// 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 FileAccess_Quickspin.cpp
*
* @brief FileAccess_Quickspin.cpp shows how to read and write images using
* camera File Access function.
* This example uploads an image to the camera File Access storage and also
* download the image from the camera File Access storage and saves it to
* the disk.
* It also provides debug message when debug mode is turned on giving more
* detail status of the progress and error messages to the users.
*
* It relies on information provided in the
* Enumeration, Acquisition, and NodeMapInfo examples.
*
* 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
*/
#include "Spinnaker.h"
#include "SpinGenApi/SpinnakerGenApi.h"
#include <cctype>
#include <iostream>
#include <sstream>
using namespace Spinnaker;
using namespace Spinnaker::GenApi;
using namespace Spinnaker::GenICam;
using namespace std;
static bool _enableDebug = false;
static gcstring _fileSelector = "UserFile1";
namespace
{
std::string SanitizeFileNameComponent(const std::string& value)
{
std::string sanitized;
sanitized.reserve(value.size());
for (std::string::const_iterator it = value.begin(); it != value.end(); ++it)
{
const unsigned char c = static_cast<unsigned char>(*it);
if (std::isalnum(c) || c == '-' || c == '_')
{
sanitized.push_back(static_cast<char>(c));
}
else if (!sanitized.empty() && sanitized[sanitized.size() - 1] != '_')
{
sanitized.push_back('_');
}
}
if (sanitized.empty())
{
sanitized = "camera";
}
return sanitized;
}
}
// Print out operation result message
static void PrintResultMessage(bool result)
{
if (result)
{
cout << "\n*** OPERATION COMPLETE ***\n";
}
else
{
cout << "\n*** OPERATION FAILED ***\n";
}
}
// This function prints the device information of the camera from the transport
// layer; please see NodeMapInfo example for more in-depth comments on printing
// device information from the nodemap.
int PrintDeviceInfo(INodeMap& nodeMap)
{
int result = 0;
cout << endl << "*** DEVICE INFORMATION ***" << endl << endl;
try
{
FeatureList_t features;
CCategoryPtr category = nodeMap.GetNode("DeviceInformation");
if (IsReadable(category))
{
category->GetFeatures(features);
FeatureList_t::const_iterator it;
for (it = features.begin(); it != features.end(); ++it)
{
try
{
CNodePtr pfeatureNode = *it;
cout << pfeatureNode->GetName() << " : ";
CValuePtr pValue = (CValuePtr)pfeatureNode;
cout << (IsReadable(pValue) ? pValue->ToString() : gcstring("Node not readable"));
cout << endl;
}
catch (Spinnaker::Exception)
{
cout << "Node not readable" << endl;
}
}
}
else
{
cout << "Device control information not readable." << endl;
}
}
catch (Spinnaker::Exception& e)
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
// Intializes the system
bool InitializeSystem(SystemPtr& system, CameraList& camList, CameraPtr& pCam)
{
// Retrieve singleton reference to system object
system = System::GetInstance();
// Retrieve list of cameras from the system
camList = system->GetCameras();
const unsigned int numCameras = camList.GetSize();
cout << "Number of cameras detected: " << numCameras << endl << endl;
// Stop if there are no cameras
if (numCameras == 0)
{
// Clear camera list before releasing system
camList.Clear();
// Release system
system->ReleaseInstance();
cout << "Not enough cameras!" << endl;
cout << "Done! Press Enter to exit..." << endl;
getchar();
return false;
}
//
// It creates shared pointer to camera
//
// *** NOTES ***
// The CameraPtr object is a shared pointer, and will generally clean itself
// up upon exiting its scope. However, if a shared pointer is created in the
// same scope that a system object is explicitly released (i.e. this scope),
// the reference to the shared point must be broken manually.
//
// *** LATER ***
// Shared pointers can be terminated manually by assigning them to nullptr.
// This keeps releasing the system from throwing an exception.
//
pCam = nullptr;
// Run example on 1st camera
pCam = camList.GetByIndex(0);
return true;
}
// Print out debug message
static void PrintDebugMessage(string msg)
{
#if DEBUG
_enableDebug = true;
#endif
if (_enableDebug)
{
cout << msg << endl;
}
}
// Acquire 5 images from a device.
bool AcquireImages(CameraPtr pCam, INodeMap& nodeMap, INodeMap& nodeMapTLDevice, ImagePtr pReferenceImage)
{
cout << endl << endl << "*** IMAGE ACQUISITION ***" << endl << endl;
bool result = true;
try
{
//
// Set acquisition mode to continuous
//
// *** NOTES ***
// Because the example acquires and saves 5 images, setting acquisition
// mode to continuous lets the example finish. If set to single frame
// or multiframe (at a lower number of images), the example would just
// hang. This would happen because the example has been written to
// acquire 5 images while the camera would have been programmed to
// retrieve less than that.
//
// Setting the value of an enumeration node is slightly more complicated
// than other node types. Two nodes must be retrieved: first, the
// enumeration node is retrieved from the nodemap; and second, the entry
// node is retrieved from the enumeration node. The integer value of the
// entry node is then set as the new value of the enumeration node.
//
// Notice that both the enumeration and the entry nodes are checked for
// availability and readability/writability. Enumeration nodes are
// generally readable and writable whereas their entry nodes are only
// ever readable.
//
// Retrieve enumeration node from nodemap
CEnumerationPtr ptrAcquisitionMode = nodeMap.GetNode("AcquisitionMode");
if (!IsReadable(ptrAcquisitionMode) || !IsWritable(ptrAcquisitionMode))
{
cout << "Unable to set acquisition mode to continuous (enum retrieval). Aborting..." << endl << endl;
return false;
}
// Retrieve entry node from enumeration node
CEnumEntryPtr ptrAcquisitionModeContinuous = ptrAcquisitionMode->GetEntryByName("Continuous");
if (!IsReadable(ptrAcquisitionModeContinuous))
{
cout << "Unable to set acquisition mode to continuous (entry retrieval). Aborting..." << endl << endl;
return false;
}
// Retrieve integer value from entry node
const int64_t acquisitionModeContinuous = ptrAcquisitionModeContinuous->GetValue();
// Set integer value from entry node as new value of enumeration node
ptrAcquisitionMode->SetIntValue(acquisitionModeContinuous);
PrintDebugMessage("Acquisition mode set to continuous...");
// Apply Small Pixel Format
CEnumEntryPtr ptrPixelFormat = pCam->PixelFormat.GetEntry(PixelFormat_Mono8);
if (IsReadable(ptrPixelFormat))
{
pCam->PixelFormat.SetValue(PixelFormat_Mono8);
}
else
{
// Use Bayer8 if Mono8 is not available
pCam->PixelFormat.SetValue(PixelFormat_BayerRG8);
}
//
// Begin acquiring images
//
// *** NOTES ***
// What happens when the camera begins acquiring images depends on the
// acquisition mode. Single frame captures only a single image, multi
// frame catures a set number of images, and continuous captures a
// continuous stream of images. Because the example calls for the
// retrieval of 10 images, continuous mode has been set.
//
// *** LATER ***
// Image acquisition must be ended when no more images are needed.
//
pCam->BeginAcquisition();
cout << "Acquiring images..." << endl;
//
// Retrieve device serial number for filename
//
// *** NOTES ***
// The device serial number is retrieved in order to keep cameras from
// overwriting one another. Grabbing image IDs could also accomplish
// this.
//
string deviceSerialNumber("");
CStringPtr ptrStringSerial = nodeMapTLDevice.GetNode("DeviceSerialNumber");
if (IsReadable(ptrStringSerial))
{
deviceSerialNumber = SanitizeFileNameComponent(ptrStringSerial->GetValue().c_str());
PrintDebugMessage("Device serial number retrieved as " + deviceSerialNumber + "...");
}
cout << endl;
// Retrieve, convert, and save images
const unsigned int k_numImages = 5;
for (unsigned int imageCnt = 0; imageCnt < k_numImages; imageCnt++)
{
try
{
//
// Retrieve next received image
//
// *** NOTES ***
// Capturing an image houses images on the camera buffer. Trying
// to capture an image that does not exist will hang the camera.
//
// *** LATER ***
// Once an image from the buffer is saved and/or no longer
// needed, the image must be released in order to keep the
// buffer from filling up.
//
ImagePtr pResultImage = pCam->GetNextImage(1000);
//
// Ensure image completion
//
// *** NOTES ***
// Images can easily be checked for completion. This should be
// done whenever a complete image is expected or required.
// Further, check image status for a little more insight into
// why an image is incomplete.
//
if (pResultImage->IsIncomplete())
{
cout << "Image incomplete with image status " << pResultImage->GetImageStatus() << "..." << endl
<< endl;
}
else
{
//
// Print image information; height and width recorded in pixels
//
// *** NOTES ***
// Images have quite a bit of available metadata including
// things such as CRC, image status, and offset values, to
// name a few.
//
PrintDebugMessage(
"Grabbed image " + to_string(static_cast<long long>(imageCnt)) +
", width = " + to_string(static_cast<long long>(pResultImage->GetWidth())) +
", height = " + to_string(static_cast<long long>(pResultImage->GetHeight())));
// It deep copies to ImagePtr to pass it on
pReferenceImage->DeepCopy(pResultImage);
}
//
// Release image
//
// *** NOTES ***
// Images retrieved directly from the camera (i.e. non-converted
// images) need to be released in order to keep from filling the
// buffer.
//
pResultImage->Release();
}
catch (Spinnaker::Exception& e)
{
cout << "Error: " << e.what() << endl;
result = false;
}
}
//
// End acquisition
//
// *** NOTES ***
// Ending acquisition appropriately helps ensure that devices clean up
// properly and do not need to be power-cycled to maintain integrity.
//
pCam->EndAcquisition();
}
catch (Spinnaker::Exception& e)
{
cout << "Error: " << e.what() << endl;
result = false;
}
return result;
}
// Execute delete operation
bool ExecuteDeleteCommand(CameraPtr pCam)
{
PrintDebugMessage("Deleting file...");
bool result = true;
try
{
pCam->FileOperationSelector.SetValue(FileOperationSelector_Delete);
pCam->FileOperationExecute.Execute();
if (pCam->FileOperationStatus.GetValue() != FileOperationStatus_Success)
{
cout << "Failed to delete file!" << endl;
return false;
}
}
catch (Spinnaker::Exception& e)
{
cout << "Unexpected exception : " << e.what() << endl;
result = false;
}
return result;
}
// Open camera file for writing
bool OpenFileToWrite(CameraPtr pCam)
{
bool result = true;
PrintDebugMessage("Opening file for writing...");
try
{
pCam->FileOperationSelector.SetValue(FileOperationSelector_Open);
pCam->FileOpenMode.SetValue(FileOpenMode_Write);
pCam->FileOperationExecute.Execute();
if (pCam->FileOperationStatus.GetValue() != FileOperationStatus_Success)
{
cout << "Failed to open file for writing!" << endl;
return false;
}
}
catch (Spinnaker::Exception& e)
{
cout << "Unexpected exception : " << e.what();
result = false;
}
return result;
}
// Execute write operation
bool ExecuteWriteCommand(CameraPtr pCam, int64_t offset, int64_t length)
{
bool result = true;
try
{
pCam->FileOperationSelector.SetValue(FileOperationSelector_Write);
if (!IsWritable(pCam->FileAccessOffset) || !IsWritable(pCam->FileAccessLength))
{
cout << "Unable to configure FileAccessOffset or FileAccessLength. Aborting..." << endl;
return false;
}
pCam->FileAccessOffset.SetValue(offset);
pCam->FileAccessLength.SetValue(length);
pCam->FileOperationExecute.Execute();
if (pCam->FileOperationStatus.GetValue() != FileOperationStatus_Success)
{
cout << "Failed to write to file!" << endl;
return false;
}
}
catch (Spinnaker::Exception& e)
{
cout << "Unexpected exception : " << e.what();
result = false;
}
return result;
}
// Close the file
bool CloseFile(CameraPtr pCam)
{
bool result = true;
PrintDebugMessage("Closing file...");
try
{
pCam->FileOperationSelector.SetValue(FileOperationSelector_Close);
pCam->FileOperationExecute.Execute();
if (pCam->FileOperationStatus.GetValue() != FileOperationStatus_Success)
{
cout << "Failed to close file!" << endl;
return false;
}
}
catch (Spinnaker::Exception& e)
{
cout << "Unexpected exception : " << e.what();
result = false;
}
return result;
}
// Upload the image to the camera file
bool UploadImage()
{
SystemPtr system;
CameraList camList;
CameraPtr pCam;
ImagePtr pReferenceImage;
bool result = true;
// Initialize System
if (!InitializeSystem(system, camList, pCam))
{
PrintResultMessage(false);
return false;
}
try
{
// Retrieve TL device nodemap and print device information
INodeMap& nodeMapTLDevice = pCam->GetTLDeviceNodeMap();
PrintDeviceInfo(nodeMapTLDevice);
// Initialize camera
pCam->Init();
// Retrieve GenICam nodemap
INodeMap& nodeMap = pCam->GetNodeMap();
ImagePtr pReferenceImage = Image::Create();
// Acquire images
if (!AcquireImages(pCam, nodeMap, nodeMapTLDevice, pReferenceImage))
{
PrintResultMessage(false);
return false;
}
// Save a raw image for debugging purpose
if (_enableDebug)
{
try
{
cout << "\nSaving raw image to disk for debugging purpose..." << endl;
// Form file path
ostringstream filename;
filename << "rawImage.png";
pReferenceImage->Save(filename.str().c_str());
cout << "Image saved at " << filename.str() << endl;
}
catch (Spinnaker::Exception& e)
{
cout << "Unable to save an image file : " << e.what();
}
}
cout << endl << "*** UPLOADING IMAGE ***" << endl;
PrintDebugMessage("Fetching \"UserFile1\" Entry from FileSelectorNode...");
// Check file selector support
if (pCam->FileSelector == NULL)
{
cout << "File selector not supported on device!";
return false;
}
NodeList_t selectorList;
pCam->FileSelector.GetEntries(selectorList);
for (unsigned int i = 0; i < selectorList.size(); i++)
{
// Get current enum entry node
CEnumEntryPtr node = selectorList.at(i);
PrintDebugMessage("Setting value to FileSelectorNode...");
// Check file selector entry support
if (!node || !IsReadable(node))
{
// Go to next entry node
cout << node->GetSymbolic() << " not supported!" << endl;
continue;
}
if (node->GetSymbolic().compare(_fileSelector) == 0)
{
PrintDebugMessage("Setting value to FileSelectorNode...");
// Set file selector
pCam->FileSelector.SetIntValue((int64_t)node->GetNumericValue());
// Delete file on camera before writing in case camera runs out of space
if (pCam->FileSize.GetValue() > 0)
{
if (ExecuteDeleteCommand(pCam) != true)
{
cout << "Failed to delete file!" << endl;
continue;
}
}
// Open file on camera for write
if (OpenFileToWrite(pCam) != true)
{
cout << "Failed to open file!" << endl;
// may be file was not closed properly last time.
// Close and re-open again
if (!CloseFile(pCam))
{
// It fails to close the file. Abort!
cout << "Problem opening file node." << endl;
return false;
}
// File was closed. Open again.
if (!OpenFileToWrite(pCam))
{
// Fails again. Abort!
cout << "Problem opening file node." << endl;
return false;
}
}
// Attempt to set FileAccessLength to FileAccessBufferNode length to speed up the write
if (pCam->FileAccessLength.GetValue() < pCam->FileAccessBuffer.GetLength())
{
try
{
pCam->FileAccessLength.SetValue(pCam->FileAccessBuffer.GetLength());
}
catch (Spinnaker::Exception& e)
{
cout << "Unable to set FileAccessLength to FileAccessBuffer length : " << e.what();
}
}
// Set File Access Offset to zero if its not
pCam->FileAccessOffset.SetValue(0);
// Compute number of write operations required
int64_t totalBytesToWrite = pReferenceImage->GetBufferSize();
int64_t intermediateBufferSize = pCam->FileAccessLength.GetValue();
int64_t writeIterations = (totalBytesToWrite / intermediateBufferSize) +
(totalBytesToWrite % intermediateBufferSize == 0 ? 0 : 1);
if (totalBytesToWrite == 0)
{
cout << "Empty Image. No data will be written to camera." << endl;
return false;
}
PrintDebugMessage("Start saving image on camera...");
PrintDebugMessage("Total Bytes to write : " + to_string(static_cast<long long>(totalBytesToWrite)));
PrintDebugMessage("FileAccessLength : " + to_string(static_cast<long long>(intermediateBufferSize)));
PrintDebugMessage("Write Iterations : " + to_string(static_cast<long long>(writeIterations)));
int64_t index = 0;
int64_t bytesLeftToWrite = totalBytesToWrite;
int64_t totalBytesWritten = 0;
bool paddingRequired = false;
int numPaddings = 0;
cout << "Writing data to device" << endl;
unsigned char* pImageData = static_cast<unsigned char*>(pReferenceImage->GetData());
for (unsigned int i = 0; i < writeIterations; i++)
{
// Check whether padding is required
if (intermediateBufferSize > bytesLeftToWrite)
{
// Check for multiple of 4 bytes
unsigned int remainder = bytesLeftToWrite % 4;
if (remainder != 0)
{
paddingRequired = true;
numPaddings = 4 - remainder;
}
}
// Setup data to write
int64_t tmpBufferSize = intermediateBufferSize <= bytesLeftToWrite
? intermediateBufferSize
: (bytesLeftToWrite + numPaddings);
std::unique_ptr<unsigned char> tmpBuffer(
new unsigned char[static_cast<unsigned int>(tmpBufferSize)]);
memcpy(
tmpBuffer.get(),
&pImageData[index],
static_cast<unsigned int>(
(intermediateBufferSize <= bytesLeftToWrite) ? intermediateBufferSize : bytesLeftToWrite));
if (paddingRequired)
{
// Fill padded bytes
for (int j = 0; j < numPaddings; j++)
{
unsigned char* pTmpBuffer = tmpBuffer.get();
pTmpBuffer[bytesLeftToWrite + j] = 255;
}
}
// Update index for next write iteration
index = index +
(intermediateBufferSize <= bytesLeftToWrite ? intermediateBufferSize : bytesLeftToWrite);
// Write to FileAccessBufferNode
pCam->FileAccessBuffer.Set(tmpBuffer.get(), tmpBufferSize);
// Determine length to write
const int64_t requestedLength =
(intermediateBufferSize <= bytesLeftToWrite ? intermediateBufferSize : bytesLeftToWrite);
// Perform Write command
if (!ExecuteWriteCommand(pCam, totalBytesWritten, requestedLength))
{
cout << "Writing to stream failed!" << endl;
break;
}
// Verify size of bytes written
int64_t sizeWritten = pCam->FileOperationResult.GetValue();
// 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;
PrintDebugMessage(
"Write returned 0 bytes at offset " + to_string(totalBytesWritten) +
". Falling back to assumed length: " + to_string(sizeWritten));
}
// Clamp: Ensure sizeWritten does not cause totalBytesWritten to exceed totalBytesToWrite
if (totalBytesWritten + sizeWritten > totalBytesToWrite)
{
sizeWritten = totalBytesToWrite - totalBytesWritten;
}
// Log current file access offset
PrintDebugMessage("File Access Offset: " + to_string(pCam->FileAccessOffset.GetValue()));
// Keep track of total bytes written
totalBytesWritten += sizeWritten;
PrintDebugMessage(
"Bytes written: " + to_string(static_cast<long long>(totalBytesWritten)) + " of " +
to_string(static_cast<long long>(totalBytesToWrite)));
// Keep track of bytes left to write
bytesLeftToWrite = totalBytesToWrite - totalBytesWritten;
PrintDebugMessage("Bytes left: " + to_string(static_cast<long long>(bytesLeftToWrite)));
cout << "Progress : " << (i * 100 / writeIterations) << " %" << endl;
}
cout << "Writing complete" << endl;
// Validate upload completion
if (totalBytesWritten < totalBytesToWrite)
{
cout << "Incomplete upload. Wrote " << totalBytesWritten << " bytes to device, but expected "
<< totalBytesToWrite << "." << endl;
result = false;
}
if (!CloseFile(pCam))
{
cout << "Failed to close file!" << endl;
}
}
}
//
// Release reference to the camera
//
// *** NOTES ***
// Had the CameraPtr object been created within the for-loop, it would not
// be necessary to manually break the reference because the shared pointer
// would have automatically cleaned itself up upon exiting the loop.
//
pCam->DeInit();
pCam = nullptr;
// Clear camera list before releasing system
camList.Clear();
// Release system
system->ReleaseInstance();
cout << endl << "Done! Press Enter to exit..." << endl;
getchar();
}
catch (Spinnaker::Exception& e)
{
cout << "Unexpected exception : " << e.what();
result = false;
}
return result;
}
// Open camera file to read
bool OpenFileToRead(CameraPtr pCamera)
{
bool result = true;
cout << "Opening file for reading..." << endl;
try
{
pCamera->FileOperationSelector.SetValue(FileOperationSelector_Open);
pCamera->FileOpenMode.SetValue(FileOpenMode_Read);
pCamera->FileOperationExecute.Execute();
if (pCamera->FileOperationStatus.GetValue() != FileOperationStatus_Success)
{
cout << "Failed to open file for reading!" << endl;
return false;
}
}
catch (Spinnaker::Exception& e)
{
cout << "Unexpected exception : " << e.what() << endl;
result = false;
}
return result;
}
// Execute read operation
bool ExecuteReadCommand(CameraPtr pCamera, int64_t offset, int64_t length)
{
bool result = true;
try
{
pCamera->FileOperationSelector.SetValue(FileOperationSelector_Read);
if (!IsWritable(pCamera->FileAccessOffset) || !IsWritable(pCamera->FileAccessLength))
{
cout << "Unable to configure FileAccessOffset or FileAccessLength. Aborting..." << endl;
return false;
}
pCamera->FileAccessOffset.SetValue(offset);
pCamera->FileAccessLength.SetValue(length);
pCamera->FileOperationExecute.Execute();
if (pCamera->FileOperationStatus.GetValue() != FileOperationStatus_Success)
{
cout << "Failed to read file!" << endl;
return false;
}
}
catch (Spinnaker::Exception& e)
{
cout << "Unexpected exception : " << e.what() << endl;
result = false;
}
return result;
}
// Download the image to the disk from camera file
bool DownloadImage()
{
SystemPtr system;
CameraList camList;
CameraPtr pCam;
bool result = true;
// Initialize System
if (!InitializeSystem(system, camList, pCam))
{
PrintResultMessage(false);
return false;
}
// Print out current library version
const LibraryVersion spinnakerLibraryVersion = system->GetLibraryVersion();
cout << "Spinnaker library version: " << spinnakerLibraryVersion.major << "." << spinnakerLibraryVersion.minor
<< "." << spinnakerLibraryVersion.type << "." << spinnakerLibraryVersion.build << endl
<< endl;
try
{
// Retrieve TL device nodemap and print device information
INodeMap& nodeMapTLDevice = pCam->GetTLDeviceNodeMap();
PrintDeviceInfo(nodeMapTLDevice);
// Initialize camera
pCam->Init();
cout << endl << "*** DOWNLOADING IMAGE ***" << endl;
// Check file selector support
PrintDebugMessage("Fetching \"UserFile1\" Entry from FileSelectorNode...");
if (pCam->FileSelector == NULL)
{
cout << "File selector not supported on device!";
return false;
}
NodeList_t selectorList;
pCam->FileSelector.GetEntries(selectorList);
for (unsigned int i = 0; i < selectorList.size(); i++)
{
// Get current enum entry node
CEnumEntryPtr node = selectorList.at(i);
PrintDebugMessage("Setting value to FileSelectorNode...");
// Check file selector entry support
if (!node || !IsReadable(node))
{
// Go to next entry node
cout << node->GetSymbolic() << " not supported!" << endl;
continue;
}
if (node->GetSymbolic().compare(_fileSelector) == 0)
{
// Set file selector
pCam->FileSelector.SetIntValue((int64_t)node->GetNumericValue());
int64_t bytesToRead = pCam->FileSize.GetValue();
if (bytesToRead == 0)
{
cout << "No data available to read!" << endl;
continue;
}
PrintDebugMessage("Total data to download : " + to_string(static_cast<long long>(bytesToRead)));
// Open file on camera for reading
if (OpenFileToRead(pCam) != true)
{
cout << "Failed to open file!" << endl;
continue;
}
// Attempt to set FileAccessLength to FileAccessBufferNode length to speed up the write
if (pCam->FileAccessLength.GetValue() < pCam->FileAccessBuffer.GetLength())
{
try
{
pCam->FileAccessLength.SetValue(pCam->FileAccessBuffer.GetLength());
}
catch (Spinnaker::Exception& e)
{
cout << "Unable to set FileAccessLength to FileAccessBuffer length : " << e.what() << endl;
}
}
// Set File Access Offset to zero if its not
pCam->FileAccessOffset.SetValue(0);
// Compute number of read operations required
int64_t intermediateBufferSize = pCam->FileAccessLength.GetValue();
int64_t iterations =
(bytesToRead / intermediateBufferSize) + (bytesToRead % intermediateBufferSize == 0 ? 0 : 1);
PrintDebugMessage("Fetching image from camera.");
int64_t index = 0;
int64_t totalSizeRead = 0;
std::unique_ptr<unsigned char> dataBuffer(new unsigned char[static_cast<unsigned int>(bytesToRead)]);
uint8_t* pData = dataBuffer.get();
for (unsigned int i = 0; i < iterations; i++)
{
const int64_t requestedLength = (intermediateBufferSize <= (bytesToRead - totalSizeRead))
? intermediateBufferSize
: (bytesToRead - totalSizeRead);
if (!ExecuteReadCommand(pCam, totalSizeRead, requestedLength))
{
cout << "Reading stream failed!" << endl;
break;
}
// Verify size of bytes read
int64_t sizeRead = pCam->FileOperationResult.GetValue();
// Fallback: If hardware returns 0, but status was success and we expect data,
// assume the requested block was read. This handles cameras that don't reliably update ResultNode.
if (sizeRead == 0 && totalSizeRead < bytesToRead)
{
sizeRead = requestedLength;
PrintDebugMessage(
"Read returned 0 bytes at offset " + to_string(totalSizeRead) +
". Falling back to requested length: " + to_string(sizeRead));
}
// Clamp: Ensure sizeRead does not cause totalSizeRead to exceed total bytesToRead
if (totalSizeRead + sizeRead > bytesToRead)
{
sizeRead = bytesToRead - totalSizeRead;
}
// Read from buffer Node
pCam->FileAccessBuffer.Get(&pData[index], sizeRead);
// Update index for next read iteration
index = index + sizeRead;
// Keep track of total bytes read
totalSizeRead += sizeRead;
PrintDebugMessage(
"Bytes read: " + to_string(static_cast<long long>(totalSizeRead)) + " of " +
to_string(static_cast<long long>(bytesToRead)));
cout << "Progress : " << (i * 100 / iterations) << " %" << endl;
}
PrintDebugMessage("Reading complete");
// Validate download completion
if (totalSizeRead < bytesToRead)
{
cout << "Incomplete download. Read " << totalSizeRead << " bytes from device, but expected "
<< bytesToRead << "." << endl;
result = false;
}
if (!CloseFile(pCam))
{
cout << "Failed to close file!" << endl;
}
cout << endl;
// Form file path
ostringstream filename;
filename << "DeviceStreamRead-";
if (pCam->DeviceSerialNumber != NULL)
{
const string sanitizedSerial =
SanitizeFileNameComponent(pCam->DeviceSerialNumber.GetValue().c_str());
filename << sanitizedSerial << "-";
}
filename << ".bmp";
// Image should be captured with Mono8 or Bayer8, it sets camera to correct pixel format
// in order to grab image ROI
CEnumEntryPtr ptrPixelFormat = pCam->PixelFormat.GetEntry(PixelFormat_Mono8);
if (IsReadable(ptrPixelFormat) || IsWritable(ptrPixelFormat))
{
pCam->PixelFormat.SetValue(PixelFormat_Mono8);
}
else
{
// Use Bayer8 if Mono8 is not available
pCam->PixelFormat.SetValue(PixelFormat_BayerGB8);
}
const int64_t width = pCam->Width.GetValue();
const int64_t height = pCam->Height.GetValue();
const int64_t offSetX = pCam->OffsetX.GetValue();
const int64_t offSetY = pCam->OffsetX.GetValue();
PixelFormatEnums pixelFormat = pCam->PixelFormat.GetValue();
// Form image and save image
cout << "Width : " << width << endl;
cout << "Height : " << height << endl;
cout << "OffSetX : " << offSetX << endl;
cout << "OffSetY : " << offSetY << endl;
ImagePtr pImage = Image::Create(
static_cast<size_t>(width),
static_cast<size_t>(height),
static_cast<size_t>(offSetX),
static_cast<size_t>(offSetY),
pixelFormat,
pData);
if (_enableDebug)
{
try
{
cout << "\nSaving raw image to disk for debugging purpose..." << endl;
// Form file path
ostringstream filename;
filename << "downloaded_output.bmp";
pImage->Save(filename.str().c_str());
cout << "Image saved at " << filename.str() << endl;
}
catch (Spinnaker::Exception& e)
{
cout << "Unable to write image data to the PC : " << e.what() << endl;
}
}
pImage->Save(filename.str().c_str());
cout << "Image saved at " << filename.str() << endl;
cout << "\n*** SAVING IMAGE ***\n";
}
}
//
// Release reference to the camera
//
// *** NOTES ***
// Had the CameraPtr object been created within the for-loop, it would not
// be necessary to manually break the reference because the shared pointer
// would have automatically cleaned itself up upon exiting the loop.
//
pCam->DeInit();
pCam = nullptr;
// Clear camera list before releasing system
camList.Clear();
// Release system
system->ReleaseInstance();
cout << endl << "Done! Press Enter to exit..." << endl;
getchar();
}
catch (Spinnaker::Exception& e)
{
cout << "Unexpected exception : " << e.what() << endl;
result = false;
}
return result;
}
// Print out usage of the application
void PrintUsage()
{
cout << "Usage: FileAcess </u | /d>" << endl;
cout << "Options:" << endl;
cout << "/u : Grab an image and store it on camera." << endl;
cout << "/d : Download saved image from camera and store it on desktop." << endl;
cout << "/v : Enable verbose output." << endl;
cout << "/? : Print usage informaion." << endl;
cout << endl << endl;
}
// Example entry point; please see Enumeration example for more in-depth
// comments on preparing and cleaning up the system.
int main(int argc, char* argv[])
{
// 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.
FILE* tempFile = fopen("test.txt", "w+");
if (tempFile == nullptr)
{
cout << "Failed to create file in current folder. Please check "
"permissions."
<< endl;
cout << "Press Enter to exit..." << endl;
getchar();
return -1;
}
fclose(tempFile);
remove("test.txt");
int result = 0;
// Print application build information
cout << "Application build date: " << __DATE__ << " " << __TIME__ << endl << endl;
// Change arguments to string for easy read
std::vector<std::string> args(argv, argv + argc);
if (args.size() == 1)
{
// Print usage when no arguments specified
PrintUsage();
return result;
}
bool uploadImage = false;
bool downloadImage = false;
// Check verbose output flag first
for (size_t i = 1; i < args.size(); ++i)
{
if (args[i] == "/v" || args[i] == "/V")
{
_enableDebug = true;
}
if (args[i] == "?")
{
PrintUsage();
return result;
}
if (args[i] == "/u" || args[i] == "/U")
{
uploadImage = true;
}
if (args[i] == "/d" || args[i] == "/D")
{
downloadImage = true;
}
}
if (uploadImage)
{
if (!UploadImage())
{
PrintResultMessage(false);
result = -1;
}
return result;
}
if (downloadImage)
{
if (!DownloadImage())
{
PrintResultMessage(false);
result = -1;
}
return result;
}
return result;
}