/*************************************************************************** multiops.c - perform multiple operations on a video signal This program will perform several functions on a video signal. They will be: unmodified, 3x3 convolution for horizontal edges, 3x3 convolution for vertical edges, recursive frame averaging filter. The results of these four operations will be displayed simultaneously one a 1kx1k monitor. The program demonstrates the use of Pat's to perform fast pipe modifications. ***************************************************************************/ #include #include /* this must use a monitor capable of displaying a 1kx1k image */ #define DISPLAYXSIZE 1024L #define DISPLAYYSIZE 1024L /* convolution pattern */ #define KERNELXSIZE 3 #define KERNELYSIZE 3 /* identify the devices used in this program */ dqLimitIPDevSet(AB AM AS AG AP AU); /* the size of the video frames that are being processed */ DqRect AcqRect = {0,0,511,483}; main() { DqSystem oSystem; /* the system */ DqIPDev oAb00, oAg00, oAs00, oAp00, oAu00; /* the devices */ DqIPDev oAm00, oAm01, oAm02, oAm03; /* the memory surfaces */ DqSurf AcqSrcSurf, AcqDstSurfs[3]; DqSurf ConvSrcSurf, ConvDstSurf; DqSurf HKernel,VKernel; DqSurf AverageSrcSurf,LastFrameSrc,AverageDstSurfs[3]; DqSurf DispSrcSurf, DispDstSurf; /* the pipes and their events */ DqPipe DispPipe, AcqPipe, ConvPipe, AveragePipe; int AcqEvent,ConvEvent; DqByte HCoefs[3][3]; /* horizontal edge kernel coefficients */ DqByte VCoefs[3][3]; /* vertical kernel coefficients */ DqRect tConvRect; /* used to specify the size of the kernal */ /* the PAT's and their events */ int NormalPat,VertPat,HorizPat,AveragePat; int NormalEvent,VertEvent,HorizEvent,AverageEvent,AveragePatEvent; /* the frame averaging filter coefficients */ float NewK; int K,OneMinusK; char UserInpBuf[80]; /* initialize the system and get handles for the devices */ dqInitEnv(); oSystem = dqCreateStdSys(); oAb00 = dqFindIPDev(oSystem, "ab00"); oAs00 = dqFindIPDev(oSystem, "as00"); oAg00 = dqFindIPDev(oSystem, "ag00"); oAp00 = dqFindIPDev(oSystem, "ap00"); oAu00 = dqFindIPDev(oSystem, "au00"); oAm00 = dqFindIPDev(oSystem, "ab00:am00"); oAm01 = dqFindIPDev(oSystem, "ab00:am01"); oAm02 = dqFindIPDev(oSystem, "ab00:am02"); oAm03 = dqFindIPDev(oSystem, "ab00:am03"); /* set the data type checking to tolerant */ dqSetDTPDogma(DQ_DTP_TOLERANT); /************************* ACQUISITION PIPE **************************/ AcqSrcSurf = dqCreateStdSizeSurf(oAs00, AS_ADC); dqAttachSurf(AcqSrcSurf, AS_XMT); /* take signed data from the AS device for the convolution */ dqSetDTT(oAs00, AS_DTM, DQ_DTT_X_SIGNED); /* use DQ_CSR for signed output from the AS device */ dqConnect(oAb00, DQ_CSR, AB_OP00); amSetRcvGateway20MHz(oAm00); AcqDstSurfs[0] = dqCreateSameSizeSurf(oAm00, AM_MEM_R8,AcqSrcSurf); dqAttachSurf(AcqDstSurfs[0],AM_RCV); /* use DQ_CSG for unsigned AS data for the frame averaging */ dqConnect(oAb00,DQ_CSG,AB_OP02); amSetRcvGateway20MHz(oAm02); AcqDstSurfs[1] = dqCreateSameSizeSurf(oAm02, AM_MEM_R8,AcqSrcSurf); dqAttachSurf(AcqDstSurfs[1],AM_RCV); /* create a continuously running multi-destination pipe, arm and fire it off */ AcqDstSurfs[2] = 0; /* terminate multiple surface list */ AcqPipe = dqCreateMultiDstPipe(AcqDstSurfs, DQ_TRG_CONTINUOUS); dqArmPipe(AcqPipe, DQ_DSM_PIPE); dqFirePipe(AcqPipe); AcqEvent = emFindPipeEvent(AcqPipe); /*********************** DISPLAY PIPE ********************************/ /* This pipe will be 1024x1024. It will not work on an NTSC monitor!! */ DispSrcSurf = dqCreateSurf(oAm01, AM_MEM_R8,DISPLAYXSIZE,DISPLAYYSIZE); gsClearView(DispSrcSurf,0); /* clear the surface */ dqAttachSurf(DispSrcSurf,AM_XMT); amSetDispGateway40MHz(oAm01); /* Because this is a higher resolution monitor the dummy expansion and shrinkage factors are different */ dqSpecXmtExpansion(oAm01,AM_XMT,1,1); dqConnect(oAg00, AG_GREEN, AG_DAC_LUT_SRC); dqConnect(oAg00, AG_DAC_LUT, AG_DAC_SRC); DispDstSurf = dqCreateSurf(oAg00, AG_DAC,DISPLAYXSIZE,DISPLAYYSIZE); dqAttachSurfGate(DispDstSurf, AG_RCV); dqSpecRcvShrinkage(oAg00,AG_RCV,1,1); DispPipe = dqCreatePipe(DispDstSurf, DQ_TRG_CONTINUOUS); dqArmPipe(DispPipe, DQ_DSM_PIPE); dqFirePipe(DispPipe); /*********************** PROCESSING PIPES *****************************/ /******** CONVOLVED IMAGE *********/ /* start with the signed data from the acquisition pipe */ ConvSrcSurf = dqDupSurf(AcqDstSurfs[0]); dqAttachSurf(ConvSrcSurf, AM_XMT); /* because this is running into the cross point for processing the memory must output at 20 MHz. */ amSetXmtGateway20MHz(oAm00); dqConnect(oAb00, DQ_IMX0, AB_OP21); /* across to the NMAC input */ /* set the configuration for the NMAC element to 8x8 convolution */ dqConnect(oAp00, AP_SHIFT8, AP_NDLY_SRC); /* the host will write directly into the surface with the coeffient values. This rectangle is defined with respect to center point. The default center point is the center pixel of the kernel. */ tConvRect.lXMin = -(KERNELXSIZE / 2); tConvRect.lXMax = KERNELXSIZE % 2 ? KERNELXSIZE / 2 : KERNELXSIZE / 2 - 1; tConvRect.lYMin = -(KERNELYSIZE / 2); tConvRect.lYMax = KERNELYSIZE % 2 ? KERNELYSIZE / 2 : KERNELYSIZE / 2 - 1; /* We can define multiple kernels in the 8x8 convolution surface. They are defined as surfaces in the NMAC element. Only one of them can be attached, ie. in use, at a time though. */ /******* HORIZONTAL EDGE KERNEL ***********/ /* create a surface to define the horizontal kernel in the NMAC */ HKernel = dqCreateSurf(oAp00, AP_NMAC8, KERNELXSIZE, KERNELYSIZE); dqSetSurfBaseDT(HKernel, DQ_DT_SIGNED); /* everything will be signed */ dqAttachSurf(HKernel, AP_NMAC8); /* you have to attach to this also */ /* this defines a 3x3 horizontal line filter */ HCoefs[0][0] = 64; HCoefs[0][1] = 64; HCoefs[0][2] = 64; HCoefs[1][0] = 0; HCoefs[1][1] = 0; HCoefs[1][2] = 0; HCoefs[2][0] = -64; HCoefs[2][1] = -64; HCoefs[2][2] = -64; dqWtRect(HKernel, &tConvRect, HCoefs); /******* VERTICAL EDGE KERNEL ***********/ /* create a surface to define the vertical kernel in the NMAC */ VKernel = dqCreateSurf(oAp00, AP_NMAC8, KERNELXSIZE, KERNELYSIZE); dqSetSurfBaseDT(VKernel, DQ_DT_SIGNED); /* everything will be signed */ dqAttachSurf(VKernel, AP_NMAC8); /* you have to attach to this also */ /* this defines a 3x3 horizontal line filter */ VCoefs[0][0] = 64; VCoefs[0][1] = 0; VCoefs[0][2] = -64; VCoefs[1][0] = 64; VCoefs[1][1] = 0; VCoefs[1][2] = -64; VCoefs[2][0] = 64; VCoefs[2][1] = 0; VCoefs[2][2] = -64; dqWtRect(VKernel, &tConvRect, VCoefs); /* define constants that feed the AP_ADD B operand */ dqSetKVal(oAb00,AB_OP17,0L); dqSetKVal(oAb00,AB_OP18,0L); dqSetKVal(oAb00,AB_OP19,0L); dqSetOpndBaseDT(oAp00,AP_ADD,DQ_OPND_A,DQ_DT_SIGNED); /* shift so that the 8-bit result comes out at DQ_CP15. This shifting must also be accounted for when setting the NMAC coefficients */ dqSpecShift(oAp00, AP_SHIFT8, DQ_SHIFT_ARITHMETIC, 17); dqSpecShift(oAp00, AP_SHIFT, DQ_SHIFT_ARITHMETIC, 7); /* now set the output format to account for the convolution. Horizontal edges are convolved toward -127 and 127. Use the absolute value output so that both edges are driven toward 127. This also shifts the values up one place. */ dqSetOpndBaseDT(oAp00,AP_FORMAT,DQ_OPND_A,DQ_DT_SIGNED); dqSetFormatOp(oAp00,AP_FORMAT,AP_FMT_ABSOLUTE); amSetRcvGateway20MHz(oAm01); /* deposit image in a duplicate of the display source surface */ ConvDstSurf = dqDupSurf(DispSrcSurf); gsClearView(ConvDstSurf,0); /* clear the screen */ /* the rest of the convolution pipe definition is in the Pat's */ /********* AVERAGING PIPE *********************/ /* start with the unsigned data from the acquisition pipe */ AverageSrcSurf = dqDupSurf(AcqDstSurfs[1]); dqAttachSurf(AverageSrcSurf, AM_XMT); amSetXmtGateway20MHz(oAm02); dqSetSurfBaseDT(AverageSrcSurf,DQ_DT_UNSIGNED); /* across the cross point to the time filter */ dqConnect(oAb00,DQ_IMX2,AB_OP08); /* current frame into Moe */ dqSetKVal(oAb00,AB_OP09,0); dqConnect(oAu00,AU_I_SEP_MOE,AU_I_MOE); dqConnect(oAu00,AU_I_MOE_X0,AU_G_OP8); /* create K * x(n) K set in AU_L_K1 */ dqConnect(oAu00,AU_L_EXT0,AU_L_MULT1_BOP); dqSetKBaseDT(oAu00,AU_L_K1,DQ_DT_SIGNED); dqConnect(oAu00,AU_L_K1,AU_L_MULT1_AOP); /* create surface to save the last frame */ LastFrameSrc = dqCreateSameSizeSurf(oAm03,AM_MEM_R8,AverageSrcSurf); dqSetSurfBaseDT(LastFrameSrc,DQ_DT_UNSIGNED); dqAttachSurf(LastFrameSrc,AM_XMT); amSetXmtGateway20MHz(oAm03); /* bring last frame across the cross point to the time filter */ dqConnect(oAb00,DQ_IMX3,AB_OP10); /* last frame into Larry */ dqSetKVal(oAb00,AB_OP11,0); dqConnect(oAu00,AU_I_SEP_LARRY,AU_I_LARRY); dqConnect(oAu00,AU_I_LARRY_X0,AU_G_OP9); /* create (1 - K) * y(n - 1) (1 - K) set in AU_L_K0 */ dqConnect(oAu00,AU_L_EXT1,AU_L_MULT0_AOP); dqSetKBaseDT(oAu00,AU_L_K0,DQ_DT_SIGNED); dqConnect(oAu00,AU_L_K0,AU_L_MULT0_BOP); /* zero down the AU_L_MULT2 and AU_L_MULT3 chains */ dqSetKVal(oAu00,AU_L_K2,0x00); dqConnect(oAu00,AU_L_K2,AU_L_MULT2_AOP); dqConnect(oAu00,AU_L_K2,AU_L_MULT2_BOP); dqSetKVal(oAu00,AU_L_K3,0x00); dqConnect(oAu00,AU_L_K3,AU_L_MULT3_AOP); dqConnect(oAu00,AU_L_K3,AU_L_MULT3_BOP); /* Combine to get K*x(n) + (1 - K)*y(n - 1) Calculations are performed with binary point at bit 7-8 This shift moves binary point to bit 0-1 when leaving AU_L_SHIFT3 */ dqSpecShift(oAu00,AU_L_SHIFT3,DQ_SHIFT_ARITHMETIC,-7); /* Then effect rounding by adding 1 before AU_L_ADD3 tosses the LSB */ dqSetKVal(oAu00,AU_L_K4,1); dqConnect(oAu00,AU_L_K4,AU_L_ADD3_BOP); /* now straight shot to AU_L_RESULT */ dqConnect(oAu00,AU_L_CLIP,AU_L_P_RES); dqConnect(oAu00,AU_L_P_RES,AU_L_RESULT); /* pass result back through the AU cross point */ dqConnect(oAu00,AU_L_RESULT_X0,AU_G_OP1); dqConnect(oAu00,AU_O_CLIP1,AU_O_C7_SRC); /* across cross point to the last frame surface */ dqConnect(oAb00,DQ_CU07,AB_OP03); /* create surface to save this frame for next time */ AverageDstSurfs[0] = dqDupSurf(LastFrameSrc); dqAttachSurf(AverageDstSurfs[0],AM_RCV); amSetRcvGateway20MHz(oAm03); /* Create surface for the display output. This is a second duplicate of the display source surface. It will be used for writting the unmodified and averaged images. */ AverageDstSurfs[1] = dqDupSurf(DispSrcSurf); dqAttachSurf(AverageDstSurfs[1],AM_RCV); /* the receive gateway on oAM01 was set up as part of the convolution pipe. The rest of the averaging pipe is set up in the pat's */ /* terminate surface list and create pipe */ AverageDstSurfs[2] = 0; /* get the averaging constant to use */ do { printf("Enter K between 0.0 and 1.0: "); scanf("%f",&NewK); } while((NewK < 0) || (NewK > 1.0)); K = irint(256.0 * NewK); OneMinusK = irint(256.0 * (1.0 - NewK)); dqSetKVal(oAu00,AU_L_K1,K); dqSetKVal(oAu00,AU_L_K0,OneMinusK); /************** PAT DEFINITIONS ***************/ /***** HORIZONTAL EDGES ***********/ emBegPatDef(); dqAttachSurf(HKernel, AP_NMAC8); /* attach horizontal kernel */ dqConnect(oAb00, DQ_CP15, AB_OP01); /* connect AP output to display */ /* attach the duplicated surface that is the convolution pipe destination. */ dqAttachSurf(ConvDstSurf,AM_RCV); dqSpecSurfAlignPoint(ConvDstSurf,0,512); /* lower left corner */ dqSetSurfProcRect(ConvDstSurf,AcqRect); /* This pipe creation is done only once. It is not a deferred operation. The purpose within Imageflow is to create an identification for the pipe that can be used by other functions. */ ConvPipe = dqCreatePipe(ConvDstSurf, DQ_TRG_ONESHOT); /* For one-shot pipes we must get a pipe event after each arming */ dqArmPipe(ConvPipe,DQ_DSM_PIPE); ConvEvent = emFindPipeEvent(ConvPipe); dqFirePipe(ConvPipe); emWaitRefEvent(ConvEvent,1); /* wait for pipe operation to complete */ /* end of the pat definition and getting an event handle for it */ HorizPat = emEndPatDef(); HorizEvent = emFindPatEvent(HorizPat); /***** VERTICAL EDGES *************/ emBegPatDef(); dqAttachSurf(VKernel,AP_NMAC8); /* vertical edge kernel */ dqSpecSurfAlignPoint(ConvDstSurf,512,512); /* lower right corner */ dqSetSurfProcRect(ConvDstSurf,AcqRect); dqArmPipe(ConvPipe, DQ_DSM_RECT); ConvEvent = emFindPipeEvent(ConvPipe); dqFirePipe(ConvPipe); emWaitRefEvent(ConvEvent,1); VertPat = emEndPatDef(); VertEvent = emFindPatEvent(VertPat); /***** AVERAGED IMAGE **************/ emBegPatDef(); /* connect the output of the AU which is the averaged frame */ dqConnect(oAb00,DQ_CU07,AB_OP01); /* attach the duplicated surface that is the destination for the frame averaging pipe */ dqAttachSurf(AverageDstSurfs[1],AM_RCV); dqSpecSurfAlignPoint(AverageDstSurfs[1],512,0); dqSetSurfProcRect(AverageDstSurfs[1],AcqRect); /* again this is not a defferd operation. It creates an identification for the pipe. */ AveragePipe = dqCreateMultiDstPipe(AverageDstSurfs,DQ_TRG_ONESHOT); dqArmPipe(AveragePipe,DQ_DSM_PIPE); AverageEvent = emFindPipeEvent(AveragePipe); dqFirePipe(AveragePipe); emWaitRefEvent(AverageEvent,1); AveragePat = emEndPatDef(); AveragePatEvent = emFindPatEvent(AveragePat); /***** NORMAL IMAGE ****************/ emBegPatDef(); dqConnect(oAb00,DQ_IMX2,AB_OP01); /* normal video to proc dest */ dqSpecSurfAlignPoint(AverageDstSurfs[1],0,0); dqSetSurfProcRect(AverageDstSurfs[1],AcqRect); dqArmPipe(AveragePipe,DQ_DSM_PIPE); AverageEvent = emFindPipeEvent(AveragePipe); dqFirePipe(AveragePipe); emWaitRefEvent(AverageEvent,1); NormalPat = emEndPatDef(); NormalEvent = emFindPatEvent(NormalPat); /********* END OF PAT DEFINITIONS ******************/ /* create the loop of pat firings */ emCyclePatOnEvent(HorizPat,NormalEvent); emCyclePatOnEvent(VertPat,HorizEvent); emCyclePatOnEvent(AveragePat,VertEvent); emCyclePatOnEvent(NormalPat,AveragePatEvent); /* simulate one of them to get things rolling */ emSimulateEvent(NormalEvent); gets(UserInpBuf); /* clear a \n from previous scanf */ printf("Hit any key to exit.\n"); /* wait for user to signal exit */ fgets(UserInpBuf,80,stdin); /* idle all pat's before trying to dispose the system. If you do not take this step the program will usually hang on exit waiting for one of these to fire. */ emIdlePat(NormalPat); emIdlePat(AveragePat); emIdlePat(VertPat); emIdlePat(HorizPat); dqDisposeSys(oSystem); /* clean before exiting */ } /* auxiliary functions to set up the AM gateways to the correct speed */ amSetXmtGateway20MHz(oAmDev) DqIPDev oAmDev; { amSetGateSysClkMult(oAmDev,AM_XMT,2); dqConnect(oAmDev,AM_XMT_OUT,AM_OP3); dqSpecLogic(oAmDev,AM_LOGIC3,0xffffffff,0); dqConnect(oAmDev,AM_SLDN0_T0,AM_OUTPUT0); dqConnect(oAmDev,AM_XMT,AM_XMT_OUT); } amSetRcvGateway20MHz(oAmDev) DqIPDev oAmDev; { dqConnect(oAmDev,AM_SPUP0,AM_INPUT0); dqConnect(oAmDev,AM_INPUT0,AM_OP0); dqSpecLogic(oAmDev,AM_LOGIC4,0,1); dqSpecLogic(oAmDev,AM_LOGIC0,0xffffffff,0); amSetGateSysClkMult(oAmDev,AM_RCV,2); } amSetDispGateway40MHz(oAmDev) DqIPDev oAmDev; { amSetGateSysClkMult(oAmDev,AM_XMT,4); dqConnect(oAmDev,AM_XMT_OUT,AM_OP3); dqSpecLogic(oAmDev,AM_LOGIC3,0xff,0); dqConnect(oAmDev,AM_LOGIC3,AM_OUTPUT0); dqConnect(oAmDev,AM_XMT,AM_XMT_OUT); }