Пример #1
0
        public Tracker90mmDish(int imageWidth, int imageHeight)
        {
            _foreground   = new Image8(imageWidth, imageHeight);
            _calc         = new Image8(imageWidth, imageHeight);
            _labelMarkers = new Image8(imageWidth, imageHeight);
            int bufferSize = 0;

            IppHelper.IppCheckCall(cv.ippiLabelMarkersGetBufferSize_8u_C1R(new IppiSize(imageWidth, imageHeight), &bufferSize));
            _markerBuffer = (byte *)Marshal.AllocHGlobal(bufferSize);
            fixed(IppiMomentState_64s **ppState = &_momentState)
            {
                //let ipp decide whether to give accurate or fast results
                IppHelper.IppCheckCall(ip.ippiMomentInitAlloc_64s(ppState, IppHintAlgorithm.ippAlgHintNone));
            }

            _frame = 0;
            //populate tracking parameters with default values
            _threshold        = 5;
            _minArea          = 10;
            _maxArea          = 300;
            _fullTrustMinArea = 20;
            _imageROI         = new IppiROI(0, 0, imageWidth, imageHeight);
            //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 * 240));
            FramesInitialBackground = 2 * FramesInBackground;
        }
Пример #2
0
        /// <summary>
        /// Method for parallelized tracking - depending on index
        /// performs tracking on an image chunk
        /// </summary>
        /// <param name="index">The chunk's index</param>
        private void TrackChunk(int index)
        {
            //each chunk has its own image region with its own wells to take care of
            //currently, only 4 chunks on a 24-well plate are supported
            if (index >= _parallelChunks)
            {
                System.Diagnostics.Debug.WriteLine("Fatal parallel tracking error. Tried to access non-existing chunk.");
            }
            //do image filtering, thresholding and closing
            if (DoMedianFiltering)
            {
                IppHelper.IppCheckCall(ip.ippiFilterMedianWeightedCenter3x3_8u_C1R(_calc[_parallelImageRegions[index].X + 1, _parallelImageRegions[index].Y + 1], _calc.Stride,
                                                                                   _foreground[_parallelImageRegions[index].X + 1, _parallelImageRegions[index].Y + 1], _foreground.Stride,
                                                                                   new IppiSize(_parallelImageRegions[index].Width - 2, _parallelImageRegions[index].Height - 2), 1));
            }
            Im2Bw(_foreground, _parallelImageRegions[index]);
            Close3x3(_foreground, _parallelImageRegions[index]);
            //loop over the wells that belong to our region, extract the fish
            //and put into our result structure
            //Important: Need to put into the chunks section of the results section!
            var ourWells = _parallelChunkWells[index];

            for (int i = 0; i < ourWells.Count; i++)
            {
                _parallelTrackResults[i + _parallelCumWellCount[index]] = ExtractWellParallel(ourWells[i], index);
            }
        }
Пример #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>
 /// Computes instants speeds from a coordinate trace and stores the result
 /// in the provided buffer
 /// </summary>
 /// <param name="path">The movement path</param>
 /// <param name="frameRate">The framerate at acquisition</param>
 /// <param name="isb">The buffer to store the speed in</param>
 public void ComputeInstantSpeeds(CentroidBuffer path, int frameRate, InstantSpeedBuffer isb)
 {
     if (IsDisposed)
     {
         throw new ObjectDisposedException(this.ToString());
     }
     if (isb.Size.width != path.Size.width)
     {
         throw new ArgumentException("Path length and size of speed buffer must be the same!");
     }
     //Check that our buffers are present and match, otherwise fix
     if (_calc1 == null)
     {
         _calc1 = new CentroidBuffer(path.Size.width);
         _calc2 = new CentroidBuffer(path.Size.width);
     }
     else if (_calc1.Size.width != path.Size.width)
     {
         _calc1.Dispose();
         _calc2.Dispose();
         _calc1 = new CentroidBuffer(path.Size.width);
         _calc2 = new CentroidBuffer(path.Size.width);
     }
     //compute difference trace subtracting the position at t from the position at t+1 by subtracting the unshifted input buffer from its shifted version
     //the result gets stored in calc1 with the first value being 0
     ip.ippiSet_32f_C1R(0, _calc1.Buffer, _calc1.Stride, _calc1.Size);
     IppHelper.IppCheckCall(ip.ippiSub_32f_C1R(path.Buffer, path.Stride, (float *)((byte *)path.Buffer + 4), path.Stride, (float *)((byte *)_calc1.Buffer + 4), _calc1.Stride, new IppiSize(path.Size.width - 1, 2)));
     //square the difference trace, storing the result in _calc2
     IppHelper.IppCheckCall(ip.ippiSqr_32f_C1R(_calc1.Buffer, _calc1.Stride, _calc2.Buffer, _calc2.Stride, _calc1.Size));
     //Add the values of the x and y coordinates, storing the sum of squares in the first row of _calc1
     IppHelper.IppCheckCall(ip.ippiAdd_32f_C1R(_calc2.Buffer, _calc2.Stride, (float *)((byte *)_calc2.Buffer + _calc2.Stride), _calc2.Stride, _calc1.Buffer, _calc1.Stride, new IppiSize(_calc1.Size.width, 1)));
     //Compute the square root of the sum-of-squares and copy the result to the first row of _calc2
     IppHelper.IppCheckCall(ip.ippiSqrt_32f_C1R(_calc1.Buffer, _calc1.Stride, _calc2.Buffer, _calc2.Stride, new IppiSize(_calc1.Size.width, 1)));
     //Multiply the distances by the framerate and store the result in our instant speed buffer
     IppHelper.IppCheckCall(ip.ippiMulC_32f_C1R(_calc2.Buffer, _calc2.Stride, frameRate, isb.Buffer, isb.Stride, isb.Size));
 }
Пример #5
0
        /// <summary>
        /// Extracts a fish (candidate) from an image by performing background subtraction, noise filtering, thresholding and closing to obtain a foreground
        /// followed by marker extraction.
        /// </summary>
        /// <param name="im">The image to extract the fish from</param>
        /// <param name="region">The ROI to search</param>
        /// <returns>The most likely fish blob or null if no suitable candidate was found</returns>
        protected BlobWithMoments ExtractFish(/*Image8 im,*/ IppiROI region)
        {
            int nMarkers = 0;

            BlobWithMoments[] blobsDetected;



            //Copy foreground to marker and label connected components
            //IppHelper.IppCheckCall(ip.ippiCopy_8u_C1R(_foreground[region.TopLeft], _foreground.Stride, _labelMarkers[region.TopLeft], _labelMarkers.Stride, region.Size));
            IppHelper.IppCheckCall(cv.ippiLabelMarkers_8u_C1IR(_foreground[region.TopLeft], _foreground.Stride, region.Size, 1, 254, IppiNorm.ippiNormInf, &nMarkers, _markerBuffer));
            //loop over returned markers and use ipp to extract blobs
            if (nMarkers > 0)
            {
                if (nMarkers > 254)
                {
                    nMarkers = 254;
                }
                blobsDetected = new BlobWithMoments[nMarkers];
                for (int i = 1; i <= nMarkers; i++)
                {
                    //label all pixels with the current marker as 255 and others as 0
                    IppHelper.IppCheckCall(ip.ippiCompareC_8u_C1R(_foreground[region.TopLeft], _foreground.Stride, (byte)i, _calc[region.TopLeft], _calc.Stride, region.Size, IppCmpOp.ippCmpEq));
                    //calculate image moments
                    IppHelper.IppCheckCall(ip.ippiMoments64s_8u_C1R(_calc[region.TopLeft], _calc.Stride, region.Size, _momentState));
                    //retrieve moments
                    long m00 = 0;
                    long m10 = 0;
                    long m01 = 0;
                    long m20 = 0;
                    long m02 = 0;
                    long m11 = 0;
                    long m30 = 0;
                    long m03 = 0;
                    long m21 = 0;
                    long m12 = 0;
                    ip.ippiGetSpatialMoment_64s(_momentState, 0, 0, 0, new IppiPoint(region.X, region.Y), &m00, 0);
                    //since our input image is not 0s and 1s but 0s and 255s we have to divide by 255 in order to re-normalize our moments
                    System.Diagnostics.Debug.Assert(m00 % 255 == 0, "M00 was not a multiple of 255");
                    m00 /= 255;
                    //only retrieve other moments if this is a "fish candidate"
                    if (m00 > MinArea && m00 <= MaxArea)
                    {
                        ip.ippiGetSpatialMoment_64s(_momentState, 1, 0, 0, new IppiPoint(region.X, region.Y), &m10, 0);
                        m10 /= 255;
                        ip.ippiGetSpatialMoment_64s(_momentState, 0, 1, 0, new IppiPoint(region.X, region.Y), &m01, 0);
                        m01 /= 255;
                        ip.ippiGetSpatialMoment_64s(_momentState, 2, 0, 0, new IppiPoint(region.X, region.Y), &m20, 0);
                        m20 /= 255;
                        ip.ippiGetSpatialMoment_64s(_momentState, 0, 2, 0, new IppiPoint(region.X, region.Y), &m02, 0);
                        m02 /= 255;
                        ip.ippiGetSpatialMoment_64s(_momentState, 1, 1, 0, new IppiPoint(region.X, region.Y), &m11, 0);
                        m11 /= 255;
                        ip.ippiGetSpatialMoment_64s(_momentState, 3, 0, 0, new IppiPoint(region.X, region.Y), &m30, 0);
                        m30 /= 255;
                        ip.ippiGetSpatialMoment_64s(_momentState, 0, 3, 0, new IppiPoint(region.X, region.Y), &m03, 0);
                        m03 /= 255;
                        ip.ippiGetSpatialMoment_64s(_momentState, 2, 1, 0, new IppiPoint(region.X, region.Y), &m21, 0);
                        m21 /= 255;
                        ip.ippiGetSpatialMoment_64s(_momentState, 1, 2, 0, new IppiPoint(region.X, region.Y), &m12, 0);
                        m12 /= 255;
                        blobsDetected[i - 1] = new BlobWithMoments(m00, m10, m01, m20, m11, m02, m30, m03, m21, m12);
                        //Determine bounding box of the blob. The following seems kinda retarded as Ipp must already
                        //have obtained that information before so maybe there is some way to actually retrieve it??
                        //Do linescans using ipp's sum function starting from the blobs centroid until we hit a line
                        //the sum of which is 0
                        int       xStart, xEnd, yStart, yEnd;
                        double    sum      = 1;
                        IppiPoint centroid = blobsDetected[i - 1].Centroid;
                        xStart = centroid.x - 5;
                        xEnd   = centroid.x + 5;
                        yStart = centroid.y - 5;
                        yEnd   = centroid.y + 5;
                        //in the following loops we PRE-increment, whence we stop the loop if we are at one coordinate short of the ends
                        //find xStart
                        while (sum > 0 && xStart > region.X + 4)
                        {
                            xStart -= 5;
                            IppHelper.IppCheckCall(ip.ippiSum_8u_C1R(_calc[xStart, region.Y], _calc.Stride, new IppiSize(1, region.Height), &sum));
                        }
                        xStart += 1;//we have a sum of 0, so go back one line towards the centroid
                        //find xEnd
                        sum = 1;
                        while (sum > 0 && xEnd < region.X + region.Width - 6)
                        {
                            xEnd += 5;
                            IppHelper.IppCheckCall(ip.ippiSum_8u_C1R(_calc[xEnd, region.Y], _calc.Stride, new IppiSize(1, region.Height), &sum));
                        }
                        xEnd -= 1;//we have sum of 0, so go back one line towards the centroid
                        //find yStart - we can limit our x-search-space as we already have those boundaries
                        sum = 1;
                        while (sum > 0 && yStart > region.Y + 4)
                        {
                            yStart -= 5;
                            IppHelper.IppCheckCall(ip.ippiSum_8u_C1R(_calc[xStart, yStart], _calc.Stride, new IppiSize(xEnd - xStart + 1, 1), &sum));
                        }
                        yStart += 1;
                        //find yEnd - again limit summation to x-search-space
                        sum = 1;
                        while (sum > 0 && yEnd < region.Y + region.Height - 6)
                        {
                            yEnd += 5;
                            IppHelper.IppCheckCall(ip.ippiSum_8u_C1R(_calc[xStart, yEnd], _calc.Stride, new IppiSize(xEnd - xStart + 1, 1), &sum));
                        }
                        yEnd -= 1;
                        blobsDetected[i - 1].BoundingBox = new IppiRect(xStart, yStart, xEnd - xStart + 1, yEnd - yStart + 1);
                    }
                    else
                    {
                        blobsDetected[i - 1] = new BlobWithMoments();
                    }
                }
            }
            else
            {
                return(null);
            }
            long maxArea  = 0;
            int  maxIndex = -1;

            for (int i = 0; i < blobsDetected.Length; i++)
            {
                if (blobsDetected[i] == null)
                {
                    break;
                }
                //Simply note down the largest blob
                if (blobsDetected[i].Area > maxArea)
                {
                    maxArea  = blobsDetected[i].Area;
                    maxIndex = i;
                }
            }

            if (maxArea < MinArea)
            {
                return(null);
            }
            else
            {
                return(blobsDetected[maxIndex]);
            }
        }
Пример #6
0
 /// <summary>
 /// Performs a 3x3 closing operation on an image
 /// </summary>
 /// <param name="im">The (thresholded) image to close</param>
 /// <param name="region">The ROI in which to perform the operation</param>
 protected void Close3x3(Image8 im, IppiROI region)
 {
     IppHelper.IppCheckCall(ip.ippiDilate3x3_8u_C1IR(im[region.TopLeft.x + 1, region.TopLeft.y + 1], im.Stride, new IppiSize(region.Width - 2, region.Height - 2)));
     IppHelper.IppCheckCall(ip.ippiErode3x3_8u_C1IR(im[region.TopLeft.x + 1, region.TopLeft.y + 1], im.Stride, new IppiSize(region.Width - 2, region.Height - 2)));
 }
Пример #7
0
 /// <summary>
 /// Implements a "greater than" threshold like MATLABS
 /// im2bw function
 /// </summary>
 /// <param name="im">The image to threshold</param>
 /// <param name="region">The ROI in which to perform the operation</param>
 /// <param name="threshold">The threshold to apply</param>
 protected void Im2Bw(Image8 im, IppiROI region)
 {
     IppHelper.IppCheckCall(ip.ippiThreshold_LTVal_8u_C1IR(im[region.TopLeft], im.Stride, region.Size, (byte)(_threshold + 1), 0));
     IppHelper.IppCheckCall(ip.ippiThreshold_GTVal_8u_C1IR(im[region.TopLeft], im.Stride, region.Size, _threshold, 255));
 }
Пример #8
0
        /// <summary>
        /// Tracks fish on a multiwell plate
        /// </summary>
        /// <param name="image">The current image to track and update the background with</param>
        /// <param name="updateBackground">If true the current image will be used to update the background</param>
        /// <param name="forceFullFrame">If set to true background updates won't exclude fish locations</param>
        /// <returns>A list of fish-blobs, one for each well or null if no fish was detected</returns>
        public BlobWithMoments[] TrackMultiWell(Image8 image, bool updateBackground = true, bool forceFullFrame = false)
        {
            if (_frame == 0)
            {
                ip.ippiSet_8u_C1R(0, Foreground.Image, Foreground.Stride, Foreground.Size);
                ip.ippiSet_8u_C1R(0, _calc.Image, _calc.Stride, _calc.Size);
            }

            BlobWithMoments[] currentFish = new BlobWithMoments[_wellnumber];


            if (_frame > FramesInitialBackground && !forceFullFrame)
            {
                //do global stuff (everything except labelMarkers if we don't do parallel tracking otherwise only the inherently parallel background subtraction)

                //cache 8-bit representation of the background
                Image8 bg = _bgModel.Background;
                //Perform background subtraction
                if (DoMedianFiltering)
                {
                    IppHelper.IppCheckCall(cv.ippiAbsDiff_8u_C1R(image.Image, image.Stride, bg.Image, bg.Stride, _calc.Image, _calc.Stride, image.Size));
                }
                else
                {
                    IppHelper.IppCheckCall(cv.ippiAbsDiff_8u_C1R(image.Image, image.Stride, bg.Image, bg.Stride, _foreground.Image, _foreground.Stride, image.Size));
                }

                if (_trackMethod == TrackMethods.Parallel && _parallelChunks > 1)
                {
                    if (!Parallel.For(0, _parallelChunks, TrackChunk).IsCompleted)
                    {
                        System.Diagnostics.Debug.WriteLine("Error in parallel tracking. Not all regions completed");
                    }
                    //copy our result into currentFish
                    for (int i = 0; i < _wellnumber; i++)
                    {
                        currentFish[i] = _parallelTrackResults[i];
                    }
                }
                else
                {
                    if (DoMedianFiltering)
                    {
                        //remove noise via median filtering
                        IppHelper.IppCheckCall(ip.ippiFilterMedianWeightedCenter3x3_8u_C1R(_calc[1, 1], _calc.Stride,
                                                                                           _foreground[1, 1], _foreground.Stride, new IppiSize(image.Width - 2, image.Height - 2), 1));
                    }
                    //Threshold and close
                    Im2Bw(_foreground, new IppiROI(new IppiPoint(0, 0), image.Size));
                    Close3x3(_foreground, new IppiROI(new IppiPoint(0, 0), image.Size));

                    //label markers and extract - depending on the method selector
                    //we label components on the whole image (new) or individual wells(old)
                    if (_trackMethod == TrackMethods.OnWholeImage)
                    {
                        currentFish = ExtractAll();
                    }
                    else
                    {
                        for (int i = 0; i < _wellnumber; i++)
                        {
                            currentFish[i] = ExtractFish(_wells[i]);
                        }
                    }
                }
            }//If We are past initial background frames

            //create/update background and advance frame counter
            if (_frame == 0)
            {
                _bgModel = new SelectiveUpdateBGModel(image, 1.0f / FramesInBackground);
            }
            else if (updateBackground || _frame <= FramesInitialBackground)//only allow reduction of background update rate AFTER initial background has been created
            {
                if (currentFish == null || forceFullFrame)
                {
                    _bgModel.UpdateBackground(image);
                }
                else
                {
                    _bgModel.UpdateBackground(image, currentFish);
                }
            }


            _frame++;
            return(currentFish);
        }
Пример #9
0
        /// <summary>
        /// Extracts for each well the most likely fish-blob or null if no suitable candidate was found
        /// </summary>
        /// <returns>All fish blobs found in the image</returns>
        private BlobWithMoments[] ExtractAll()
        {
            int nMarkers = 0;

            //we have fixed bounding boxes for each fish that are square and 1.5 times the typical fish-length
            //with the fishes centroid centered in the box
            int bbOffset = (int)(_typicalFishLength * 0.75);
            int bbSize   = (int)(_typicalFishLength * 1.5);

            //Copy foreground to marker and label connected components
            //IppHelper.IppCheckCall(ip.ippiCopy_8u_C1R(_foreground.Image, _foreground.Stride, _labelMarkers.Image, _labelMarkers.Stride, _foreground.Size));
            IppHelper.IppCheckCall(cv.ippiLabelMarkers_8u_C1IR(_foreground.Image, _foreground.Stride, _foreground.Size, 1, 254, IppiNorm.ippiNormInf, &nMarkers, _markerBuffer));

            if (nMarkers > 254)
            {
                nMarkers = 254;
            }

            //fish are identified by looping over all wells and computing the histogram of pixel values for each well. This will effectively tell us
            //which marker is the most abundant in the given well => this would be the fish by our general detection logic
            //The maximum value that ippiLabelMarkers has used is equal to nMarkers. Therefore, when we compute our histogram, we supply
            //(nMarkers+2) as the nLevels parameter - this will give us (nMarkers+1) bins containing the counts from 0->nMarkers with 0 being our background

            //loop over wells
            for (int i = 0; i < _wellnumber; i++)
            {
                IppiROI well = _wells[i];
                //get well-specific maxvalue
                //byte maxVal = 0;
                //IppHelper.IppCheckCall(ip.ippiMax_8u_C1R(_labelMarkers[well.TopLeft], _labelMarkers.Stride, well.Size, &maxVal));
                //compute histogram in this well - from 0 until maxval
                IppHelper.IppCheckCall(ip.ippiHistogramRange_8u_C1R(_foreground[well.TopLeft], _foreground.Stride, well.Size, _hist, _histogramLevels, nMarkers + 2));
                //_hist now contains in position i the abundance of marker i => by looping over it from 1->nMarkers we can find the largest blob. If this blob is
                //larger than our minimum size, we compute the moments and initialize a BlobWithMoments structure for it
                int maxCount = 0;
                int maxIndex = -1;
                for (int j = 1; j <= nMarkers; j++)
                {
                    if (_hist[j] > maxCount && _hist[j] > _minArea && _hist[j] <= _maxArea)
                    {
                        maxCount = _hist[j];
                        maxIndex = j;
                    }
                }
                if (maxIndex == -1)//no suitable fish found
                {
                    _allFish[i] = null;
                }
                else
                {
                    //compare and compute moments
                    //label all pixels with the current marker as 255 and others as 0
                    IppHelper.IppCheckCall(ip.ippiCompareC_8u_C1R(_foreground[well.TopLeft], _foreground.Stride, (byte)maxIndex, _wellCompare.Image, _wellCompare.Stride, well.Size, IppCmpOp.ippCmpEq));
                    //calculate image moments
                    IppHelper.IppCheckCall(ip.ippiMoments64s_8u_C1R(_wellCompare.Image, _wellCompare.Stride, well.Size, _momentState));
                    //retrieve moments
                    long m00 = 0;
                    long m10 = 0;
                    long m01 = 0;
                    long m20 = 0;
                    long m02 = 0;
                    long m11 = 0;
                    long m30 = 0;
                    long m03 = 0;
                    long m21 = 0;
                    long m12 = 0;
                    ip.ippiGetSpatialMoment_64s(_momentState, 0, 0, 0, well.TopLeft, &m00, 0);
                    m00 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 1, 0, 0, well.TopLeft, &m10, 0);
                    m10 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 0, 1, 0, well.TopLeft, &m01, 0);
                    m01 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 2, 0, 0, well.TopLeft, &m20, 0);
                    m20 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 0, 2, 0, well.TopLeft, &m02, 0);
                    m02 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 1, 1, 0, well.TopLeft, &m11, 0);
                    m11 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 3, 0, 0, well.TopLeft, &m30, 0);
                    m30 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 0, 3, 0, well.TopLeft, &m03, 0);
                    m03 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 2, 1, 0, well.TopLeft, &m21, 0);
                    m21 /= 255;
                    ip.ippiGetSpatialMoment_64s(_momentState, 1, 2, 0, well.TopLeft, &m12, 0);
                    m12        /= 255;
                    _allFish[i] = new BlobWithMoments(m00, m10, m01, m20, m11, m02, m30, m03, m21, m12);
                    //assign bounding box
                    IppiRect bBox = new IppiRect(_allFish[i].Centroid.x - bbOffset, _allFish[i].Centroid.y - bbOffset, bbSize, bbSize);
                    if (bBox.x < well.X)
                    {
                        bBox.x = well.X;
                    }
                    if (bBox.y < well.Y)
                    {
                        bBox.y = well.Y;
                    }
                    if (bBox.x + bBox.width > well.X + well.Width)
                    {
                        bBox.width = well.X + well.Width - bBox.x;
                    }
                    if (bBox.y + bBox.height > well.Y + well.Height)
                    {
                        bBox.height = well.Y + well.Height - bBox.y;
                    }
                    _allFish[i].BoundingBox = bBox;
                }
            }//end looping over all wells

            return(_allFish);
        }
Пример #10
0
        /// <summary>
        /// Constructs a new multi-well tracker
        /// </summary>
        /// <param name="imageWidth">The total width of the image we want to track</param>
        /// <param name="imageHeight">The total height of the image we want to track</param>
        /// <param name="plate_index">This index is appended to the ROI file name to load file for proper plate</param>
        /// <param name="parallelChunks">The number of parallel chunks to track</param>
        public TrackerMultiWell(int imageWidth, int imageHeight, int plate_index, int parallelChunks = 2) : base(imageWidth, imageHeight)
        {
            _typicalFishLength = 30;
            _trackMethod       = TrackMethods.PerWell;//default to per-well tracking method
            //Load ROIs - the following way to assess how many lines we have and hence how many
            //ROIs we are dealing with is ugly but what the heck its quick to think up
            _wellnumber = 0;
            var          assembly   = Assembly.GetExecutingAssembly();
            StreamReader roiStream  = null;
            string       fileToOpen = string.Format("SleepTracker.ROI_defs_{0}.txt", plate_index);

            //open for prescanning
            var s = assembly.GetManifestResourceStream(fileToOpen);

            roiStream = new StreamReader(s);
            while (!roiStream.EndOfStream)
            {
                string   line   = roiStream.ReadLine();
                string[] values = line.Split('\t');
                if (values.Length == 4)
                {
                    _wellnumber++;
                }
            }
            roiStream.Dispose();
            //reopen to load wells
            s         = assembly.GetManifestResourceStream(fileToOpen);
            roiStream = new StreamReader(s);
            _wells    = new IppiROI[_wellnumber];
            //we use the following hash-table to keep track of wells belonging
            //to each row. This will give us the number of rows and their boundaries
            //for later parallel chunk boundary determination as well as which wells
            //should be tracked in which chunk
            Dictionary <int, List <IppiROI> > wellsPerRow  = new Dictionary <int, List <IppiROI> >();
            Dictionary <int, int>             columnStarts = new Dictionary <int, int>();

            for (int i = 0; i < _wellnumber; i++)
            {
                System.Diagnostics.Debug.Assert(!roiStream.EndOfStream, "Unexpectedly reached end of ROI file");
                string   line   = roiStream.ReadLine();
                string[] values = line.Split('\t');
                System.Diagnostics.Debug.Assert(values.Length == 4, "Found line in ROI file that does not contain 4 tab-separated strings");
                int[] numValues = new int[4];
                for (int j = 0; j < 4; j++)
                {
                    numValues[j] = int.Parse(values[j]);
                }
                _wells[i] = new IppiROI(numValues[1], numValues[0], numValues[2], numValues[3]);
                if (wellsPerRow.ContainsKey(_wells[i].Y))
                {
                    wellsPerRow[_wells[i].Y].Add(_wells[i]);
                }
                else
                {
                    //create a new list for this row and append our first well
                    wellsPerRow[_wells[i].Y] = new List <IppiROI>();
                    wellsPerRow[_wells[i].Y].Add(_wells[i]);
                }
                if (columnStarts.ContainsKey(_wells[i].X))
                {
                    columnStarts[_wells[i].X]++;
                }
                else
                {
                    columnStarts[_wells[i].X] = 1;
                }
            }
            roiStream.Dispose();
            //can't have more parallel regions than we have rows of wells
            if (parallelChunks > wellsPerRow.Count)
            {
                parallelChunks = wellsPerRow.Count;
            }
            _allFish = new BlobWithMoments[_wellnumber];
            //initialize our histogram bin boundaries (=histogram levels)
            //the following assignment will allow us to study marker values from 0 to 254
            //since: h[k] = countof(pLevels[k] <= pixels(x,y) < pLevels[k+1])
            _histogramLevels = (int *)Marshal.AllocHGlobal(sizeof(int) * 256);
            for (int i = 0; i < 256; i++)
            {
                _histogramLevels[i] = i;
            }
            _hist = (int *)Marshal.AllocHGlobal(sizeof(int) * 255);
            //assume all wells have the same size
            _wellCompare = new Image8(_wells[1].Size.width, _wells[1].Size.height);

            _parallelChunks = parallelChunks;

            //Initialize result array for parallel tracking
            _parallelTrackResults = new BlobWithMoments[_wellnumber];
            //Set up image regions for parallel tracking
            _parallelImageRegions = new IppiROI[_parallelChunks];
            //populate image regions and parallel buffers if _parallelChunks is larger 1
            if (_parallelChunks > 1)
            {
                _parallelMarkerBuffers = new byte *[_parallelChunks];
                _parallelMomentStates  = new IppiMomentState_64s *[_parallelChunks];
                //determine the number of rows in each chunk - integer division and last chunk get's the remainder tucked on
                int nPerChunk  = wellsPerRow.Count / _parallelChunks;
                int nLastChunk = wellsPerRow.Count - (_parallelChunks - 1) * nPerChunk;
                //obtain all our row-starting coordinates and sort ascending
                var rowCoordinates = wellsPerRow.Keys.ToArray();
                Array.Sort(rowCoordinates);
                //do the same for column starting coordinates
                var colCoordinates = columnStarts.Keys.ToArray();
                Array.Sort(colCoordinates);
                //Inititalize our parallel-well list array
                _parallelChunkWells = new List <IppiROI> [_parallelChunks];
                for (int i = 0; i < _parallelChunks; i++)
                {
                    //for each chunk initialize it's well-list
                    _parallelChunkWells[i] = new List <IppiROI>();
                    int startRowInChunk = i * nPerChunk;
                    int endRowInChunk;
                    //are we dealing with the last chunk - this one potentially has a different number of rows!
                    if (i == _parallelChunks - 1)
                    {
                        endRowInChunk = startRowInChunk + nLastChunk - 1;
                    }
                    else
                    {
                        endRowInChunk = startRowInChunk + nPerChunk - 1;
                    }
                    //add all wells of this chunk to the list by looping over rows
                    //finding their start coordinate and using that to index into
                    //our dictionary. Then loop over the list in the dictionary
                    for (int j = startRowInChunk; j <= endRowInChunk; j++)
                    {
                        foreach (IppiROI wr in wellsPerRow[rowCoordinates[j]])
                        {
                            _parallelChunkWells[i].Add(wr);
                        }
                    }
                    //determine top-left corner as well as width and height of this chunk
                    int y_top    = wellsPerRow[rowCoordinates[0]][0].Y;
                    int y_bottom = wellsPerRow[rowCoordinates[endRowInChunk]][0].Y + wellsPerRow[rowCoordinates[endRowInChunk]][0].Height - 1;
                    int height   = y_bottom - y_top + 1;
                    System.Diagnostics.Debug.Assert(height > 1);
                    int x_left  = colCoordinates[0];
                    int x_right = colCoordinates[colCoordinates.Length - 1] + wellsPerRow[rowCoordinates[0]][0].Width - 1;
                    int width   = x_right - x_left + 1;
                    System.Diagnostics.Debug.Assert(width > 1);
                    _parallelImageRegions[i] = new IppiROI(x_left, y_top, width, height);
                    //Initialize marker buffer for this chunk
                    int bufferSize = 0;
                    IppHelper.IppCheckCall(cv.ippiLabelMarkersGetBufferSize_8u_C1R(_parallelImageRegions[i].Size, &bufferSize));
                    _parallelMarkerBuffers[i] = (byte *)Marshal.AllocHGlobal(bufferSize);
                    //initialize moment state for this chunk
                    fixed(IppiMomentState_64s **ppState = &_parallelMomentStates[i])
                    {
                        //let ipp decide whether to give accurate or fast results
                        IppHelper.IppCheckCall(ip.ippiMomentInitAlloc_64s(ppState, IppHintAlgorithm.ippAlgHintNone));
                    }
                }
                //determine each chunks start index in the fish output array based on the number of wells
                //in lower chunks
                _parallelCumWellCount = new int[_parallelChunks];
                for (int i = 1; i < _parallelChunks; i++)
                {
                    _parallelCumWellCount[i] = _parallelCumWellCount[i - 1] + _parallelChunkWells[i - 1].Count;
                }
            }
        }
Пример #11
0
        /// <summary>
        /// Extracts a fish (candidate) from an image by performing background subtraction, noise filtering, thresholding and closing to obtain a foreground
        /// followed by marker extraction.
        /// </summary>
        /// <param name="im">The image to extract the fish from</param>
        /// <param name="region">The ROI to search</param>
        /// <returns>The most likely fish blob or null if no suitable candidate was found</returns>
        BlobWithMoments ExtractFish(Image8 im, IppiROI region)
        {
            int nMarkers = 0;


            //Perform background subtraction - CASH BACKGROUND IMAGE POINTER - otherwise we actually do the whole costly
            //32f conversion twice - once for accessing the actual image, and once for accessing the stride...
            var bg = _bgModel.Background;

            IppHelper.IppCheckCall(cv.ippiAbsDiff_8u_C1R(im[region.TopLeft], im.Stride, bg[region.TopLeft], bg.Stride, _calc[region.TopLeft], _calc.Stride, region.Size));
            //remove noise via median filtering
            _mFiltSize.width  = region.Width - 2;
            _mFiltSize.height = region.Height - 2;
            IppHelper.IppCheckCall(ip.ippiFilterMedianWeightedCenter3x3_8u_C1R(_calc[region.X + 1, region.Y + 1], _calc.Stride,
                                                                               _bgSubtracted[region.X + 1, region.Y + 1], _bgSubtracted.Stride, _mFiltSize, 1));
            //Threshold and close
            Im2Bw(_bgSubtracted, _foreground, region);
            //Do as two step to get information back into _foreground
            _strel3x3.Dilate(_foreground, _calc, region);
            _strel3x3.Erode(_calc, _foreground, region);
            //Label connected components
            IppHelper.IppCheckCall(cv.ippiLabelMarkers_8u_C1IR(_foreground[region.TopLeft], _foreground.Stride, region.Size, 1, 254, IppiNorm.ippiNormInf, &nMarkers, _markerBuffer));
            //loop over returned markers and use ipp to extract blobs
            if (nMarkers > 0)
            {
                if (nMarkers > 254)
                {
                    nMarkers = 254;
                }
                //create or update our intermediate blob storage to store the required number of marker representations
                if (_blobsDetected == null || _blobsDetected.Length < nMarkers)
                {
                    _blobsDetected = new BlobWithMoments[nMarkers];
                }

                for (int i = 1; i <= nMarkers; i++)
                {
                    //label all pixels with the current marker as 255 and others as 0
                    IppHelper.IppCheckCall(ip.ippiCompareC_8u_C1R(_foreground[region.TopLeft], _foreground.Stride, (byte)i, _calc[region.TopLeft], _calc.Stride, region.Size, IppCmpOp.ippCmpEq));
                    //calculate image moments
                    IppHelper.IppCheckCall(ip.ippiMoments64f_8u_C1R(_calc[region.TopLeft], _calc.Stride, region.Size, _momentState));
                    //retrieve moments
                    double m00 = 0;
                    double m10 = 0;
                    double m01 = 0;
                    double m20 = 0;
                    double m02 = 0;
                    double m11 = 0;
                    double m30 = 0;
                    double m03 = 0;
                    double m21 = 0;
                    double m12 = 0;
                    ip.ippiGetSpatialMoment_64f(_momentState, 0, 0, 0, region.TopLeft, &m00);
                    //since our input image is not 0s and 1s but 0s and 255s we have to divide by 255 in order to re-normalize our moments
                    //System.Diagnostics.Debug.Assert(m00 % 255 == 0, "M00 was not a multiple of 255");
                    m00 /= 255;
                    //only retrieve other moments if this is a "fish candidate"
                    if (m00 >= MinArea)
                    {
                        ip.ippiGetSpatialMoment_64f(_momentState, 1, 0, 0, region.TopLeft, &m10);
                        m10 /= 255;
                        ip.ippiGetSpatialMoment_64f(_momentState, 0, 1, 0, region.TopLeft, &m01);
                        m01 /= 255;
                        ip.ippiGetSpatialMoment_64f(_momentState, 2, 0, 0, region.TopLeft, &m20);
                        m20 /= 255;
                        ip.ippiGetSpatialMoment_64f(_momentState, 0, 2, 0, region.TopLeft, &m02);
                        m02 /= 255;
                        ip.ippiGetSpatialMoment_64f(_momentState, 1, 1, 0, region.TopLeft, &m11);
                        m11 /= 255;
                        ip.ippiGetSpatialMoment_64f(_momentState, 3, 0, 0, region.TopLeft, &m30);
                        m30 /= 255;
                        ip.ippiGetSpatialMoment_64f(_momentState, 0, 3, 0, region.TopLeft, &m03);
                        m03 /= 255;
                        ip.ippiGetSpatialMoment_64f(_momentState, 2, 1, 0, region.TopLeft, &m21);
                        m21 /= 255;
                        ip.ippiGetSpatialMoment_64f(_momentState, 1, 2, 0, region.TopLeft, &m12);
                        m12 /= 255;
                        if (_blobsDetected[i - 1] == null)
                        {
                            _blobsDetected[i - 1] = new BlobWithMoments((long)m00, (long)m10, (long)m01,
                                                                        (long)m20, (long)m11, (long)m02, (long)m30, (long)m03, (long)m21, (long)m12);
                        }
                        else
                        {
                            _blobsDetected[i - 1].UpdateBlob((long)m00, (long)m10, (long)m01, (long)m20,
                                                             (long)m11, (long)m02, (long)m30, (long)m03, (long)m21, (long)m12);
                        }
                        //Determine bounding box of the blob. The following seems kinda retarded as Ipp must already
                        //have obtained that information before so maybe there is some way to actually retrieve it??
                        //Do linescans using ipp's sum function starting from the blobs centroid until we hit a line
                        //the sum of which is 0


                        int       xStart, xEnd, yStart, yEnd;
                        double    sum      = 1;
                        IppiPoint centroid = _blobsDetected[i - 1].Centroid;
                        xStart = centroid.x - 1;
                        xEnd   = centroid.x + 1;
                        yStart = centroid.y - 1;
                        yEnd   = centroid.y + 1;
                        //in the following loops we PRE-increment, whence we stop the loop if we are at one coordinate short of the ends
                        //find xStart
                        _bboxScan.width  = 1;
                        _bboxScan.height = region.Height;
                        while (sum > 0 && xStart > (region.X + 4))
                        {
                            xStart -= 5;
                            IppHelper.IppCheckCall(ip.ippiSum_8u_C1R(_calc[xStart, region.Y], _calc.Stride, _bboxScan, &sum));
                        }
                        xStart += 1;//we have a sum of 0, so go back one line towards the centroid
                        //find xEnd
                        sum = 1;
                        while (sum > 0 && xEnd < region.X + region.Width - 5)
                        {
                            xEnd += 5;
                            IppHelper.IppCheckCall(ip.ippiSum_8u_C1R(_calc[xEnd, region.Y], _calc.Stride, _bboxScan, &sum));
                        }
                        xEnd -= 1;//we have sum of 0, so go back one line towards the centroid
                        //find yStart - we can limit our x-search-space as we already have those boundaries
                        _bboxScan.width  = xEnd - xStart + 1;
                        _bboxScan.height = 1;
                        sum = 1;
                        while (sum > 0 && yStart > (region.Y + 4))
                        {
                            yStart -= 5;
                            IppHelper.IppCheckCall(ip.ippiSum_8u_C1R(_calc[xStart, yStart], _calc.Stride, _bboxScan, &sum));
                        }
                        yStart += 1;
                        //find yEnd - again limit summation to x-search-space
                        sum = 1;
                        while (sum > 0 && yEnd < region.Y + region.Height - 5)
                        {
                            yEnd += 5;
                            IppHelper.IppCheckCall(ip.ippiSum_8u_C1R(_calc[xStart, yEnd], _calc.Stride, _bboxScan, &sum));
                        }
                        yEnd -= 1;
                        _blobsDetected[i - 1].UpdateBoundingBox(xStart, yStart, xEnd - xStart + 1, yEnd - yStart + 1);
                    }
                    else
                    {
                        if (_blobsDetected[i - 1] == null)
                        {
                            _blobsDetected[i - 1] = new BlobWithMoments();
                        }
                        else
                        {
                            _blobsDetected[i - 1].ResetBlob();
                        }
                    }
                }
            }
            else
            {
                return(null);
            }
            //decide which of the detected objects is the fish
            //usually we pick the larger blob - however to avoid
            //tracking reflections on the wall, if the largest blob
            //and second largest blob are of comparable size
            //we pick the one which is closer to the center (as reflections
            //are always more eccentric)
            long maxArea        = 0;
            long secondMaxArea  = 0;
            int  maxIndex       = -1;
            int  secondMaxIndex = -1;

            for (int i = 0; i < nMarkers; i++)
            {
                if (_blobsDetected[i] == null)
                {
                    break;
                }
                //Note down the largest and second-largest blob - but only if those blobs aren't larger than the maxArea and if they eccentricity is at least MinEccentricity
                //this comparison allows that if we find two exactly same-sized blobs to consider both but to not consider any further blobs of this size (which we hopefully never have anyways)
                //Eccentricity and MaxAllowedArea checks removed at this point as they were mainly concieved to not track the laser.
                if (_blobsDetected[i].Area >= maxArea && _blobsDetected[i].Area > secondMaxArea /*&& blobsDetected[i].Area<=MaxAllowedArea && blobsDetected[i].Eccentricity>=MinEccentricity*/)
                {
                    secondMaxArea  = maxArea;
                    maxArea        = _blobsDetected[i].Area;
                    secondMaxIndex = maxIndex;
                    maxIndex       = i;
                }
            }

            if (maxArea < MinArea)
            {
                return(null);
            }
            else
            {
                //if our second-largest blob is at least two-thirds the size
                //of the largest blob we also consider distance and swap accordingly
                if ((float)secondMaxArea * 1.5 >= (float)maxArea)
                {
                    double distMax, distSecondMax;
                    distMax       = Distance.Euclidian(_blobsDetected[maxIndex].Centroid, DishCenter);
                    distSecondMax = Distance.Euclidian(_blobsDetected[secondMaxIndex].Centroid, DishCenter);
                    if (distMax > distSecondMax)
                    {
                        maxIndex = secondMaxIndex;
                    }
                }
                return(_blobsDetected[maxIndex]);
            }
        }
Пример #12
0
 /// <summary>
 /// Implements a "greater than" threshold like MATLABS
 /// im2bw function
 /// </summary>
 /// <param name="imIn">The image to threshold</param>
 /// <param name="imThresh">The image after thresholding</param>
 /// <param name="region">The ROI in which to perform the operation</param>
 /// <param name="threshold">The threshold to apply</param>
 void Im2Bw(Image8 imIn, Image8 imThresh, IppiROI region)
 {
     IppHelper.IppCheckCall(ip.ippiCompareC_8u_C1R(imIn[region.TopLeft], imIn.Stride, _threshold, imThresh[region.TopLeft], imThresh.Stride, region.Size, IppCmpOp.ippCmpGreater));
 }
Пример #13
0
        /// <summary>
        /// Tracks the tailsegments on the supplied image and returns
        /// the angles and distances of each segment from the tailstart
        /// </summary>
        /// <param name="image">The image on which to id the tail</param>
        /// <returns>NSegments number of TailPoints</returns>
        public TailSegment[] TrackTail(Image8 image)
        {
            lock (_regionLock)
            {
                //CURRENTLY ONLY DOWNWARD FACING TAILS ARE PROPERLY VERIFIED!!!
                //Generate background by morphology operation - 10 times a second or whenever our coordinates changed
                //in the default case where the tail is darker than the background, using closing operation otherwise opening
                if (_frameNumber % (_frameRate / 10) == 0 || !_bgValid)
                {
                    if (!_bgValid)
                    {
                        //if our regions changed, reblank our images!
                        ip.ippiSet_8u_C1R(0, _background.Image, _background.Stride, _background.Size);
                        ip.ippiSet_8u_C1R(0, _foreground.Image, _foreground.Stride, _foreground.Size);
                        ip.ippiSet_8u_C1R(0, _thresholded.Image, _thresholded.Stride, _thresholded.Size);
                    }
                    if (_lightOnDark)
                    {
                        BWImageProcessor.Open(image, _background, _calc1, _strel, _trackRegionOuter);
                    }
                    else
                    {
                        BWImageProcessor.Close(image, _background, _calc1, _strel, _trackRegionOuter);
                    }
                    _bgValid = true;
                }
                //Compute foreground
                IppHelper.IppCheckCall(cv.ippiAbsDiff_8u_C1R(_background[_trackRegionInner.TopLeft], _background.Stride, image[_trackRegionInner.TopLeft], image.Stride, _foreground[_trackRegionInner.TopLeft], _foreground.Stride, _trackRegionInner.Size));
                //Threshold
                BWImageProcessor.Im2Bw(_foreground, _thresholded, _trackRegionInner, _threshold);
                //Fill small holes
                BWImageProcessor.Close3x3(_thresholded, _thresholded, _calc1, _trackRegionOuter);
            }
            //Tracking concept: We track the angle of each segment end (TailStart+SegmentLength:TailEnd)
            //as the angular displacement from the previous segment end
            //To do this we define one full-circle with radius SegmentLength and an angle step corresponding
            //to ~1 Pixel. Then for each angle we pre-compute in InitializeScanPoints the corresponding x
            //and y offsets. From the full circle set we track -90 to +90 degrees around the angle of the
            //previous segment - for the initial segment that angle will be 0. For each segment we will return
            //the segment angle and its associated end-point coordinate
            var retval = new TailSegment[_nSegments];

            lock (_scanPointLock)
            {
                //The index of the absolute angle of the previous segment in our angle sweep array
                //determines which angles we sweep over for the next segment
                int prevAngleIndex   = _scanAngles.Length / 2;
                int nAnglesPerHalfPi = prevAngleIndex / 2;//this is the number of entries in the array that we have to walk to cover 90 degrees
                //we scan beginning with one segment length away from tail start and then walk
                //down the tail rather than using circles fixed arount the tailstart
                IppiPoint prevSegmentEnd = TailStart;

                //loop over tail segments
                for (int i = 0; i < _nSegments; i++)
                {
                    //loop over scan-points from -pi/2 to +pi/2 (i.e. nAnglesPerHalfPi) around previous segment angle
                    //interpreting the scan points as offsets around the previous tail segment
                    //end point
                    //then find tail angle of this segment
                    int pointsFound = 0;//the number of non-zero pixels identified
                    for (int j = -1 * nAnglesPerHalfPi; j < nAnglesPerHalfPi + 1; j++)
                    {
                        //Determine the index to scan - usually this will simply be "prevAngleIndex+j" however we
                        //have to loop around our angle array properly in case we screen more extreme angles
                        int currIndex = prevAngleIndex + j;
                        if (currIndex >= _scanAngles.Length)
                        {
                            currIndex = currIndex % _scanAngles.Length;
                        }
                        else if (currIndex < 0)
                        {
                            currIndex = currIndex + _scanAngles.Length;
                        }

                        //If current point is outside of the image, ignore it
                        IppiPoint pt = new IppiPoint(_coordinateOffsets[currIndex].x + prevSegmentEnd.x, _coordinateOffsets[currIndex].y + prevSegmentEnd.y);
                        if (pt.x < 0 || pt.y < 0 || pt.x >= _imageSize.width || pt.y >= _imageSize.height)
                        {
                            continue;
                        }
                        //if the value at the current point >0 we mark that angle as valid
                        //by storing the index in our anglestore
                        if (*_thresholded[pt] > 0)
                        {
                            _angleStore[pointsFound] = currIndex;
                            pointsFound++;
                        }
                    }
                    //find the median point in our angle store if we have more than 2 points stored
                    //the value in our angle store will directly give us the absolute screen angle of the segment
                    //as well as the coordinate offset which we can used together with the previous segment endpoint
                    //to compute the tail segment end coordinate - to get the delta angle we need to get the difference
                    //between the stored index and the previously stored index, prevAngleIndex
                    //after computing the appropriate return values we update prevAngleIndex with the index from the angle store
                    //and prevSegmentEnd with the returned coordinate of the current tail segment
                    double    deltaTailAngle;
                    IppiPoint coordinate;
                    int       pos;        //the index in the angle store that we determine to be the tail-center

                    if (pointsFound == 0) //we have lost the tail - no reason to keep tracking - fill remaining positions in array with NaNs for their angle
                    {
                        for (int k = i; k < _nSegments; k++)
                        {
                            deltaTailAngle = double.NaN;
                            coordinate     = new IppiPoint();
                            retval[k]      = new TailSegment(deltaTailAngle, coordinate);
                        }
                        break;
                    }
                    else if (pointsFound < 3)
                    {
                        pos = _angleStore[0];
                    }
                    else
                    {
                        //we want the angle at the median position of valid points
                        //we don't compute intermediate positions however (we should be able to afford this since our angle step is so small), so in case
                        //of an even number of points, the top of the lower half currently
                        //wins - since array indexing is zero based, we have to subtract 1
                        //from the computed median position
                        pos = _angleStore[(int)(Math.Ceiling(pointsFound / 2.0) - 1)];
                    }
                    coordinate     = new IppiPoint(prevSegmentEnd.x + _coordinateOffsets[pos].x, prevSegmentEnd.y + _coordinateOffsets[pos].y);
                    deltaTailAngle = (pos - prevAngleIndex) * _angleStep;
                    //the condition above of wrapping around the "angle circle":if (currIndex >= _scanAngles.Length)....
                    //results in one very large (close to +360 or -360) angle of opposite sign at the switch point
                    //however, since we usually only scan over offsets from +90 to -90 this case is easy to spot
                    //and remedy:
                    if (deltaTailAngle > 90)
                    {
                        deltaTailAngle = deltaTailAngle - 360;//this should create the appropriate tail angle btw. 0 and -90
                    }
                    else if (deltaTailAngle < -90)
                    {
                        deltaTailAngle = 360 + deltaTailAngle;//this should create the appropriate tail angle btw. 0 and +90
                    }
                    prevAngleIndex = pos;
                    prevSegmentEnd = coordinate;
                    retval[i]      = new TailSegment(deltaTailAngle, coordinate);
                }//loop over tail segments
            }
            _frameNumber++;
            return(retval);
        }
Пример #14
0
        /// <summary>
        /// Implements coordinate smoothing analogous to using filtfilt in matlab with a step function kernel
        /// (moving average with no peak displacement)
        /// </summary>
        /// <param name="src">The source track to smoothen</param>
        /// <param name="dst">The destination buffer for the smoothened track</param>
        /// <param name="windowSize">The windowsize for averaging</param>
        public void SmoothenTrack(CentroidBuffer src, CentroidBuffer dst, int windowSize)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(this.ToString());
            }
            if (src.Size.width != dst.Size.width)
            {
                throw new ArgumentException("Source and destination buffers need to have the same size!");
            }
            //For the internal buffers we require a size that fits both the coordinate buffer we
            //intend to filter as well as the border pixels required for filtering
            int borderSize = (int)Math.Ceiling(windowSize / 2.0);
            int reqSize    = src.Size.width + borderSize * 2;

            //Adjust internal buffers if necessary
            if (_calc1 == null)
            {
                _calc1 = new CentroidBuffer(reqSize);
                _calc2 = new CentroidBuffer(reqSize);
            }
            else if (_calc1.Size.width != reqSize)
            {
                _calc1.Dispose();
                _calc2.Dispose();
                _calc1 = new CentroidBuffer(reqSize);
                _calc2 = new CentroidBuffer(reqSize);
            }
            if (_kernel == null)
            {
                _kernelSize = windowSize;
                _kernel     = (float *)Marshal.AllocHGlobal(_kernelSize * 4);
                int i = 0;
                while (i < _kernelSize)
                {
                    _kernel[i++] = 1 / (float)_kernelSize;
                }
            }
            else if (_kernelSize != windowSize)
            {
                Marshal.FreeHGlobal((IntPtr)_kernel);
                _kernelSize = windowSize;
                _kernel     = (float *)Marshal.AllocHGlobal(_kernelSize * 4);
                int i = 0;
                while (i < _kernelSize)
                {
                    _kernel[i++] = 1 / (float)_kernelSize;
                }
            }
            //filter parameters
            IppiSize  regionSize  = new IppiSize(src.Size.width, 1);
            IppiPoint anchor      = new IppiPoint(borderSize, 0);
            IppiSize  kernelSize  = new IppiSize(_kernelSize, 1);
            float *   calc1XStart = (float *)((byte *)_calc1.Buffer + borderSize * 4);
            float *   calc1YStart = (float *)((byte *)_calc1.Buffer + borderSize * 4 + _calc1.Stride);
            float *   calc2XStart = (float *)((byte *)_calc2.Buffer + borderSize * 4);
            float *   calc2YStart = (float *)((byte *)_calc2.Buffer + borderSize * 4 + _calc2.Stride);

            //Copy src buffer adding borders
            IppHelper.IppCheckCall(ip.ippiCopyConstBorder_32f_C1R(src.Buffer, src.Stride, src.Size, _calc1.Buffer, _calc1.Stride, _calc1.Size, 0, borderSize, 0));
            //Fill calc2 to have borders ready after filtering
            IppHelper.IppCheckCall(ip.ippiSet_32f_C1R(0, _calc2.Buffer, _calc2.Stride, _calc2.Size));
            //filter x-coordinates with our kernel
            IppHelper.IppCheckCall(ip.ippiFilter_32f_C1R(calc1XStart, _calc1.Stride, calc2XStart, _calc2.Stride, regionSize, _kernel, kernelSize, anchor));
            //filter y-coordinates with our kernel
            IppHelper.IppCheckCall(ip.ippiFilter_32f_C1R(calc1YStart, _calc1.Stride, calc2YStart, _calc2.Stride, regionSize, _kernel, kernelSize, anchor));

            //invert buffer - mirror on vertical axis
            IppHelper.IppCheckCall(ip.ippiMirror_32f_C1R(_calc2.Buffer, _calc2.Stride, _calc1.Buffer, _calc1.Stride, _calc2.Size, IppiAxis.ippAxsVertical));
            //filter x-coordinates with our kernel - now on inverted buffer
            IppHelper.IppCheckCall(ip.ippiFilter_32f_C1R(calc1XStart, _calc1.Stride, calc2XStart, _calc2.Stride, regionSize, _kernel, kernelSize, anchor));
            //filter y-coordinates with our kernel - now on inverted buffer
            IppHelper.IppCheckCall(ip.ippiFilter_32f_C1R(calc1YStart, _calc1.Stride, calc2YStart, _calc2.Stride, regionSize, _kernel, kernelSize, anchor));
            //flip buffer back
            IppHelper.IppCheckCall(ip.ippiMirror_32f_C1R(_calc2.Buffer, _calc2.Stride, _calc1.Buffer, _calc1.Stride, _calc2.Size, IppiAxis.ippAxsVertical));
            //copy to dest
            IppHelper.IppCheckCall(ip.ippiCopy_32f_C1R(calc1XStart, _calc1.Stride, dst.Buffer, dst.Stride, dst.Size));
        }