/// <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 Image<Gray, float> image, int winSize = 10, float minEigVal = 0.3f, float minimalDistance = 3)
        {
            var strengthImg = new Image<Gray, float>(image.Size);

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

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

            var proc = new ParallelProcessor<bool, bool>(image.Size,
                                                         () => true,
                                                         (_, __, area) =>
                                                         {
                                                             Rectangle srcArea = new Rectangle
                                                             {
                                                                 X = 0,
                                                                 Y = area.Y,
                                                                 Width = image.Width,
                                                                 Height = area.Height + winSize
                                                             };
                                                             srcArea.Intersect(new Rectangle(new Point(), image.Size));

                                                             goodFeaturesToTrack(Dxx.GetSubRect(srcArea), Dxy.GetSubRect(srcArea), Dyy.GetSubRect(area),
                                                                                 winSize, minEigVal, strengthImg.GetSubRect(srcArea));
                                                         },
                                                         new ParallelOptions2D {  /*ForceSequential = true*/ },
                                                         winSize);

            proc.Process(true);

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

            List<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>
        /// Find non-zero locations in the image.
        /// </summary>
        /// <typeparam name="TDepth">Channel type.</typeparam>
        /// <param name="img">Image.</param>
        /// <param name="values">Values for the found locations.</param>
        /// <returns>List of found non-zero locations.</returns>
        public static List <Point> FindNonZero <TDepth>(this Image <Gray, TDepth> img, out List <TDepth> values)
            where TDepth : struct
        {
            FindNonZeroFunc findNonZeroFunc = null;

            if (findNonZeroFuncs.TryGetValue(img.ColorInfo.ChannelType, out findNonZeroFunc) == false)
            {
                throw new NotSupportedException(string.Format("Can not perform FindNonZero on an image of type {0}", img.ColorInfo.ChannelType.Name));
            }

            var locations = new List <Point>();
            var _values   = new List <TDepth>();

            var proc = new ParallelProcessor <IImage, bool>(img.Size,
                                                            () => true,
                                                            (_src, _, area) =>
            {
                List <Point> locationsPatch;
                IList valuesPatch;
                findNonZeroFunc(img.GetSubRect(area), out locationsPatch, out valuesPatch);

                lock (locations)
                    lock (_values)
                    {
                        locationsPatch.ForEach(x =>
                        {
                            locations.Add(x + area.Location);
                        });

                        _values.AddRange(valuesPatch  as IList <TDepth>);
                    }
            });

            proc.Process(img);

            values = _values;
            return(locations);
        }
Beispiel #3
0
        internal static IImage Convolve(IImage src, IImage kernel, ConvolutionBorder options)
        {
            ConvolutionFunc convolutionFunc = null;

            if (convolutionFuncs.TryGetValue(src.ColorInfo.ChannelType, out convolutionFunc) == false)
            {
                throw new NotSupportedException(string.Format("Can not perform spatial convolution on an image of type {0}", src.ColorInfo.ChannelType.Name));
            }

            Rectangle validRegion;
            var       preparedSrc = prepareSourceImage(src, kernel.Size, options, out validRegion);

            var proc = new ParallelProcessor <IImage, IImage>(src.Size,
                                                              () => preparedSrc.CopyBlank(), //in-place convolution is not supported due to parallel processing (junction patches handling)
                                                              (_src, _dest, area) =>
            {
                Rectangle srcArea = new Rectangle
                {
                    X      = 0,
                    Y      = area.Y,
                    Width  = _src.Width,
                    Height = area.Height + kernel.Height                                     //get area sufficient to process with the selected kernel; area.Height is processed
                };
                //srcArea.Inflate(-kernel.Width , -kernel.Height );
                srcArea.Width  -= kernel.Width;
                srcArea.Height -= kernel.Height;

                srcArea.Intersect(new Rectangle(new Point(), _src.Size));

                convolutionFunc(_src, srcArea, _dest, new Point(kernel.Width / 2, area.Y + kernel.Height / 2), kernel);
            }
                                                              /*,new ParallelOptions2D { ForceSequential = true }*/);

            var dest = proc.Process(preparedSrc);

            return(dest.GetSubRect(validRegion));
        }
Beispiel #4
0
        private static void calculate(MathOps mathOpIdx, IImage src1, IImage src2, IImage dest, Image <Gray, byte> mask = null)
        {
            Debug.Assert(src1.ColorInfo.Equals(src2.ColorInfo) && src1.Size.Equals(src2.Size));

            if (mask == null)
            {
                mask = new Image <Gray, byte>(dest.Width, dest.Height);
                mask.SetValue(new Gray(255));
            }

            var mathOperationOnTypes = mathOperatorFuncs[(int)mathOpIdx];

            MathOpFunc mathOpFunc = null;

            if (mathOperationOnTypes.TryGetValue(src1.ColorInfo.ChannelType, out mathOpFunc) == false)
            {
                throw new Exception(string.Format("Math operation {0} can not be executed on an image of type {1}", mathOpIdx.ToString(), src1.ColorInfo.ChannelType));
            }

            var proc = new ParallelProcessor <bool, bool>(dest.Size,
                                                          () =>
            {
                return(true);
            },
                                                          (bool _, bool __, Rectangle area) =>
            {
                var src1Patch = src1.GetSubRect(area);
                var src2Patch = src2.GetSubRect(area);
                var destPatch = dest.GetSubRect(area);
                var maskPatch = mask.GetSubRect(area);

                mathOpFunc(src1Patch, src2Patch, destPatch, maskPatch);
            }
                                                          /*,new ParallelOptions { ForceSequential = true}*/);

            proc.Process(true);
        }
Beispiel #5
0
        private static IImage magnitude(IImage imageA, IImage imageB)
        {
            Type channelType = imageA.ColorInfo.ChannelType;

            MagnitudeFunc magnitudeFunc = null;

            if (magnitudeFuncs.TryGetValue(channelType, out magnitudeFunc) == false)
            {
                throw new NotSupportedException(string.Format("Can not calculate magnitude from a image of type {0}", channelType));
            }

            var proc = new ParallelProcessor <bool, IImage>(imageA.Size,
                                                            () =>
            {
                return(Image.Create(imageA.ColorInfo, imageA.Width, imageA.Height));
            },
                                                            (bool _, IImage dest, Rectangle area) =>
            {
                magnitudeFunc(imageA.GetSubRect(area), imageB.GetSubRect(area), dest.GetSubRect(area));
            }
                                                            /*, new ParallelOptions { ForceSequential = true }*/);

            return(proc.Process(true));
        }
Beispiel #6
0
        private Image <Gray, TDepth> BackProject <TDepth>(Image <Gray, TDepth>[] srcs)
            where TDepth : struct
        {
            var destColor = ColorInfo.GetInfo <Gray, TDepth>();

            BackpropagateFunc backpropagateFunc = null;

            if (backpropagateFuncs.TryGetValue(destColor.ChannelType, out backpropagateFunc) == false)
            {
                throw new Exception(string.Format("Back-propagate function does not support an image of type {0}", destColor.ChannelType));
            }

            var imgSize = srcs[0].Size;

            var proc = new ParallelProcessor <IImage[], IImage>(imgSize,
                                                                () => //executed once
            {
                return(Image.Create(destColor, imgSize.Width, imgSize.Height));
            },

                                                                (IImage[] srcImgs, IImage destImg, Rectangle area) => //executed for each thread
            {
                var channelPatches = new IImage[srcImgs.Length];
                for (int i = 0; i < channelPatches.Length; i++)
                {
                    channelPatches[i] = srcImgs[i].GetSubRect(area);
                }

                var projPatch = destImg.GetSubRect(area);

                backpropagateFunc(this, channelPatches, projPatch);
            }
                                                                /*,new ParallelOptions { ForceSequential = true}*/);

            return(proc.Process(srcs) as Image <Gray, TDepth>);
        }
        private static void calculate(MathOps mathOpIdx, IImage src1, IImage src2, IImage dest, Image<Gray, byte> mask = null)
        {
            Debug.Assert(src1.ColorInfo.Equals(src2.ColorInfo) && src1.Size.Equals(src2.Size));

            if (mask == null)
            {
                mask = new Image<Gray, byte>(dest.Width, dest.Height);
                mask.SetValue(new Gray(255));
            }

            var mathOperationOnTypes = mathOperatorFuncs[(int)mathOpIdx];

            MathOpFunc mathOpFunc = null;
            if (mathOperationOnTypes.TryGetValue(src1.ColorInfo.ChannelType, out mathOpFunc) == false)
                throw new Exception(string.Format("Math operation {0} can not be executed on an image of type {1}", mathOpIdx.ToString(), src1.ColorInfo.ChannelType));

            var proc = new ParallelProcessor<bool, bool>(dest.Size,
                                                            () =>
                                                            {
                                                                return true;
                                                            },
                                                            (bool _, bool __, Rectangle area) =>
                                                            {
                                                                var src1Patch = src1.GetSubRect(area);
                                                                var src2Patch = src2.GetSubRect(area);
                                                                var destPatch = dest.GetSubRect(area);
                                                                var maskPatch = mask.GetSubRect(area);

                                                                mathOpFunc(src1Patch, src2Patch, destPatch, maskPatch);
                                                            }
                                                            /*,new ParallelOptions { ForceSequential = true}*/);

            proc.Process(true);
        }
Beispiel #8
0
        /// <summary>
        /// Two dimensional Fast Fourier Transform.
        /// </summary>
        /// <param name="width">Image width.</param>
        /// <param name="height">Image height.</param>
        /// <param name="stride">Image stride.</param>
        /// <param name="data">Data to transform.</param>
        /// <param name="direction">Transformation direction.</param>
        ///
        /// <remarks><para><note>The method accepts <paramref name="data"/> array of 2<sup>n</sup> size
        /// only in each dimension, where <b>n</b> may vary in the [1, 14] range. For example, 16x16 array
        /// is valid, but 15x15 is not.</note></para></remarks>
        ///
        /// <exception cref="ArgumentException">Incorrect data length.</exception>
        ///
        public unsafe static void FFT2(ComplexF *data, int width, int height, int stride, Direction direction)
        {
            const int MIN_PATCH_SIZE = 32; //how much rows/columns should one thread process

            int k = height;
            int n = width;

            // check data size
            if (
                (!AForge.Math.Tools.IsPowerOf2(k)) ||
                (!AForge.Math.Tools.IsPowerOf2(n)) ||
                (k < minLength) || (k > maxLength) ||
                (n < minLength) || (n > maxLength)
                )
            {
                throw new ArgumentException("Incorrect data length.");
            }

            // process rows
            var procRow = new ParallelProcessor <bool, bool>(new Size(1 /*does not matter*/, height),
                                                             () => true,
                                                             (_, __, area) =>
            {
                ComplexF *dataPatchPtr = data + area.Y * stride / sizeof(ComplexF);                                                 //get row

                for (int i = 0; i < area.Height; i++)
                {
                    // transform it
                    FourierTransform.FFT(dataPatchPtr, n, direction);

                    dataPatchPtr += stride / sizeof(ComplexF);
                }
            },
                                                             new ParallelOptions2D {
                ParallelTrigger = (size) => size.Height >= MIN_PATCH_SIZE
                                  /*,ForceSequential = true*/ }
                                                             );

            // process columns
            //(y and x are swaped => proc thinks it is diving horizontal pacthes but instead we are using them as vertical ones)
            var procCol = new ParallelProcessor <bool, bool>(new Size(1 /*does not matter*/, width),
                                                             () => true,
                                                             (_, __, area) =>
            {
                ComplexF *dataPatchPtr = &data[area.Y];                                                 //get column

                fixed(ComplexF * _col = new ComplexF[k])
                {
                    ComplexF *col = _col;

                    for (int j = 0; j < area.Height; j++)
                    {
                        // copy column
                        ComplexF *dataColPtr = &dataPatchPtr[j];
                        for (int i = 0; i < k; i++)
                        {
                            col[i]      = *dataColPtr;
                            dataColPtr += stride / sizeof(ComplexF);
                        }

                        // transform it
                        FourierTransform.FFT(col, k, direction);

                        // copy back
                        dataColPtr = &dataPatchPtr[j];
                        for (int i = 0; i < k; i++)
                        {
                            *dataColPtr = col[i];
                            dataColPtr += stride / sizeof(ComplexF);
                        }
                    }
                }
            },
                                                             new ParallelOptions2D {
                ParallelTrigger = (size) => size.Height >= MIN_PATCH_SIZE
                                  /*,ForceSequential = true */ }
                                                             );

            procRow.Process(true);
            procCol.Process(true);
        }
        internal override void Compute(IImage image)
        {
            Reset();

            RawMomentsFunc rawMomentsFunc = null;
            if (rawMomentsFuncs.TryGetValue(image.ColorInfo.ChannelType, out rawMomentsFunc) == false)
                throw new Exception(string.Format("Raw moments can not be calculated of an image of type {0}", image.ColorInfo.ChannelType));

            object sync = new object();
            ParallelProcessor<IImage, bool> proc = new ParallelProcessor<IImage, bool>(image.Size,
                                                                                      () =>
                                                                                      {
                                                                                          return true;
                                                                                      },
                                                                                      (IImage src, bool _, Rectangle area) => //called for every thread
                                                                                      {
                                                                                          IntPoint offset = new IntPoint(area.X, area.Y);

                                                                                          float m00, m01, m10, m11, m02, m20, m12, m21, m30, m03;
                                                                                          rawMomentsFunc(src.GetSubRect(area), offset, Order,
                                                                                                         out m00, out m01, out m10,
                                                                                                         out m11, out m02, out m20,
                                                                                                         out m12, out m21, out m30, out m03);

                                                                                          lock (sync)
                                                                                          {
                                                                                              this.M00 += m00; this.M01 += m01; this.M10 += m10;
                                                                                              this.M11 += m11; this.M02 += m02; this.M20 += m20;
                                                                                              this.M12 += m12; this.M21 += m21; this.M30 += m30; this.M03 += m03;
                                                                                          }
                                                                                      }
                                                                                      /*,new ParallelOptions { ForceSequential = true}*/);

            proc.Process(image);

            InvM00 = 1f / M00;
            CenterX = M10 * InvM00;
            CenterY = M01 * InvM00;
        }
        /// <summary>
        /// Two dimensional Fast Fourier Transform.
        /// </summary>
        /// <param name="width">Image width.</param> 
        /// <param name="height">Image height.</param>
        /// <param name="stride">Image stride.</param>
        /// <param name="data">Data to transform.</param>
        /// <param name="direction">Transformation direction.</param>
        /// 
        /// <remarks><para><note>The method accepts <paramref name="data"/> array of 2<sup>n</sup> size
        /// only in each dimension, where <b>n</b> may vary in the [1, 14] range. For example, 16x16 array
        /// is valid, but 15x15 is not.</note></para></remarks>
        /// 
        /// <exception cref="ArgumentException">Incorrect data length.</exception>
        /// 
        public static unsafe void FFT2(ComplexF* data, int width, int height, int stride, Direction direction)
        {
            const int MIN_PATCH_SIZE = 32; //how much rows/columns should one thread process

            int k = height;
            int n = width;

            // check data size
            if (
                    (!AForge.Math.Tools.IsPowerOf2(k)) ||
                    (!AForge.Math.Tools.IsPowerOf2(n)) ||
                    (k < minLength) || (k > maxLength) ||
                    (n < minLength) || (n > maxLength)
                    )
            {
                throw new ArgumentException("Incorrect data length.");
            }

            // process rows
            var procRow = new ParallelProcessor<bool, bool>(new Size(1 /*does not matter*/, height),
                                                            () => true,
                                                            (_, __, area) =>
                                                            {
                                                                ComplexF* dataPatchPtr = data + area.Y * stride / sizeof(ComplexF); //get row

                                                                for (int i = 0; i < area.Height; i++)
                                                                {
                                                                    // transform it
                                                                    FourierTransform.FFT(dataPatchPtr, n, direction);

                                                                    dataPatchPtr += stride / sizeof(ComplexF);
                                                                }
                                                            },
                                                            new ParallelOptions2D { ParallelTrigger = (size) => size.Height >= MIN_PATCH_SIZE
                                                                                  /*,ForceSequential = true*/}
                                                              );

            // process columns
            //(y and x are swaped => proc thinks it is diving horizontal pacthes but instead we are using them as vertical ones)
            var procCol = new ParallelProcessor<bool, bool>(new Size(1 /*does not matter*/, width),
                                                            () => true,
                                                            (_, __, area) =>
                                                            {
                                                                ComplexF* dataPatchPtr = &data[area.Y]; //get column

                                                                fixed (ComplexF* _col = new ComplexF[k])
                                                                {
                                                                    ComplexF* col = _col;

                                                                    for (int j = 0; j < area.Height; j++)
                                                                    {
                                                                        // copy column
                                                                        ComplexF* dataColPtr = &dataPatchPtr[j];
                                                                        for (int i = 0; i < k; i++)
                                                                        {
                                                                            col[i] = *dataColPtr;
                                                                            dataColPtr += stride / sizeof(ComplexF);
                                                                        }

                                                                        // transform it
                                                                        FourierTransform.FFT(col, k, direction);

                                                                        // copy back
                                                                        dataColPtr = &dataPatchPtr[j];
                                                                        for (int i = 0; i < k; i++)
                                                                        {
                                                                            *dataColPtr = col[i];
                                                                            dataColPtr += stride / sizeof(ComplexF);
                                                                        }
                                                                    }
                                                                }
                                                            },
                                                            new ParallelOptions2D { ParallelTrigger = (size) => size.Height >= MIN_PATCH_SIZE
                                                                                 /*,ForceSequential = true */}
                                                            );

               procRow.Process(true);
               procCol.Process(true);
        }