-
Notifications
You must be signed in to change notification settings - Fork 70
/
Copy pathNDPluginROI.cpp
424 lines (382 loc) · 18.8 KB
/
NDPluginROI.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/*
* NDPluginROI.cpp
*
* Region-of-Interest (ROI) plugin
* Author: Mark Rivers
*
* Created April 23, 2008
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <epicsTypes.h>
#include <epicsMessageQueue.h>
#include <epicsThread.h>
#include <epicsEvent.h>
#include <epicsTime.h>
#include <iocsh.h>
#include <asynDriver.h>
#include <epicsExport.h>
#include "NDPluginDriver.h"
#include "NDPluginROI.h"
//static const char *driverName="NDPluginROI";
#define MAX(A,B) (A)>(B)?(A):(B)
#define MIN(A,B) (A)<(B)?(A):(B)
static const char *driverName="NDPluginROI";
/** Callback function that is called by the NDArray driver with new NDArray data.
* Extracts the NthrDArray data into each of the ROIs that are being used.
* Computes statistics on the ROI if NDPluginROIComputeStatistics is 1.
* Computes the histogram of ROI values if NDPluginROIComputeHistogram is 1.
* \param[in] pArray The NDArray from the callback.
*/
void NDPluginROI::processCallbacks(NDArray *pArray)
{
/* This function computes the ROIs.
* It is called with the mutex already locked. It unlocks it during long calculations when private
* structures don't need to be protected.
*/
int dataType;
int dim;
NDDimension_t dims[ND_ARRAY_MAX_DIMS], tempDim, *pDim;
size_t userDims[ND_ARRAY_MAX_DIMS];
NDArrayInfo arrayInfo, scratchInfo;
NDArray *pScratch, *pOutput;
NDColorMode_t colorMode;
double *pData;
int enableScale, enableDim[3], autoSize[3];
size_t i;
double scale;
int collapseDims;
//static const char* functionName = "processCallbacks";
memset(dims, 0, sizeof(NDDimension_t) * ND_ARRAY_MAX_DIMS);
/* Get all parameters while we have the mutex */
getIntegerParam(NDPluginROIDim0Bin, &dims[0].binning);
getIntegerParam(NDPluginROIDim1Bin, &dims[1].binning);
getIntegerParam(NDPluginROIDim2Bin, &dims[2].binning);
getIntegerParam(NDPluginROIDim0Reverse, &dims[0].reverse);
getIntegerParam(NDPluginROIDim1Reverse, &dims[1].reverse);
getIntegerParam(NDPluginROIDim2Reverse, &dims[2].reverse);
getIntegerParam(NDPluginROIDim0Enable, &enableDim[0]);
getIntegerParam(NDPluginROIDim1Enable, &enableDim[1]);
getIntegerParam(NDPluginROIDim2Enable, &enableDim[2]);
getIntegerParam(NDPluginROIDim0AutoSize, &autoSize[0]);
getIntegerParam(NDPluginROIDim1AutoSize, &autoSize[1]);
getIntegerParam(NDPluginROIDim2AutoSize, &autoSize[2]);
getIntegerParam(NDPluginROIDataType, &dataType);
getIntegerParam(NDPluginROIEnableScale, &enableScale);
getDoubleParam(NDPluginROIScale, &scale);
getIntegerParam(NDPluginROICollapseDims, &collapseDims);
/* Call the base class method */
NDPluginDriver::beginProcessCallbacks(pArray);
/* Get information about the array */
pArray->getInfo(&arrayInfo);
userDims[0] = arrayInfo.xDim;
userDims[1] = arrayInfo.yDim;
userDims[2] = arrayInfo.colorDim;
/* Make sure dimensions are valid, fix them if they are not */
for (dim=0; dim<pArray->ndims; dim++) {
pDim = &dims[dim];
if (enableDim[dim]) {
size_t newDimSize = pArray->dims[userDims[dim]].size;
pDim->offset = requestedOffset_[dim];
pDim->size = requestedSize_[dim];
pDim->offset = MAX(pDim->offset, 0);
pDim->offset = MIN(pDim->offset, newDimSize-1);
if (autoSize[dim]) pDim->size = newDimSize;
pDim->size = MAX(pDim->size, 1);
pDim->size = MIN(pDim->size, newDimSize - pDim->offset);
pDim->binning = MAX(pDim->binning, 1);
pDim->binning = MIN(pDim->binning, (int)pDim->size);
} else {
pDim->offset = 0;
pDim->size = pArray->dims[userDims[dim]].size;
pDim->binning = 1;
}
}
/* Update the parameters that may have changed */
setIntegerParam(NDPluginROIDim0MaxSize, 0);
setIntegerParam(NDPluginROIDim1MaxSize, 0);
setIntegerParam(NDPluginROIDim2MaxSize, 0);
if (pArray->ndims > 0) {
pDim = &dims[0];
setIntegerParam(NDPluginROIDim0MaxSize, (int)pArray->dims[userDims[0]].size);
if (enableDim[0]) {
setIntegerParam(NDPluginROIDim0Min, (int)pDim->offset);
setIntegerParam(NDPluginROIDim0Size, (int)pDim->size);
setIntegerParam(NDPluginROIDim0Bin, pDim->binning);
}
}
if (pArray->ndims > 1) {
pDim = &dims[1];
setIntegerParam(NDPluginROIDim1MaxSize, (int)pArray->dims[userDims[1]].size);
if (enableDim[1]) {
setIntegerParam(NDPluginROIDim1Min, (int)pDim->offset);
setIntegerParam(NDPluginROIDim1Size, (int)pDim->size);
setIntegerParam(NDPluginROIDim1Bin, pDim->binning);
}
}
if (pArray->ndims > 2) {
pDim = &dims[2];
setIntegerParam(NDPluginROIDim2MaxSize, (int)pArray->dims[userDims[2]].size);
if (enableDim[2]) {
setIntegerParam(NDPluginROIDim2Min, (int)pDim->offset);
setIntegerParam(NDPluginROIDim2Size, (int)pDim->size);
setIntegerParam(NDPluginROIDim2Bin, pDim->binning);
}
}
/* This function is called with the lock taken, and it must be set when we exit.
* The following code can be exected without the mutex because we are not accessing memory
* that other threads can access. */
this->unlock();
/* 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;
}
/* 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. */
if (arrayInfo.colorMode == NDColorModeRGB1) {
tempDim = dims[0];
dims[0] = dims[2];
dims[2] = dims[1];
dims[1] = tempDim;
}
else if (arrayInfo.colorMode == NDColorModeRGB2) {
tempDim = dims[1];
dims[1] = dims[2];
dims[2] = tempDim;
}
if (enableScale && (scale != 0) && (scale != 1)) {
/* This is tricky. We want to do the operation to avoid errors due to integer truncation.
* For example, if an image with all pixels=1 is binned 3x3 with scale=9 (divide by 9), then
* the output should also have all pixels=1.
* We do this by extracting the ROI and converting to double, do the scaling, then convert
* to the desired data type. */
this->pNDArrayPool->convert(pArray, &pScratch, NDFloat64, dims);
pScratch->getInfo(&scratchInfo);
pData = (double *)pScratch->pData;
for (i=0; i<scratchInfo.nElements; i++) pData[i] = pData[i]/scale;
this->pNDArrayPool->convert(pScratch, &pOutput, (NDDataType_t)dataType);
pScratch->release();
}
else {
this->pNDArrayPool->convert(pArray, &pOutput, (NDDataType_t)dataType, dims);
}
/* If we selected just one color from the array, then we need to collapse the
* dimensions and set the color mode to mono */
colorMode = NDColorModeMono;
if ((pOutput->ndims == 3) &&
(arrayInfo.colorMode == NDColorModeRGB1) &&
(pOutput->dims[0].size == 1))
{
collapseDims = 1;
pOutput->pAttributeList->add("ColorMode", "Color mode", NDAttrInt32, &colorMode);
}
else if ((pOutput->ndims == 3) &&
(arrayInfo.colorMode == NDColorModeRGB2) &&
(pOutput->dims[1].size == 1))
{
collapseDims = 1;
pOutput->pAttributeList->add("ColorMode", "Color mode", NDAttrInt32, &colorMode);
}
else if ((pOutput->ndims == 3) &&
(arrayInfo.colorMode == NDColorModeRGB3) &&
(pOutput->dims[2].size == 1))
{
collapseDims = 1;
pOutput->pAttributeList->add("ColorMode", "Color mode", NDAttrInt32, &colorMode);
}
/* If collapseDims is set then collapse any dimensions of size 1 */
if (collapseDims) {
int i=0, j;
while ((i < pOutput->ndims) && (pOutput->ndims > 1)) {
if (pOutput->dims[i].size == 1) {
for (j=i+1; j<pOutput->ndims; j++) {
pOutput->dims[j-1] = pOutput->dims[j];
}
if (pOutput->ndims > 1) pOutput->ndims--;
} else {
i++;
}
}
}
this->lock();
/* Calculate ROI bitsPerElement */
double bitsPerPixel = arrayInfo.bitsPerElement;
size_t binFactor = 1;
for ( size_t iDim=0; iDim < static_cast<unsigned>(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);
setIntegerParam(NDArraySizeY, 0);
setIntegerParam(NDArraySizeZ, 0);
if (pOutput->ndims > 0) setIntegerParam(NDArraySizeX, (int)pOutput->dims[userDims[0]].size);
if (pOutput->ndims > 1) setIntegerParam(NDArraySizeY, (int)pOutput->dims[userDims[1]].size);
if (pOutput->ndims > 2) setIntegerParam(NDArraySizeZ, (int)pOutput->dims[userDims[2]].size);
NDPluginDriver::endProcessCallbacks(pOutput, false, true);
callParamCallbacks();
}
/** Called when asyn clients call pasynInt32->write().
* This function performs actions for some parameters, including NDPluginDriverEnableCallbacks and
* NDPluginDriverArrayAddr.
* For all parameters it sets the value in the parameter library and calls any registered callbacks..
* \param[in] pasynUser pasynUser structure that encodes the reason and address.
* \param[in] value Value to write. */
asynStatus NDPluginROI::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
int function = pasynUser->reason;
asynStatus status = asynSuccess;
static const char* functionName = "writeInt32";
/* Set the parameter in the parameter library. */
status = (asynStatus) setIntegerParam(function, value);
if (function == NDPluginROIDim0Min) {
requestedOffset_[0] = value;
} else if (function == NDPluginROIDim1Min) {
requestedOffset_[1] = value;
} else if (function == NDPluginROIDim2Min) {
requestedOffset_[2] = value;
} else if (function == NDPluginROIDim0Size) {
requestedSize_[0] = value;
} else if (function == NDPluginROIDim1Size) {
requestedSize_[1] = value;
} else if (function == NDPluginROIDim2Size) {
requestedSize_[2] = value;
} else {
/* If this parameter belongs to a base class call its method */
if (function < FIRST_NDPLUGIN_ROI_PARAM)
status = NDPluginDriver::writeInt32(pasynUser, value);
}
/* Do callbacks so higher layers see any changes */
callParamCallbacks();
if (status)
asynPrint(pasynUser, ASYN_TRACE_ERROR,
"%s:%s: function=%d, value=%d\n",
driverName, functionName, function, value);
else
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
"%s:%s: function=%d, value=%d\n",
driverName, functionName, function, value);
return status;
}
/** Constructor for NDPluginROI; most parameters are simply passed to NDPluginDriver::NDPluginDriver.
* After calling the base class constructor this method sets reasonable default values for all of the
* ROI parameters.
* \param[in] portName The name of the asyn port driver to be created.
* \param[in] queueSize The number of NDArrays that the input queue for this plugin can hold when
* NDPluginDriverBlockingCallbacks=0. Larger queues can decrease the number of dropped arrays,
* at the expense of more NDArray buffers being allocated from the underlying driver's NDArrayPool.
* \param[in] blockingCallbacks Initial setting for the NDPluginDriverBlockingCallbacks flag.
* 0=callbacks are queued and executed by the callback thread; 1 callbacks execute in the thread
* of the driver doing the callbacks.
* \param[in] NDArrayPort Name of asyn port driver for initial source of NDArray callbacks.
* \param[in] NDArrayAddr asyn port driver address for initial source of NDArray callbacks.
* \param[in] maxBuffers The maximum number of NDArray buffers that the NDArrayPool for this driver is
* allowed to allocate. Set this to 0 to allow an unlimited number of buffers.
* \param[in] maxMemory The maximum amount of memory that the NDArrayPool for this driver is
* allowed to allocate. Set this to 0 to allow an unlimited amount of memory.
* \param[in] priority The thread priority for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags.
* \param[in] stackSize The stack size for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags.
* \param[in] maxThreads The maximum number of threads this driver is allowed to use. If 0 then 1 will be used.
*/
NDPluginROI::NDPluginROI(const char *portName, int queueSize, int blockingCallbacks,
const char *NDArrayPort, int NDArrayAddr,
int maxBuffers, size_t maxMemory,
int priority, int stackSize, int maxThreads)
/* Invoke the base class constructor */
: NDPluginDriver(portName, queueSize, blockingCallbacks,
NDArrayPort, NDArrayAddr, 1, maxBuffers, maxMemory,
asynInt32ArrayMask | asynFloat64ArrayMask | asynGenericPointerMask,
asynInt32ArrayMask | asynFloat64ArrayMask | asynGenericPointerMask,
ASYN_MULTIDEVICE, 1, priority, stackSize, maxThreads)
{
//static const char *functionName = "NDPluginROI";
/* ROI general parameters */
createParam(NDPluginROINameString, asynParamOctet, &NDPluginROIName);
/* ROI definition */
createParam(NDPluginROIDim0MinString, asynParamInt32, &NDPluginROIDim0Min);
createParam(NDPluginROIDim1MinString, asynParamInt32, &NDPluginROIDim1Min);
createParam(NDPluginROIDim2MinString, asynParamInt32, &NDPluginROIDim2Min);
createParam(NDPluginROIDim0SizeString, asynParamInt32, &NDPluginROIDim0Size);
createParam(NDPluginROIDim1SizeString, asynParamInt32, &NDPluginROIDim1Size);
createParam(NDPluginROIDim2SizeString, asynParamInt32, &NDPluginROIDim2Size);
createParam(NDPluginROIDim0MaxSizeString, asynParamInt32, &NDPluginROIDim0MaxSize);
createParam(NDPluginROIDim1MaxSizeString, asynParamInt32, &NDPluginROIDim1MaxSize);
createParam(NDPluginROIDim2MaxSizeString, asynParamInt32, &NDPluginROIDim2MaxSize);
createParam(NDPluginROIDim0BinString, asynParamInt32, &NDPluginROIDim0Bin);
createParam(NDPluginROIDim1BinString, asynParamInt32, &NDPluginROIDim1Bin);
createParam(NDPluginROIDim2BinString, asynParamInt32, &NDPluginROIDim2Bin);
createParam(NDPluginROIDim0ReverseString, asynParamInt32, &NDPluginROIDim0Reverse);
createParam(NDPluginROIDim1ReverseString, asynParamInt32, &NDPluginROIDim1Reverse);
createParam(NDPluginROIDim2ReverseString, asynParamInt32, &NDPluginROIDim2Reverse);
createParam(NDPluginROIDim0EnableString, asynParamInt32, &NDPluginROIDim0Enable);
createParam(NDPluginROIDim1EnableString, asynParamInt32, &NDPluginROIDim1Enable);
createParam(NDPluginROIDim2EnableString, asynParamInt32, &NDPluginROIDim2Enable);
createParam(NDPluginROIDim0AutoSizeString, asynParamInt32, &NDPluginROIDim0AutoSize);
createParam(NDPluginROIDim1AutoSizeString, asynParamInt32, &NDPluginROIDim1AutoSize);
createParam(NDPluginROIDim2AutoSizeString, asynParamInt32, &NDPluginROIDim2AutoSize);
createParam(NDPluginROIDataTypeString, asynParamInt32, &NDPluginROIDataType);
createParam(NDPluginROIEnableScaleString, asynParamInt32, &NDPluginROIEnableScale);
createParam(NDPluginROIScaleString, asynParamFloat64, &NDPluginROIScale);
createParam(NDPluginROICollapseDimsString, asynParamInt32, &NDPluginROICollapseDims);
/* Set the plugin type string */
setStringParam(NDPluginDriverPluginType, "NDPluginROI");
/* Try to connect to the array port */
connectToArrayPort();
}
/** Configuration command */
extern "C" int NDROIConfigure(const char *portName, int queueSize, int blockingCallbacks,
const char *NDArrayPort, int NDArrayAddr,
int maxBuffers, size_t maxMemory,
int priority, int stackSize, int maxThreads)
{
NDPluginROI *pPlugin = new NDPluginROI(portName, queueSize, blockingCallbacks, NDArrayPort, NDArrayAddr,
maxBuffers, maxMemory, priority, stackSize, maxThreads);
return pPlugin->start();
}
/* EPICS iocsh shell commands */
static const iocshArg initArg0 = { "portName",iocshArgString};
static const iocshArg initArg1 = { "frame queue size",iocshArgInt};
static const iocshArg initArg2 = { "blocking callbacks",iocshArgInt};
static const iocshArg initArg3 = { "NDArrayPort",iocshArgString};
static const iocshArg initArg4 = { "NDArrayAddr",iocshArgInt};
static const iocshArg initArg5 = { "maxBuffers",iocshArgInt};
static const iocshArg initArg6 = { "maxMemory",iocshArgInt};
static const iocshArg initArg7 = { "priority",iocshArgInt};
static const iocshArg initArg8 = { "stackSize",iocshArgInt};
static const iocshArg initArg9 = { "maxThreads",iocshArgInt};
static const iocshArg * const initArgs[] = {&initArg0,
&initArg1,
&initArg2,
&initArg3,
&initArg4,
&initArg5,
&initArg6,
&initArg7,
&initArg8,
&initArg9};
static const iocshFuncDef initFuncDef = {"NDROIConfigure",10,initArgs};
static void initCallFunc(const iocshArgBuf *args)
{
NDROIConfigure(args[0].sval, args[1].ival, args[2].ival,
args[3].sval, args[4].ival, args[5].ival,
args[6].ival, args[7].ival, args[8].ival,
args[9].ival);
}
extern "C" void NDROIRegister(void)
{
iocshRegister(&initFuncDef,initCallFunc);
}
extern "C" {
epicsExportRegistrar(NDROIRegister);
}