Browse Source

Companding now off by default when resampling.

This doubles the resizer speed.


Former-commit-id: abbf63f863635d4df1a04d322eccd425db7e82db
Former-commit-id: 63b218a375e80832a49c4575d341febe08cb48ca
Former-commit-id: 1327e6da18486c8a3b97d20cc4dead0b77e1f82b
af/merge-core
James Jackson-South 10 years ago
parent
commit
b59fa63a1d
  1. 33
      README.md
  2. 2
      src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
  3. 25
      src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs
  4. 7
      src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs
  5. 19
      src/ImageProcessorCore/Formats/Jpg/JpegSubsample.cs
  6. 4
      src/ImageProcessorCore/Samplers/IImageSampler.cs
  7. 2
      src/ImageProcessorCore/Samplers/ImageSampler.cs
  8. 47
      src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs
  9. 17
      src/ImageProcessorCore/Samplers/Resize.cs
  10. 10
      src/ImageProcessorCore/Samplers/Rotate.cs
  11. 8
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

33
README.md

@ -3,17 +3,17 @@
## This branch contains the new cross platform version: ImageProcessorCore.
---
#ImageProcessor Needs Your Help
## ImageProcessor Needs Your Help
### ImageProcessor is the work of a very, very, small number of developers who struggle balancing time to contribute to the project with family time and work commitments. If the project is to survive we need more contribution from the community at large. There are several issues, most notably [#324](https://github.com/JimBobSquarePants/ImageProcessor/issues/324) that we cannot possibly solve on our own.
**ImageProcessor is the work of a very, very, small number of developers who struggle balancing time to contribute to the project with family time and work commitments. If the project is to survive we need more contribution from the community at large. There are several issues, most notably [#264](https://github.com/JimBobSquarePants/ImageProcessor/issues/264) that we cannot possibly solve on our own.**
### We, and we believe many others in the community at large want a first-class 2D imaging library with a simple API that is not simply a wrapper round an existing library. We want it to have a low contribution bar which we believe can only happen if the library is written in C#. We want it to be written to cover as many use cases as possible. We want to write the same code once and have it work on any platform supporting CoreFX.
**We, and we believe many others in the community at large want a first-class 2D imaging library with a simple API that is not simply a wrapper round an existing library. We want it to have a low contribution bar which we believe can only happen if the library is written in C#. We want it to be written to cover as many use cases as possible. We want to write the same code once and have it work on any platform supporting CoreFX.**
### With your help we can make all that a reality.
**With your help we can make all that a reality.**
### If you can donate any time to improve on the project, be it helping with documentation, tests or contributing code please do.
**If you can donate any time to improve on the project, be it helping with documentation, tests or contributing code please do.**
### Thankyou for reading this.
**Thankyou for reading this**
---
This is a complete rewrite from the ground up to allow the processing of images without the use of `System.Drawing` using a cross-platform class library. It's still in early stages but progress has been pretty quick.
@ -50,8 +50,8 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
###What works so far/ What is planned?
- Encoding/decoding of image formats (plugable)
- [x] jpeg (Includes progressive)
- Encoding/decoding of image formats (plugable), progressive required
- [x] jpeg (Includes Subsampling, Progressive required)
- [x] bmp (More bmp format saving support required, 24bit just now)
- [x] png (Need updating for saving indexed support)
- [x] gif
@ -69,7 +69,7 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Size
- [x] Point
- [x] Ellipse
- Resampling algorithms. (Performance improvements?)
- Resampling algorithms. (Optional gamma correction, Performance improvements?)
- [x] Box
- [x] Bicubic
- [x] Lanczos3
@ -94,10 +94,20 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] BlackWhite
- [x] Greyscale BT709
- [x] Greyscale BT601
- [x] Hue
- [x] Saturation
- [x] Lomograph
- [x] Polaroid
- [x] Kodachrome
- [x] Sepia
- [x] Achromatomaly
- [x] Achromatopsia
- [x] Deuteranomaly
- [x] Deuteranopia
- [x] Protanomaly
- [x] Protanopia
- [x] Tritanomaly
- [x] Tritanopia
- Edge Detection
- [x] Kayyali
- [x] Kirsch
@ -119,12 +129,11 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] BackgroundColor
- [x] Brightness
- [x] Pixelate
- [x] Saturation
- [x] Hue
- [x] Blend
- [ ] Mask
- [x] Vignette
- [x] Glow
- [x] Threshold
- Effects
- [ ] Path brush (Need help) [#264](https://github.com/JimBobSquarePants/ImageProcessor/issues/264)
- [ ] Pattern brush (Need help) [#264](https://github.com/JimBobSquarePants/ImageProcessor/issues/264)
@ -142,7 +151,7 @@ With this version the API will change dramatically. Without the constraints of `
Image methods are also fluent which allow chaining much like the `ImageFactory` class in the Framework version.
Here's an example of the code required to resize an image using the default Robidoux resampler then turn the colors into their greyscale equivalent using the BT709 standard matrix.
Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their greyscale equivalent using the BT709 standard matrix.
```csharp
using (FileStream stream = File.OpenRead("foo.jpg"))

2
src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id

@ -1 +1 @@
4b32515bda4abce4bd01c9ad019ce9a581b1c56f
23454141b1a1c974ca0881ae725b6f4ce4d8786b

25
src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs

@ -19,15 +19,22 @@ namespace ImageProcessorCore.Formats
private int quality = 75;
/// <summary>
/// The subsamples used to encode the image.
/// The subsamples scheme used to encode the image.
/// </summary>
private JpegSubsample subsample = JpegSubsample.Ratio420;
private bool subsampleSet = false;
/// <summary>
/// Whether subsampling has been specifically set.
/// </summary>
private bool subsampleSet;
/// <summary>
/// Gets or sets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).
/// </summary>
/// <remarks>
/// If the quality is less than or equal to 80, the subsampling ratio will switch to <see cref="JpegSubsample.Ratio420"/>
/// </remarks>
/// <value>The quality of the jpg image from 0 to 100.</value>
public int Quality
{
@ -42,7 +49,11 @@ namespace ImageProcessorCore.Formats
public JpegSubsample Subsample
{
get { return this.subsample; }
set { this.subsample = value; subsampleSet = true; }
set
{
this.subsample = value;
this.subsampleSet = true;
}
}
/// <inheritdoc/>
@ -73,10 +84,14 @@ namespace ImageProcessorCore.Formats
Guard.NotNull(stream, nameof(stream));
JpegEncoderCore encode = new JpegEncoderCore();
if(subsampleSet)
if (this.subsampleSet)
{
encode.Encode(stream, image, this.Quality, this.Subsample);
}
else
encode.Encode(stream, image, this.Quality, this.Quality >= 80 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
{
encode.Encode(stream, image, this.Quality, this.Quality >= 80 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
}
}
}
}

7
src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs

@ -1,4 +1,9 @@
namespace ImageProcessorCore.Formats
// <copyright file="JpegEncoderCore.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
using System;
using System.IO;

19
src/ImageProcessorCore/Formats/Jpg/JpegSubsample.cs

@ -1,8 +1,25 @@
// <copyright file="JpegSubsample.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
/// <summary>
/// Enumerates the chroma subsampling method applied to the image.
/// </summary>
public enum JpegSubsample
{
/// <summary>
/// High Quality - Each of the three Y'CbCr components have the same sample rate,
/// thus there is no chroma subsampling.
/// </summary>
Ratio444,
Ratio420,
/// <summary>
/// Medium Quality - The horizontal sampling is halved and the Cb and Cr channels are only
/// sampled on each alternate line.
/// </summary>
Ratio420
}
}

4
src/ImageProcessorCore/Samplers/IImageSampler.cs

@ -10,5 +10,9 @@ namespace ImageProcessorCore.Samplers
/// </summary>
public interface IImageSampler : IImageProcessor
{
/// <summary>
/// Gets or sets a value indicating whether to compand the value on processing.
/// </summary>
bool Compand { get; set; }
}
}

2
src/ImageProcessorCore/Samplers/ImageSampler.cs

@ -11,5 +11,7 @@ namespace ImageProcessorCore.Samplers
/// </summary>
public abstract class ImageSampler : ParallelImageProcessor, IImageSampler
{
/// <inheritdoc/>
public virtual bool Compand { get; set; } = false;
}
}

47
src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs

@ -93,7 +93,22 @@ namespace ImageProcessorCore.Samplers
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static Image Resize(this Image source, int width, int height, ProgressEventHandler progressHandler = null)
{
return Resize(source, width, height, new BicubicResampler(), progressHandler);
return Resize(source, width, height, new BicubicResampler(), false, progressHandler);
}
/// <summary>
/// Resizes an image to the given width and height.
/// </summary>
/// <param name="source">The image to resize.</param>
/// <param name="width">The target image width.</param>
/// <param name="height">The target image height.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static Image Resize(this Image source, int width, int height, bool compand, ProgressEventHandler progressHandler = null)
{
return Resize(source, width, height, new BicubicResampler(), compand, progressHandler);
}
/// <summary>
@ -103,12 +118,13 @@ namespace ImageProcessorCore.Samplers
/// <param name="width">The target image width.</param>
/// <param name="height">The target image height.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static Image Resize(this Image source, int width, int height, IResampler sampler, ProgressEventHandler progressHandler = null)
public static Image Resize(this Image source, int width, int height, IResampler sampler, bool compand, ProgressEventHandler progressHandler = null)
{
return Resize(source, width, height, sampler, source.Bounds, progressHandler);
return Resize(source, width, height, sampler, source.Bounds, compand, progressHandler);
}
/// <summary>
@ -122,10 +138,11 @@ namespace ImageProcessorCore.Samplers
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, ProgressEventHandler progressHandler = null)
public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, bool compand = false, ProgressEventHandler progressHandler = null)
{
if (width == 0 && height > 0)
{
@ -137,7 +154,7 @@ namespace ImageProcessorCore.Samplers
height = source.Height * width / source.Width;
}
Resize processor = new Resize(sampler);
Resize processor = new Resize(sampler) { Compand = compand };
processor.OnProgress += progressHandler;
try
@ -159,7 +176,20 @@ namespace ImageProcessorCore.Samplers
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
return Rotate(source, degrees, new BicubicResampler(), progressHandler);
return Rotate(source, degrees, new BicubicResampler(), false, progressHandler);
}
/// <summary>
/// Rotates an image by the given angle in degrees.
/// </summary>
/// <param name="source">The image to resize.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, bool compand, ProgressEventHandler progressHandler = null)
{
return Rotate(source, degrees, new BicubicResampler(), compand, progressHandler);
}
/// <summary>
@ -168,11 +198,12 @@ namespace ImageProcessorCore.Samplers
/// <param name="source">The image to resize.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, IResampler sampler, ProgressEventHandler progressHandler = null)
public static Image Rotate(this Image source, float degrees, IResampler sampler, bool compand, ProgressEventHandler progressHandler = null)
{
Rotate processor = new Rotate(sampler) { Angle = degrees };
Rotate processor = new Rotate(sampler) { Angle = degrees, Compand = compand };
processor.OnProgress += progressHandler;
try

17
src/ImageProcessorCore/Samplers/Resize.cs

@ -57,6 +57,7 @@ namespace ImageProcessorCore.Samplers
int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
bool compand = this.Compand;
if (this.Sampler is NearestNeighborResampler)
{
@ -107,11 +108,15 @@ namespace ImageProcessorCore.Samplers
foreach (Weight xw in horizontalValues)
{
int originX = xw.Index;
Color sourceColor = Color.Expand(source[originX, y]);
Color sourceColor = compand ? Color.Expand(source[originX, y]) : source[originX, y];
destination += sourceColor * xw.Value;
}
destination = Color.Compress(destination);
if (compand)
{
destination = Color.Compress(destination);
}
this.firstPass[x, y] = destination;
}
});
@ -135,11 +140,15 @@ namespace ImageProcessorCore.Samplers
{
int originY = yw.Index;
int originX = x;
Color sourceColor = Color.Expand(this.firstPass[originX, originY]);
Color sourceColor = compand ? Color.Expand(this.firstPass[originX, originY]) : this.firstPass[originX, originY];
destination += sourceColor * yw.Value;
}
destination = Color.Compress(destination);
if (compand)
{
destination = Color.Compress(destination);
}
target[x, y] = destination;
}
this.OnRowProcessed();

10
src/ImageProcessorCore/Samplers/Rotate.cs

@ -73,6 +73,7 @@ namespace ImageProcessorCore.Samplers
int endX = targetRectangle.Right;
float negativeAngle = -this.angle;
Point centre = Rectangle.Center(sourceRectangle);
bool compand = this.Compand;
if (this.Sampler is NearestNeighborResampler)
{
@ -144,15 +145,20 @@ namespace ImageProcessorCore.Samplers
if (sourceRectangle.Contains(rotated.X, rotated.Y))
{
Color sourceColor = Color.Expand(source[rotated.X, rotated.Y]);
Color sourceColor = compand ? Color.Expand(source[rotated.X, rotated.Y]) : source[rotated.X, rotated.Y];
destination += sourceColor * yw.Value * xw.Value;
}
}
}
destination = Color.Compress(destination);
if (compand)
{
destination = Color.Compress(destination);
}
target[x, y] = destination;
}
this.OnRowProcessed();
}
});

8
tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

@ -92,7 +92,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
image.Resize(image.Width / 2, image.Height / 2, sampler, this.ProgressUpdate)
image.Resize(image.Width / 2, image.Height / 2, sampler, false, this.ProgressUpdate)
.Save(output);
}
@ -120,7 +120,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
image.Resize(image.Width / 3, 0, new TriangleResampler(), this.ProgressUpdate)
image.Resize(image.Width / 3, 0, new TriangleResampler(), false, this.ProgressUpdate)
.Save(output);
}
@ -148,7 +148,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
image.Resize(0, image.Height / 3, new TriangleResampler(), this.ProgressUpdate)
image.Resize(0, image.Height / 3, new TriangleResampler(), false, this.ProgressUpdate)
.Save(output);
}
@ -202,7 +202,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{
image.Rotate(45, sampler, this.ProgressUpdate)
image.Rotate(45, sampler, false, this.ProgressUpdate)
//.BackgroundColor(Color.Aqua)
.Save(output);
}

Loading…
Cancel
Save