Browse Source

Finishing Mask plus BGColor fix

Former-commit-id: ebe77f5da23989c54de19b9279c78babe85866e3
Former-commit-id: 6ed3adce5e0c9d17e9382a373ea208b25f9c8cd5
pull/17/head
James South 12 years ago
parent
commit
494db89c52
  1. 12
      src/ImageProcessor.Playground/Program.cs
  2. BIN
      src/ImageProcessor.Playground/images/input/mask.png
  3. 14
      src/ImageProcessor/ImageFactory.cs
  4. 60
      src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs
  5. 71
      src/ImageProcessor/Imaging/Helpers/Effects.cs
  6. 40
      src/ImageProcessor/Imaging/Helpers/ImageMaths.cs
  7. 7
      src/ImageProcessor/Processors/BackgroundColor.cs
  8. 1
      src/ImageProcessor/Processors/Filter.cs
  9. 41
      src/ImageProcessor/Processors/Mask.cs

12
src/ImageProcessor.Playground/Program.cs

@ -18,15 +18,19 @@ namespace ImageProcessor.PlayGround
using System.Linq; using System.Linq;
using ImageProcessor; using ImageProcessor;
using ImageProcessor.Configuration;
using ImageProcessor.Imaging; using ImageProcessor.Imaging;
using ImageProcessor.Imaging.Filters.EdgeDetection; using ImageProcessor.Imaging.Filters.EdgeDetection;
using ImageProcessor.Imaging.Filters.Photo; using ImageProcessor.Imaging.Filters.Photo;
using ImageProcessor.Imaging.Formats;
/// <summary> /// <summary>
/// The program. /// The program.
/// </summary> /// </summary>
public class Program public class Program
{ {
protected static readonly IEnumerable<ISupportedImageFormat> formats = ImageProcessorBootstrapper.Instance.SupportedImageFormats;
/// <summary> /// <summary>
/// The main routine. /// The main routine.
/// </summary> /// </summary>
@ -45,6 +49,7 @@ namespace ImageProcessor.PlayGround
di.Create(); di.Create();
} }
Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask.png"));
IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".jpg"); IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".jpg");
//IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".gif", ".webp", ".bmp", ".jpg", ".png", ".tif"); //IEnumerable<FileInfo> files = GetFilesByExtensions(di, ".gif", ".webp", ".bmp", ".jpg", ".png", ".tif");
@ -73,15 +78,16 @@ namespace ImageProcessor.PlayGround
//.BackgroundColor(Color.White) //.BackgroundColor(Color.White)
//.Resize(new Size((int)(size.Width * 1.1), 0)) //.Resize(new Size((int)(size.Width * 1.1), 0))
//.ContentAwareResize(layer) //.ContentAwareResize(layer)
.Constrain(size) //.Constrain(size)
.Mask() //.Mask(mask)
//.BackgroundColor(Color.HotPink)
//.ReplaceColor(Color.FromArgb(255, 1, 107, 165), Color.FromArgb(255, 1, 165, 13), 80) //.ReplaceColor(Color.FromArgb(255, 1, 107, 165), Color.FromArgb(255, 1, 165, 13), 80)
//.Resize(layer) //.Resize(layer)
//.DetectEdges(new SobelEdgeFilter(), false) //.DetectEdges(new SobelEdgeFilter(), false)
//.DetectEdges(new LaplacianOfGaussianEdgeFilter()) //.DetectEdges(new LaplacianOfGaussianEdgeFilter())
//.EntropyCrop() //.EntropyCrop()
//.Filter(MatrixFilters.Invert) //.Filter(MatrixFilters.Invert)
//.Filter(MatrixFilters.Comic) .Filter(MatrixFilters.Comic)
//.Filter(MatrixFilters.HiSatch) //.Filter(MatrixFilters.HiSatch)
//.Pixelate(8) //.Pixelate(8)
//.GaussianSharpen(10) //.GaussianSharpen(10)

BIN
src/ImageProcessor.Playground/images/input/mask.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

14
src/ImageProcessor/ImageFactory.cs

@ -670,11 +670,21 @@ namespace ImageProcessor
return this; return this;
} }
public ImageFactory Mask() /// <summary>
/// Applies the given image mask to the current image. If the mask is not the same size as the image
/// it will be centered against the image.
/// </summary>
/// <param name="imageMask">
/// The image containing the mask to apply.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Mask(Image imageMask)
{ {
if (this.ShouldProcess) if (this.ShouldProcess)
{ {
Mask mask = new Mask(); Mask mask = new Mask { DynamicParameter = imageMask };
this.CurrentImageFormat.ApplyProcessor(mask.ProcessImage, this); this.CurrentImageFormat.ApplyProcessor(mask.ProcessImage, this);
} }

60
src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs

@ -10,7 +10,6 @@
namespace ImageProcessor.Imaging.Filters.Photo namespace ImageProcessor.Imaging.Filters.Photo
{ {
using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
@ -106,8 +105,8 @@ namespace ImageProcessor.Imaging.Filters.Photo
} }
} }
// Transfer the alpha channel from the mask to the high saturation image. // Transfer the alpha channel from the mask to the low saturation image.
ApplyMask(patternBitmap, lowBitmap); lowBitmap = Effects.ApplyMask(lowBitmap, patternBitmap);
using (Graphics graphics = Graphics.FromImage(newImage)) using (Graphics graphics = Graphics.FromImage(newImage))
{ {
@ -195,8 +194,8 @@ namespace ImageProcessor.Imaging.Filters.Photo
using (Bitmap temp = filter.Process2DFilter(source)) using (Bitmap temp = filter.Process2DFilter(source))
{ {
destination = new InvertMatrixFilter().TransformImage(temp, destination); destination = new InvertMatrixFilter().TransformImage(temp, destination);
// Darken it slightly // Darken it slightly to aid detection
destination = Adjustments.Brightness(destination, -5); destination = Adjustments.Brightness(destination, -5);
} }
@ -222,56 +221,9 @@ namespace ImageProcessor.Imaging.Filters.Photo
}); });
} }
// Darken it again to average out the color.
destination = Adjustments.Brightness(destination, -5);
return (Bitmap)destination; return (Bitmap)destination;
} }
/// <summary>
/// Applies a mask .
/// </summary>
/// <param name="source">
/// The source.
/// </param>
/// <param name="destination">
/// The destination.
/// </param>
/// <exception cref="ArgumentException">
/// Thrown if the two images are of different size.
/// </exception>
private static void ApplyMask(Image source, Image destination)
{
if (source.Size != destination.Size)
{
throw new ArgumentException();
}
using (FastBitmap sourceBitmap = new FastBitmap(source))
{
using (FastBitmap destinationBitmap = new FastBitmap(destination))
{
int width = source.Width;
int height = source.Height;
Parallel.For(
0,
height,
y =>
{
for (int x = 0; x < width; x++)
{
// ReSharper disable AccessToDisposedClosure
Color sourceColor = sourceBitmap.GetPixel(x, y);
Color destinationColor = destinationBitmap.GetPixel(x, y);
if (destinationColor.A != 0)
{
destinationBitmap.SetPixel(x, y, Color.FromArgb(sourceColor.B, destinationColor.R, destinationColor.G, destinationColor.B));
}
// ReSharper restore AccessToDisposedClosure
}
});
}
}
}
} }
} }

71
src/ImageProcessor/Imaging/Helpers/Effects.cs

@ -13,6 +13,7 @@ namespace ImageProcessor.Imaging.Helpers
using System; using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Threading.Tasks;
/// <summary> /// <summary>
/// Provides reusable effect methods to apply to images. /// Provides reusable effect methods to apply to images.
@ -69,8 +70,8 @@ namespace ImageProcessor.Imaging.Helpers
} }
else else
{ {
centerColor = Color.FromArgb(0, baseColor.R, baseColor.G, baseColor.B); centerColor = Color.FromArgb(0, baseColor.R, baseColor.G, baseColor.B);
edgeColor = Color.FromArgb(255, baseColor.R, baseColor.G, baseColor.B); edgeColor = Color.FromArgb(255, baseColor.R, baseColor.G, baseColor.B);
} }
brush.WrapMode = WrapMode.Tile; brush.WrapMode = WrapMode.Tile;
@ -108,5 +109,71 @@ namespace ImageProcessor.Imaging.Helpers
{ {
return Vignette(source, baseColor, rectangle, true); return Vignette(source, baseColor, rectangle, true);
} }
/// <summary>
/// Applies the given image mask to the source.
/// </summary>
/// <param name="source">
/// The source <see cref="Image"/>.
/// </param>
/// <param name="mask">
/// The mask <see cref="Image"/>.
/// </param>
/// <exception cref="ArgumentException">
/// Thrown if the two images are of different size.
/// </exception>
/// <returns>
/// The masked <see cref="Bitmap"/>.
/// </returns>
public static Bitmap ApplyMask(Image source, Image mask)
{
if (mask.Size != source.Size)
{
throw new ArgumentException();
}
int width = mask.Width;
int height = mask.Height;
Bitmap toMask = new Bitmap(source);
// Loop through and replace the alpha channel
using (FastBitmap maskBitmap = new FastBitmap(mask))
{
using (FastBitmap sourceBitmap = new FastBitmap(toMask))
{
Parallel.For(
0,
height,
y =>
{
for (int x = 0; x < width; x++)
{
// ReSharper disable AccessToDisposedClosure
Color maskColor = maskBitmap.GetPixel(x, y);
Color sourceColor = sourceBitmap.GetPixel(x, y);
if (sourceColor.A != 0)
{
sourceBitmap.SetPixel(x, y, Color.FromArgb(maskColor.B, sourceColor.R, sourceColor.G, sourceColor.B));
}
// ReSharper restore AccessToDisposedClosure
}
});
}
}
// Ensure the background is cleared out on non alpha supporting formats.
Bitmap clear = new Bitmap(width, height);
using (Graphics graphics = Graphics.FromImage(clear))
{
graphics.Clear(Color.Transparent);
graphics.DrawImage(toMask, 0, 0, width, height);
}
toMask.Dispose();
return clear;
}
} }
} }

40
src/ImageProcessor/Imaging/Helpers/ImageMaths.cs

@ -15,7 +15,7 @@ namespace ImageProcessor.Imaging.Helpers
/// <summary> /// <summary>
/// Provides reusable mathematical methods to apply to images. /// Provides reusable mathematical methods to apply to images.
/// </summary> /// </summary>
public class ImageMaths public static class ImageMaths
{ {
/// <summary> /// <summary>
/// Gets the bounding <see cref="Rectangle"/> from the given points. /// Gets the bounding <see cref="Rectangle"/> from the given points.
@ -29,13 +29,13 @@ namespace ImageProcessor.Imaging.Helpers
/// <returns> /// <returns>
/// The bounding <see cref="Rectangle"/>. /// The bounding <see cref="Rectangle"/>.
/// </returns> /// </returns>
public Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight)
{ {
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
} }
/// <summary> /// <summary>
/// Gets a <see cref="Rectangle"/> centered within it's parent. /// Gets a <see cref="Rectangle"/> representing the child centered relative to the parent.
/// </summary> /// </summary>
/// <param name="parent"> /// <param name="parent">
/// The parent <see cref="Rectangle"/>. /// The parent <see cref="Rectangle"/>.
@ -46,17 +46,33 @@ namespace ImageProcessor.Imaging.Helpers
/// <returns> /// <returns>
/// The centered <see cref="Rectangle"/>. /// The centered <see cref="Rectangle"/>.
/// </returns> /// </returns>
public Rectangle CenteredRectangle(Rectangle parent, Rectangle child) public static RectangleF CenteredRectangle(Rectangle parent, Rectangle child)
{ {
if (parent.Size.Width < child.Size.Width && parent.Size.Height < child.Size.Height) float x = (parent.Width - child.Width) / 2.0F;
{ float y = (parent.Height - child.Height) / 2.0F;
return parent; int width = child.Width;
} int height = child.Height;
return new RectangleF(x, y, width, height);
int x = (parent.Width - child.Width) / 2; }
int y = (parent.Height - child.Height) / 2;
return new Rectangle(x, y, child.Width, child.Height); /// <summary>
/// Returns the array of <see cref="Point"/> matching the bounds of the given rectangle.
/// </summary>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> to return the points from.
/// </param>
/// <returns>
/// The <see cref="Point"/> array.
/// </returns>
public static Point[] ToPoints(Rectangle rectangle)
{
return new[]
{
new Point(rectangle.Left, rectangle.Top),
new Point(rectangle.Right, rectangle.Top),
new Point(rectangle.Right, rectangle.Bottom),
new Point(rectangle.Left, rectangle.Bottom)
};
} }
} }
} }

7
src/ImageProcessor/Processors/BackgroundColor.cs

@ -55,8 +55,11 @@ namespace ImageProcessor.Processors
try try
{ {
int width = image.Width;
int height = image.Height;
Color backgroundColor = this.DynamicParameter; Color backgroundColor = this.DynamicParameter;
newImage = new Bitmap(image.Width, image.Height); newImage = new Bitmap(width, height);
// Make a graphics object from the empty bitmap. // Make a graphics object from the empty bitmap.
using (Graphics graphics = Graphics.FromImage(newImage)) using (Graphics graphics = Graphics.FromImage(newImage))
@ -65,7 +68,7 @@ namespace ImageProcessor.Processors
graphics.Clear(backgroundColor); graphics.Clear(backgroundColor);
// Draw passed in image onto graphics object. // Draw passed in image onto graphics object.
graphics.DrawImage(image, 0, 0); graphics.DrawImage(image, 0, 0, width, height);
} }
image.Dispose(); image.Dispose();

1
src/ImageProcessor/Processors/Filter.cs

@ -13,7 +13,6 @@ namespace ImageProcessor.Processors
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using ImageProcessor.Common.Exceptions; using ImageProcessor.Common.Exceptions;
using ImageProcessor.Imaging.Filters.Photo; using ImageProcessor.Imaging.Filters.Photo;

41
src/ImageProcessor/Processors/Mask.cs

@ -15,9 +15,11 @@ namespace ImageProcessor.Processors
using System.Drawing; using System.Drawing;
using ImageProcessor.Common.Exceptions; using ImageProcessor.Common.Exceptions;
using ImageProcessor.Imaging.Helpers;
/// <summary> /// <summary>
/// Applies a mask to the given image. /// Applies a mask to the given image. If the mask is not the same size as the image
/// it will be centered against the image.
/// </summary> /// </summary>
public class Mask : IGraphicsProcessor public class Mask : IGraphicsProcessor
{ {
@ -60,29 +62,46 @@ namespace ImageProcessor.Processors
public Image ProcessImage(ImageFactory factory) public Image ProcessImage(ImageFactory factory)
{ {
Bitmap newImage = null; Bitmap newImage = null;
Bitmap mask = null;
Bitmap maskResized = null;
Image image = factory.Image; Image image = factory.Image;
Size original = image.Size;
Size smaller = new Size(image.Width / 2, image.Height / 2);
int x = (original.Width - smaller.Width) / 2;
int y = (original.Height - smaller.Height) / 2;
int width = image.Width;
int height = image.Height;
try try
{ {
newImage = new Bitmap(original.Width, image.Height); int width = image.Width;
int height = image.Height;
mask = new Bitmap(this.DynamicParameter);
Rectangle parent = new Rectangle(0, 0, width, height);
Rectangle child = new Rectangle(0, 0, mask.Width, mask.Height);
RectangleF centered = ImageMaths.CenteredRectangle(parent, child);
using (Graphics graphics = Graphics.FromImage(newImage)) // Resize the mask to the size of the input image so that we can apply it.
maskResized = new Bitmap(width, height);
using (Graphics graphics = Graphics.FromImage(maskResized))
{ {
graphics.DrawImage(image, x, y, smaller.Width, smaller.Height); graphics.Clear(Color.Transparent);
graphics.DrawImage(mask, new PointF(centered.X, centered.Y));
} }
newImage = Effects.ApplyMask(image, maskResized);
mask.Dispose();
maskResized.Dispose();
image.Dispose(); image.Dispose();
image = newImage; image = newImage;
} }
catch (Exception ex) catch (Exception ex)
{ {
if (mask != null)
{
mask.Dispose();
}
if (maskResized != null)
{
maskResized.Dispose();
}
if (newImage != null) if (newImage != null)
{ {
newImage.Dispose(); newImage.Dispose();

Loading…
Cancel
Save