Beispiel #1
0
        /// <summary>
        /// Searches the image for the good features to track.
        /// <para>For each location a Hessian matrix is made and min eig-value is compared against threshold.</para>
        /// </summary>
        /// <param name="image">Image.</param>
        /// <param name="winSize">Window size.</param>
        /// <param name="minEigVal">Minimum eigen value.</param>
        /// <param name="minimalDistance">Minimum distance from two features.</param>
        /// <returns>List of locations that have eigen value larger than <paramref name="minEigVal"/>.</returns>
        public static List <Point> GoodFeaturesToTrack(this Gray <float>[,] image, int winSize = 10, float minEigVal = 0.3f, float minimalDistance = 3)
        {
            var strengthImg = image.CopyBlank();

            var Dx = image.Sobel(1, 0, 3);
            var Dy = image.Sobel(0, 1, 3);

            var Dxx = Dx.MulFloat(Dx).MakeIntegral();
            var Dxy = Dx.MulFloat(Dy).MakeIntegral();
            var Dyy = Dy.MulFloat(Dy).MakeIntegral();

            goodFeaturesToTrack(Dxx, Dxy, Dyy,
                                winSize, minEigVal, strengthImg);

            var filteredStrengthImg = strengthImg.SupressNonMaxima();
            //var filteredStrengthImg = strengthImg;

            IList <float> values;
            var           locations = filteredStrengthImg.FindNonZero(out values);

            var sortedFeatures = locations.Zip(values, (f, s) => new { f, s })
                                 .OrderByDescending(x => x.s)
                                 .Select(x => x.f)
                                 .ToList();

            sortedFeatures = sortedFeatures.EnforceMinimalDistance(minimalDistance);

            return(sortedFeatures);
        }
Beispiel #2
0
        /// <summary>
        /// Does non-maxima supression for the following gray image. Can be useful for detections filtering (e.g. post-processing output from Harris detector).
        /// </summary>
        /// <param name="img">Image.</param>
        /// <param name="radius">Non-maxima supression radius (kernel radius).</param>
        /// <param name="discardValue">The value will be discarded (0 - for black).</param>
        /// <returns>Processed image.</returns>
        public static Gray <float>[,] SupressNonMaxima(this Gray <float>[,] img, int radius = 3, int discardValue = 0)
        {
            var dest = img.CopyBlank();

            SupressNonMaxima(img, dest, radius);

            return(dest);
        }
        /// <summary>
        /// Computes gradient orientations from the color image. Orientation from the channel which has the maximum gradient magnitude is taken as the orientation for a location.
        /// </summary>
        /// <param name="frame">Image.</param>
        /// <param name="magnitudeSqrImage">Squared magnitude image.</param>
        /// <param name="minValidMagnitude">Minimal valid magnitude.</param>
        /// <returns>Orientation image (angles are in degrees).</returns>
        public static unsafe Gray<int>[,] Compute(Bgr<byte>[,] frame, out Gray<int>[,] magnitudeSqrImage, int minValidMagnitude)
        {
            var minSqrMagnitude = minValidMagnitude * minValidMagnitude;

            var orientationImage = new Gray<int>[frame.Height(), frame.Width()];
            var _magnitudeSqrImage = orientationImage.CopyBlank();

            using (var uFrame = frame.Lock())
            {
                ParallelLauncher.Launch(thread =>
                {
                    computeColor(thread, (byte*)uFrame.ImageData, uFrame.Stride, orientationImage, _magnitudeSqrImage, minSqrMagnitude);
                },
                frame.Width() - 2 * kernelRadius, frame.Height() - 2 * kernelRadius);
            }

            magnitudeSqrImage = _magnitudeSqrImage;
            return orientationImage;
        }
        private static Gray<byte>[,] SpreadOrientations(Gray<byte>[,] quantizedOrientationImage, int neghborhood)
        {
            var destImg = quantizedOrientationImage.CopyBlank();

            using (var uQuantizedOrientationImage = quantizedOrientationImage.Lock())
            using(var uDestImg = destImg.Lock())
            {
                byte* srcImgPtr = (byte*)uQuantizedOrientationImage.ImageData;
                int imgStride = uQuantizedOrientationImage.Stride;
                byte* destImgPtr = (byte*)uDestImg.ImageData;

                int imgHeight = uDestImg.Height;
                int imgWidth = uDestImg.Width;

                for (int row = 0; row < neghborhood; row++)
                {
                    int subImageHeight = imgHeight - row;
                    for (int col = 0; col < neghborhood; col++)
                    {
                        OrImageBits(&srcImgPtr[col], destImgPtr,
                                    imgStride,
                                    imgWidth - col, subImageHeight);
                    }

                    srcImgPtr += imgStride;
                }
            }
           
            return destImg;
        }
        /// <summary>
        /// Take only those orientations that have MINIMAL_NUM_OF_SAME_ORIENTED_PIXELS in 3x3 negborhood.
        /// Performs angle transformation into binary form ([0..7] -> [1, 2, 4, 8, ..., 128]) as well.
        /// </summary>
        /// <param name="qunatizedOrientionImg">Quantized orientation image where angles are represented by lables [0..GlobalParameters.NUM_OF_QUNATIZED_ORIENTATIONS] (invalid orientation label included).</param>
        /// <param name="minSameOrientations">Minimal number of same orientations for 3x3 neigborhood. The range is: [0..9] (3x3 neigborhood).</param>
        private static Gray<byte>[,] RetainImportantQuantizedOrientations(Gray<byte>[,] qunatizedOrientionImg, int minSameOrientations)
        {
            if (minSameOrientations < 0 || minSameOrientations > 9 /*3x3 neigborhood*/)
                throw new Exception("Minimal number of same orientations should be in: [0..9].");

            var quantizedFilteredOrient = qunatizedOrientionImg.CopyBlank();

            using (var uQunatizedOrientionImg = qunatizedOrientionImg.Lock())
            using (var uQuantizedFilteredOrient = quantizedFilteredOrient.Lock())
            {
                //debugImg = new Image<Hsv, byte>(orientDegImg.Width, orientDegImg.Height);
                //debugImg = null;
                int qOrinetStride = uQunatizedOrientionImg.Stride;
                int qOrinetAllign = uQunatizedOrientionImg.Stride - uQunatizedOrientionImg.Width;

                byte* qOrinetUnfilteredPtr = (byte*)uQunatizedOrientionImg.ImageData + qOrinetStride + 1;
                byte* qOrinetFilteredPtr = (byte*)uQuantizedFilteredOrient.ImageData + qOrinetStride + 1;

                //Debug.Assert(qunatizedOrientionImg.Stride == quantizedFilteredOrient.Stride);

                int imgWidth = uQunatizedOrientionImg.Width;
                int imgHeight = uQunatizedOrientionImg.Height;

                for (int j = 1; j < imgHeight - 1; j++)
                {
                    for (int i = 1; i < imgWidth - 1; i++)
                    {
                        if (*qOrinetUnfilteredPtr != INVALID_QUANTIZED_ORIENTATION)
                        {
                            byte[] histogram = new byte[INVALID_QUANTIZED_ORIENTATION + 1]; //gleda se susjedstvo 3x3

                            histogram[qOrinetUnfilteredPtr[-qOrinetStride - 1]]++; histogram[qOrinetUnfilteredPtr[-qOrinetStride + 0]]++; histogram[qOrinetUnfilteredPtr[-qOrinetStride + 1]]++;
                            histogram[qOrinetUnfilteredPtr[-1]]++; histogram[qOrinetUnfilteredPtr[0]]++; histogram[qOrinetUnfilteredPtr[+1]]++;
                            histogram[qOrinetUnfilteredPtr[+qOrinetStride - 1]]++; histogram[qOrinetUnfilteredPtr[+qOrinetStride + 0]]++; histogram[qOrinetUnfilteredPtr[+qOrinetStride + 1]]++;

                            int maxBinVotes = 0; byte quantizedAngle = 0;
                            for (byte histBinIdx = 0; histBinIdx < GlobalParameters.NUM_OF_QUNATIZED_ORIENTATIONS /*discard invalid orientation*/; histBinIdx++)
                            {
                                if (histogram[histBinIdx] > maxBinVotes)
                                {
                                    maxBinVotes = histogram[histBinIdx];
                                    quantizedAngle = histBinIdx;
                                }
                            }

                            if (maxBinVotes >= minSameOrientations)
                                *qOrinetFilteredPtr = (byte)(1 << quantizedAngle); //[1,2,4,8...128] (8 orientations)

                            //*qOrinetFilteredPtr = (byte)(1 << *qOrinetUnfilteredPtr); //[1,2,4,8...128] (8 orientations)
                            //debugImg[j, i] = new Hsv((*qOrinetFilteredPtr-1) * 35, 100, 100);
                        }

                        qOrinetUnfilteredPtr++;
                        qOrinetFilteredPtr++;
                    }

                    qOrinetUnfilteredPtr += 1 + qOrinetAllign + 1;
                    qOrinetFilteredPtr += 1 + qOrinetAllign + 1; //preskoči zadnji piksel, poravnanje, i početni piksel
                }
            }

            //magnitudeImg.Save("magnitude.bmp");
            //quantizedFilteredOrient.Save("quantizedImg.bmp");
            return quantizedFilteredOrient;
        }