예제 #1
0
 public PointAnalysis(IppiPoint coordinate, IppiPoint_32f smoothenedCoordinate, float instantSpeed)
 {
     CompletedBout        = null;
     OriginalCoordinate   = coordinate;
     SmoothenedCoordinate = smoothenedCoordinate;
     InstantSpeed         = instantSpeed;
 }
예제 #2
0
        /// <summary>
        /// Based on the current segment length computes the
        /// scanpoint offsets for each angle used during tracking
        /// </summary>
        protected virtual void InitializeScanPoints()
        {
            //Compute the angle step. Ideally we want to scan every pixel
            //on a 360 degree circle around the current point with radius
            //SegmentLength - so set angle step and NAngles to roughly
            //give us this coverage

            bool Up = false;//true indicates both a vertical up and a horizontal left facing tail

            //TODO: Experimental support for horizontal and upward facing tails added - however, left/right might be wrong!
            if (!TailIsVertical())
            {
                System.Diagnostics.Debug.WriteLine("Warning: Horizontal tail implementation is experimental.");
                if (TailStart.x > TailEnd.x)
                {
                    Up = true;
                }
            }
            else if (TailStart.y > TailEnd.y)
            {
                Up = true;
            }

            int circumference = (int)Math.Floor(2 * Math.PI * SegmentLength);

            _angleStep = 2 * Math.PI / circumference;


            lock (_scanPointLock)
            {
                //Create angle and offset arrays
                _scanAngles        = new double[circumference];
                _coordinateOffsets = new IppiPoint[circumference];


                //establish scan angles and offsets:
                //loop over sweep angles - points are assigned correctly for downward facing tail
                //use hack for horizontal tail (swap x and y) as well as for upward facing tail (invert both x and y in place)
                for (int j = 0; j < _scanAngles.Length; j++)
                {
                    //facing down (up, left, right) is centered in the array
                    _scanAngles[j] = j * _angleStep - Math.PI;

                    int offX = (int)Math.Floor(SegmentLength * Math.Sin(_scanAngles[j]));
                    int offY = (int)Math.Floor(SegmentLength * Math.Cos(_scanAngles[j]));
                    if (Up)//rotate coordinates by 180
                    {
                        offX *= -1;
                        offY *= -1;
                    }
                    if (!TailIsVertical())//swap role of x and y coordinates
                    {
                        int o = offX;
                        offX = offY;
                        offY = o;
                    }
                    _coordinateOffsets[j] = new IppiPoint(offX, offY);
                }
            }//release lock
        }
예제 #3
0
        public Tracker90mmDish(int imageWidth, int imageHeight, IppiPoint dishCenter)
        {
            _foreground   = new Image8(imageWidth, imageHeight);
            _bgSubtracted = new Image8(imageWidth, imageHeight);
            _calc         = new Image8(imageWidth, imageHeight);
            _dishCenter   = dishCenter;
            int bufferSize = 0;

            IppHelper.IppCheckCall(cv.ippiLabelMarkersGetBufferSize_8u_C1R(new IppiSize(imageWidth, imageHeight), &bufferSize));
            _markerBuffer = (byte *)Marshal.AllocHGlobal(bufferSize);
            int momentSize = 0;

            IppHelper.IppCheckCall(ip.ippiMomentGetStateSize_64f(IppHintAlgorithm.ippAlgHintNone, &momentSize));
            _momentState = (IppiMomentState_64f *)Marshal.AllocHGlobal(momentSize);
            //let ipp decide whether to give accurate or fast results
            IppHelper.IppCheckCall(ip.ippiMomentInit_64f(_momentState, IppHintAlgorithm.ippAlgHintNone));
            _frame = 0;
            //populate tracking parameters with default values
            _threshold        = 6;
            _minArea          = 11;
            _maxAllowedArea   = 120;
            _minEccentricity  = 0.3;
            _fullTrustMinArea = 20;
            _imageROI         = new IppiROI(0, 0, imageWidth, imageHeight);
            _searchRegionSize = 90;
            _removeCMOSISBrightLineArtefact = false;
            _strel3x3 = Morphology.Generate3x3Mask(_foreground.Size);
            //The following calculation for FramesInBackground means that after ~30s of movie
            //a stationary object will have dissappeared into the background (at 63% level)
            FramesInBackground      = (int)((30 * 250));
            FramesInitialBackground = 2 * 30 * 250;
            BGUpdateEvery           = 2;
        }
예제 #4
0
        /// <summary>
        /// Generates a circular mask to use in morphological operations
        /// with the anchor in the center
        /// </summary>
        /// <param name="radius">The radius of the mask as maximum distance from center pixel (i.e. radius=0 would be 1-pixel mask)</param>
        /// <returns>The mask</returns>
        public static MorphologyMask GenerateDiskMask(int radius)
        {
            if (radius < 1)
            {
                throw new ArgumentOutOfRangeException("radius");
            }
            //Masks in ipps morphological functions don't really allow for stride
            //unless it is guaranteed, that the pixels within the stride are of value0
            //therefore we generate the mask with the minimum possible width and height
            //that is dividable by 4
            int       diameter   = 1 + 2 * radius;//center pixel is extra
            int       imageWidth = (int)(Math.Ceiling(diameter / 4.0) * 4);
            IppiPoint center     = new IppiPoint(radius, radius);
            Image8    mask       = new Image8(imageWidth, diameter);

            //Set all pixels to 0
            IppHelper.IppCheckCall(ip.ippiSet_8u_C1R(0, mask.Image, mask.Stride, mask.Size));
            //Loop over pixels, check distance and set to 1 if within circle
            for (int x = 0; x < mask.Width; x++)
            {
                for (int y = 0; y < mask.Width; y++)
                {
                    if (Distance.Euclidian(new IppiPoint(x, y), center) <= radius)
                    {
                        *mask[x, y] = 1;
                    }
                }
            }
            //default anchor is in the circle's center
            return(new MorphologyMask(mask, center));
        }
예제 #5
0
        /// <summary>
        /// Three-point calibration of coordinate-voltage mapping. Using the origin at 0V/0V and two points
        /// for averaging purposes
        /// </summary>
        /// <param name="origin">The coordinates of the origin at 0V/0V</param>
        /// <param name="p1">First calibration point</param>
        /// <param name="p2">Second calibration point</param>
        public void Calibrate(IppiPoint origin, PointVoltagePair p1, PointVoltagePair p2)
        {
            //distances from plane to mirror calculated for each point
            double x1d, x2d, y1d, y2d;
            //distance from the point in question to the origin
            double pointdist;

            //Calculate first distance measure for x-mirror
            pointdist = p1.Coordinate.x - origin.x;
            x1d       = pointdist / Math.Tan(4 * Math.PI * p1.XVoltage / 180);
            //Calculate second distance measure for x-mirror
            pointdist = p2.Coordinate.x - origin.x;
            x2d       = pointdist / Math.Tan(4 * Math.PI * p2.XVoltage / 180);
            //Calculate first distance measure for y-mirror
            pointdist = p1.Coordinate.y - origin.y;
            y1d       = pointdist / Math.Tan(4 * Math.PI * p1.YVoltage / 180);
            //Calculate second distance measure for y-mirror
            pointdist = p2.Coordinate.y - origin.y;
            y2d       = pointdist / Math.Tan(4 * Math.PI * p2.YVoltage / 180);
            //Calculate distance for x-mirror
            XDistance = (x1d + x2d) / 2;
            //Calculate distance for y-mirror
            YDistance = (y1d + y2d) / 2;
            //Create x-lookup table
            for (int x = 0; x < _imageWidth; x++)
            {
                _xVolts[x] = (float)(Math.Atan((x - origin.x) / XDistance) * 45 / Math.PI);
            }
            //Create y-lookup table
            for (int y = 0; y < _imageHeight; y++)
            {
                _yVolts[y] = (float)(Math.Atan((y - origin.y) / YDistance) * 45 / Math.PI);
            }
            Complete = true;
        }
예제 #6
0
        /// <summary>
        /// Safely copies region around given centroid
        /// </summary>
        /// <param name="centroid">The centroid around which image region should be copied</param>
        /// <param name="regionImage">The target of the copy</param>
        /// <param name="image">The source of the copy operation</param>
        public static void CopyRegionImage(IppiPoint centroid, Image8 regionImage, Image8 image)
        {
            IppiPoint copyStart = new IppiPoint(centroid.x - regionImage.Width / 2, centroid.y - regionImage.Height / 2);
            int       destX     = 0;
            int       destY     = 0;

            if (copyStart.x < 0)
            {
                destX      -= copyStart.x;
                copyStart.x = 0;
            }
            if (copyStart.y < 0)
            {
                destY      -= copyStart.y;
                copyStart.y = 0;
            }
            IppiSize copySize = new IppiSize(regionImage.Width - destX, regionImage.Height - destY);

            if (copyStart.x + copySize.width > image.Width)
            {
                copySize.width = image.Width - copyStart.x;
            }
            if (copyStart.y + copySize.height > image.Height)
            {
                copySize.height = image.Height - copyStart.y;
            }
            if (copySize.width > 0 && copySize.height > 0)
            {
                ip.ippiCopy_8u_C1R(image[copyStart], image.Stride, regionImage[destX, destY], regionImage.Stride, copySize);
            }
        }
예제 #7
0
 /// <summary>
 /// Adds a new pixel to the blob
 /// </summary>
 /// <param name="x">The x-coordinate of the pixel</param>
 /// <param name="y">The y-coordinate of the pixel</param>
 /// <param name="value">The intensity value of the pixel</param>
 public void AddPixel(int x, int y)
 {
     //Update bounding points of bounding box and values important for contrast calculation
     if (Moment00 < 1)
     {
         //we are adding the first element
         _boundingBoxUpperLeft  = new IppiPoint(x, y);
         _boundingBoxLowerRight = new IppiPoint(x, y);
     }
     else
     {
         _boundingBoxUpperLeft.x  = Math.Min(_boundingBoxUpperLeft.x, x);
         _boundingBoxLowerRight.x = Math.Max(_boundingBoxLowerRight.x, x);
         _boundingBoxUpperLeft.y  = Math.Min(_boundingBoxUpperLeft.y, y);
         _boundingBoxLowerRight.y = Math.Max(_boundingBoxLowerRight.y, y);
     }
     //Update moments
     Moment00++;
     Moment10 += x;
     Moment01 += y;
     Moment20 += x * x;
     Moment11 += x * y;
     Moment02 += y * y;
     Moment30 += x * x * x;
     Moment03 += y * y * y;
     Moment21 += x * x * y;
     Moment12 += x * y * y;
 }
예제 #8
0
        /// <summary>
        /// Process the next frame
        /// </summary>
        /// <param name="frameNumber">The frame index</param>
        /// <param name="camImage">The camera image</param>
        /// <param name="poi">The fish location</param>
        /// <returns>Whether experiment should continue or not</returns>
        public override bool ProcessNext(int frameNumber, Image8 camImage, out IppiPoint?poi)
        {
            base.ProcessNext(frameNumber, camImage, out poi);
            if (_scanner == null)
            {
                System.Diagnostics.Debug.WriteLine("Scanner was not initialized. Terminating experiment");
                return(false);
            }
            int writeEvery = Properties.Settings.Default.FrameRate * 4;

            if (frameNumber % writeEvery == writeEvery - 1)
            {
                _laser.LaserPower = Properties.Settings.Default.LaserCalibPowermW;
            }
            else
            {
                _laser.LaserPower = 0;
            }
            _lastFrame = frameNumber;
            if (frameNumber >= _totalFrames)
            {
                return(false);
            }
            BlobWithMoments fish = null;

            // Every 4s we turn on the laser - those frames won't be tracked but the image will be saved
            if (frameNumber % writeEvery != 0)
            {
                fish = Tracker.Track(camImage);
            }
            if (fish != null)
            {
                poi = new IppiPoint(fish.Centroid.x, fish.Centroid.y);
                if (_scanner != null)
                {
                    _scanner.Hit(poi.GetValueOrDefault());
                }
            }
            if (_trackWriter != null)
            {
                if (fish != null)
                {
                    _trackWriter.WriteLine("{0}\t{1}\t{2}\t{3}", frameNumber, fish.Centroid.x, fish.Centroid.y, fish.Angle);
                }
                else
                {
                    _trackWriter.WriteLine("NaN\tNaN\tNaN\tNaN");
                }
                // Write image of Laser On frames and the two surrounding frames on each side to file
                if (frameNumber % writeEvery <= 2 || frameNumber % writeEvery >= writeEvery - 2)
                {
                    _imageWriter.WriteFrame(camImage);
                }
            }
            return(true);
        }
예제 #9
0
        /// <summary>
        /// Draws a multiplicative mask for positioning a circular disk highlighting the center
        /// and circumference of a defined circle. Marked pixels are set to 0 all other to 1
        /// </summary>
        /// <param name="maskImage">The image on which the mask is drawn</param>
        /// <param name="center">The center of the circle - will be marked with 10px sized filled circle</param>
        /// <param name="maskRadius">The radius of the circle - will be marked with 3px wide line on the outside of maskRadius</param>
        public static void CreateCircularMask(Image8 maskImage, IppiPoint center, int maskRadius)
        {
            if (maskImage == null)
            {
                throw new ArgumentNullException(nameof(maskImage));
            }
            if (maskImage.IsDisposed)
            {
                throw new ObjectDisposedException(nameof(maskImage));
            }
            //Set all pixels to 1
            ip.ippiSet_8u_C1R(1, maskImage.Image, maskImage.Stride, maskImage.Size);
            //Loop over pixels that are within the outer square of the mask, considering the line thickness as well
            int minX = center.x - maskRadius - 3;

            if (minX < 0)
            {
                minX = 0;
            }
            int maxX = center.x + maskRadius + 3;

            if (maxX >= maskImage.Size.width)
            {
                maxX = maskImage.Size.width - 1;
            }
            int minY = center.y - maskRadius - 3;

            if (minY < 0)
            {
                minY = 0;
            }
            int maxY = center.y + maskRadius + 3;

            if (maxY >= maskImage.Size.height)
            {
                maxY = maskImage.Size.height - 1;
            }
            IppiPoint testP = new IppiPoint();

            for (int x = minX; x <= maxX; x++)
            {
                for (int y = minY; y <= maxY; y++)
                {
                    testP.x = x;
                    testP.y = y;
                    //Test if we are either in the boundary line or within the innner marker
                    var dist = Distance.Euclidian(center, testP);
                    if (dist <= 10 || (dist >= maskRadius && dist <= maskRadius + 3))
                    {
                        byte *pixel = maskImage.Image + x + maskImage.Stride * y;
                        *     pixel = 0;
                    }
                }
            }
        }
예제 #10
0
 /// <summary>
 /// The BGR components of a given pixel in the image
 /// </summary>
 /// <param name="point">The point referencing the pixel</param>
 /// <returns>Pointer to the pixel in the buffer</returns>
 public byte[] this[IppiPoint point]
 {
     get
     {
         return(this[point.x, point.y]);
     }
     set
     {
         this[point.x, point.y] = value;
     }
 }
예제 #11
0
 /// <summary>
 /// Pointer to a given pixel in the image buffer
 /// </summary>
 /// <param name="point">The point referencing the pixel</param>
 /// <returns>Pointer to the pixel in the buffer</returns>
 public byte *this[IppiPoint point]
 {
     get
     {
         if (point.x >= Width || point.y >= Height)
         {
             throw new IndexOutOfRangeException("The indexed point lies outside of the image");
         }
         return(Image + point.x + point.y * Stride);
     }
 }
예제 #12
0
        /// <summary>
        /// Process the next preview frame
        /// </summary>
        /// <param name="frameNumber">The current frame number (ignored)</param>
        /// <param name="camImage">The current camera image</param>
        /// <param name="poi">The centroid of the fish if found</param>
        /// <returns>True</returns>
        public override bool ProcessNext(int frameNumber, Image8 camImage, out IppiPoint?poi)
        {
            base.ProcessNext(frameNumber, camImage, out poi);
            var fish = Tracker.Track(camImage);

            if (fish != null)
            {
                poi = new IppiPoint(fish.Centroid.x, fish.Centroid.y);
            }
            return(true);//Preview only stopped from outside
        }
예제 #13
0
 /// <summary>
 /// Creates a new morhpology mask
 /// </summary>
 /// <param name="mask">The mask image</param>
 /// <param name="anchor">The anchor</param>
 public MorphologyMask(Image8 mask, IppiPoint anchor)
 {
     if (mask == null)
     {
         throw new ArgumentNullException("mask");
     }
     if (anchor.x >= mask.Width || anchor.y >= mask.Height || anchor.x < 0 || anchor.y < 0)
     {
         throw new ArgumentOutOfRangeException("anchor", "Anchor point has to lie within the image");
     }
     _mask   = mask;
     _anchor = anchor;
 }
예제 #14
0
        /// <summary>
        /// Based on the current position computes the next
        /// position in the 8-connected neighborhood
        /// </summary>
        /// <param name="current">The current position</param>
        /// <returns>The next position</returns>
        public static IppiPoint Next(IppiPoint current)
        {
            var       decider = _decider.Next(0, 8);
            IppiPoint retval  = new IppiPoint(current.x, current.y);

            switch (decider)
            {
            case 0:
                retval.y++;
                break;

            case 1:
                retval.x++;
                retval.y++;
                break;

            case 2:
                retval.x++;
                break;

            case 3:
                retval.x++;
                retval.y--;
                break;

            case 4:
                retval.y--;
                break;

            case 5:
                retval.x--;
                retval.y--;
                break;

            case 6:
                retval.x--;
                break;

            case 7:
                retval.x--;
                retval.y++;
                break;

            default:
                System.Diagnostics.Debug.Assert(false, "Unrecognized decider in random walk");
                break;
            }
            return(retval);
        }
예제 #15
0
 public override IppiPoint_32f this[IppiPoint p]
 {
     get
     {
         if (p.x >= _imageWidth || p.x < 0 || p.y >= _imageHeight || p.y < 0)
         {
             throw new ArgumentOutOfRangeException("Requested coordinate is outside of the calibrated image dimensions");
         }
         if (!Complete)
         {
             throw new NotSupportedException("Can't obtain voltages before calibration");
         }
         IppiPoint_32f retval = new IppiPoint_32f(_xVolts[p.x], _yVolts[p.y]);
         return(retval);
     }
 }
예제 #16
0
        /// <summary>
        /// Uses three point calibration data to suggest mirror voltages to hit a given point
        /// </summary>
        /// <param name="coordinates"></param>
        /// <returns></returns>
        private IppiPoint_32f GetMirrorVolts(IppiPoint coordinates_desired)
        {
            //Subract origin coordinates from the desired coordinates
            IppiPoint coordinates      = new IppiPoint(coordinates_desired.x - _p0.x, coordinates_desired.y - _p0.y);
            var       desired_rotation = _camera_theta * -1;//back rotation
            var       dx_mirror        = coordinates.x * Math.Cos(desired_rotation) - coordinates.y * Math.Sin(desired_rotation);
            var       dy_mirror        = coordinates.x * Math.Sin(desired_rotation) + coordinates.y * Math.Cos(desired_rotation);
            var       vx = Math.Atan(dx_mirror / _camera_height);
            var       vy = Math.Atan(dy_mirror / _camera_height);

            if (_isYReflected)
            {
                vy *= -1;
            }
            return(new IppiPoint_32f((float)MirrorVoltage(vx), (float)MirrorVoltage(vy)));
        }
예제 #17
0
        /// <summary>
        /// Adds a new point to the calibration table without checking its validity. Returns
        /// true until the table is complete and provides the next point to target
        /// </summary>
        /// <param name="calibrationPoint">The new calibration point to add to the table</param>
        /// <param name="nextPoint">The point expected next in the calibration</param>
        /// <returns>True until the table is complete</returns>
        public bool ForceAddNext(PointVoltagePair calibrationPoint, out IppiPoint nextPoint)
        {
            if (_addIndex >= _xVolts.Length)
            {
                System.Diagnostics.Debug.WriteLine("Tried to add new point to already complete calibration table");
                nextPoint = new IppiPoint(-1, -1);
                return(false);
            }

            int x, y;

            if (_addIndex == -1)
            {
                _addIndex++;
                //Compute next point
                x         = _addIndex % _c;
                x         = x * _spacing + _scanRoi.X;
                y         = (_addIndex - _addIndex % _c) / _c;
                y         = y * _spacing + _scanRoi.Y;
                nextPoint = new IppiPoint(x, y);
                return(true);
            }



            //Add point to table
            _xVolts[_addIndex] = calibrationPoint.XVoltage;
            _yVolts[_addIndex] = calibrationPoint.YVoltage;

            _addIndex++;
            if (_addIndex >= _xVolts.Length)
            {
                nextPoint = new IppiPoint(-1, -1);
                _complete = true;
                return(false);
            }
            else
            {
                //Compute next point
                x         = _addIndex % _c;
                x         = x * _spacing + _scanRoi.X;
                y         = (_addIndex - _addIndex % _c) / _c;
                y         = y * _spacing + _scanRoi.Y;
                nextPoint = new IppiPoint(x, y);
                return(true);
            }
        }
예제 #18
0
        /// <summary>
        /// Inverses a coordinate rotation within a given reference frame
        /// </summary>
        /// <param name="original">The rotated coordinate</param>
        /// <param name="size">The dimensions of the reference frame</param>
        /// <param name="rot">The original rotation</param>
        /// <returns>The original point</returns>
        public static IppiPoint RotateInverse(IppiPoint rotated, IppiSize size, Rotation rot)
        {
            switch (rot)
            {
            case Rotation.None:
                return(rotated);

            case Rotation.Clock90:
                return(new IppiPoint(rotated.x, size.height - rotated.y));

            case Rotation.Clock180:
                return(new IppiPoint(size.width - rotated.x, size.height - rotated.y));

            default:
                return(new IppiPoint(size.width - rotated.y, rotated.x));
            }
        }
예제 #19
0
        /// <summary>
        /// Rotates a coordinate within a given reference frame
        /// </summary>
        /// <param name="original">The original coordinate</param>
        /// <param name="size">The dimensions of the reference frame</param>
        /// <param name="rot">The desired rotation</param>
        /// <returns>The rotated coordinate</returns>
        public static IppiPoint Rotate(IppiPoint original, IppiSize size, Rotation rot)
        {
            switch (rot)
            {
            case Rotation.None:
                return(original);

            case Rotation.Clock90:
                return(new IppiPoint(size.height - original.y, original.x));

            case Rotation.Clock180:
                return(new IppiPoint(size.width - original.x, size.height - original.y));

            default:
                return(new IppiPoint(original.y, size.width - original.x));
            }
        }
예제 #20
0
 /// <summary>
 /// Creates a new tail tracker
 /// </summary>
 /// <param name="regionToTrack">The ROI in which we should track the tail</param>
 /// <param name="tailStart">The designated starting point of the tail</param>
 /// <param name="tailEnd">The designated end point of the tail</param>
 /// <param name="nsegments">The number of tail segments to track btw. start and end</param>
 public TailTracker(IppiSize imageSize, IppiPoint tailStart, IppiPoint tailEnd, int nsegments)
 {
     _threshold = 20;
     _morphSize = 8;
     _frameRate = 200;
     _strel     = BWImageProcessor.GenerateDiskMask(_morphSize);
     _nSegments = 5;
     _imageSize = imageSize;
     _tailStart = tailStart;
     _tailEnd   = tailEnd;
     //set up our track regions based on the tail positions
     DefineTrackRegions();
     NSegments = nsegments;
     InitializeImageBuffers(); //set up image buffers
     InitializeScanPoints();   //create scan points appropriate for the tail parameters
     //Initialize our angle store for tracking (size never changes)
     _angleStore = (int *)System.Runtime.InteropServices.Marshal.AllocHGlobal(900 * 4);
 }
예제 #21
0
        /// <summary>
        /// Process the next frame
        /// </summary>
        /// <param name="frameNumber">The frame index</param>
        /// <param name="camImage">The camera image</param>
        /// <param name="poi">The fish location</param>
        /// <returns>Whether experiment should continue or not</returns>
        public override bool ProcessNext(int frameNumber, Image8 camImage, out IppiPoint?poi)
        {
            base.ProcessNext(frameNumber, camImage, out poi);
            _lastFrame = frameNumber;
            if (frameNumber >= _totalFrames)
            {
                return(false);
            }
            var fish = Tracker.Track(camImage);

            if (fish != null)
            {
                poi = new IppiPoint(fish.Centroid.x, fish.Centroid.y);
            }
            if (_trackWriter != null)
            {
                if (fish != null)
                {
                    _trackWriter.WriteLine("{0}\t{1}\t{2}\t{3}", frameNumber, fish.Centroid.x, fish.Centroid.y, fish.Angle);
                    if (_writeFishImages)
                    {
                        //blank and copy
                        ip.ippiSet_8u_C1R(0, _camRegion.Image, _camRegion.Stride, _camRegion.Size);
                        MainViewModel.CopyRegionImage(fish.Centroid, _camRegion, camImage);
                        _imageWriter.WriteFrame(_camRegion);
                        MainViewModel.CopyRegionImage(fish.Centroid, _camRegion, Tracker.Background);
                        _backgroundWriter.WriteFrame(_camRegion);
                    }
                }
                else
                {
                    _trackWriter.WriteLine("NaN\tNaN\tNaN\tNaN");
                    if (_writeFishImages)
                    {
                        //blank
                        ip.ippiSet_8u_C1R(0, _camRegion.Image, _camRegion.Stride, _camRegion.Size);
                        _imageWriter.WriteFrame(_camRegion);
                        _backgroundWriter.WriteFrame(_camRegion);
                    }
                }
            }
            return(true);
        }
예제 #22
0
        /// <summary>
        /// Adds a new point to the calibration table, verifies that it has the expected coordinates
        /// and returns true until the table is complete
        /// </summary>
        /// <param name="calibrationPoint">The new calibration point to add to the table</param>
        /// <param name="nextPoint">The point expected next in the calibration</param>
        /// <returns>True while still expecting new points</returns>
        /// <exception cref="ArgumentExcpetion">Thrown if the wrong calibration point is provided</exception>
        public bool AddNext(PointVoltagePair calibrationPoint, out IppiPoint nextPoint)
        {
            int x, y;

            //Verify new point
            if (_addIndex > -1 && _addIndex < _xVolts.Length)
            {
                x = _addIndex % _c;
                x = x * _spacing + _scanRoi.X;
                y = (_addIndex - _addIndex % _c) / _c;
                y = y * _spacing + _scanRoi.Y;
                if (calibrationPoint.Coordinate.x != x || calibrationPoint.Coordinate.y != y)
                {
                    throw new ArgumentException("Wrong calibration point provided");
                }
            }

            return(ForceAddNext(calibrationPoint, out nextPoint));
        }
예제 #23
0
 /// <summary>
 /// Tries to target a given coordinate
 /// </summary>
 /// <param name="coordinate">The 2D point coordinate which should be targeted</param>
 /// <returns>The status code of the targeting attempt</returns>
 public HitStatus Hit(IppiPoint coordinate)
 {
     if (CoordinateConverter == null)
     {
         //No lookup table present. Try to interpret coordinates as raw voltages
         if (coordinate.x < VMin || coordinate.y < VMin || coordinate.x > VMax || coordinate.y > VMax)
         {
             return(HitStatus.OutOfRange);
         }
         //_xWriter.WriteSingleSample(true, coordinate.x);
         //_yWriter.WriteSingleSample(true, coordinate.y);
         double[] voltages = new double[2];
         voltages[0] = coordinate.x;
         voltages[1] = coordinate.y;
         _multiWriter.WriteSingleSample(true, voltages);
         return(HitStatus.NoConversion);
     }
     return(Hit(CoordinateConverter[coordinate]));
 }
예제 #24
0
        /// <summary>
        /// Generates a rectangular mask to use in morphological operations
        /// with the anchor in the center
        /// </summary>
        /// <param name="width">The width of the rectangle</param>
        /// <param name="height">The height of the rectangle</param>
        /// <returns>The morphology mask</returns>
        public static MorphologyMask GenerateRectMask(int width, int height)
        {
            if (width < 1)
            {
                throw new ArgumentOutOfRangeException("width");
            }
            if (height < 1)
            {
                throw new ArgumentOutOfRangeException("height");
            }
            int       imageWidth = (int)(Math.Ceiling(width / 4.0) * 4);
            IppiPoint center     = new IppiPoint((int)Math.Floor(width / 4.0), (int)Math.Floor(height / 4.0));
            Image8    mask       = new Image8(imageWidth, height);

            //Set all pixels to zero
            IppHelper.IppCheckCall(ip.ippiSet_8u_C1R(0, mask.Image, mask.Stride, mask.Size));
            //Set rectangle to 1
            IppHelper.IppCheckCall(ip.ippiSet_8u_C1R(1, mask.Image, mask.Stride, new IppiSize(width, height)));
            return(new MorphologyMask(mask, center));
        }
예제 #25
0
        /// <summary>
        /// Three point calibration for coordinate voltage mapping. Uses three arbitrary points to calculate the mapping.
        /// The three points are used to form two pairs for mapping
        /// </summary>
        /// <param name="p1">The first point and its corresponding voltage</param>
        /// <param name="p2">The second point and its corresponding voltage</param>
        /// <param name="p3">The third point and its corresponding voltage</param>
        public void Calibrate(PointVoltagePair p1, PointVoltagePair p2, PointVoltagePair p3)
        {
            //distance from the point in question to the first point
            double pointdist;


            //To build the lookup table we need the coordinates of the origin
            //to have a rectangular triangle reference
            //We try linear interpolation based on the points we got
            IppiPoint origin = new IppiPoint();

            pointdist = p2.Coordinate.x - p1.Coordinate.x;
            double distpervolts = pointdist / (p2.XVoltage - p1.XVoltage);

            origin.x     = (int)(p1.Coordinate.x - p1.XVoltage * distpervolts);
            pointdist    = p2.Coordinate.y - p1.Coordinate.y;
            distpervolts = pointdist / (p2.YVoltage - p1.YVoltage);
            origin.y     = (int)(p1.Coordinate.y - p1.YVoltage * distpervolts);

            Calibrate(origin, p2, p3);
            return;
        }
예제 #26
0
        /// <summary>
        /// Calculates the rotation of the camera FOV relative to the mirror FOV
        /// </summary>
        /// <param name="pAlign">The pixel coordinates of the alignment beam</param>
        /// <param name="is_y_displacement">If this is calculated for y-excursion on the mirror need to adjust angle</param>
        /// <returns>The rotation angle of the camera axes relative to the mirror axes in radians</returns>
        private double CalculateCameraTheta(IppiPoint pAlign, bool is_y_displacement)
        {
            double dx     = pAlign.x - _p0.x;
            double dy     = pAlign.y - _p0.y;
            double radius = Math.Sqrt(dx * dx + dy * dy);

            if (radius == 0)
            {
                throw new ArgumentException("Alignment radius is 0. Increase alignment mirror excursion voltages.");
            }
            double offset = is_y_displacement ? Math.PI / 2 : 0;
            double theta  = Math.Acos(dx / radius);

            if (dy >= 0)
            {
                return(theta - offset);
            }
            else
            {
                return(2 * Math.PI - theta - offset);
            }
        }
예제 #27
0
        /// <summary>
        /// Based on the current position computes the next
        /// position in the 8-connected neighborhood only returning
        /// points within the given rectangular limits
        /// </summary>
        /// <param name="current">The current position</param>
        /// <param name="xmin">The minimal allowed x-coordinate (inclusive)</param>
        /// <param name="xmax">The maximal allowed x-coordinate (inclusive)</param>
        /// <param name="ymin">The minimal allowed y-coordinate (inclusive)</param>
        /// <param name="ymax">The maximal allowed y-coordinate (inclusive)</param>
        /// <returns>The new point within the limits</returns>
        public static IppiPoint Next(IppiPoint current, int xmin, int xmax, int ymin, int ymax)
        {
            if (xmin >= xmax || ymin >= ymax)
            {
                throw new ArgumentException("Minimal limit must be less than maximal limit");
            }
            if (current.x < xmin || current.x > xmax || current.y < ymin || current.y > ymax)
            {
                throw new ArgumentException("Current point can't be outside the set limits");
            }

            //increment our recursion counter
            _recurseDepth++;

            IppiPoint retval = Next(current);

            //if the returned point is outside of the limits, retry recursively
            if (retval.x < xmin || retval.x > xmax || retval.y < ymin || retval.y > ymax)
            {
                if (_recurseDepth > MaxRecurseDepth)
                {
                    //have reached recursion limit - don't walk
                    _recurseDepth--;
                    return(current);
                }
                retval = Next(current, xmin, xmax, ymin, ymax);
                //we are about to return, decrement recursion counter
                _recurseDepth--;
                return(retval);
            }
            else
            {
                //return, decrement recursion counter
                _recurseDepth--;
                return(retval);
            }
        }
예제 #28
0
        /// <summary>
        /// Based on the current position computes the next
        /// position in the 8-connected neighborhood only returning
        /// points within the given circle
        /// </summary>
        /// <param name="current">The current position</param>
        /// <param name="center">The center of the circle of valid positions</param>
        /// <param name="radius">The radius of the valid area</param>
        /// <returns>The new point within the limits</returns>
        public static IppiPoint Next(IppiPoint current, IppiPoint center, float radius)
        {
            if (radius < 2)
            {
                throw new ArgumentOutOfRangeException("radius", "The radius has to be two or more pixels");
            }
            if (Distance.Euclidian(current, center) > radius)
            {
                throw new ArgumentException("The current point can't be outside of the valid circle.");
            }

            //increment our recursion counter
            _recurseDepth++;

            IppiPoint retval = Next(current);

            //if the returned point is outside of the limits, retry recursively
            if (Distance.Euclidian(retval, center) > radius)
            {
                if (_recurseDepth > MaxRecurseDepth)
                {
                    //have reached recursion limit - don't walk
                    _recurseDepth--;
                    return(current);
                }
                retval = Next(current, center, radius);
                //we are about to return, decrement recursion counter
                _recurseDepth--;
                return(retval);
            }
            else
            {
                //return, decrement recursion counter
                _recurseDepth--;
                return(retval);
            }
        }
예제 #29
0
 public PointVoltagePair(IppiPoint coordinate, float xvoltage, float yvoltage)
 {
     Coordinate = coordinate;
     XVoltage   = xvoltage;
     YVoltage   = yvoltage;
 }
예제 #30
0
        /// <summary>
        /// Processes the next point, filtering its position
        /// computing speeds and detecting bouts
        /// </summary>
        /// <param name="coordinate">The coordinate to analyze</param>
        /// <returns>The analysis of the given coordinate</returns>
        public PointAnalysis ProcessNextPoint(IppiPoint coordinate)
        {
            //set up locals for processing
            float x = coordinate.x;
            float y = coordinate.y;
            float xSmooth, ySmooth, speed;

            xSmooth = ySmooth = speed = 0;
            //optional filtered speed
            float spdSmooth = 0;

            //filter x-coordinate
            sp.ippsFIR_32f(&x, &xSmooth, 1, _filterStateX);
            //filter y-coordinate
            sp.ippsFIR_32f(&y, &ySmooth, 1, _filterStateY);
            //compute instant speed
            speed  = (float)Math.Sqrt((xSmooth - _lastX) * (xSmooth - _lastX) + (ySmooth - _lastY) * (ySmooth - _lastY));
            speed *= 240;
            //if requested, also filter the instant-speed value
            if (_spdTapLength > 0)
            {
                sp.ippsFIR_32f(&speed, &spdSmooth, 1, _filterStateSpd);
            }
            else
            {
                spdSmooth = speed;
            }
            //update last coordinates
            _lastX = xSmooth;
            _lastY = ySmooth;

            //Bout detection
            if (_boutDetectionState.InPutativeBout)                                                                                    //we are currently adding frames to a new putative bout
            {
                if (spdSmooth < _speedThreshold)                                                                                       //putative bout is finished
                {
                    _boutDetectionState.InPutativeBout = false;                                                                        //reset
                    if (_boutDetectionState.FramesInBout >= _minFramesPerBout && _boutDetectionState.FramesAtPeak <= _maxFramesAtPeak) //bout was valid, fill bout structure and return it
                    {
                        Bout bout = new Bout();
                        bout.BoutEnd   = _index - 1;
                        bout.BoutStart = _boutDetectionState.StartFrame;
                        bout.BoutPeak  = _boutDetectionState.PeakFrame;
                        if (bout.BoutPeak == bout.BoutStart)
                        {
                            bout.BoutStart--;//don't start bout on peakframe
                        }
                        bout.PeakSpeed          = _boutDetectionState.PeakSpeed;
                        bout.TotalDisplacement  = _boutDetectionState.TotalDisplacement;
                        bout.StartingCoordinate = _boutDetectionState.StartingCoordinate;
                        bout.FinishCoordinate   = new IppiPoint_32f(xSmooth, ySmooth);
                        //don't forget to update the index before we return
                        _index++;
                        return(new PointAnalysis(bout, coordinate, new IppiPoint_32f(xSmooth, ySmooth), spdSmooth));
                    }
                }
                else//putative bout continues
                {
                    _boutDetectionState.FramesInBout++;
                    _boutDetectionState.TotalDisplacement += spdSmooth / _frameRate;
                    if (spdSmooth > _boutDetectionState.PeakSpeed)//new peak-speed detected
                    {
                        _boutDetectionState.PeakSpeed    = spdSmooth;
                        _boutDetectionState.PeakFrame    = _index;
                        _boutDetectionState.FramesAtPeak = 1;            //reset
                    }
                    else if (spdSmooth == _boutDetectionState.PeakSpeed) //maybe not the best criterion - equality of float values very unlikely
                    {
                        _boutDetectionState.FramesAtPeak++;
                    }
                }
            }
            else//currently the fish is stationary
            {
                if (spdSmooth > _speedThreshold)//we should start a new putative bout
                {
                    _boutDetectionState.StartBout(spdSmooth, _index, _frameRate);
                    _boutDetectionState.StartingCoordinate.x = xSmooth;
                    _boutDetectionState.StartingCoordinate.y = ySmooth;
                }
            }
            //advance index
            _index++;
            //return our analysis
            return(new PointAnalysis(coordinate, new IppiPoint_32f(xSmooth, ySmooth), spdSmooth));
        }