From 7a80d34d7e90794fe4cc355d10e370e47e83fd8e Mon Sep 17 00:00:00 2001 From: Lars Kool Date: Tue, 3 Dec 2024 15:36:33 +0100 Subject: [PATCH] Added support for most PixelFormats, and added 16-bit support --- DeviceAdapters/IDSPeak/IDSPeak.cpp | 396 +++++++++++++++++++++-------- DeviceAdapters/IDSPeak/IDSPeak.h | 4 + 2 files changed, 299 insertions(+), 101 deletions(-) diff --git a/DeviceAdapters/IDSPeak/IDSPeak.cpp b/DeviceAdapters/IDSPeak/IDSPeak.cpp index 89c6dad8d..d3e4d94b1 100644 --- a/DeviceAdapters/IDSPeak/IDSPeak.cpp +++ b/DeviceAdapters/IDSPeak/IDSPeak.cpp @@ -37,17 +37,150 @@ #include #include #include +#include using namespace std; const double CIDSPeak::nominalPixelSizeUm_ = 1.0; double g_IntensityFactor_ = 1.0; const char* g_PixelType_8bit = "8bit"; +const char* g_PixelType_16bit = "16bit"; const char* g_PixelType_32bitRGBA = "32bit RGBA"; // External names used used by the rest of the system // to load particular device from the "IDSPeak.dll" library const char* g_CameraDeviceName = "IDSCam"; const char* g_IDSPeakHubName = "IDS Peak Hub"; +const char* g_keyword_Peak_PixelFormat = "IDS Pixel Format"; + + +string pixelFormatToString(peak_pixel_format pixelFormat) { + switch(pixelFormat) { + case PEAK_PIXEL_FORMAT_BAYER_GR8: + return "BAYER_GR8"; + case PEAK_PIXEL_FORMAT_BAYER_GR10: + return "BAYER_GR10"; + case PEAK_PIXEL_FORMAT_BAYER_GR12: + return "BAYER_GR12"; + case PEAK_PIXEL_FORMAT_BAYER_RG8: + return "BAYER_RG8"; + case PEAK_PIXEL_FORMAT_BAYER_RG10: + return "BAYER_RG10"; + case PEAK_PIXEL_FORMAT_BAYER_RG12: + return "BAYER_RG12"; + case PEAK_PIXEL_FORMAT_BAYER_GB8: + return "BAYER_GB8"; + case PEAK_PIXEL_FORMAT_BAYER_GB10: + return "BAYER_GB10"; + case PEAK_PIXEL_FORMAT_BAYER_GB12: + return "BAYER_GB12"; + case PEAK_PIXEL_FORMAT_BAYER_BG8: + return "BAYER_BG8"; + case PEAK_PIXEL_FORMAT_BAYER_BG10: + return "BAYER_BG10"; + case PEAK_PIXEL_FORMAT_BAYER_BG12: + return "BAYER_BG12"; + case PEAK_PIXEL_FORMAT_MONO8: + return "MONO8"; + case PEAK_PIXEL_FORMAT_MONO10: + return "MONO10"; + case PEAK_PIXEL_FORMAT_MONO12: + return "MONO12"; + case PEAK_PIXEL_FORMAT_RGB8: + return "RGB8"; + case PEAK_PIXEL_FORMAT_RGB10: + return "RGB10"; + case PEAK_PIXEL_FORMAT_RGB12: + return "RGB12"; + case PEAK_PIXEL_FORMAT_BGR8: + return "BGR8"; + case PEAK_PIXEL_FORMAT_BGR10: + return "BGR10"; + case PEAK_PIXEL_FORMAT_BGR12: + return "BGR12"; + case PEAK_PIXEL_FORMAT_RGBA8: + return "RGBA8"; + case PEAK_PIXEL_FORMAT_RGBA10: + return "RGBA10"; + case PEAK_PIXEL_FORMAT_RGBA12: + return "RGBA12"; + case PEAK_PIXEL_FORMAT_BGRA8: + return "BGRA8"; + case PEAK_PIXEL_FORMAT_BGRA10: + return "BGRA10"; + case PEAK_PIXEL_FORMAT_BGRA12: + return "BGRA12"; + case PEAK_PIXEL_FORMAT_BAYER_GR10P: + return "BAYER_GR10P"; + case PEAK_PIXEL_FORMAT_BAYER_GR12P: + return "BAYER_GR12P"; + case PEAK_PIXEL_FORMAT_BAYER_RG10P: + return "BAYER_RG10P"; + case PEAK_PIXEL_FORMAT_BAYER_RG12P: + return "BAYER_RG12P"; + case PEAK_PIXEL_FORMAT_BAYER_GB10P: + return "BAYER_GB10P"; + case PEAK_PIXEL_FORMAT_BAYER_GB12P: + return "BAYER_GB12P"; + case PEAK_PIXEL_FORMAT_BAYER_BG10P: + return "BAYER_BG10P"; + case PEAK_PIXEL_FORMAT_BAYER_BG12P: + return "BAYER_BG12P"; + case PEAK_PIXEL_FORMAT_MONO10P: + return "MONO10P"; + case PEAK_PIXEL_FORMAT_MONO12P: + return "MONO12P"; + case PEAK_PIXEL_FORMAT_RGB10P32: + return "RGB10P32"; + case PEAK_PIXEL_FORMAT_BGR10P32: + return "BGR10P32"; + default: + return ""; + } +} +unordered_map stringPixelFormat = { + { "BAYER_GR8", PEAK_PIXEL_FORMAT_BAYER_GR8 }, + { "BAYER_GR10", PEAK_PIXEL_FORMAT_BAYER_GR10 }, + { "BAYER_GR12", PEAK_PIXEL_FORMAT_BAYER_GR12 }, + { "BAYER_RG8", PEAK_PIXEL_FORMAT_BAYER_RG8 }, + { "BAYER_RG10", PEAK_PIXEL_FORMAT_BAYER_RG10 }, + { "BAYER_RG12", PEAK_PIXEL_FORMAT_BAYER_RG12 }, + { "BAYER_GB8", PEAK_PIXEL_FORMAT_BAYER_GB8 }, + { "BAYER_GB10", PEAK_PIXEL_FORMAT_BAYER_GB10 }, + { "BAYER_GB12", PEAK_PIXEL_FORMAT_BAYER_GB12 }, + { "BAYER_BG8", PEAK_PIXEL_FORMAT_BAYER_BG8 }, + { "BAYER_BG10", PEAK_PIXEL_FORMAT_BAYER_BG10 }, + { "BAYER_BG12", PEAK_PIXEL_FORMAT_BAYER_BG12 }, + { "MONO8", PEAK_PIXEL_FORMAT_MONO8 }, + { "MONO10", PEAK_PIXEL_FORMAT_MONO10 }, + { "MONO12", PEAK_PIXEL_FORMAT_MONO12 }, + { "RGB8", PEAK_PIXEL_FORMAT_RGB8 }, + { "RGB10", PEAK_PIXEL_FORMAT_RGB10 }, + { "RGB12", PEAK_PIXEL_FORMAT_RGB12 }, + { "BGR8", PEAK_PIXEL_FORMAT_BGR8 }, + { "BGR10", PEAK_PIXEL_FORMAT_BGR10 }, + { "BGR12", PEAK_PIXEL_FORMAT_BGR12 }, + { "RGBA8", PEAK_PIXEL_FORMAT_RGBA8 }, + { "RGBA10", PEAK_PIXEL_FORMAT_RGBA10 }, + { "RGBA12", PEAK_PIXEL_FORMAT_RGBA12 }, + { "BGRA8", PEAK_PIXEL_FORMAT_BGRA8 }, + { "BGRA10", PEAK_PIXEL_FORMAT_BGRA10 }, + { "BGRA12", PEAK_PIXEL_FORMAT_BGRA12 }, + { "BAYER_GR10P", PEAK_PIXEL_FORMAT_BAYER_GR10P }, + { "BAYER_GR12P", PEAK_PIXEL_FORMAT_BAYER_GR12P }, + { "BAYER_RG10P", PEAK_PIXEL_FORMAT_BAYER_RG10P }, + { "BAYER_RG12P", PEAK_PIXEL_FORMAT_BAYER_RG12P }, + { "BAYER_GB10P", PEAK_PIXEL_FORMAT_BAYER_GB10P }, + { "BAYER_GB12P", PEAK_PIXEL_FORMAT_BAYER_GB12P }, + { "BAYER_BG10P", PEAK_PIXEL_FORMAT_BAYER_BG10P }, + { "BAYER_BG12P", PEAK_PIXEL_FORMAT_BAYER_BG12P }, + { "MONO10P", PEAK_PIXEL_FORMAT_MONO10P }, + { "MONO12P", PEAK_PIXEL_FORMAT_MONO12P }, + { "RGB10P32", PEAK_PIXEL_FORMAT_RGB10P32 }, + { "BGR10P32", PEAK_PIXEL_FORMAT_BGR10P32 } +}; +peak_pixel_format stringToPixelFormat(string PixelFormat) { + return stringPixelFormat[PixelFormat]; +} /////////////////////////////////////////////////////////////////////////////// // MMDevice API @@ -285,12 +418,13 @@ int CIDSPeak::Initialize() if (initialized_) return DEVICE_OK; + // Get handle to Hub IDSPeakHub* pHub = static_cast(GetParentHub()); if (pHub) { char hubLabel[MM::MaxStrLength]; pHub->GetLabel(hubLabel); - SetParentID(hubLabel); // for backward comp. + SetParentID(hubLabel); } else LogMessage(NoHubError); @@ -322,26 +456,6 @@ int CIDSPeak::Initialize() // set property list // ----------------- - - - - - - - - - - - - - - - - - - - - // Name int nRet = CreateStringProperty(MM::g_Keyword_Name, g_CameraDeviceName, true); @@ -368,35 +482,33 @@ int CIDSPeak::Initialize() nRet = SetAllowedBinning(); if (nRet != DEVICE_OK) { return nRet; } - // Pixel type - pAct = new CPropertyAction(this, &CIDSPeak::OnPixelType); - nRet = CreateStringProperty(MM::g_Keyword_PixelType, "pixeltype placeholder", false, pAct); - assert(nRet == DEVICE_OK); + // MM Pixel type vector pixelTypeValues; pixelTypeValues.push_back(g_PixelType_8bit); - if (isColorCamera()) - { - pixelTypeValues.push_back(g_PixelType_32bitRGBA); - } - nRet = ClearAllowedValues(MM::g_Keyword_PixelType); + pixelTypeValues.push_back(g_PixelType_16bit); + if (isColorCamera()) { pixelTypeValues.push_back(g_PixelType_32bitRGBA); } + + pAct = new CPropertyAction(this, &CIDSPeak::OnPixelType); + nRet = CreateStringProperty(MM::g_Keyword_PixelType, g_PixelType_8bit, false, pAct); nRet = SetAllowedValues(MM::g_Keyword_PixelType, pixelTypeValues); + pixelType_ = g_PixelType_8bit; if (nRet != DEVICE_OK) return nRet; - //peak_pixel_format format; - //status = peak_PixelFormat_Get(hCam, &format); - //if (format == PEAK_PIXEL_FORMAT_MONO8) - //{ - // pixelType_ = g_PixelType_8bit; - // nComponents_ = 1; - // SetProperty(MM::g_Keyword_PixelType, g_PixelType_8bit); - //} - //else - //{ - // pixelType_ = g_PixelType_32bitRGBA; - // nComponents_ = 8; - // SetProperty(MM::g_Keyword_PixelType, g_PixelType_32bitRGBA); - //} + // Pixel formats (IDS side) + size_t nPixelFormats = 0; + status = peak_PixelFormat_GetList(hCam, NULL, &nPixelFormats); + peak_pixel_format* pixelFormats = (peak_pixel_format*)calloc(nPixelFormats, sizeof(peak_pixel_format)); + + status = peak_PixelFormat_GetList(hCam, pixelFormats, &nPixelFormats); + for (size_t i = 0; i < nPixelFormats; i++) { + availablePixelFormats.push_back(pixelFormatToString(pixelFormats[i])); + } + status = peak_PixelFormat_Get(hCam, &currPixelFormat); + pAct = new CPropertyAction(this, &CIDSPeak::OnPeakPixelFormat); + nRet = CreateStringProperty(g_keyword_Peak_PixelFormat, pixelFormatToString(currPixelFormat).c_str(), false, pAct); + nRet = ClearAllowedValues(g_keyword_Peak_PixelFormat); + nRet = SetAllowedValues(g_keyword_Peak_PixelFormat, availablePixelFormats); // Exposure time nRet = CreateFloatProperty(MM::g_Keyword_Exposure, exposureCur_, false); @@ -1630,7 +1742,6 @@ int CIDSPeak::OnGainBlue(MM::PropertyBase* pProp, MM::ActionType eAct) */ int CIDSPeak::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) { - int nRet = DEVICE_OK; switch (eAct) { case MM::AfterSet: @@ -1641,47 +1752,73 @@ int CIDSPeak::OnPixelType(MM::PropertyBase* pProp, MM::ActionType eAct) string pixelType; pProp->Get(pixelType); - if (peak_PixelFormat_GetAccessStatus(hCam) == PEAK_ACCESS_READWRITE) - { - if (pixelType == g_PixelType_8bit) - { - status = peak_PixelFormat_Set(hCam, PEAK_PIXEL_FORMAT_MONO8); - nComponents_ = 1; - } - else - { - status = peak_PixelFormat_Set(hCam, PEAK_PIXEL_FORMAT_BAYER_RG8); - nComponents_ = 4; - } + if (pixelType == g_PixelType_8bit) { + nComponents_ = 1; + bitDepth_ = 8; } - else - { - return ERR_NO_WRITE_ACCESS; + else if (pixelType == g_PixelType_16bit) { + nComponents_ = 1; + bitDepth_ = 16; } - - // Only 8bit formats are supported for now - bitDepth_ = 8; + else if (pixelType == g_PixelType_32bitRGBA) { + nComponents_ = 4; + bitDepth_ = 8; + } + else { + return DEVICE_CAN_NOT_SET_PROPERTY; + } + pixelType_ = pixelType; // Resize buffer to accomodate the new image img_.Resize(img_.Width(), img_.Height(), nComponents_ * (bitDepth_ / 8)); - nRet = DEVICE_OK; } break; case MM::BeforeGet: { - if (nComponents_ == 1) - { + if (nComponents_ == 1 && bitDepth_ == 8) { pProp->Set(g_PixelType_8bit); } - else - { + else if (nComponents_ == 1 && bitDepth_ == 16) { + pProp->Set(g_PixelType_16bit); + } + else if (nComponents_ == 4 && bitDepth_ == 8) { pProp->Set(g_PixelType_32bitRGBA); } - + else { + return DEVICE_CAN_NOT_SET_PROPERTY; + } } break; default: break; } + return DEVICE_OK; +} + +/** +* Handles "PixelType" property. +*/ +int CIDSPeak::OnPeakPixelFormat(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + int nRet = DEVICE_OK; + string pixelFormat; + + switch (eAct) { + case MM::AfterSet: + if (IsCapturing()) + return DEVICE_CAMERA_BUSY_ACQUIRING; + + pProp->Get(pixelFormat); + if (peak_PixelFormat_GetAccessStatus(hCam) == PEAK_ACCESS_READWRITE) { + currPixelFormat = stringToPixelFormat(pixelFormat); + peak_PixelFormat_Set(hCam, currPixelFormat); + } + else { return ERR_NO_WRITE_ACCESS; } + break; + + case MM::BeforeGet: + pProp->Set(pixelFormatToString(currPixelFormat).c_str()); + break; + } return nRet; } @@ -2040,41 +2177,98 @@ void CIDSPeak::initializeAutoWBConversion() stringToPeakAuto.insert(pair("Continuous", PEAK_AUTO_FEATURE_MODE_CONTINUOUS)); } -int CIDSPeak::transferBuffer(peak_frame_handle hFrame, ImgBuffer& img) -{ +int CIDSPeak::transferBuffer(peak_frame_handle hFrame, ImgBuffer& img) { peak_frame_handle hFrameConverted; peak_buffer peakBuffer; - uint8_t* memoryAddress; - size_t memorySize; unsigned char* pBuf = (unsigned char*) const_cast(img.GetPixels()); - // Convert data types to MM supported data types - // Monochrome is natively supported by MM, so no conversion is needed - if (nComponents_ == 1) - { - status = peak_Frame_Buffer_Get(hFrame, &peakBuffer); - // Transfer the frame buffer to the img buffer expected by MM. - memoryAddress = peakBuffer.memoryAddress; - memorySize = peakBuffer.memorySize; - memcpy(pBuf, memoryAddress, memorySize); - } - // Convert all 8bit pixel formats into BGRA8 (8bit format expected by MM) - else if (nComponents_ == 4) - { - status = peak_IPL_PixelFormat_Set(hCam, PEAK_PIXEL_FORMAT_BGRA8); - if (status != PEAK_STATUS_SUCCESS) { return DEVICE_UNSUPPORTED_DATA_FORMAT; } - status = peak_IPL_ProcessFrame(hCam, hFrame, &hFrameConverted); - if (status != PEAK_STATUS_SUCCESS) { return DEVICE_UNSUPPORTED_DATA_FORMAT; } - status = peak_Frame_Buffer_Get(hFrameConverted, &peakBuffer); - // Transfer the frame buffer to the img buffer expected by MM. - memoryAddress = peakBuffer.memoryAddress; - memorySize = peakBuffer.memorySize; - memcpy(pBuf, memoryAddress, memorySize); - peak_Frame_Release(hCam, hFrameConverted); - } - else - { - return DEVICE_UNSUPPORTED_DATA_FORMAT; + LogMessage(pixelType_); + if (pixelType_ == g_PixelType_8bit) { + if (currPixelFormat == PEAK_PIXEL_FORMAT_MONO8) { + // Simply get framebuffer + status = peak_Frame_Buffer_Get(hFrame, &peakBuffer); + if (status != PEAK_STATUS_SUCCESS) { return ERR_ACQ_FRAME; } + } + else { + // Get buffer and convert it to 8bit mono + status = peak_IPL_PixelFormat_Set(hCam, PEAK_PIXEL_FORMAT_MONO8); + if (status != PEAK_STATUS_SUCCESS) { + LogMessage("IDS does not support the conversion of pixel format " + + pixelFormatToString(currPixelFormat) + + " to 8bit mono. Please select a different " + + g_keyword_Peak_PixelFormat); + return ERR_ACQ_FRAME; + } + status = peak_IPL_ProcessFrame(hCam, hFrame, &hFrameConverted); + if (status != PEAK_STATUS_SUCCESS) { + LogMessage("Something went wrong while converting the image to 8bit mono. It is best to select the " + + (string)g_keyword_Peak_PixelFormat + + pixelFormatToString(PEAK_PIXEL_FORMAT_MONO8) + + " to bypass the image conversion."); + } + status = peak_Frame_Buffer_Get(hFrameConverted, &peakBuffer); + } + // Copy 8bit mono buffer to imageBuffer + memcpy(pBuf, peakBuffer.memoryAddress, peakBuffer.memorySize); + } + else if (pixelType_ == g_PixelType_16bit) { + // MONO10 and MONO12 are already 16 bit, so they don't need to be converted + if (currPixelFormat == PEAK_PIXEL_FORMAT_MONO10 || + currPixelFormat == PEAK_PIXEL_FORMAT_MONO12) { + // Simply get framebuffer + status = peak_Frame_Buffer_Get(hFrame, &peakBuffer); + if (status != PEAK_STATUS_SUCCESS) { return ERR_ACQ_FRAME; } + } + else { + // Get buffer and convert it to 8bit mono + status = peak_IPL_PixelFormat_Set(hCam, PEAK_PIXEL_FORMAT_MONO12); + if (status != PEAK_STATUS_SUCCESS) { + LogMessage("IDS does not support the conversion of pixel format " + + pixelFormatToString(currPixelFormat) + + " to 16bit mono. Please select a different " + + g_keyword_Peak_PixelFormat); + return ERR_ACQ_FRAME; + } + status = peak_IPL_ProcessFrame(hCam, hFrame, &hFrameConverted); + if (status != PEAK_STATUS_SUCCESS) { + LogMessage("Something went wrong while converting the image to 16bit mono. It is best to select the " + + (string)g_keyword_Peak_PixelFormat + + pixelFormatToString(PEAK_PIXEL_FORMAT_MONO12) + + " to bypass the image conversion."); + } + status = peak_Frame_Buffer_Get(hFrameConverted, &peakBuffer); + } + // Copy 16bit mono buffer to imageBufer + memcpy(pBuf, peakBuffer.memoryAddress, peakBuffer.memorySize); + } + else if (pixelType_ == g_PixelType_32bitRGBA) { + // MONO10 and MONO12 are already 16 bit, so they don't need to be converted + if (currPixelFormat == PEAK_PIXEL_FORMAT_BGRA8) { + // Simply get framebuffer + status = peak_Frame_Buffer_Get(hFrame, &peakBuffer); + if (status != PEAK_STATUS_SUCCESS) { return ERR_ACQ_FRAME; } + } + else { + // Get buffer and convert it to 8bit mono + status = peak_IPL_PixelFormat_Set(hCam, PEAK_PIXEL_FORMAT_BGRA8); + if (status != PEAK_STATUS_SUCCESS) { + LogMessage("IDS does not support the conversion of pixel format " + + pixelFormatToString(currPixelFormat) + + " to 32bitRGBA mono. Please select a different " + + g_keyword_Peak_PixelFormat); + return ERR_ACQ_FRAME; + } + status = peak_IPL_ProcessFrame(hCam, hFrame, &hFrameConverted); + if (status != PEAK_STATUS_SUCCESS) { + LogMessage("Something went wrong while converting the image to 32bitRGBA. It is best to select the " + + (string)g_keyword_Peak_PixelFormat + + pixelFormatToString(PEAK_PIXEL_FORMAT_BGRA8) + + " to bypass the image conversion."); + } + status = peak_Frame_Buffer_Get(hFrameConverted, &peakBuffer); + } + // Copy 16bit mono buffer to imageBufer + memcpy(pBuf, peakBuffer.memoryAddress, peakBuffer.memorySize); } // Exit if something went wrong during the conversion/obtaining the buffer. @@ -2146,4 +2340,4 @@ bool CIDSPeak::isColorCamera() if (pixelFormatList[i] == PEAK_PIXEL_FORMAT_BAYER_RG8) { return true; } } return false; -} \ No newline at end of file +} diff --git a/DeviceAdapters/IDSPeak/IDSPeak.h b/DeviceAdapters/IDSPeak/IDSPeak.h index b67a882cc..63628c6a5 100644 --- a/DeviceAdapters/IDSPeak/IDSPeak.h +++ b/DeviceAdapters/IDSPeak/IDSPeak.h @@ -195,6 +195,7 @@ class CIDSPeak : public CCameraBase int OnGainRed(MM::PropertyBase* pProp, MM::ActionType eAct); int OnGainGreen(MM::PropertyBase* pProp, MM::ActionType eAct); int OnGainBlue(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnPeakPixelFormat(MM::PropertyBase* pProp, MM::ActionType eAct); long GetCCDXSize() { return cameraCCDXSize_; } long GetCCDYSize() { return cameraCCDYSize_; } @@ -274,6 +275,9 @@ class CIDSPeak : public CCameraBase double gainMax_; double gainInc_; + std::vector availablePixelFormats; + peak_pixel_format currPixelFormat; + bool stopOnOverflow_;