/// <inheritdoc/>
        public void Apply(ImageBase target, ImageBase source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle))
        {
            try
            {
                float[] pixels = new float[width * height * 4];
                target.SetPixels(width, height, pixels);

                if (sourceRectangle == Rectangle.Empty)
                {
                    sourceRectangle = source.Bounds;
                }

                // We don't want to affect the original source pixels so we make clone here.
                ImageFrame frame = source as ImageFrame;
                Image temp = frame != null ? new Image(frame) : new Image((Image)source);
                this.OnApply(temp, target, target.Bounds, sourceRectangle);

                targetRectangle = target.Bounds;
                this.numRowsProcessed = 0;
                this.totalRows = targetRectangle.Bottom;

                if (this.Parallelism > 1)
                {
                    int partitionCount = this.Parallelism;

                    Task[] tasks = new Task[partitionCount];

                    for (int p = 0; p < partitionCount; p++)
                    {
                        int current = p;
                        tasks[p] = Task.Run(() =>
                        {
                            int batchSize = targetRectangle.Bottom / partitionCount;
                            int yStart = current * batchSize;
                            int yEnd = current == partitionCount - 1 ? targetRectangle.Bottom : yStart + batchSize;

                            this.Apply(target, temp, targetRectangle, sourceRectangle, yStart, yEnd);
                        });
                    }

                    Task.WaitAll(tasks);
                }
                else
                {
                    this.Apply(target, temp, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom);
                }

                this.AfterApply(temp, target, target.Bounds, sourceRectangle);
            }
            catch (Exception ex)
            {

                throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
            }
        }
예제 #2
0
        /// <summary>
        /// Finds the bounding rectangle based on the first instance of any color component other
        /// than the given one.
        /// </summary>
        /// <param name="bitmap">
        /// The <see cref="Image"/> to search within.
        /// </param>
        /// <param name="componentValue">
        /// The color component value to remove.
        /// </param>
        /// <param name="channel">
        /// The <see cref="RgbaComponent"/> channel to test against.
        /// </param>
        /// <returns>
        /// The <see cref="Rectangle"/>.
        /// </returns>
        public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
        {
            const float Epsilon = .00001f;
            int width = bitmap.Width;
            int height = bitmap.Height;
            Point topLeft = new Point();
            Point bottomRight = new Point();

            Func<ImageBase, int, int, float, bool> delegateFunc;

            // Determine which channel to check against
            switch (channel)
            {
                case RgbaComponent.R:
                    delegateFunc = (imageBase, x, y, b) => Math.Abs(imageBase[x, y].R - b) > Epsilon;
                    break;

                case RgbaComponent.G:
                    delegateFunc = (imageBase, x, y, b) => Math.Abs(imageBase[x, y].G - b) > Epsilon;
                    break;

                case RgbaComponent.A:
                    delegateFunc = (imageBase, x, y, b) => Math.Abs(imageBase[x, y].A - b) > Epsilon;
                    break;

                default:
                    delegateFunc = (imageBase, x, y, b) => Math.Abs(imageBase[x, y].B - b) > Epsilon;
                    break;
            }

            Func<ImageBase, int> getMinY = imageBase =>
            {
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        if (delegateFunc(imageBase, x, y, componentValue))
                        {
                            return y;
                        }
                    }
                }

                return 0;
            };

            Func<ImageBase, int> getMaxY = imageBase =>
            {
                for (int y = height - 1; y > -1; y--)
                {
                    for (int x = 0; x < width; x++)
                    {
                        if (delegateFunc(imageBase, x, y, componentValue))
                        {
                            return y;
                        }
                    }
                }

                return height;
            };

            Func<ImageBase, int> getMinX = imageBase =>
            {
                for (int x = 0; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (delegateFunc(imageBase, x, y, componentValue))
                        {
                            return x;
                        }
                    }
                }

                return 0;
            };

            Func<ImageBase, int> getMaxX = imageBase =>
            {
                for (int x = width - 1; x > -1; x--)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (delegateFunc(imageBase, x, y, componentValue))
                        {
                            return x;
                        }
                    }
                }

                return height;
            };

            topLeft.Y = getMinY(bitmap);
            topLeft.X = getMinX(bitmap);
            bottomRight.Y = (getMaxY(bitmap) + 1).Clamp(0, height);
            bottomRight.X = (getMaxX(bitmap) + 1).Clamp(0, width);

            return GetBoundingRectangle(topLeft, bottomRight);
        }
예제 #3
0
 /// <summary>
 /// Copies the properties from the other <see cref="ImageBase{T,TP}"/>.
 /// </summary>
 /// <param name="other">
 /// The other <see cref="ImageBase{T,TP}"/> to copy the properties from.
 /// </param>
 protected void CopyProperties(ImageBase <T, TP> other)
 {
     this.Quality    = other.Quality;
     this.FrameDelay = other.FrameDelay;
 }
예제 #4
0
        /// <summary>
        /// Calculates the target rectangle for crop mode.
        /// </summary>
        /// <typeparam name="T">The type of pixels contained within the image.</typeparam>
        /// <param name="source">The source image.</param>
        /// <param name="options">The resize options.</param>
        /// <returns>
        /// The <see cref="Rectangle"/>.
        /// </returns>
        private static Rectangle CalculateCropRectangle <T, TP>(ImageBase <T, TP> source, ResizeOptions options)
            where T : IPackedVector <TP>
            where TP : struct
        {
            int width  = options.Size.Width;
            int height = options.Size.Height;

            if (width <= 0 || height <= 0)
            {
                return(new Rectangle(0, 0, source.Width, source.Height));
            }

            double ratio;
            int    sourceWidth  = source.Width;
            int    sourceHeight = source.Height;

            int destinationX      = 0;
            int destinationY      = 0;
            int destinationWidth  = width;
            int destinationHeight = height;

            // Fractional variants for preserving aspect ratio.
            double percentHeight = Math.Abs(height / (double)sourceHeight);
            double percentWidth  = Math.Abs(width / (double)sourceWidth);

            if (percentHeight < percentWidth)
            {
                ratio = percentWidth;

                if (options.CenterCoordinates.Any())
                {
                    double center = -(ratio * sourceHeight) * options.CenterCoordinates.First();
                    destinationY = (int)center + (height / 2);

                    if (destinationY > 0)
                    {
                        destinationY = 0;
                    }

                    if (destinationY < (int)(height - (sourceHeight * ratio)))
                    {
                        destinationY = (int)(height - (sourceHeight * ratio));
                    }
                }
                else
                {
                    switch (options.Position)
                    {
                    case AnchorPosition.Top:
                    case AnchorPosition.TopLeft:
                    case AnchorPosition.TopRight:
                        destinationY = 0;
                        break;

                    case AnchorPosition.Bottom:
                    case AnchorPosition.BottomLeft:
                    case AnchorPosition.BottomRight:
                        destinationY = (int)(height - (sourceHeight * ratio));
                        break;

                    default:
                        destinationY = (int)((height - (sourceHeight * ratio)) / 2);
                        break;
                    }
                }

                destinationHeight = (int)Math.Ceiling(sourceHeight * percentWidth);
            }
            else
            {
                ratio = percentHeight;

                if (options.CenterCoordinates.Any())
                {
                    double center = -(ratio * sourceWidth) * options.CenterCoordinates.ToArray()[1];
                    destinationX = (int)center + (width / 2);

                    if (destinationX > 0)
                    {
                        destinationX = 0;
                    }

                    if (destinationX < (int)(width - (sourceWidth * ratio)))
                    {
                        destinationX = (int)(width - (sourceWidth * ratio));
                    }
                }
                else
                {
                    switch (options.Position)
                    {
                    case AnchorPosition.Left:
                    case AnchorPosition.TopLeft:
                    case AnchorPosition.BottomLeft:
                        destinationX = 0;
                        break;

                    case AnchorPosition.Right:
                    case AnchorPosition.TopRight:
                    case AnchorPosition.BottomRight:
                        destinationX = (int)(width - (sourceWidth * ratio));
                        break;

                    default:
                        destinationX = (int)((width - (sourceWidth * ratio)) / 2);
                        break;
                    }
                }

                destinationWidth = (int)Math.Ceiling(sourceWidth * percentHeight);
            }

            return(new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
        }
예제 #5
0
        /// <summary>
        /// Calculates the target rectangle for box pad mode.
        /// </summary>
        /// <typeparam name="T">The type of pixels contained within the image.</typeparam>
        /// <param name="source">The source image.</param>
        /// <param name="options">The resize options.</param>
        /// <returns>
        /// The <see cref="Rectangle"/>.
        /// </returns>
        private static Rectangle CalculateBoxPadRectangle <T, TP>(ImageBase <T, TP> source, ResizeOptions options)
            where T : IPackedVector <TP>
            where TP : struct
        {
            int width  = options.Size.Width;
            int height = options.Size.Height;

            if (width <= 0 || height <= 0)
            {
                return(new Rectangle(0, 0, source.Width, source.Height));
            }

            int sourceWidth  = source.Width;
            int sourceHeight = source.Height;

            // Fractional variants for preserving aspect ratio.
            double percentHeight = Math.Abs(height / (double)sourceHeight);
            double percentWidth  = Math.Abs(width / (double)sourceWidth);

            int boxPadHeight = height > 0 ? height : Convert.ToInt32(sourceHeight * percentWidth);
            int boxPadWidth  = width > 0 ? width : Convert.ToInt32(sourceWidth * percentHeight);

            // Only calculate if upscaling.
            if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight)
            {
                int destinationX;
                int destinationY;
                int destinationWidth  = sourceWidth;
                int destinationHeight = sourceHeight;
                width  = boxPadWidth;
                height = boxPadHeight;

                switch (options.Position)
                {
                case AnchorPosition.Left:
                    destinationY = (height - sourceHeight) / 2;
                    destinationX = 0;
                    break;

                case AnchorPosition.Right:
                    destinationY = (height - sourceHeight) / 2;
                    destinationX = width - sourceWidth;
                    break;

                case AnchorPosition.TopRight:
                    destinationY = 0;
                    destinationX = width - sourceWidth;
                    break;

                case AnchorPosition.Top:
                    destinationY = 0;
                    destinationX = (width - sourceWidth) / 2;
                    break;

                case AnchorPosition.TopLeft:
                    destinationY = 0;
                    destinationX = 0;
                    break;

                case AnchorPosition.BottomRight:
                    destinationY = height - sourceHeight;
                    destinationX = width - sourceWidth;
                    break;

                case AnchorPosition.Bottom:
                    destinationY = height - sourceHeight;
                    destinationX = (width - sourceWidth) / 2;
                    break;

                case AnchorPosition.BottomLeft:
                    destinationY = height - sourceHeight;
                    destinationX = 0;
                    break;

                default:
                    destinationY = (height - sourceHeight) / 2;
                    destinationX = (width - sourceWidth) / 2;
                    break;
                }

                return(new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
            }

            // Switch to pad mode to downscale and calculate from there.
            return(CalculatePadRectangle(source, options));
        }
예제 #6
0
        /// <summary>
        /// Calculates the target rectangle for pad mode.
        /// </summary>
        /// <typeparam name="T">The type of pixels contained within the image.</typeparam>
        /// <param name="source">The source image.</param>
        /// <param name="options">The resize options.</param>
        /// <returns>
        /// The <see cref="Rectangle"/>.
        /// </returns>
        private static Rectangle CalculatePadRectangle <T, TP>(ImageBase <T, TP> source, ResizeOptions options)
            where T : IPackedVector <TP>
            where TP : struct
        {
            int width  = options.Size.Width;
            int height = options.Size.Height;

            if (width <= 0 || height <= 0)
            {
                return(new Rectangle(0, 0, source.Width, source.Height));
            }

            double ratio;
            int    sourceWidth  = source.Width;
            int    sourceHeight = source.Height;

            int destinationX      = 0;
            int destinationY      = 0;
            int destinationWidth  = width;
            int destinationHeight = height;

            // Fractional variants for preserving aspect ratio.
            double percentHeight = Math.Abs(height / (double)sourceHeight);
            double percentWidth  = Math.Abs(width / (double)sourceWidth);

            if (percentHeight < percentWidth)
            {
                ratio            = percentHeight;
                destinationWidth = Convert.ToInt32(sourceWidth * percentHeight);

                switch (options.Position)
                {
                case AnchorPosition.Left:
                case AnchorPosition.TopLeft:
                case AnchorPosition.BottomLeft:
                    destinationX = 0;
                    break;

                case AnchorPosition.Right:
                case AnchorPosition.TopRight:
                case AnchorPosition.BottomRight:
                    destinationX = (int)(width - (sourceWidth * ratio));
                    break;

                default:
                    destinationX = Convert.ToInt32((width - (sourceWidth * ratio)) / 2);
                    break;
                }
            }
            else
            {
                ratio             = percentWidth;
                destinationHeight = Convert.ToInt32(sourceHeight * percentWidth);

                switch (options.Position)
                {
                case AnchorPosition.Top:
                case AnchorPosition.TopLeft:
                case AnchorPosition.TopRight:
                    destinationY = 0;
                    break;

                case AnchorPosition.Bottom:
                case AnchorPosition.BottomLeft:
                case AnchorPosition.BottomRight:
                    destinationY = (int)(height - (sourceHeight * ratio));
                    break;

                default:
                    destinationY = (int)((height - (sourceHeight * ratio)) / 2);
                    break;
                }
            }

            return(new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
        }
 /// <summary>
 /// This method is called after the process is applied to prepare the processor.
 /// </summary>
 /// <param name="source">The source image. Cannot be null.</param>
 /// <param name="target">Target image to apply the process to.</param>
 /// <param name="targetRectangle">
 /// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
 /// The image is scaled to fit the rectangle.
 /// </param>
 /// <param name="sourceRectangle">
 /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
 /// </param>
 protected virtual void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
 {
 }
 /// <summary>
 /// Applies the process to the specified portion of the specified <see cref="ImageBase"/> at the specified location
 /// and with the specified size.
 /// </summary>
 /// <param name="target">Target image to apply the process to.</param>
 /// <param name="source">The source image. Cannot be null.</param>
 /// <param name="targetRectangle">
 /// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
 /// The image is scaled to fit the rectangle.
 /// </param>
 /// <param name="sourceRectangle">
 /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
 /// </param>
 /// <param name="startY">The index of the row within the source image to start processing.</param>
 /// <param name="endY">The index of the row within the source image to end processing.</param>
 /// <remarks>
 /// The method keeps the source image unchanged and returns the
 /// the result of image process as new image.
 /// </remarks>
 protected abstract void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY);
예제 #9
0
 /// <summary>
 /// Combines the given image together with the current one by blending their pixels.
 /// </summary>
 /// <param name="source">The image this method extends.</param>
 /// <param name="image">The image to blend with the currently processing image.</param>
 /// <typeparam name="T">The pixel format.</typeparam>
 /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
 /// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
 /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
 /// <returns>The <see cref="Image{T,TP}"/>.</returns>
 public static Image <T, TP> Blend <T, TP>(this Image <T, TP> source, ImageBase <T, TP> image, int percent = 50, ProgressEventHandler progressHandler = null)
     where T : IPackedVector <TP>
     where TP : struct
 {
     return(Blend(source, image, percent, source.Bounds, progressHandler));
 }
예제 #10
0
        /// <summary>
        /// Finds the bounding rectangle based on the first instance of any color component other
        /// than the given one.
        /// </summary>
        /// <typeparam name="T">The pixel format.</typeparam>
        /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
        /// <param name="bitmap">The <see cref="Image"/> to search within.</param>
        /// <param name="componentValue">The color component value to remove.</param>
        /// <param name="channel">The <see cref="RgbaComponent"/> channel to test against.</param>
        /// <returns>
        /// The <see cref="Rectangle"/>.
        /// </returns>
        public static Rectangle GetFilteredBoundingRectangle <T, TP>(ImageBase <T, TP> bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
            where T : IPackedVector <TP>
            where TP : struct
        {
            const float Epsilon     = .00001f;
            int         width       = bitmap.Width;
            int         height      = bitmap.Height;
            Point       topLeft     = new Point();
            Point       bottomRight = new Point();

            Func <IPixelAccessor <T, TP>, int, int, float, bool> delegateFunc;

            // Determine which channel to check against
            switch (channel)
            {
            case RgbaComponent.R:
                delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[0] - b) > Epsilon;
                break;

            case RgbaComponent.G:
                delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[1] - b) > Epsilon;
                break;

            case RgbaComponent.B:
                delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[2] - b) > Epsilon;
                break;

            default:
                delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[3] - b) > Epsilon;
                break;
            }

            Func <IPixelAccessor <T, TP>, int> getMinY = pixels =>
            {
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        if (delegateFunc(pixels, x, y, componentValue))
                        {
                            return(y);
                        }
                    }
                }

                return(0);
            };

            Func <IPixelAccessor <T, TP>, int> getMaxY = pixels =>
            {
                for (int y = height - 1; y > -1; y--)
                {
                    for (int x = 0; x < width; x++)
                    {
                        if (delegateFunc(pixels, x, y, componentValue))
                        {
                            return(y);
                        }
                    }
                }

                return(height);
            };

            Func <IPixelAccessor <T, TP>, int> getMinX = pixels =>
            {
                for (int x = 0; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (delegateFunc(pixels, x, y, componentValue))
                        {
                            return(x);
                        }
                    }
                }

                return(0);
            };

            Func <IPixelAccessor <T, TP>, int> getMaxX = pixels =>
            {
                for (int x = width - 1; x > -1; x--)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (delegateFunc(pixels, x, y, componentValue))
                        {
                            return(x);
                        }
                    }
                }

                return(height);
            };

            using (IPixelAccessor <T, TP> bitmapPixels = bitmap.Lock())
            {
                topLeft.Y     = getMinY(bitmapPixels);
                topLeft.X     = getMinX(bitmapPixels);
                bottomRight.Y = (getMaxY(bitmapPixels) + 1).Clamp(0, height);
                bottomRight.X = (getMaxX(bitmapPixels) + 1).Clamp(0, width);
            }

            return(GetBoundingRectangle(topLeft, bottomRight));
        }