Radiation-resistantCamera/Vimba_6_0/VimbaCPP/Examples/VimbaViewer/Source/ImageWriter.cpp

526 lines
19 KiB
C++
Raw Permalink Normal View History

2025-04-30 01:26:04 +00:00
#include "ImageWriter.h"
#include <QLibrary>
#include <QString>
#include <QSharedPointer>
#include <QDebug>
#include <stdint.h>
#include "VmbTransform.h"
#include <QMutex>
#include <QMutexLocker>
typedef struct tiff TIFF;
// constants for configuring Tiff tag data
#define IMGWRITER_IMAGEWIDTH 256 /* image width in pixels (TIFFTAG_IMAGEWIDTH) */
#define IMGWRITER_IMAGELENGTH 257 /* image height in pixels (TIFFTAG_IMAGELENGTH) */
#define IMGWRITER_BITSPERSAMPLE 258 /* bits per channel (sample) (TIFFTAG_BITSPERSAMPLE) */
#define IMGWRITER_COMPRESSION 259 /* data compression technique (TIFFTAG_COMPRESSION) */
#define IMGWRITER_PHOTOMETRIC 262 /* photometric interpretation (TIFFTAG_PHOTOMETRIC) */
#define IMGWRITER_XRESOLUTION 282 /* pixels/resolution in x (TIFFTAG_XRESOLUTION) */
#define IMGWRITER_YRESOLUTION 283 /* pixels/resolution in y (TIFFTAG_YRESOLUTION) */
#define IMGWRITER_RESOLUTIONUNIT 296 /* units of resolutions (TIFFTAG_RESOLUTIONUNIT) */
#define IMGWRITER_PLANARCONFIG 284 /* storage organization (TIFFTAG_PLANARCONFIG) */
#define IMGWRITER_SAMPLESPERPIXEL 277 /* samples per pixel (TIFFTAG_SAMPLESPERPIXEL) */
class TiffLibrary
{
bool m_DidLoad;
QLibrary m_LibTiffLibrary;
static QMutex m_LibraryLock;
// function pointer typedefs
typedef TIFF* (*TIFFOpenPrototype)( const char*, const char* );
typedef void (*TIFFClosePrototype)( TIFF* );
typedef int (*TIFFSetFieldProtoype)( TIFF*, uint32_t, ... );
typedef int (*TIFFWriteScanlinePrototype)( TIFF*, void*, uint32_t, uint16_t );
TiffLibrary()
{
m_LibTiffLibrary.setFileNameAndVersion("libtiff", 5);
m_DidLoad = m_LibTiffLibrary.load();
if (!m_DidLoad )
{
m_LibTiffLibrary.setFileNameAndVersion("libtiff", 4);
m_DidLoad = m_LibTiffLibrary.load();
}
if( m_DidLoad )
{
TiffOpen = (TIFFOpenPrototype) m_LibTiffLibrary.resolve("TIFFOpen");
if( TiffOpen == NULL)
{
m_DidLoad = false;
return;
}
TiffClose = (TIFFClosePrototype) m_LibTiffLibrary.resolve("TIFFClose");
if( TiffClose == NULL)
{
m_DidLoad = false;
return;
}
TiffSetField = (TIFFSetFieldProtoype) m_LibTiffLibrary.resolve("TIFFSetField");
if( TiffSetField == NULL)
{
m_DidLoad = false;
return;
}
TiffWriteScanLine = (TIFFWriteScanlinePrototype) m_LibTiffLibrary.resolve("TIFFWriteScanline");
if( TiffWriteScanLine == NULL)
{
m_DidLoad = false;
return;
}
}
}
public:
static const TiffLibrary& Get()
{
QMutexLocker local_lock( &m_LibraryLock );
static const TiffLibrary staticLibrary;
return staticLibrary;
}
bool Valid() const { return m_DidLoad; }
QString GetLastError( void ) const
{
return m_LibTiffLibrary.errorString();
}
TIFFOpenPrototype TiffOpen;
TIFFClosePrototype TiffClose;
TIFFSetFieldProtoype TiffSetField;
TIFFWriteScanlinePrototype TiffWriteScanLine;
};
// static init
QMutex TiffLibrary::m_LibraryLock;
VmbError_t GetBitsPerPixelUsed( VmbUint32_t * BitsPerPixelUsed, VmbPixelFormat_t PixelFormat)
{
if( BitsPerPixelUsed == 0 ) return VmbErrorBadParameter;
switch(PixelFormat)
{
case VmbPixelFormatBayerGR8:
case VmbPixelFormatBayerRG8:
case VmbPixelFormatBayerGB8:
case VmbPixelFormatBayerBG8:
case VmbPixelFormatMono8:
*BitsPerPixelUsed = 8;
return VmbErrorSuccess;
case VmbPixelFormatBayerGR10:
case VmbPixelFormatBayerRG10:
case VmbPixelFormatBayerGB10:
case VmbPixelFormatBayerBG10:
case VmbPixelFormatBayerGR10p:
case VmbPixelFormatBayerRG10p:
case VmbPixelFormatBayerGB10p:
case VmbPixelFormatBayerBG10p:
case VmbPixelFormatMono10:
case VmbPixelFormatMono10p:
*BitsPerPixelUsed = 10;
return VmbErrorSuccess;
case VmbPixelFormatBayerGR12:
case VmbPixelFormatBayerRG12:
case VmbPixelFormatBayerGB12:
case VmbPixelFormatBayerBG12:
case VmbPixelFormatMono12:
case VmbPixelFormatMono12Packed:
case VmbPixelFormatMono12p:
case VmbPixelFormatBayerGR12Packed:
case VmbPixelFormatBayerRG12Packed:
case VmbPixelFormatBayerGB12Packed:
case VmbPixelFormatBayerBG12Packed:
case VmbPixelFormatBayerGR12p:
case VmbPixelFormatBayerRG12p:
case VmbPixelFormatBayerGB12p:
case VmbPixelFormatBayerBG12p:
*BitsPerPixelUsed = 12;
return VmbErrorSuccess;
case VmbPixelFormatMono14:
*BitsPerPixelUsed = 14;
return VmbErrorSuccess;
case VmbPixelFormatBayerGR16:
case VmbPixelFormatBayerRG16:
case VmbPixelFormatBayerGB16:
case VmbPixelFormatBayerBG16:
case VmbPixelFormatMono16:
*BitsPerPixelUsed = 16;
return VmbErrorSuccess;
case VmbPixelFormatRgb8:
case VmbPixelFormatBgr8:
*BitsPerPixelUsed = 24;
return VmbErrorSuccess;
case VmbPixelFormatArgb8:
case VmbPixelFormatBgra8:
*BitsPerPixelUsed = 32;
return VmbErrorSuccess;
case VmbPixelFormatRgb10:
case VmbPixelFormatBgr10:
*BitsPerPixelUsed = 30;
return VmbErrorSuccess;
case VmbPixelFormatRgb12:
case VmbPixelFormatBgr12:
*BitsPerPixelUsed = 36;
return VmbErrorSuccess;
case VmbPixelFormatRgb14:
case VmbPixelFormatBgr14:
*BitsPerPixelUsed = 42;
return VmbErrorSuccess;
case VmbPixelFormatRgb16:
case VmbPixelFormatBgr16:
*BitsPerPixelUsed = 48;
return VmbErrorSuccess;
case VmbPixelFormatRgba10:
case VmbPixelFormatBgra10:
*BitsPerPixelUsed = 40;
return VmbErrorSuccess;
case VmbPixelFormatRgba12:
case VmbPixelFormatBgra12:
*BitsPerPixelUsed = 48;
return VmbErrorSuccess;
case VmbPixelFormatRgba14:
case VmbPixelFormatBgra14:
*BitsPerPixelUsed = 56;
return VmbErrorSuccess;
case VmbPixelFormatRgba16:
case VmbPixelFormatBgra16:
*BitsPerPixelUsed = 64;
return VmbErrorSuccess;
case VmbPixelFormatYuv411:
case VmbPixelFormatYCbCr411_8_CbYYCrYY:
*BitsPerPixelUsed = 12;
return VmbErrorSuccess;
case VmbPixelFormatYuv422:
case VmbPixelFormatYCbCr422_8_CbYCrY:
*BitsPerPixelUsed = 16;
return VmbErrorSuccess;
case VmbPixelFormatYuv444:
case VmbPixelFormatYCbCr8_CbYCr:
*BitsPerPixelUsed = 24;
return VmbErrorSuccess;
default:
return VmbErrorBadParameter;
}
}
// helper functions to test if pixel format is of type Mono
bool IsMonochrome( VmbPixelFormat_t format )
{
bool isMono;
switch(format)
{
case VmbPixelFormatMono8:
case VmbPixelFormatMono10:
case VmbPixelFormatMono10p:
case VmbPixelFormatMono12:
case VmbPixelFormatMono12Packed:
case VmbPixelFormatMono12p:
case VmbPixelFormatMono14:
case VmbPixelFormatMono16:
isMono = true;
break;
default:
isMono = false;
}
return isMono;
}
// helper functions to test if pixel format is packed
bool IsPackedFormat( VmbPixelFormat_t format )
{
bool isPacked;
switch(format)
{
case VmbPixelFormatMono10p:
case VmbPixelFormatMono12Packed:
case VmbPixelFormatMono12p:
case VmbPixelFormatBayerGR12Packed:
case VmbPixelFormatBayerRG12Packed:
case VmbPixelFormatBayerGB12Packed:
case VmbPixelFormatBayerBG12Packed:
case VmbPixelFormatBayerGR10p:
case VmbPixelFormatBayerRG10p:
case VmbPixelFormatBayerGB10p:
case VmbPixelFormatBayerBG10p:
case VmbPixelFormatBayerGR12p:
case VmbPixelFormatBayerRG12p:
case VmbPixelFormatBayerGB12p:
case VmbPixelFormatBayerBG12p:
isPacked = true;
break;
default:
isPacked = false;
}
return isPacked;
}
// Helper function to fill in the TIFF file parameters
bool WriteStandardTags( TIFF* TiffFile, const tFrameInfo &frame_info)
{
int result = 0;
VmbUint32_t BitsPerPixelUsed;
// calculate bit depth
GetBitsPerPixelUsed(&BitsPerPixelUsed, frame_info.PixelFormat() );
VmbUint16_t BitsPerSample = static_cast<VmbUint16_t>( ( BitsPerPixelUsed > 8 ) ? 16 : 8 );
// check if function symbol could be resolved (using QLibrary)
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_IMAGEWIDTH, static_cast<unsigned long>(frame_info.Width() ));
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_IMAGELENGTH, static_cast<unsigned long>(frame_info.Height() ));
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_COMPRESSION, static_cast<unsigned short>(1) ); // No compression
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_XRESOLUTION, static_cast<float>(1.0F));
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_YRESOLUTION, static_cast<float>(1.0F));
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_RESOLUTIONUNIT, static_cast<unsigned short>(1)); // No units
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_PLANARCONFIG, static_cast<unsigned short>(1));
if (result == 0)
return false;
// raw image data, bayer or mono
if ( IsMonochrome( frame_info.PixelFormat() ) || ! frame_info.UseColorInterpolation() )
{
// Monochrome
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_BITSPERSAMPLE, BitsPerSample );
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_PHOTOMETRIC, static_cast<unsigned short>(1)); // Monochrome, black=0
if (result == 0)
return false;
}
else // bayer format && color interpolation on
{
// Color
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_BITSPERSAMPLE, BitsPerSample, BitsPerSample, BitsPerSample );
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_SAMPLESPERPIXEL, static_cast<unsigned short>(3));
if (result == 0)
return false;
result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_PHOTOMETRIC, static_cast<unsigned short>(2)); // RGB color
if (result == 0)
return false;
}
return true;
}
// Helper function to write the data buffer to the TIFF file
bool WriteFrameBuffer( TIFF* TiffFile, const tFrameInfo &frame_info )
{
const VmbUint32_t ImageSizePixels = frame_info.Width() * frame_info.Height();
VmbUint32_t BitsPerPixelUsed;
GetBitsPerPixelUsed(&BitsPerPixelUsed, frame_info.PixelFormat() );
VmbUint32_t BytesPerPixel = ( BitsPerPixelUsed > 8 ) ? 2 : 1;
// allocate the temporary buffer used to write to the file
try
{
QSharedPointer<unsigned char> tmpBuffer;
// copy or manipulate data into temporary buffer
if ( IsMonochrome(frame_info.PixelFormat() )
|| !frame_info.UseColorInterpolation()
) // raw mono or bayer (no color interpolation)
{
if( 8 == BitsPerPixelUsed ) // mono or raw 8 bit transfer
{
tmpBuffer = frame_info.DataPtr();
}
else // 16 bit tranfer formats
{
if( IsPackedFormat(frame_info.PixelFormat() ) ) // unpack mono_p raw_p
{
tmpBuffer = QSharedPointer<unsigned char> (new unsigned char [ImageSizePixels * 2], DeleteArray<unsigned char> ); // space for mono 16
if( tmpBuffer == NULL)
{
return false;
}
VmbImage sourceImage;
VmbImage destinationImage;
// set size member for verification inside API
sourceImage.Size = sizeof ( sourceImage );
destinationImage.Size = sizeof ( destinationImage );
// attach the data buffers
sourceImage.Data = (void*) frame_info.Data(); // hard cast from const
destinationImage.Data = tmpBuffer.data();
// fill image info from pixel format string
VmbSetImageInfoFromPixelFormat ( frame_info.PixelFormat(), frame_info.Width(), frame_info.Height(), &sourceImage );
// fill image info from pixel format
VmbSetImageInfoFromInputImage(&sourceImage, VmbPixelLayoutMono, 16, &destinationImage);
// perform the transformation
if ( VmbErrorSuccess != VmbImageTransform ( &sourceImage , &destinationImage , 0 , 0 ) )
{
return false;
}
}
else
{
tmpBuffer = frame_info.DataPtr();
}
// change to msb
unsigned short* pData = (unsigned short*)tmpBuffer.data();
const unsigned short* pDataEnd = pData + ImageSizePixels;
const unsigned short bitshift = 16 - BitsPerPixelUsed;
while (pData < pDataEnd)
{
*(pData) <<= bitshift;
++pData;
}
}
}
else // bayer && color interpolation on
{
if( BytesPerPixel == 1 ) // bayer8
{
tmpBuffer = QSharedPointer<unsigned char>( new unsigned char[ ImageSizePixels * 3], DeleteArray<unsigned char> ); // storage for RGB pixel
VmbImage sourceImage, destinationImage;
sourceImage.Size = sizeof( destinationImage );
sourceImage.Data = (void*)frame_info.Data();
VmbSetImageInfoFromPixelFormat( frame_info.PixelFormat(), frame_info.Width(), frame_info.Height(), &sourceImage );
destinationImage.Size = sizeof( destinationImage );
destinationImage.Data = tmpBuffer.data();
VmbSetImageInfoFromInputImage(&sourceImage, VmbPixelLayoutRGB, 8, &destinationImage);
if( VmbErrorSuccess != VmbImageTransform( &sourceImage, &destinationImage, NULL, 0) )
{
return false;
}
}
else
{
tmpBuffer = QSharedPointer<unsigned char>( new unsigned char[ ImageSizePixels * 3 * BytesPerPixel ], DeleteArray<unsigned char> ); // storage for RGB 16 bit
VmbImage sourceImage;
sourceImage.Size = sizeof( sourceImage );
sourceImage.Data = (void*)frame_info.Data();
VmbSetImageInfoFromPixelFormat( frame_info.PixelFormat(), frame_info.Width(), frame_info.Height(), &sourceImage );
VmbImage destinationImage;
destinationImage.Size = sizeof( destinationImage );
destinationImage.Data = tmpBuffer.data();
VmbSetImageInfoFromInputImage(&sourceImage, VmbPixelLayoutRGB, 16, &destinationImage);
if( VmbErrorSuccess != VmbImageTransform( &sourceImage, &destinationImage, NULL, 0) )
{
return false;
}
unsigned short* pDest = (unsigned short*)tmpBuffer.data();
const unsigned short* const pDestEnd = pDest + ImageSizePixels * 3;
const unsigned short bitshift = 16 - BitsPerPixelUsed;
while (pDest < pDestEnd)
{
*(pDest++) <<= bitshift;
}
}
}
// Write data into TIFF file.
const VmbUint32_t ColorsPerPixel = ( IsMonochrome( frame_info.PixelFormat() ) || !frame_info.UseColorInterpolation() ) ? 1 : 3;
const VmbUint32_t LineSize = BytesPerPixel * frame_info.Width() * ColorsPerPixel; // in bytes
unsigned char* pScanLine = tmpBuffer.data();
for (unsigned long i = 0; i < frame_info.Height(); i++)
{
int result = (*TiffLibrary::Get().TiffWriteScanLine)( TiffFile, pScanLine, i, 0);
if (result == 0)
return false;
pScanLine += LineSize;
}
}
catch (std::bad_alloc& badAlloc)
{
qDebug() << "bad_alloc caught: " << badAlloc.what() << '\n';
return false;
}
catch(...)
{
return false;
}
return true;
}
// Attempts to load the LibTiff library dynamically using Qt QLibrary
bool ImageWriter::IsAvailable( void )
{
return TiffLibrary::Get().Valid();
}
// Get the error message from the last QLibrary call
QString ImageWriter::GetLastError( void )
{
return TiffLibrary::Get().GetLastError();
}
// Write the frame to the specified file
bool ImageWriter::WriteTiff( const tFrameInfo & frame_info, const char* filename, ILogTarget *log)
{
bool result = true;
// Create a new TIFF file by constructing a Tiff object
TIFF *TiffFile = (*TiffLibrary::Get().TiffOpen)(filename, "w");
if( NULL != TiffFile )
{
// Set the TIFF tags
result = WriteStandardTags(TiffFile, frame_info );
if( true == result)
{
// Write the image data
result = WriteFrameBuffer(TiffFile, frame_info);
if( !result)
{
if( NULL != log)
{
log->Log("error could not write frame buffer");
}
}
}
else
{
if( NULL != log)
{
log->Log("error could not write TIFF standard flags");
}
}
(*TiffLibrary::Get().TiffClose)(TiffFile);
}
else
{
if( NULL != log)
{
log->Log("error could not open tiff file for writing");
}
result = false;
}
return result;
}