#include "ImageWriter.h" #include #include #include #include #include #include "VmbTransform.h" #include #include 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( ( BitsPerPixelUsed > 8 ) ? 16 : 8 ); // check if function symbol could be resolved (using QLibrary) result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_IMAGEWIDTH, static_cast(frame_info.Width() )); if (result == 0) return false; result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_IMAGELENGTH, static_cast(frame_info.Height() )); if (result == 0) return false; result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_COMPRESSION, static_cast(1) ); // No compression if (result == 0) return false; result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_XRESOLUTION, static_cast(1.0F)); if (result == 0) return false; result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_YRESOLUTION, static_cast(1.0F)); if (result == 0) return false; result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_RESOLUTIONUNIT, static_cast(1)); // No units if (result == 0) return false; result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_PLANARCONFIG, static_cast(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(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(3)); if (result == 0) return false; result = (*TiffLibrary::Get().TiffSetField)( TiffFile, IMGWRITER_PHOTOMETRIC, static_cast(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 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 (new unsigned char [ImageSizePixels * 2], DeleteArray ); // 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( new unsigned char[ ImageSizePixels * 3], DeleteArray ); // 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( new unsigned char[ ImageSizePixels * 3 * BytesPerPixel ], DeleteArray ); // 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; }