diff --git a/MMCore/MMCore.cpp b/MMCore/MMCore.cpp index 0ac29080a..318c85ffe 100644 --- a/MMCore/MMCore.cpp +++ b/MMCore/MMCore.cpp @@ -106,7 +106,7 @@ namespace mmi = mmcore::internal; * (Keep the 3 numbers on one line to make it easier to look at diffs when * merging/rebasing.) */ -const int MMCore_versionMajor = 11, MMCore_versionMinor = 11, MMCore_versionPatch = 0; +const int MMCore_versionMajor = 11, MMCore_versionMinor = 12, MMCore_versionPatch = 0; /////////////////////////////////////////////////////////////////////////////// @@ -4407,6 +4407,210 @@ double CMMCore::getExposure(const char* label) MMCORE_LEGACY_THROW(CMMError) return 0.0; } +/** + * Returns the current binning setting of the camera. + * Binning values in "NxN" format (e.g., "2x2") are parsed to return the integer factor (e.g., 2). + * @return the binning factor (1-100) + */ +int CMMCore::getBinning() MMCORE_LEGACY_THROW(CMMError) +{ + std::shared_ptr camera = currentCameraDevice_.lock(); + if (!camera) + { + throw CMMError(getCoreErrorText(MMERR_CameraNotAvailable).c_str(), MMERR_CameraNotAvailable); + } + else + { + std::string cameraName; + { + mmi::DeviceModuleLockGuard guard(camera); + cameraName = camera->GetLabel(); + } + return getBinning(cameraName.c_str()); + } +} + +/** + * Returns the current binning setting of the specified camera. + * Binning values in "NxN" format (e.g., "2x2") are parsed to return the integer factor (e.g., 2). + * @param label the camera device label + * @return the binning factor (1-100) + */ +int CMMCore::getBinning(const char* label) MMCORE_LEGACY_THROW(CMMError) +{ + std::shared_ptr pCamera = + deviceManager_->GetDeviceOfType(label); + + mmi::DeviceModuleLockGuard guard(pCamera); + + if (!pCamera->HasProperty(MM::g_Keyword_Binning)) + { + throw CMMError("Camera does not support binning property"); + } + + std::string binningValue = pCamera->GetProperty(MM::g_Keyword_Binning); + + if (binningValue.empty()) + { + throw CMMError("Binning property returned empty value"); + } + + // Parse the binning value - handle both integer ("2") and NxN ("2x2") formats + int binning; + size_t xPos = binningValue.find('x'); + if (xPos != std::string::npos) + { + // NxN format - parse both parts and verify they match + int binX = atoi(binningValue.c_str()); + int binY = atoi(binningValue.c_str() + xPos + 1); + if (binX != binY) + { + throw CMMError("Asymmetric binning not supported: " + binningValue); + } + binning = binX; + } + else + { + // Integer format + binning = atoi(binningValue.c_str()); + } + + if (binning < 1 || binning > 100) + { + throw CMMError("Binning value out of valid range (1-100): " + binningValue); + } + + return binning; +} + +/** + * Sets the binning setting of the camera. + * Automatically handles both integer ("2") and "NxN" ("2x2") property formats. + * @param binning the binning factor (1-100) + */ +void CMMCore::setBinning(int binning) MMCORE_LEGACY_THROW(CMMError) +{ + if (binning < 1 || binning > 100) + { + throw CMMError("Binning must be between 1 and 100"); + } + + std::shared_ptr camera = currentCameraDevice_.lock(); + if (!camera) + { + throw CMMError(getCoreErrorText(MMERR_CameraNotAvailable).c_str(), MMERR_CameraNotAvailable); + } + else + { + std::string cameraName; + { + mmi::DeviceModuleLockGuard guard(camera); + cameraName = camera->GetLabel(); + } + setBinning(cameraName.c_str(), binning); + } +} + +/** + * Sets the binning setting of the specified camera. + * Automatically handles both integer ("2") and "NxN" ("2x2") property formats. + * @param label the camera device label + * @param binning the binning factor (1-100) + */ +void CMMCore::setBinning(const char* label, int binning) MMCORE_LEGACY_THROW(CMMError) +{ + if (binning < 1 || binning > 100) + { + throw CMMError("Binning must be between 1 and 100"); + } + + std::shared_ptr pCamera = + deviceManager_->GetDeviceOfType(label); + + { + mmi::DeviceModuleLockGuard guard(pCamera); + LOG_DEBUG(coreLogger_) << "Will set camera " << label << + " binning to " << binning; + + if (!pCamera->HasProperty(MM::g_Keyword_Binning)) + { + throw CMMError("Camera does not support binning property"); + } + + // Try to determine the format by checking the current property value + std::string currentValue; + bool useNxNFormat = false; + + try + { + currentValue = pCamera->GetProperty(MM::g_Keyword_Binning); + // If current value contains 'x' and looks like NxN format, use that + size_t xPos = currentValue.find('x'); + if (!currentValue.empty() && xPos != std::string::npos) + { + // Validate that the current value has symmetric binning + int binX = atoi(currentValue.c_str()); + int binY = atoi(currentValue.c_str() + xPos + 1); + if (binX != binY) + { + throw CMMError("Asymmetric binning not supported: " + currentValue); + } + useNxNFormat = true; + } + } + catch (...) + { + // If we can't get current value, we'll try integer format first + } + + // Build the property value string + std::string binningValue; + if (useNxNFormat) + { + // NxN format: "2x2", "4x4", etc. + std::string binStr = CDeviceUtils::ConvertToString(binning); + binningValue = binStr + "x" + binStr; + } + else + { + // Integer format: "2", "4", etc. + binningValue = CDeviceUtils::ConvertToString(binning); + } + + // Try to set the property with detected format + try + { + pCamera->SetProperty(MM::g_Keyword_Binning, binningValue); + } + catch (const CMMError&) + { + // If it failed and we haven't tried the other format yet, try it + if (useNxNFormat) + { + // Tried NxN, now try integer + binningValue = CDeviceUtils::ConvertToString(binning); + } + else + { + // Tried integer, now try NxN + std::string binStr = CDeviceUtils::ConvertToString(binning); + binningValue = binStr + "x" + binStr; + } + + // Try with alternate format (this will throw if it still fails) + pCamera->SetProperty(MM::g_Keyword_Binning, binningValue); + } + + { + MMThreadGuard scg(stateCacheLock_); + stateCache_.addSetting(PropertySetting(label, MM::g_Keyword_Binning, binningValue.c_str())); + } + } + + LOG_DEBUG(coreLogger_) << "Did set camera " << label << + " binning to " << binning; +} + /** * Set the hardware region of interest for the current camera. * diff --git a/MMCore/MMCore.h b/MMCore/MMCore.h index b8d68e056..c1b529f27 100644 --- a/MMCore/MMCore.h +++ b/MMCore/MMCore.h @@ -394,6 +394,11 @@ class CMMCore double getExposure() MMCORE_LEGACY_THROW(CMMError); double getExposure(const char* label) MMCORE_LEGACY_THROW(CMMError); + int getBinning() MMCORE_LEGACY_THROW(CMMError); + int getBinning(const char* label) MMCORE_LEGACY_THROW(CMMError); + void setBinning(int binning) MMCORE_LEGACY_THROW(CMMError); + void setBinning(const char* label, int binning) MMCORE_LEGACY_THROW(CMMError); + void snapImage() MMCORE_LEGACY_THROW(CMMError); void* getImage() MMCORE_LEGACY_THROW(CMMError); void* getImage(unsigned numChannel) MMCORE_LEGACY_THROW(CMMError);