예제 #1
0
 /// <summary>
 /// Processes one frame during the gradient
 /// </summary>
 private void RunGradient(BlobWithMoments fish)
 {
     //compute laser power based on distance from dish center
     if (fish != null)
     {
         var radius      = Math.Sqrt((fish.xc - DishCenter.x) * (fish.xc - DishCenter.x) + (fish.yc - DishCenter.y) * (fish.yc - DishCenter.y));
         var radFraction = radius / Radius;
         if (radFraction > 1)
         {
             radFraction = 1;
         }
         _laser.LaserPower = radFraction * (EdgeLaserPower - CenterLaserPower) + CenterLaserPower;
     }
     if (_currentPhaseFrame >= _gradFrames)
     {
         if (_currentTrial < _nTrials)
         {
             SwitchToPrePhase();
         }
         else
         {
             _laser.LaserPower = 0;
             _experimentPhase  = ExperimentPhase.Done;
         }
     }
     else
     {
         _currentPhaseFrame++;
     }
 }
예제 #2
0
        /// <summary>
        /// Finds the location of the laser beam in the camera image
        /// </summary>
        /// <param name="image">The current image in which to find the beam</param>
        /// <param name="background">The calculated background image</param>
        /// <param name="calc">Image buffer for intermediate calculations</param>
        /// <param name="foreground">Image buffer for foreground extraction</param>
        /// <returns></returns>
        private IppiPoint FindBeamLocation(Image8 image, Image8 background, Image8 calc, Image8 foreground)
        {
            IppiSize imageSize = new IppiSize(image.Width, image.Height);

            //Clear foreground and calc
            ip.ippiSet_8u_C1R(0, foreground.Image, foreground.Stride, imageSize);
            ip.ippiSet_8u_C1R(0, calc.Image, calc.Stride, imageSize);
            //Perform background subtraction
            cv.ippiAbsDiff_8u_C1R(image.Image, image.Stride, background.Image, background.Stride, calc.Image, calc.Stride, imageSize);
            //remove noise via 3x3 median filtering
            ip.ippiFilterMedianWeightedCenter3x3_8u_C1R(calc[2, 2], calc.Stride, foreground[2, 2], foreground.Stride, new IppiSize(image.Width - 4, image.Height - 4), 1);
            //threshold difference
            ip.ippiThreshold_LTVal_8u_C1IR(foreground.Image, foreground.Stride, imageSize, 111, 0);
            ip.ippiThreshold_GTVal_8u_C1IR(foreground.Image, foreground.Stride, imageSize, 110, 255);
            //perform closing operation - 2 step to put result back into foreground
            _strel3x3.Dilate(foreground, calc, imageSize);
            _strel3x3.Erode(calc, foreground, imageSize);
            //feature extraction
            int nMarkers = 0;

            cv.ippiLabelMarkers_8u_C1IR(foreground.Image, foreground.Stride, imageSize, 1, 254, IppiNorm.ippiNormInf, &nMarkers, _markerBuffer);
            BlobWithMoments[] blobsDetected = BlobWithMoments.ExtractBlobs(foreground, nMarkers, new IppiROI(0, 0, image.Width, image.Height));
            //re-threshold foreground for display
            ip.ippiThreshold_LTVal_8u_C1IR(foreground.Image, foreground.Stride, imageSize, 1, 0);   //6
            ip.ippiThreshold_GTVal_8u_C1IR(foreground.Image, foreground.Stride, imageSize, 0, 255); //5
            long maxArea  = 0;
            int  maxIndex = -1;

            if (blobsDetected != null)
            {
                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;
                    }
                }
                // System.Diagnostics.Debug.WriteLine("Area: {0}", blobsDetected[maxIndex].Area);
                // System.Diagnostics.Debug.WriteLine("Eccentricity: {0}", blobsDetected[maxIndex].Eccentricity);
                if (maxArea > 30)
                {
                    return(blobsDetected[maxIndex].Centroid);
                }
                else
                {
                    return(new IppiPoint(-1, -1));
                }
            }
            else
            {
                return(new IppiPoint(-1, -1));
            }
        }
예제 #3
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);
        }
예제 #4
0
 /// <summary>
 /// Write information to track file
 /// </summary>
 /// <param name="fish"></param>
 private void WriteTrackInfo(int frameNumber, BlobWithMoments fish, int originalPhase, int originalTrial)
 {
     if (fish != null)
     {
         _trackWriter.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", frameNumber, fish.Centroid.x, fish.Centroid.y, fish.Angle, originalPhase, originalTrial, _laser.LaserPower);
     }
     else
     {
         _trackWriter.WriteLine("NaN\tNaN\tNaN\tNaN\t{0}\t{1}\tNaN", originalPhase, originalTrial);
     }
 }
예제 #5
0
 /// <summary>
 /// Write information to track file
 /// </summary>
 /// <param name="fish"></param>
 private void WriteTrackInfo(int frameNumber, BlobWithMoments fish)
 {
     if (fish != null)
     {
         _trackWriter.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", frameNumber, fish.Centroid.x, fish.Centroid.y, fish.Angle, (int)_experimentPhase, _currentTrial, _laser.LaserPower);
     }
     else
     {
         _trackWriter.WriteLine("NaN\tNaN\tNaN\tNaN\t{0}\t{1}\tNaN", (int)_experimentPhase, _currentTrial);
     }
 }
예제 #6
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]);
            }
        }
예제 #7
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 (_originalType == OriginalType.Unknown)
            {
                //This should never happen...
                System.Diagnostics.Debug.WriteLine("Unknown original experiment type. Terminating experiment");
                return(false);
            }
            if (_scanner == null)
            {
                System.Diagnostics.Debug.WriteLine("Scanner was not initialized. Terminating experiment");
                return(false);
            }
            if (_experimentPhase == ExperimentPhase.Done)
            {
                return(false);
            }

            BlobWithMoments fish = null;

            fish = Tracker.Track(camImage);
            if (fish != null)
            {
                poi = new IppiPoint(fish.Centroid.x, fish.Centroid.y);
                try
                {
                    if (_scanner != null)
                    {
                        _scanner.Hit(poi.GetValueOrDefault());
                    }
                }
                catch (ArgumentOutOfRangeException)
                {
                    System.Diagnostics.Debug.WriteLine("Tried to hit coordinates outside scan table area.");
                    System.Diagnostics.Debug.WriteLine("Coordinates were: x={0}, y={1}", fish.xc, fish.yc);
                    System.Diagnostics.Debug.WriteLine("Terminating Experiment");
                    _laser.LaserPower = 0;
                    return(false);
                }
            }
            //Get the next data item
            string currentData = _originalTrackData.Dequeue();

            string[] currentItems = currentData.Split('\t');
            int      phase, trial;
            double   power = ProcessTrackItems(currentItems, out phase, out trial);

            if (double.IsNaN(power))
            {
                power = 0;
            }
            //Write track information
            WriteTrackInfo(frameNumber, fish, phase, trial);
            //Write images
            ip.ippiSet_8u_C1R(0, _camRegion.Image, _camRegion.Stride, _camRegion.Size);
            if (fish != null)
            {
                MainViewModel.CopyRegionImage(fish.Centroid, _camRegion, camImage);
            }
            _imageWriter.WriteFrame(_camRegion);
            if (fish != null)
            {
                MainViewModel.CopyRegionImage(fish.Centroid, _camRegion, Tracker.Background);
            }
            _backgroundWriter.WriteFrame(_camRegion);
            RunReplay(power);
            //If we have lost the fish, disengage the laser
            if (fish == null)
            {
                _laser.LaserPower = 0;
            }
            return(true);
        }
예제 #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>
        /// 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]);
            }
        }
예제 #11
0
        /// <summary>
        /// Given an image identifies a fish in it and returns
        /// it's properties such as position and heading
        /// </summary>
        /// <param name="image">The image to be tracked.</param>
        /// <returns>A blob representation of the fish</returns>
        public BlobWithMoments Track(Image8 image)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException("Tracker90mmDish", "Can't track after disposal!");
            }

            BlobWithMoments currentFish = null;

            if (RemoveCMOSISBrightLineArtefact)
            {
                //Do as two step to get information back into image
                _strel3x3.Erode(image, _calc, _imageROI);
                _strel3x3.Dilate(_calc, image, _imageROI);
            }

            if (_frame > FramesInitialBackground)
            {
                //if _previousFish is present we only check an area around it
                if (_previousFish != null)
                {
                    //compute search region
                    //top-left
                    tl_x = _previousFish.Centroid.x - _searchRegionSize;
                    tl_y = _previousFish.Centroid.y - _searchRegionSize;
                    tl_x = tl_x < 0 ? 0 : tl_x;
                    tl_y = tl_y < 0 ? 0 : tl_y;
                    //bottom-right
                    br_x = _previousFish.Centroid.x + _searchRegionSize;
                    br_y = _previousFish.Centroid.y + _searchRegionSize;
                    br_x = br_x >= image.Width ? image.Width - 1 : br_x;
                    br_y = br_y >= image.Height ? image.Height - 1 : br_y;
                    //update search region
                    _searchRegion.X      = tl_x;
                    _searchRegion.Y      = tl_y;
                    _searchRegion.Width  = br_x - tl_x + 1;
                    _searchRegion.Height = br_y - tl_y + 1;
                    //extract fish within region
                    currentFish = ExtractFish(image, _searchRegion);
                }
                else
                {
                    currentFish = ExtractFish(image, _imageROI);
                }
            }//If We are past initial background frames

            //create/update background and advance frame counter
            if (_frame == 0)
            {
                //Blank images - necessary specifically because of the median filter step
                //which otherwise will leave pixels in _bgSubtracted un-initialized
                ip.ippiSet_8u_C1R(0, Foreground.Image, Foreground.Stride, Foreground.Size);
                ip.ippiSet_8u_C1R(0, _calc.Image, _calc.Stride, _calc.Size);
                ip.ippiSet_8u_C1R(0, _bgSubtracted.Image, _bgSubtracted.Stride, _bgSubtracted.Size);
                _bgModel = new SelectiveUpdateBGModel(image, 1.0f / FramesInBackground);
            }
            else
            {
                //update background every nth frame only
                if (_frame % BGUpdateEvery == 0)
                {
                    if (currentFish == null)
                    {
                        _bgModel.UpdateBackground(image);
                    }
                    else
                    {
                        _bgModel.UpdateBackground(image, currentFish);
                    }
                }
            }
            //Update knowledge about previous fish
            //if current fish is trustworthy
            if (currentFish != null && currentFish.Area > FullTrustMinArea)
            {
                _previousFish = currentFish;
            }
            else
            {
                _previousFish = null;
            }

            _frame++;
            return(currentFish);
        }
예제 #12
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++;
            if (_scanner == null)
            {
                System.Diagnostics.Debug.WriteLine("Scanner was not initialized. Terminating experiment");
                return(false);
            }
            if (_experimentPhase == ExperimentPhase.Done)
            {
                return(false);
            }

            BlobWithMoments fish = null;

            fish = Tracker.Track(camImage);
            if (fish != null)
            {
                poi = new IppiPoint(fish.Centroid.x, fish.Centroid.y);
                try
                {
                    if (_scanner != null)
                    {
                        _scanner.Hit(poi.GetValueOrDefault());
                    }
                }
                catch (ArgumentOutOfRangeException)
                {
                    System.Diagnostics.Debug.WriteLine("Tried to hit coordinates outside scan table area.");
                    System.Diagnostics.Debug.WriteLine("Coordinates were: x={0}, y={1}", fish.xc, fish.yc);
                    System.Diagnostics.Debug.WriteLine("Terminating Experiment");
                    _laser.LaserPower = 0;
                    return(false);
                }
            }
            //Write track information including current phase, current laser power
            WriteTrackInfo(frameNumber, fish);
            //Write images
            ip.ippiSet_8u_C1R(0, _camRegion.Image, _camRegion.Stride, _camRegion.Size);
            if (fish != null)
            {
                MainViewModel.CopyRegionImage(fish.Centroid, _camRegion, camImage);
            }
            _imageWriter.WriteFrame(_camRegion);
            if (fish != null)
            {
                MainViewModel.CopyRegionImage(fish.Centroid, _camRegion, Tracker.Background);
            }
            _backgroundWriter.WriteFrame(_camRegion);
            //Depending on phase call process method to control laser power and switch phase if appropriate
            switch (_experimentPhase)
            {
            case ExperimentPhase.Habituation:
                RunHabituation();
                break;

            case ExperimentPhase.Pre:
                RunPrePhase();
                break;

            case ExperimentPhase.Gradient:
                RunGradient(fish);
                break;
            }
            //If we have lost the fish, disengage the laser
            if (fish == null)
            {
                _laser.LaserPower = 0;
            }
            return(true);
        }