Skip to content

Commit

Permalink
Added fast chroma key, brightness and contrast filters
Browse files Browse the repository at this point in the history
  • Loading branch information
fschultz committed Sep 10, 2016
1 parent 6e51442 commit 38cb6b1
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 40 deletions.
70 changes: 70 additions & 0 deletions FastFilters/FastBrightnessFilter.cs
@@ -0,0 +1,70 @@
#region License and copyright notice
/*
* Kaliko Image Library
*
* Copyright (c) Fredrik Schultz and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#endregion

namespace Kaliko.ImageLibrary.FastFilters {
using System.Drawing;
using System.Threading.Tasks;
using Filters;

public class FastBrightnessFilter : BrightnessFilter {

public FastBrightnessFilter(int changeInBrightness) : base(changeInBrightness) {
}

public override void Run(KalikoImage image) {
ChangeBrightness(image);
}

private void ChangeBrightness(KalikoImage image) {
var lookupTable = BuildLookupTable();

unsafe {
var bitmapData = image.LockBits();

var bytesPerPixel = Image.GetPixelFormatSize(bitmapData.PixelFormat)/8;
var height = bitmapData.Height;
var widthInBytes = bitmapData.Width*bytesPerPixel;
var startOffset = (byte*)bitmapData.Scan0;

Parallel.For(0, height, y => {
var currentLine = startOffset + (y*bitmapData.Stride);
for (var x = 0; x < widthInBytes; x = x + bytesPerPixel) {
var red = currentLine[x];
var green = currentLine[x + 1];
var blue = currentLine[x + 2];
currentLine[x] = lookupTable[red];
currentLine[x + 1] = lookupTable[green];
currentLine[x + 2] = lookupTable[blue];
}
});

image.UnlockBits(bitmapData);
}
}
}
}
117 changes: 117 additions & 0 deletions FastFilters/FastChromaKeyFilter.cs
@@ -0,0 +1,117 @@
#region License and copyright notice
/*
* Kaliko Image Library
*
* Copyright (c) Fredrik Schultz and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#endregion

namespace Kaliko.ImageLibrary.FastFilters {
using System;
using System.Drawing;
using System.Threading.Tasks;
using ColorSpace;
using Filters;

public class FastChromaKeyFilter : IFilter {
public float ToleranceHue { get; set; }

public float ToleranceSaturnation { get; set; }

public float ToleranceBrightness { get; set; }

public Color KeyColor { get; set; }

public FastChromaKeyFilter() {
KeyColor = Color.FromArgb(0, 255, 0);
ToleranceHue = 10;
ToleranceSaturnation = 0.7f;
ToleranceBrightness = 0.5f;
}

public FastChromaKeyFilter(Color keyColor) {
KeyColor = keyColor;
ToleranceHue = 10;
ToleranceSaturnation = 0.7f;
ToleranceBrightness = 0.5f;
}

public FastChromaKeyFilter(Color keyColor, float toleranceHue, float toleranceSaturnation, float toleranceBrightness) {
KeyColor = keyColor;
ToleranceHue = toleranceHue;
ToleranceSaturnation = toleranceSaturnation;
ToleranceBrightness = toleranceBrightness;
}

public void Run(KalikoImage image) {
ValidateParameters();

ApplyChromaKey(image);
}

public void ApplyChromaKey(KalikoImage image) {
unsafe {
var bitmapData = image.LockBits();

var bytesPerPixel = Image.GetPixelFormatSize(bitmapData.PixelFormat)/8;
var height = bitmapData.Height;
var widthInBytes = bitmapData.Width*bytesPerPixel;
var startOffset = (byte*)bitmapData.Scan0;

var keyHsb = ColorSpaceHelper.RGBtoHSB(KeyColor);

Parallel.For(0, height, y => {
var currentLine = startOffset + (y * bitmapData.Stride);
for (var x = 0; x < widthInBytes; x = x + bytesPerPixel) {
var red = currentLine[x];
var green = currentLine[x + 1];
var blue = currentLine[x + 2];
var hsb = ColorSpaceHelper.RGBtoHSB(red, green, blue);
if (Abs(hsb.Hue, keyHsb.Hue) < ToleranceHue && Abs(hsb.Saturation, keyHsb.Saturation) < ToleranceSaturnation && Abs(hsb.Brightness, keyHsb.Brightness) < ToleranceBrightness) {
// TODO: Add alpha check
currentLine[x + 3] = 0;
}
}
});

image.UnlockBits(bitmapData);
}
}

private static double Abs(double b1, double b2) {
return b1 > b2 ? b1 - b2 : b2 - b1;
}

private void ValidateParameters() {
if (ToleranceHue < 0 || ToleranceHue > 360) {
throw new ArgumentException("ToleranceHue out of range (0..360)");
}
if (ToleranceSaturnation < 0 || ToleranceSaturnation > 1) {
throw new ArgumentException("ToleranceSaturnation out of range (0..1)");
}
if (ToleranceBrightness < 0 || ToleranceBrightness > 1) {
throw new ArgumentException("ToleranceBrightness out of range (0..1)");
}
}
}
}
70 changes: 70 additions & 0 deletions FastFilters/FastContrastFilter.cs
@@ -0,0 +1,70 @@
#region License and copyright notice
/*
* Kaliko Image Library
*
* Copyright (c) Fredrik Schultz and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#endregion

namespace Kaliko.ImageLibrary.FastFilters {
using System.Drawing;
using System.Threading.Tasks;
using Filters;

public class FastContrastFilter : ContrastFilter {

public FastContrastFilter(int changeInContrast) : base(changeInContrast) {
}

public override void Run(KalikoImage image) {
ChangeContrast(image);
}

private void ChangeContrast(KalikoImage image) {
var lookupTable = BuildLookupTable();

unsafe {
var bitmapData = image.LockBits();

var bytesPerPixel = Image.GetPixelFormatSize(bitmapData.PixelFormat)/8;
var height = bitmapData.Height;
var widthInBytes = bitmapData.Width*bytesPerPixel;
var startOffset = (byte*)bitmapData.Scan0;

Parallel.For(0, height, y => {
var currentLine = startOffset + (y*bitmapData.Stride);
for (var x = 0; x < widthInBytes; x = x + bytesPerPixel) {
var red = currentLine[x];
var green = currentLine[x + 1];
var blue = currentLine[x + 2];
currentLine[x] = lookupTable[red];
currentLine[x + 1] = lookupTable[green];
currentLine[x + 2] = lookupTable[blue];
}
});

image.UnlockBits(bitmapData);
}
}
}
}
4 changes: 3 additions & 1 deletion FastFilters/ImageLibrary.FastFilters.csproj
Expand Up @@ -57,13 +57,15 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Drawing" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Properties\SolutionInfo.cs">
<Link>Properties\SolutionInfo.cs</Link>
</Compile>
<Compile Include="FastContrastFilter.cs" />
<Compile Include="FastBrightnessFilter.cs" />
<Compile Include="FastChromaKeyFilter.cs" />
<Compile Include="FastUnsharpMaskFilter.cs" />
<Compile Include="FastDesaturationFilter.cs" />
<Compile Include="FastInvertFilter.cs" />
Expand Down
29 changes: 15 additions & 14 deletions Filters/BrightnessFilter.cs
Expand Up @@ -31,7 +31,6 @@ namespace Kaliko.ImageLibrary.Filters {
/// <summary>Simple filter for adjusting brightness in images.</summary>
public class BrightnessFilter : IFilter {
private readonly double _brightness;
private byte[] _precalcTable;

/// <param name="changeInBrightness">The amount of change to be applied to the brightness. Entered as either a positive - to make it brighter - or negative - to make it darker - value (zero
/// means no change).</param>
Expand All @@ -40,37 +39,39 @@ public class BrightnessFilter : IFilter {
}

/// <summary>Execute the filter.</summary>
public void Run(KalikoImage image) {
PrecalculateTable();
public virtual void Run(KalikoImage image) {
ChangeBrightness(image);
}

private void PrecalculateTable() {
_precalcTable = new byte[256];
protected byte[] BuildLookupTable() {
var lookupTable = new byte[256];

for(int i = 0;i < 256;i++) {
int val = (int)Math.Round(i * _brightness);
for(var i = 0;i < 256;i++) {
var val = (int)Math.Round(i * _brightness);
if(val < 0) {
val = 0;
}
else if(val > 255) {
val = 255;
}
_precalcTable[i] = (byte)val;
lookupTable[i] = (byte)val;
}

return lookupTable;
}

private void ChangeBrightness(KalikoImage image) {
var lookupTable = BuildLookupTable();

byte[] b = image.ByteArray;
var byteArray = image.ByteArray;

for(int i = 0, l = b.Length;i < l;i += 4) {
b[i] = _precalcTable[b[i]]; // b
b[i + 1] = _precalcTable[b[i + 1]]; // g
b[i + 2] = _precalcTable[b[i + 2]]; // r
for(int i = 0, l = byteArray.Length;i < l;i += 4) {
byteArray[i] = lookupTable[byteArray[i]]; // b
byteArray[i + 1] = lookupTable[byteArray[i + 1]]; // g
byteArray[i + 2] = lookupTable[byteArray[i + 2]]; // r
}

image.ByteArray = b;
image.ByteArray = byteArray;
}
}
}

0 comments on commit 38cb6b1

Please sign in to comment.