//called from within ProcessDepthFrameData iff the frame is the nth frame of n = frameAcceptance //calculates movement, checks for events from arduino, and sends email updates private void calculateMovement(int[] depth, int fwidth, int fheight) { #region GMVCalculation //breaks the area of interest into a set number of quadrands //calculates the leftmost, rightmost, topmost, bottommost, closest, and furthest pixels associated with the viewable object //adds the magnitude of these values together for each quadrant to create the GMV //This section can be easily adapted for more complex behavioral measurements int numberofQuadrants = quadrantDiv * quadrantDiv; int quadHeight = (quadMarginYB - quadMarginYT) / quadrantDiv; int quadWidth = (quadMarginXR - quadMarginXL) / quadrantDiv; int[] iLeftPos = new int[numberofQuadrants]; int[] iRightPos = new int[numberofQuadrants]; int[] iTopPos = new int[numberofQuadrants]; int[] iBotPos = new int[numberofQuadrants]; int[] iDepthMin = new int[numberofQuadrants]; int[] iDepthMax = new int[numberofQuadrants]; int[] allMovementValue = new int[numberofQuadrants]; for (int quadY = 0; quadY < quadrantDiv; quadY++) { for (int quadX = 0; quadX < quadrantDiv; quadX++) { int yStart = (quadY * quadHeight + quadMarginYT); int yMax = (quadY * quadHeight + quadHeight + quadMarginYT); int quadDex = quadX + quadY * quadrantDiv; for (int iiy = yStart; iiy < yMax; iiy++) { int xStart = (quadX * quadWidth + quadMarginXL); int xMax = (quadX * quadWidth + quadWidth + quadMarginXL); for (int iii = xStart; iii < xMax; iii++) { int depthIndex = iii + iiy * fwidth; if (depth[depthIndex] != 0) //only use values that have a depth value (if above or below depth thresholds=> gets set to zero) { if (iii < iLeftPos[quadDex]) //if a value is "more left" than the current value then update the value { iLeftPos[quadDex] = iii; } if (iii > iRightPos[quadDex]) { iRightPos[quadDex] = iii; } if (iTopPos[quadDex] == 0) //can just grab first value since starting at the top right corner { iTopPos[quadDex] = iiy; } if (iiy > iBotPos[quadDex]) //if a value is "lower" than the current value then update the value { iBotPos[quadDex] = iiy; } if (depth[depthIndex] < iDepthMin[quadDex]) //if a value is "further forward" than the current value then update the value { iDepthMin[quadDex] = depth[depthIndex]; } if (depth[depthIndex] > iDepthMax[quadDex])//if a value is "further back" than the current value then update the value { iDepthMax[quadDex] = depth[depthIndex]; } } } } } } // initializer when no previous depth value exists if (iLeftPosOld == null) { iLeftPosOld = iLeftPos; iRightPosOld = iRightPos; iTopPosOld = iTopPos; iBotPosOld = iBotPos; iDepthMinOld = iDepthMin; iDepthMaxOld = iDepthMax; } //calculate the difference between the old and new frame values for each quadrant and add them together int iMovementValue = 0; for (int quadY = 0; quadY < quadrantDiv; quadY++) { for (int quadX = 0; quadX < quadrantDiv; quadX++) { int quadDex = quadX + quadY * quadrantDiv; int leftDiff = 0; int rightDiff = 0; int topDiff = 0; int botDiff = 0; int depthMinDiff = 0; int depthMaxDiff = 0; //dont want to subtract zero values from old or new since zero value means invalid calculation (above noise threshold) if (iLeftPos[quadDex] != 0 && iLeftPosOld[quadDex] != 0) { leftDiff = Math.Abs(iLeftPos[quadDex] - iLeftPosOld[quadDex]); } if (iRightPos[quadDex] != 0 && iRightPosOld[quadDex] != 0) { rightDiff = Math.Abs(iRightPos[quadDex] - iRightPosOld[quadDex]); } if (iTopPos[quadDex] != 0 && iTopPosOld[quadDex] != 0) { topDiff = Math.Abs(iTopPos[quadDex] - iTopPosOld[quadDex]); } if (iBotPos[quadDex] != 0 && iBotPosOld[quadDex] != 0) { botDiff = Math.Abs(iBotPos[quadDex] - iBotPosOld[quadDex]); } if (iDepthMin[quadDex] != 0 && iDepthMinOld[quadDex] != 0) { depthMinDiff = Math.Abs(iDepthMin[quadDex] - iDepthMinOld[quadDex]); } if (iDepthMax[quadDex] != 0 && iDepthMaxOld[quadDex] != 0) { depthMaxDiff = Math.Abs(iDepthMax[quadDex] - iDepthMaxOld[quadDex]); } if (leftDiff < noiseCutOff && rightDiff < noiseCutOff && topDiff < noiseCutOff && botDiff < noiseCutOff && depthMinDiff < noiseCutOff && depthMaxDiff < noiseCutOff) { allMovementValue[quadDex] = leftDiff + rightDiff + topDiff + depthMinDiff; } iMovementValue += allMovementValue[quadDex]; //calculate final movement value } } //display GMV on UI this.DepthDiff.Dispatcher.BeginInvoke(new Action(() => { DepthDiff.Text = string.Format("GMV: {0}", iMovementValue); })); //assign old values for next frame comparison iLeftPosOld = iLeftPos; iRightPosOld = iRightPos; iTopPosOld = iTopPos; iBotPosOld = iBotPos; iDepthMinOld = iDepthMin; iDepthMaxOld = iDepthMax; #endregion //update UI display with elapsed time information TimeSpan elapsed = DateTime.Now.Subtract(timeStart); this.TimeElapsed.Dispatcher.BeginInvoke(new Action(() => { int s = (int)elapsed.TotalSeconds; int ds = (int)(elapsed.TotalSeconds * 10 % 10); TimeElapsed.Text = "elapsed: " + s.ToString() + "." + ds.ToString() + "s"; })); //send flag to email handler to send email (will only send if enough time has passed since last email) double currentTimeElapsed = Math.Floor(elapsed.TotalSeconds); emailHandler.CheckEmailSend(currentTimeElapsed, counter); //save the movement data to file fileHandler.SaveMovementData(elapsed.TotalMilliseconds, allMovementValue); //check to see if there was a control hub trigger send to the portHandler, if so then increment the event counter and flip the saving video flag if (!savingVideo && saveVideoOnEvent) //only can occur if not already saving video { bool KinectFeederTrigger = portHandler.checkSerialInput(DateTime.Now.Subtract(timeStart).TotalSeconds); if (KinectFeederTrigger) // { counter++; fileHandler.SaveEventData(elapsed.TotalMilliseconds, "TIEvent"); this.TimeElapsed.Dispatcher.BeginInvoke(new Action(() => { Counter.Text = string.Format("{0} Events", counter); })); savingVideo = true; } } // This is also the place to modify the system to set up a behavior contingency on the feeder. //For example -> if GMV> threshold then could trigger the feeder, or could "turn on" the control hub such that other events can allow for the feeder to trigger // this could allow for experiments where movement and a neural event must both be above a threshold to train complex behaviors or movement/activity dissociation }