/// <summary>
        /// Вычисление градиентной карты (многопоточная)
        /// </summary>
        /// <param name="image">Изображение</param>
        /// <param name="threadsNumber">Число потоков</param>
        /// <returns></returns>
        public override GreyImage Apply(GreyImage image, int threadsNumber)
        {
            try
            {
                if (image == null)
                    throw new ArgumentNullException("Null image in Apply");
                if (threadsNumber <= 0)
                    throw new ArgumentException("Error threadsNumber in Apply");

                this._gradientXMap = new GreyImage(image.Width, image.Height);
                this._gradientYMap = new GreyImage(image.Width, image.Height);

                this.Threads = new List<Thread>();
                int deltaI = image.Height / threadsNumber;
                int lowIndexI = 0;
                int highIndexI = lowIndexI + deltaI;

                for (int i = 0; i < threadsNumber; i++)
                {
                    if (i == threadsNumber - 1)
                        highIndexI = image.Height - 1;
                    MatrixFilterData matrixFilterData = new ThreadData.MatrixFilterData(image, lowIndexI, highIndexI, 0, image.Width - 1);
                    Thread thread = new Thread(new ParameterizedThreadStart(this.ApplyThread));
                    this.Threads.Add(thread);
                    this.Threads[i].Start(matrixFilterData);

                    lowIndexI = highIndexI;
                    highIndexI += deltaI;
                }
                WaitForThreads();
                return image;
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }
        /// <summary>
        /// Применение фильтра Гаусса к серому изображению (многопоточная)
        /// </summary>
        /// <param name="image">Серое изображение</param>
        /// <param name="threadsNumber">Число потоков</param>
        public override GreyImage Apply(GreyImage image, int threadsNumber)
        {
            try
            {
                if (image == null)
                    throw new ArgumentNullException("Null image in Apply");
                if (image.Height < this.Size)
                    throw new ArgumentException("Image height must be >= filter size");
                if (image.Width < this.Size)
                    throw new ArgumentException("Image width must be >= filter size");
                if (threadsNumber <= 0)
                    throw new ArgumentException("Error threadsNumber in Apply");

                this._copyImage = (GreyImage)image.Copy();
                if (this._copyImage == null)
                    throw new NullReferenceException("Null copy image in Apply");

                this.Threads = new List<Thread>();

                int deltaI = image.Height / threadsNumber;
                int filterSize = this.Size;
                int lowIndex = filterSize / 2;
                int lowIndexI = lowIndex;
                int highIndexI = lowIndexI + deltaI;
                int highIndexJ = image.Width - lowIndex;

                for (int i = 0; i < threadsNumber; i++)
                {
                    if (i == threadsNumber - 1)
                        highIndexI = image.Height - lowIndex;

                    MatrixFilterData matrixFilterData = new ThreadData.MatrixFilterData(image, lowIndexI, highIndexI, lowIndex, highIndexJ);
                    Thread thread = new Thread(new ParameterizedThreadStart(this.ApplyThread));
                    this.Threads.Add(thread);
                    this.Threads[i].Start(matrixFilterData);

                    lowIndexI = highIndexI;
                    highIndexI += deltaI;
                }
                WaitForThreads();

                return this._copyImage;
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }