diff --git a/ADApp/ADSrc/NDArray.cpp b/ADApp/ADSrc/NDArray.cpp index ecc1c0a54..bff3a32d1 100755 --- a/ADApp/ADSrc/NDArray.cpp +++ b/ADApp/ADSrc/NDArray.cpp @@ -30,7 +30,7 @@ NDArray::NDArray() : referenceCount(0), pNDArrayPool(NULL), uniqueId(0), timeStamp(0.0), ndims(0), dataType(NDInt8), - dataSize(0), pData(NULL) + dataSize(0), bitsPerElement(0), pData(NULL) { this->epicsTS.secPastEpoch = 0; this->epicsTS.nsec = 0; @@ -148,6 +148,7 @@ int NDArray::getInfo(NDArrayInfo_t *pInfo) pInfo->ySize = this->dims[pInfo->yDim].size; pInfo->colorSize = this->dims[pInfo->colorDim].size; } + pInfo->bitsPerElement = this->bitsPerElement; return(ND_SUCCESS); } @@ -201,8 +202,8 @@ int NDArray::report(FILE *fp, int details) this->ndims); for (dim=0; dimndims; dim++) fprintf(fp, "%d ", (int)this->dims[dim].size); fprintf(fp, "]\n"); - fprintf(fp, " dataType=%d, dataSize=%d, pData=%p\n", - this->dataType, (int)this->dataSize, this->pData); + fprintf(fp, " dataType=%d, bitsPerElement=%d, dataSize=%d, pData=%p\n", + this->dataType, this->bitsPerElement, (int)this->dataSize, this->pData); fprintf(fp, " uniqueId=%d, timeStamp=%f, referenceCount=%d\n", this->uniqueId, this->timeStamp, this->referenceCount); fprintf(fp, " number of attributes=%d\n", this->pAttributeList->count()); @@ -212,3 +213,41 @@ int NDArray::report(FILE *fp, int details) return ND_SUCCESS; } +int GetNDColorModeBits( NDColorMode_t tyColor, NDDataType_t tyData ) +{ + int nBits; + switch ( tyColor ) + { + default: + case NDColorModeMono: + nBits = GetNDDataTypeBits( tyData ); + break; + // For defined color standards, set nBits to the + // number of combined bits in one pixel element + case NDColorModeBayer: + // Optimal packing uses all bits of data type + nBits = GetNDDataTypeBits( tyData ); + break; + case NDColorModeRGB1: + case NDColorModeRGB2: + case NDColorModeRGB3: + // 3 pixels packed with room for a 4th + nBits = GetNDDataTypeBits( tyData ) * 3 / 4; + break; + case NDColorModeYUV444: + // 3 values for 1 pixel + nBits = 3 * GetNDDataTypeBits( tyData ); + break; + case NDColorModeYUV422: + // 4 values for 2 pixels + nBits = 4 * GetNDDataTypeBits( tyData ); + break; + case NDColorModeYUV411: + // 6 values for 4 pixels + nBits = 6 * GetNDDataTypeBits( tyData ); + break; + } + return nBits; +} + + diff --git a/ADApp/ADSrc/NDArray.h b/ADApp/ADSrc/NDArray.h index b6457ca94..46e1efd56 100644 --- a/ADApp/ADSrc/NDArray.h +++ b/ADApp/ADSrc/NDArray.h @@ -35,6 +35,8 @@ typedef enum NDColorModeYUV411 /**< YUV image, 6 bytes encodes 4 RGB pixels */ } NDColorMode_t; +extern int GetNDColorModeBits( NDColorMode_t, NDDataType_t ); + /** Enumeration of Bayer patterns for NDArray attribute "bayerPattern". * This value is only meaningful if colorMode is NDColorModeBayer. * This value is needed because the Bayer pattern will change when reading out a @@ -68,7 +70,8 @@ typedef struct NDDimension { /** Structure returned by NDArray::getInfo */ typedef struct NDArrayInfo { size_t nElements; /**< The total number of elements in the array */ - int bytesPerElement; /**< The number of bytes per element in the array */ + int bitsPerElement; /**< The number of bits per element in the array */ + int bytesPerElement; /**< The number of bytes per element in the array */ size_t totalBytes; /**< The total number of bytes required to hold the array; * this may be less than NDArray::dataSize. */ /**< The following are mostly useful for color images (RGB1, RGB2, RGB3) */ @@ -115,6 +118,7 @@ class epicsShareClass NDArray { NDDataType_t dataType; /**< Data type for this array. */ size_t dataSize; /**< Data size for this array; actual amount of memory allocated for *pData, may be more than * required to hold the array*/ + int bitsPerElement; /**< The number of bits per element in the array */ void *pData; /**< Pointer to the array data. * The data is assumed to be stored in the order of dims[0] changing fastest, and * dims[ndims-1] changing slowest. */ @@ -154,7 +158,7 @@ class epicsShareClass NDArrayPool { epicsMutexId listLock_; /**< Mutex to protect the free list */ int maxBuffers_; /**< Maximum number of buffers this object is allowed to allocate; -1=unlimited */ int numBuffers_; /**< Number of buffers this object has currently allocated */ - size_t maxMemory_; /**< Maximum bytes of memory this object is allowed to allocate; -1=unlimited */ + size_t maxMemory_; /**< Maximum bytes of memory this object is allowed to allocate; 0=unlimited */ size_t memorySize_; /**< Number of bytes of memory this object has currently allocated */ int numFree_; /**< Number of NDArray objects in the free list */ }; diff --git a/ADApp/ADSrc/NDArrayPool.cpp b/ADApp/ADSrc/NDArrayPool.cpp index 8cbefc7f6..5b22f7f8f 100644 --- a/ADApp/ADSrc/NDArrayPool.cpp +++ b/ADApp/ADSrc/NDArrayPool.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -90,6 +91,7 @@ NDArray* NDArrayPool::alloc(int ndims, size_t *dims, NDDataType_t dataType, size /* Initialize fields */ pArray->pNDArrayPool = this; pArray->dataType = dataType; + pArray->bitsPerElement = GetNDDataTypeBits(dataType); pArray->ndims = ndims; memset(pArray->dims, 0, sizeof(pArray->dims)); for (i=0; ialloc(pIn->ndims, dimSizeOut, pIn->dataType, 0, NULL); if(NULL==pOut) return NULL; } + pOut->bitsPerElement = pIn->bitsPerElement; pOut->uniqueId = pIn->uniqueId; pOut->timeStamp = pIn->timeStamp; pOut->epicsTS = pIn->epicsTS; @@ -523,6 +526,18 @@ int NDArrayPool::convert(NDArray *pIn, driverName, functionName); return(ND_ERROR); } + + /* Use log2 of binning factors to calculate effect on bitsPerElement */ + pOut->bitsPerElement = pIn->bitsPerElement; + size_t binFactor = 1; + for (i=0; indims; i++) + binFactor *= dimsOutCopy[i].binning; + pOut->bitsPerElement += lround( log2( binFactor ) ); + + /* Clip bitsPerElement to max for output dataType */ + if( pOut->bitsPerElement > GetNDDataTypeBits(pOut->dataType) ) + pOut->bitsPerElement = GetNDDataTypeBits(pOut->dataType); + /* Copy fields from input to output */ pOut->timeStamp = pIn->timeStamp; pOut->epicsTS = pIn->epicsTS; diff --git a/ADApp/ADSrc/NDAttribute.cpp b/ADApp/ADSrc/NDAttribute.cpp index bf073860a..0d22d1efb 100644 --- a/ADApp/ADSrc/NDAttribute.cpp +++ b/ADApp/ADSrc/NDAttribute.cpp @@ -451,4 +451,29 @@ int NDAttribute::report(FILE *fp, int details) return ND_SUCCESS; } +/** Get the max number of bits per pixel for the specified data type + * \param[in] NDDataType_t Data type + */ +int GetNDDataTypeBits( NDDataType_t tyData ) +{ + int nBits; + switch ( tyData ) + { + // Default to large number so conversions to + // integer data types will be clipped to max + default: nBits = 99; break; + case NDInt8: nBits = 8; break; + case NDUInt8: nBits = 8; break; + case NDInt16: nBits = 16; break; + case NDUInt16: nBits = 16; break; + case NDInt32: nBits = 32; break; + case NDUInt32: nBits = 32; break; + // nBits is more an indicator of max pixel value for mono rendering + // than a measure of accuracy, so we set floating point types to + // a large number to avoid clipping w/ wierd binning like 512x512 + case NDFloat32: nBits = 99; break; + case NDFloat64: nBits = 99; break; + } + return nBits; +} diff --git a/ADApp/ADSrc/NDAttribute.h b/ADApp/ADSrc/NDAttribute.h index a356131ce..8c68ad85c 100644 --- a/ADApp/ADSrc/NDAttribute.h +++ b/ADApp/ADSrc/NDAttribute.h @@ -36,6 +36,8 @@ typedef enum NDFloat64 /**< 64-bit float */ } NDDataType_t; +extern int GetNDDataTypeBits( NDDataType_t ); + /** Enumeration of NDAttribute attribute data types */ typedef enum { diff --git a/ADApp/ADSrc/asynNDArrayDriver.cpp b/ADApp/ADSrc/asynNDArrayDriver.cpp index a8985e311..7f282a637 100644 --- a/ADApp/ADSrc/asynNDArrayDriver.cpp +++ b/ADApp/ADSrc/asynNDArrayDriver.cpp @@ -57,8 +57,8 @@ static const char *driverName = "asynNDArrayDriver"; /** Checks whether the directory specified exists. * - * This is a convenience function that determines the directory specified exists. - * It adds a trailing '/' or '\' character to the path if one is not present. + * This is a convenience function that determines if the directory specified exists. + * It adds a trailing '/', (WIN32 '\'), character to the path if one is not present. * It returns true if the directory exists and false if it does not */ bool asynNDArrayDriver::checkPath(std::string &filePath) @@ -231,8 +231,8 @@ asynStatus asynNDArrayDriver::createFileName(int maxChars, char *fullFileName) * \param[out] filePath The file path. * \param[out] fileName The constructed file name without file file path. * - * This is a convenience function that constructs a file path and file name - * from the NDFilePath, NDFileName, NDFileNumber, and + * This is a convenience function that constructs the directory path and the + * file name in that directory from the NDFilePath, NDFileName, NDFileNumber, and * NDFileTemplate parameters. If NDAutoIncrement is true then it increments the * NDFileNumber after creating the file name. */ @@ -720,6 +720,7 @@ asynNDArrayDriver::asynNDArrayDriver(const char *portName, int maxAddr, int maxB createParam(NDNDimensionsString, asynParamInt32, &NDNDimensions); createParam(NDDimensionsString, asynParamInt32, &NDDimensions); createParam(NDDataTypeString, asynParamInt32, &NDDataType); + createParam(NDBitsPerPixelString, asynParamInt32, &NDBitsPerPixel); createParam(NDColorModeString, asynParamInt32, &NDColorMode); createParam(NDUniqueIdString, asynParamInt32, &NDUniqueId); createParam(NDTimeStampString, asynParamFloat64, &NDTimeStamp); @@ -777,6 +778,8 @@ asynNDArrayDriver::asynNDArrayDriver(const char *portName, int maxAddr, int maxB setIntegerParam(NDArraySizeZ, 0); setIntegerParam(NDArraySize, 0); setIntegerParam(NDNDimensions, 0); + setIntegerParam(NDDataType, NDUInt8); + setIntegerParam(NDBitsPerPixel, 8); setIntegerParam(NDColorMode, NDColorModeMono); setIntegerParam(NDUniqueId, 0); setDoubleParam (NDTimeStamp, 0.); diff --git a/ADApp/ADSrc/asynNDArrayDriver.h b/ADApp/ADSrc/asynNDArrayDriver.h index 9ba270ae4..8514f4cd6 100755 --- a/ADApp/ADSrc/asynNDArrayDriver.h +++ b/ADApp/ADSrc/asynNDArrayDriver.h @@ -49,6 +49,7 @@ typedef enum { #define NDNDimensionsString "ARRAY_NDIMENSIONS" /**< (asynInt32, r/o) Number of dimensions in array */ #define NDDimensionsString "ARRAY_DIMENSIONS" /**< (asynInt32Array, r/o) Array dimensions */ #define NDDataTypeString "DATA_TYPE" /**< (asynInt32, r/w) Data type (NDDataType_t) */ +#define NDBitsPerPixelString "BITS_PER_PIXEL" /**< (asynInt32, r/w) Number of bits per pixel */ #define NDColorModeString "COLOR_MODE" /**< (asynInt32, r/w) Color mode (NDColorMode_t) */ #define NDUniqueIdString "UNIQUE_ID" /**< (asynInt32, r/o) Unique ID number of array */ #define NDTimeStampString "TIME_STAMP" /**< (asynFloat64, r/o) Time stamp of array */ @@ -143,6 +144,7 @@ class epicsShareFunc asynNDArrayDriver : public asynPortDriver { int NDNDimensions; int NDDimensions; int NDDataType; + int NDBitsPerPixel; int NDColorMode; int NDUniqueId; int NDTimeStamp; diff --git a/ADApp/Db/NDArrayBase.template b/ADApp/Db/NDArrayBase.template index 0f29aede9..c3ca8e17a 100755 --- a/ADApp/Db/NDArrayBase.template +++ b/ADApp/Db/NDArrayBase.template @@ -492,6 +492,13 @@ record(mbbi, "$(P)$(R)DataType_RBV") field(SCAN, "I/O Intr") } +record(longin, "$(P)$(R)BitsPerPixel_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))BITS_PER_PIXEL") + field(SCAN, "I/O Intr") +} + ################################################################### # These records control the color mode # # These choices must agree with NDColorMode_t in NDArray.h # diff --git a/ADApp/pluginSrc/NDPluginColorConvert.cpp b/ADApp/pluginSrc/NDPluginColorConvert.cpp index aa5a48282..f0847943e 100644 --- a/ADApp/pluginSrc/NDPluginColorConvert.cpp +++ b/ADApp/pluginSrc/NDPluginColorConvert.cpp @@ -497,6 +497,12 @@ void NDPluginColorConvert::convertColor(NDArray *pArray) default: break; } + + if ( pArrayOut ) { + /* Set nBits based on total bits used to represent one pixel element. */ + pArrayOut->bitsPerElement = GetNDColorModeBits( colorModeOut, pArrayOut->dataType ); + } + /* If the output array pointer is null then no conversion was done, copy the input to the output */ if (!pArrayOut) pArrayOut = this->pNDArrayPool->copy(pArray, NULL, 1); this->lock(); @@ -569,7 +575,7 @@ void NDPluginColorConvert::processCallbacks(NDArray *pArray) driverName, functionName, pArray->dataType); break; } - + callParamCallbacks(); } diff --git a/ADApp/pluginSrc/NDPluginDriver.cpp b/ADApp/pluginSrc/NDPluginDriver.cpp index cc53cf127..e3080de81 100644 --- a/ADApp/pluginSrc/NDPluginDriver.cpp +++ b/ADApp/pluginSrc/NDPluginDriver.cpp @@ -187,7 +187,7 @@ NDPluginDriver::~NDPluginDriver() * This method takes care of some bookkeeping for callbacks, updating parameters * from data in the class and in the NDArray. It does asynInt32Array callbacks * for the dimensions array if the dimensions of the NDArray data have changed. */ - void NDPluginDriver::beginProcessCallbacks(NDArray *pArray) +void NDPluginDriver::beginProcessCallbacks(NDArray *pArray) { int arrayCounter; int i, dimsChanged; @@ -200,19 +200,24 @@ NDPluginDriver::~NDPluginDriver() if (pAttribute) pAttribute->getValue(NDAttrInt32, &colorMode); pAttribute = pArray->pAttributeList->find("BayerPattern"); if (pAttribute) pAttribute->getValue(NDAttrInt32, &bayerPattern); - + getIntegerParam(NDArrayCounter, &arrayCounter); arrayCounter++; - setIntegerParam(NDArrayCounter, arrayCounter); - setIntegerParam(NDNDimensions, pArray->ndims); - setIntegerParam(NDDataType, pArray->dataType); - setIntegerParam(NDColorMode, colorMode); - setIntegerParam(NDBayerPattern, bayerPattern); - setIntegerParam(NDUniqueId, pArray->uniqueId); + setIntegerParam(NDArrayCounter, arrayCounter); + setIntegerParam(NDNDimensions, pArray->ndims); + setIntegerParam(NDDataType, pArray->dataType); + + NDArrayInfo_t arrayInfo; + pArray->getInfo(&arrayInfo); + setIntegerParam(NDBitsPerPixel, arrayInfo.bitsPerElement); + + setIntegerParam(NDColorMode, colorMode); + setIntegerParam(NDBayerPattern, bayerPattern); + setIntegerParam(NDUniqueId, pArray->uniqueId); setTimeStamp(&pArray->epicsTS); - setDoubleParam(NDTimeStamp, pArray->timeStamp); - setIntegerParam(NDEpicsTSSec, pArray->epicsTS.secPastEpoch); - setIntegerParam(NDEpicsTSNsec, pArray->epicsTS.nsec); + setDoubleParam(NDTimeStamp, pArray->timeStamp); + setIntegerParam(NDEpicsTSSec, pArray->epicsTS.secPastEpoch); + setIntegerParam(NDEpicsTSNsec, pArray->epicsTS.nsec); /* See if the array dimensions have changed. If so then do callbacks on them. */ for (i=0, dimsChanged=0; idims[i].size; diff --git a/ADApp/pluginSrc/NDPluginProcess.cpp b/ADApp/pluginSrc/NDPluginProcess.cpp index e99d2bd09..da69db9a8 100644 --- a/ADApp/pluginSrc/NDPluginProcess.cpp +++ b/ADApp/pluginSrc/NDPluginProcess.cpp @@ -242,6 +242,19 @@ void NDPluginProcess::processCallbacks(NDArray *pArray) this->pNDArrayPool->convert(pScratch, &pArrayOut, (NDDataType_t)dataType); } + if (enableOffsetScale && (NULL != pArrayOut)) { + /* Update bitsPerElement */ + int bitsPerElement = arrayInfo.bitsPerElement; + bitsPerElement += static_cast( ceil( log2( scale ) ) ); + + /* Clip bitsPerElement to max for output dataType */ + if( bitsPerElement > GetNDDataTypeBits(pArrayOut->dataType) ) + bitsPerElement = GetNDDataTypeBits(pArrayOut->dataType); + + /* Set the bits per pixel of the ROI output */ + pArrayOut->bitsPerElement = bitsPerElement; + } + if (autoOffsetScale && (NULL != pArrayOut)) { pArrayOut->getInfo(&arrayInfo); double maxScale = pow(2., arrayInfo.bytesPerElement*8) - 1; diff --git a/ADApp/pluginSrc/NDPluginROI.cpp b/ADApp/pluginSrc/NDPluginROI.cpp index e21e809be..b34d78a96 100644 --- a/ADApp/pluginSrc/NDPluginROI.cpp +++ b/ADApp/pluginSrc/NDPluginROI.cpp @@ -150,7 +150,9 @@ void NDPluginROI::processCallbacks(NDArray *pArray) /* Extract this ROI from the input array. The convert() function allocates * a new array and it is reserved (reference count = 1) */ - if (dataType == -1) dataType = (int)pArray->dataType; + if (dataType == -1) { + dataType = (int)pArray->dataType; + } /* We treat the case of RGB1 data specially, so that NX and NY are the X and Y dimensions of the * image, not the first 2 dimensions. This makes it much easier to switch back and forth between * RGB1 and mono mode when using an ROI. */ @@ -178,7 +180,7 @@ void NDPluginROI::processCallbacks(NDArray *pArray) for (i=0; ipNDArrayPool->convert(pScratch, &pOutput, (NDDataType_t)dataType); pScratch->release(); - } + } else { this->pNDArrayPool->convert(pArray, &pOutput, (NDDataType_t)dataType, dims); } @@ -223,6 +225,21 @@ void NDPluginROI::processCallbacks(NDArray *pArray) } } this->lock(); + + /* Calculate ROI bitsPerElement */ + double bitsPerPixel = arrayInfo.bitsPerElement; + size_t binFactor = 1; + for ( size_t iDim=0; iDim < static_cast(pArray->ndims); iDim++ ) + binFactor *= dims[iDim].binning; + if ( binFactor != 1 ) + bitsPerPixel += log2( binFactor ); + if ( enableScale && scale != 0 && scale != 1 ) + bitsPerPixel -= log2( scale ); + /* Clip bitsPerElement to max for output dataType */ + if( bitsPerPixel > GetNDDataTypeBits(pOutput->dataType) ) + bitsPerPixel = GetNDDataTypeBits(pOutput->dataType); + /* Set the bits per pixel of the ROI output */ + pOutput->bitsPerElement = lrint( bitsPerPixel ); /* Set the image size of the ROI image data */ setIntegerParam(NDArraySizeX, 0); diff --git a/documentation/areaDetectorDoc.html b/documentation/areaDetectorDoc.html index e1e53efcd..fb95bb45e 100644 --- a/documentation/areaDetectorDoc.html +++ b/documentation/areaDetectorDoc.html @@ -1576,6 +1576,24 @@

longin + + + NDBitsPerPixel + + asynInt32 + + r/o + + Number of bits per pixel. + Only defined if set by driver. + + + BITS_PER_PIXEL + + $(P)$(R)BitsPerPixel_RBV
+ + longin + ADTemperature