FLI libflipro API
SimpleAPIMerging.cpp

Simple API Unpacking and Merging Example Code.
This example shows you how to create a simple image loop and use the API to unpack and merge the image planes.

#include "stdint.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include <string>
#include "libflipro.h"
#define FLI_TEST_MAX_SUPPORTED_CAMERAS (4)
// Static Function declarations
static int32_t SetFrameInfo(int32_t iDeviceHandle, uint32_t uiWidth, uint32_t uiHeight);
static std::string MakeProcessedFileName(std::string& rstrFileName, uint32_t uiFrameNumber, const char* pSuffix, const char* pExt);
static void SaveProcessedFile(std::string strFileName, uint8_t* pMetaData, uint32_t uiMetaSize, uint8_t* pImageData, uint64_t uiImageByteSize);
static void ShowPlaneStats(FPROPLANESTATS *pStats);
static void ShowStats(FPROUNPACKEDSTATS *pStats);
static int32_t CheckReferenceMetaData(FILE* pFile);
static int32_t SendHWMergeReferenceFiles(int32_t iHandle, char* pDSNUFileName, char* pPRNUFileName, uint32_t uiWidth, uint32_t uiHeight);
// Static Data declarations
static int32_t s_iDeviceHandle;
uint32_t uiNumDetectedDevices;
static FPRODEVICEINFO s_camDeviceInfo[FLI_TEST_MAX_SUPPORTED_CAMERAS];
//static const char* DSNU_FILE_NAME = "REF_DSNU_Data_4040.rcd";
//static const char* PRNU_FILE_NAME = "REF_PRNU_Data_4040.rcd";
int main()
{
int32_t iResult;
int32_t iGrabResult;
uint32_t i;
uint8_t *pFrame;
uint32_t uiFramSizeInBytes;
FPROUNPACKEDIMAGES fproUnpacked;
FPROUNPACKEDSTATS fproStats;
FPRO_HWMERGEENABLE mergeEnables;
std::string strFileName;
uint32_t uiCapLength;
FPROCAP devCaps;
pFrame = NULL;
// first get the list of available devices
uiNumDetectedDevices = FLI_TEST_MAX_SUPPORTED_CAMERAS;
iResult = FPROCam_GetCameraList(s_camDeviceInfo, &uiNumDetectedDevices);
if ((iResult >= 0) && (uiNumDetectedDevices > 0))
{
// Open the first device in the list
s_iDeviceHandle = -1;
iResult = FPROCam_Open(&s_camDeviceInfo[0], &s_iDeviceHandle);
if ((iResult >= 0) && (s_iDeviceHandle >= 0))
{
// This is not strictly required but we use the information in the
// capabilities structure to set the appropriate image dimensions
// for the camera model.
uiCapLength= sizeof(FPROCAP);
iResult= FPROSensor_GetCapabilities(s_iDeviceHandle, &devCaps,&uiCapLength);
// Set up the API merging structures. This main() program uses the
// Reference Frame algorithm to perform the merge. It requires the generation of
// Reference Frames (with Pilot or your own implementation) in order to produce
// good merged images. For merging without reference frames, see the DefaultAPIMerging()
// function below (or simply change the line below in this code block that assigns
// fproUnapacked.eMergeAlgo and set it to FPROMERGE_ALGO as the commented out line shows).
// For additional information see the documentation for FPROUNPACKEDIMAGES and FPROUNPACKEDSTATS.
memset(&fproUnpacked, 0, sizeof(fproUnpacked));
memset(&fproStats, 0, sizeof(fproStats));
fproUnpacked.bLowImageRequest = true;
fproUnpacked.bHighImageRequest = true;
fproUnpacked.bMergedImageRequest = true;
fproUnpacked.bMetaDataRequest = true;
fproStats.bLowRequest = true;
fproStats.bHighRequest = true;
fproStats.bMergedRequest = true;
//fproUnpacked.eMergAlgo = FPROMERGE_ALGO;
// When using the FPROMERGE_ALGO_REF_FRAME merge algorithm, it is important to set
// that algorithm up as well. You will need to send the appropriate reference frames
// to the API in order to get the best merge results. If no files are given to the API,
// the merge algorithm computes a set of reference frames based on the sensor gain values in
// the image Meta Data. This is a global calculation and not as refined as the per pixel
// reference files that should be created and used. This example function here expects
// reference files created by the FLI Pilot application. If you
// generate your own files, you will need to develop your own implementation.
// If you have created reference files with Pilot, you can use the FPROAlgo_SetHardwareMergeReferenceFiles()
// function to setup the API. If not, you will have to refer to the example function below.
// See the documentation for FPROAlgo_SetHardwareMergeReferenceFrames()
// for a description of this function and parameters.
//SendHWMergeReferenceFiles(s_iDeviceHandle, (char*)DSNU_FILE_NAME, (char*)PRNU_FILE_NAME, 4096, 4096);
// You can also adjust the FPROMERGE_ALGO_REF_FRAME algorithm with the following function.
// This function is optional as default thresholds are already set up in the API.
// See the documentation for a description of this function and parameters.
// FPROAlgo_SetHardwareMergeThresholds(int32_t iHandle, uint16_t uiHighGainThreshold, uint16_t uiMergeDifferenceThreshold);
// You also need to enable the merge when using the FPROMERGE_ALGO_REF_FRAME algorithm
// In this case, we are also generating a TIFF formatted merged file.
// In general, when using FPROMERGE_ALGO, you do not need to call FPROAlgo_SetHardwareMergeEnables(). But if
// you want to generate tiff images with the FPRMERGE_ALGO setting, this is how you would do it.
mergeEnables.bMergeEnable = true;
mergeEnables.eMergeFormat = IFORMAT_RCD;
if (iResult >= 0)
iResult= FPROAlgo_SetHardwareMergeEnables(s_iDeviceHandle, mergeEnables);
// Set up your exposure and frame parameters
if (iResult >= 0)
iResult = SetFrameInfo(s_iDeviceHandle, devCaps.uiMaxPixelImageWidth, devCaps.uiMaxPixelImageHeight);
if (iResult >= 0)
{
// Make sure we have space for the raw image frame. See the SimpleImageLoop.cpp sample
// for an explanation of this call.
uiFramSizeInBytes = FPROFrame_ComputeFrameSize(s_iDeviceHandle);
pFrame = (uint8_t *)malloc(uiFramSizeInBytes);
if (pFrame)
{
if (iResult >= 0)
{
// all is well - now you can start grabbing image frames
// This loop will grab 10 frames. Note there is no more set up
// of the fproUnpacked structure within this loop. The API allocated
// the requested buffers the first time through and are reused on subsequent
// passes. If you change imaging parameters, you must free the buffers and
// re-initialize with the new settings. The buffers are free'd below after
// the loop terminates.
iGrabResult = 0;
for (i = 0; (i < 10) && (iResult >= 0) && (iGrabResult >= 0); ++i)
{
// Start the capture and tell it to get 1 frame
iResult = FPROFrame_CaptureStart(s_iDeviceHandle, 1);
if (iResult >= 0)
{
// Grab the frame- and all the requested unpacked/merged frames, and statistics.
// Merging takes a lot longer when asking for statistics.
#if 1
iGrabResult = FPROFrame_GetVideoFrameUnpacked(s_iDeviceHandle, pFrame, &uiFramSizeInBytes, 0, &fproUnpacked, &fproStats);
ShowStats(&fproStats);
#else
iGrabResult = FPROFrame_GetVideoFrameUnpacked(s_iDeviceHandle, pFrame, &uiFramSizeInBytes, 0, &fproUnpacked, NULL);
#endif
// Regardless of how the capture turned out, stop the capture
iResult = FPROFrame_CaptureAbort(s_iDeviceHandle);
// If the FPROFrame_GetVideoFrameUnpacked() succeeded- then process it
if (iGrabResult >= 0)
{
printf("Got frame %d.\n",i);
#if 1
strFileName = "API_Unpack_Sample.rcd";
if (fproUnpacked.pLowImage)
SaveProcessedFile(MakeProcessedFileName(strFileName, i, "_LOW16", ".rcd"), fproUnpacked.pMetaData, fproUnpacked.uiMetaDataSize, (uint8_t*)fproUnpacked.pLowImage, fproUnpacked.uiLowBufferSize);
if (fproUnpacked.pHighImage)
SaveProcessedFile(MakeProcessedFileName(strFileName, i, "_HIGH16", ".rcd"), fproUnpacked.pMetaData, fproUnpacked.uiMetaDataSize, (uint8_t*)fproUnpacked.pHighImage, fproUnpacked.uiHighBufferSize);
if (fproUnpacked.pMergedImage)
{
if (IFORMAT_TIFF == mergeEnables.eMergeFormat)
SaveProcessedFile(MakeProcessedFileName(strFileName, i, "_MERGED_TIFF", ".tiff"), NULL, 0, (uint8_t*)fproUnpacked.pMergedImage, fproUnpacked.uiMergedBufferSize);
else if (IFORMAT_FITS == mergeEnables.eMergeFormat)
SaveProcessedFile(MakeProcessedFileName(strFileName, i, "_MERGED_FITS", ".fits"), NULL, 0, (uint8_t*)fproUnpacked.pMergedImage, fproUnpacked.uiMergedBufferSize);
else
SaveProcessedFile(MakeProcessedFileName(strFileName, i, "_MERGED16", ".rcd"), fproUnpacked.pMetaData, fproUnpacked.uiMetaDataSize, (uint8_t*)fproUnpacked.pMergedImage, fproUnpacked.uiMergedBufferSize);
}
#endif
}
}
}
}
}
else
{
printf("ERROR getting frame memory.... leaving\n");
}
}
// Free the unpacking buffers
// Close up shop
iResult = FPROCam_Close(s_iDeviceHandle);
}
}
else
{
printf("ERROR: No cameras detected!\n\n");
}
// Give our memory back
if (pFrame)
free(pFrame);
return 0;
}
int DefaultAPIMerging()
{
int32_t iResult;
int32_t iGrabResult;
uint32_t i;
uint8_t* pFrame;
uint32_t uiFramSizeInBytes;
FPROUNPACKEDIMAGES fproUnpacked;
FPROUNPACKEDSTATS fproStats;
std::string strFileName;
uint32_t uiCapLength;
FPROCAP devCaps;
pFrame = NULL;
// first get the list of available devices
uiNumDetectedDevices = FLI_TEST_MAX_SUPPORTED_CAMERAS;
iResult = FPROCam_GetCameraList(s_camDeviceInfo, &uiNumDetectedDevices);
if ((iResult >= 0) && (uiNumDetectedDevices > 0))
{
// Open the first device in the list
s_iDeviceHandle = -1;
iResult = FPROCam_Open(&s_camDeviceInfo[0], &s_iDeviceHandle);
if ((iResult >= 0) && (s_iDeviceHandle >= 0))
{
// This is not strictly required but we use the information in the
// capabilities structure to set the appropriate image dimensions
// for the camera model.
uiCapLength= sizeof(FPROCAP);
iResult= FPROSensor_GetCapabilities(s_iDeviceHandle, &devCaps,&uiCapLength);
// Set up the API merging structures. See the documentation
// for explanations of the use of these fields
memset(&fproUnpacked, 0, sizeof(fproUnpacked));
memset(&fproStats, 0, sizeof(fproStats));
fproUnpacked.bLowImageRequest = true;
fproUnpacked.bMetaDataRequest = true;
fproUnpacked.bHighImageRequest = true;
fproUnpacked.bMergedImageRequest = true;
fproStats.bLowRequest = true;
fproStats.bHighRequest = true;
fproStats.bMergedRequest = true;
fproUnpacked.eMergAlgo = FPROMERGE_ALGO;
// Set up your exposure and frame parameters
if (iResult >= 0)
iResult = SetFrameInfo(s_iDeviceHandle, devCaps.uiMaxPixelImageWidth, devCaps.uiMaxPixelImageHeight);
if (iResult >= 0)
{
// Make sure we have space for the raw image frame. See the SimpleImageLoop.cpp sample
// for an explanation of this call.
uiFramSizeInBytes = FPROFrame_ComputeFrameSize(s_iDeviceHandle);
pFrame = (uint8_t*)malloc(uiFramSizeInBytes);
if (pFrame)
{
if (iResult >= 0)
{
// all is well - now you can start grabbing image frames
// This loop will grab 10 frames. Note there is no more set up
// of the fproUnpacked structure within this loop. The API allocated
// the requested buffers the first time through and are reused on subsequent
// passes. If you change imaging parameters, you must free the buffers and
// re-initialize with the new settings. The buffers are free'd below after
// the loop terminates.
iGrabResult = 0;
for (i = 0; (i < 10) && (iResult >= 0) && (iGrabResult >= 0); ++i)
{
// Start the capture and tell it to get 1 frame
iResult = FPROFrame_CaptureStart(s_iDeviceHandle, 1);
if (iResult >= 0)
{
// Grab the frame- and all the requested unpacked/merged frames, and statistics.
// Merging takes a lot longer when asking for statistics.
#if 0
iGrabResult = FPROFrame_GetVideoFrameUnpacked(s_iDeviceHandle, pFrame, &uiFramSizeInBytes, 0, &fproUnpacked, &fproStats);
#else
iGrabResult = FPROFrame_GetVideoFrameUnpacked(s_iDeviceHandle, pFrame, &uiFramSizeInBytes, 0, &fproUnpacked, NULL);
#endif
// Regardless of how the capture turned out, stop the capture
iResult = FPROFrame_CaptureStop(s_iDeviceHandle);
// If the FPROFrame_GetVideoFrameUnpacked() succeeded- then process it
if (iGrabResult >= 0)
{
printf("Got frame %d.\n", i);
strFileName = "API_Unpack_Sample.rcd";
if (fproUnpacked.pLowImage)
SaveProcessedFile(MakeProcessedFileName(strFileName, i, "_LOW16", ".rcd"), fproUnpacked.pMetaData, fproUnpacked.uiMetaDataSize, (uint8_t*)fproUnpacked.pLowImage, fproUnpacked.uiLowBufferSize);
if (fproUnpacked.pHighImage)
SaveProcessedFile(MakeProcessedFileName(strFileName, i, "_HIGH16", ".rcd"), fproUnpacked.pMetaData, fproUnpacked.uiMetaDataSize, (uint8_t*)fproUnpacked.pHighImage, fproUnpacked.uiHighBufferSize);
if (fproUnpacked.pMergedImage)
SaveProcessedFile(MakeProcessedFileName(strFileName, i, "_MERGED16", ".rcd"), fproUnpacked.pMetaData, fproUnpacked.uiMetaDataSize, (uint8_t*)fproUnpacked.pMergedImage, fproUnpacked.uiMergedBufferSize);
}
}
}
}
}
else
{
printf("ERROR getting frame memory.... leaving\n");
}
}
// Free the unpacking buffers
// Close up shop
iResult = FPROCam_Close(s_iDeviceHandle);
}
}
// Give our memory back
if (pFrame)
free(pFrame);
return 0;
}
int32_t
SetFrameInfo(int32_t iDeviceHandle, uint32_t uiWidth, uint32_t uiHeight)
{
int32_t iResult;
// assume success
iResult = 0;
// Enable/disable image data
// The power on default of the camera is to have image data enabled.
// This is just shown here in case you are working with test frames and
// need to set the camera back up to get you real image data.
iResult = FPROFrame_SetImageDataEnable(iDeviceHandle, true);
// Set the exposure time
// The default camera exposure time is 50msecs (for the GSENSE 400)
// The FPROCtrl_SetExposureTime() API expects the exposure time in
// nano seconds. The frameDelay parameter is also in nanoseconds
if (iResult >= 0)
iResult = FPROCtrl_SetExposure(iDeviceHandle, 50000000,10000000,false);
// Set the Image area
if (iResult >= 0)
iResult = FPROFrame_SetImageArea(iDeviceHandle, 0, 0, uiWidth, uiHeight);
// return our result
return(iResult);
}
std::string MakeProcessedFileName(std::string & rstrFileName, uint32_t uiFrameNumber, const char* pSuffix, const char* pExt)
{
std::string strNew;
std::string strExt;
char cNumBuff[32];
size_t iIndex;
// find the last '.' in the file name-
iIndex = rstrFileName.find_last_of('.', rstrFileName.length());
if (iIndex != std::string::npos)
{
strNew = rstrFileName.substr(0,iIndex);
snprintf(cNumBuff, sizeof(cNumBuff), "_%d_", uiFrameNumber);
strNew += cNumBuff;
strNew += pSuffix;
}
else
{
// No '.', just add the suffix to the end
strNew = rstrFileName + pSuffix;
}
strNew += pExt;
return(strNew);
}
void SaveProcessedFile(std::string rstrFileName, uint8_t* pMetaData, uint32_t uiMetaSize, uint8_t* pImageData, uint64_t uiImageByteSize)
{
FILE* pFile;
int32_t iResult;
#if defined(_WIN32) || defined(_WINDOWS)
fopen_s(&pFile, rstrFileName.c_str(), "w+b");
#else
pFile= fopen(rstrFileName.c_str(), "w+b");
#endif
if (pFile)
{
iResult = 0;
if (pMetaData)
{
if (fwrite(pMetaData, 1, uiMetaSize, pFile) != uiMetaSize)
{
printf("Error writing Meta Data to file %s\n", rstrFileName.c_str());
iResult = -1;
}
}
if (iResult >= 0)
{
if (fwrite(pImageData, 1, (size_t)uiImageByteSize, pFile) != uiImageByteSize)
{
printf("Error writing Image Data to file %s\n", rstrFileName.c_str());
}
}
fclose(pFile);
}
else
{
printf("Error opening output file %s\n", rstrFileName.c_str());
}
}
int32_t CheckReferenceMetaData(FILE* pFile)
{
int32_t iResult;
size_t uiBytesRead;
uint32_t uiMetaSize;
uint32_t uiFileSize;
uint8_t uiMetaCheck[6];
iResult = -1;
fseek(pFile, 0L, SEEK_END);
uiFileSize = ftell(pFile);
fseek(pFile, 0L, SEEK_SET);
#if defined(_WIN32) || defined(_WINDOWS)
uiBytesRead = fread_s(uiMetaCheck, 6, 1, 6, pFile);
#else
uiBytesRead= fread(uiMetaCheck, 1, 6, pFile);
#endif
if (uiBytesRead == 6)
{
if ((uiMetaCheck[0] == 'M') && (uiMetaCheck[1] == 'e') && (uiMetaCheck[2] == 't') && (uiMetaCheck[3] == 'a'))
{
uiMetaSize = ((uint32_t)uiMetaCheck[4] & 0xFF) << 8;
uiMetaSize |= (uint32_t)uiMetaCheck[5] & 0xFF;
if (uiMetaSize < uiFileSize)
{
// looks like a valid file with meta data so seek past the meta data
iResult= 0;
fseek(pFile, uiMetaSize, SEEK_SET);
}
}
}
return(iResult);
}
void ShowPlaneStats(FPROPLANESTATS *pStats)
{
if (pStats)
{
printf("Mean: %f\n"
"Median: %f\n"
"Mode: %f\n"
"Std Dev: %f\n"
"Brightest: %d, (%d,%d)\n"
"Dimmest: %d, (%d,%d)\n",
pStats->dblMean,
pStats->dblMedian,
pStats->dblMode,
}
else
{
printf("Unallocated\n");
}
}
void ShowStats(FPROUNPACKEDSTATS *pStats)
{
if (NULL == pStats)
return;
printf("Low Stats: ");
if (!(pStats->bLowRequest))
printf("Not Requested\n");
else
{
printf("\n");
ShowPlaneStats(&pStats->statsLowImage);
}
printf("\nHigh Stats: ");
if (!(pStats->bHighRequest))
printf("Not Requested\n");
else
{
printf("\n");
ShowPlaneStats(&pStats->statsHighImage);
}
printf("\nMerged Stats: ");
if (!(pStats->bMergedRequest))
printf("Not Requested\n");
else
{
printf("\n");
ShowPlaneStats(&pStats->statsMergedImage);
}
printf("\n");
}
// This is an example function to send HW Merge reference frames from RCD files created by the
// FLI Pilot application. It is provided as an example to help you
// extract the data from the RCD files for the FPRO_REFFRAMES structure required by the API.
int32_t SendHWMergeReferenceFiles(int32_t iHandle, char* pDSNUFileName, char* pPRNUFileName, uint32_t uiWidth, uint32_t uiHeight)
{
int32_t iResult;
FILE* pDSNUFile;
FILE* pPRNUFile;
FPRO_REFFRAMES refFrames;
uint32_t uiFrameSize;
size_t uiBytesRead;
iResult = -1;
// Open the files
pDSNUFile = NULL;
pPRNUFile = NULL;
#if defined(_WIN32) || defined(_WINDOWS)
fopen_s(&pDSNUFile, pDSNUFileName, "r+b");
fopen_s(&pPRNUFile, pPRNUFileName, "r+b");
#else
pDSNUFile= fopen(pDSNUFileName, "r+b");
pPRNUFile= fopen(pPRNUFileName, "r+b");
#endif
// Check the meta data and seek past it
iResult = CheckReferenceMetaData(pDSNUFile);
if (iResult >= 0)
iResult = CheckReferenceMetaData(pPRNUFile);
if (iResult >= 0)
{
// Set up the width and height- should be the full image size e.g. 4096 x 4096)
iResult = 0;
refFrames.uiWidth = uiWidth;
refFrames.uiHeight = uiHeight;
// Get space for the files
refFrames.pAdditiveLowGain = new int16_t[uiWidth * uiHeight];
refFrames.pAdditiveHighGain = new int16_t[uiWidth * uiHeight];
refFrames.pMultiplicativeLowGain = new uint16_t[uiWidth * uiHeight];
refFrames.pMultiplicativeHighGain = new uint16_t[uiWidth * uiHeight];
if ((refFrames.pAdditiveLowGain) && (refFrames.pAdditiveHighGain) &&
(refFrames.pMultiplicativeLowGain) && (refFrames.pMultiplicativeHighGain))
{
// Read the DSNU (additive) data
uiFrameSize = uiWidth * uiHeight * sizeof(uint16_t);
#if defined(_WIN32) || defined(_WINDOWS)
uiBytesRead = fread_s(refFrames.pAdditiveLowGain, uiFrameSize, 1, uiFrameSize, pDSNUFile);
#else
uiBytesRead= fread(refFrames.pAdditiveLowGain, 1, uiFrameSize, pDSNUFile);
#endif
if (uiBytesRead != uiFrameSize)
iResult = -1;
if (iResult >= 0)
{
// The high gain data starts immediately after the low gain data
#if defined(_WIN32) || defined(_WINDOWS)
uiBytesRead = fread_s(refFrames.pAdditiveHighGain, uiFrameSize, 1, uiFrameSize, pDSNUFile);
#else
uiBytesRead= fread(refFrames.pAdditiveHighGain, 1, uiFrameSize, pDSNUFile);
#endif
if (uiBytesRead != uiFrameSize)
iResult = -1;
}
// Read the PRNU (multiplicative) data
if (iResult >= 0)
{
#if defined(_WIN32) || defined(_WINDOWS)
uiBytesRead = fread_s(refFrames.pMultiplicativeLowGain, uiFrameSize, 1, uiFrameSize, pPRNUFile);
#else
uiBytesRead= fread(refFrames.pMultiplicativeLowGain, 1, uiFrameSize, pPRNUFile);
#endif
if (uiBytesRead != uiFrameSize)
iResult = -1;
}
if (iResult >= 0)
{
// The high gain data starts immediately after the low gain data
#if defined(_WIN32) || defined(_WINDOWS)
uiBytesRead = fread_s(refFrames.pMultiplicativeHighGain, uiFrameSize, 1, uiFrameSize, pPRNUFile);
#else
uiBytesRead= fread(refFrames.pMultiplicativeHighGain, 1, uiFrameSize, pPRNUFile);
#endif
if (uiBytesRead != uiFrameSize)
iResult = -1;
}
if (iResult >= 0)
{
// Finally we can call the API.....
iResult = FPROAlgo_SetHardwareMergeReferenceFrames(iHandle, &refFrames);
}
}
else
{
iResult = -1;
}
// give the memory back
if (refFrames.pAdditiveLowGain)
delete[]refFrames.pAdditiveLowGain;
if (refFrames.pAdditiveHighGain)
delete[]refFrames.pAdditiveHighGain;
if (refFrames.pMultiplicativeLowGain)
delete[]refFrames.pMultiplicativeLowGain;
if (refFrames.pMultiplicativeHighGain)
delete[]refFrames.pMultiplicativeHighGain;
if (pDSNUFile)
fclose(pDSNUFile);
if (pPRNUFile)
fclose(pPRNUFile);
}
return(iResult);
}