Browse Source

Fix HSV/BGRA conversions.

Former-commit-id: 15347486e210642b35e35e1c3220d86778897773
Former-commit-id: ce3d10d48dc5149cb84ac3c6fb2d442c98d0950a
Former-commit-id: ed21c57a33d1f5da11052bb97746f0ebeb63597e
pull/17/head
James Jackson-South 11 years ago
parent
commit
8764274411
  1. 66
      src/ImageProcessor/Colors/Hsv.cs
  2. 56
      tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs

66
src/ImageProcessor/Colors/Hsv.cs

@ -52,7 +52,7 @@ namespace ImageProcessor
/// <remarks>A value ranging between 0 and 100.</remarks> /// <remarks>A value ranging between 0 and 100.</remarks>
/// </summary> /// </summary>
public readonly float S; public readonly float S;
/// <summary> /// <summary>
/// Gets the V value (brightness) component. /// Gets the V value (brightness) component.
/// <remarks>A value ranging between 0 and 100.</remarks> /// <remarks>A value ranging between 0 and 100.</remarks>
@ -117,30 +117,33 @@ namespace ImageProcessor
/// </returns> /// </returns>
public static implicit operator Hsv(Bgra color) public static implicit operator Hsv(Bgra color)
{ {
float max = Math.Max(color.R, Math.Max(color.G, color.B)); float r = color.R / 255f;
float min = Math.Min(color.R, Math.Min(color.G, color.B)); float g = color.G / 255f;
float delta = max - min; float b = color.B / 255f;
if (Math.Abs(max) < Epsilon) float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
float chroma = max - min;
float h = 0;
float s = 0;
float v = max;
if (Math.Abs(chroma) < Epsilon)
{ {
return new Hsv(0, 0, 0); return new Hsv(0, s * 100, v * 100);
} }
float h = 0.0F; if (Math.Abs(chroma) < Epsilon) { h = 0; }
float s; else if (Math.Abs(r - max) < Epsilon) { h = (g - b) / chroma; }
float v; else if (Math.Abs(g - max) < Epsilon) { h = 2 + (b - r) / chroma; }
else if (Math.Abs(b - max) < Epsilon) { h = 4 + (r - g) / chroma; }
if (Math.Abs(delta) < Epsilon) { h = 0; }
else if (Math.Abs(color.R - max) < Epsilon) { h = (color.G - color.B) / delta; }
else if (Math.Abs(color.G - max) < Epsilon) { h = 2 + (color.B - color.R) / delta; }
else if (Math.Abs(color.B - max) < Epsilon) { h = 4 + (color.R - color.G) / delta; }
h *= 60; h *= 60;
if (h < 0.0) { h += 360; } if (h < 0.0) { h += 360; }
s = delta / max; s = (chroma / v);
v = max / 255F;
return new Hsv(h, s, v); return new Hsv(h, s * 100, v * 100);
} }
/// <summary> /// <summary>
@ -155,9 +158,12 @@ namespace ImageProcessor
/// </returns> /// </returns>
public static implicit operator Bgra(Hsv color) public static implicit operator Bgra(Hsv color)
{ {
if (Math.Abs(color.S) < Epsilon) float s = color.S / 100;
float v = color.V / 100;
if (Math.Abs(s) < Epsilon)
{ {
byte component = (byte)(color.V * 255); byte component = (byte)(v * 255);
return new Bgra(component, component, component, 255); return new Bgra(component, component, component, 255);
} }
@ -165,51 +171,51 @@ namespace ImageProcessor
int i = (int)(Math.Truncate(h)); int i = (int)(Math.Truncate(h));
float f = h - i; float f = h - i;
float p = color.V * (1.0f - color.S); float p = v * (1.0f - s);
float q = color.V * (1.0f - (color.S * f)); float q = v * (1.0f - (s * f));
float t = color.V * (1.0f - (color.S * (1.0f - f))); float t = v * (1.0f - (s * (1.0f - f)));
float r, g, b; float r, g, b;
switch (i) switch (i)
{ {
case 0: case 0:
r = color.V; r = v;
g = t; g = t;
b = p; b = p;
break; break;
case 1: case 1:
r = q; r = q;
g = color.V; g = v;
b = p; b = p;
break; break;
case 2: case 2:
r = p; r = p;
g = color.V; g = v;
b = t; b = t;
break; break;
case 3: case 3:
r = p; r = p;
g = q; g = q;
b = color.V; b = v;
break; break;
case 4: case 4:
r = t; r = t;
g = p; g = p;
b = color.V; b = v;
break; break;
default: default:
r = color.V; r = v;
g = p; g = p;
b = q; b = q;
break; break;
} }
return new Bgra((byte)b, (byte)g, (byte)r); return new Bgra((byte)(Math.Round(b * 255)), (byte)(Math.Round(g * 255)), (byte)(Math.Round(r * 255)));
} }
/// <summary> /// <summary>

56
tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs

@ -10,6 +10,7 @@
namespace ImageProcessor.Tests namespace ImageProcessor.Tests
{ {
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Xunit; using Xunit;
@ -94,13 +95,21 @@ namespace ImageProcessor.Tests
Justification = "Reviewed. Suppression is OK here.")] Justification = "Reviewed. Suppression is OK here.")]
public void BgrToHsv() public void BgrToHsv()
{ {
// Black
Bgra b = new Bgra(0, 0, 0, 255);
Hsv h = b;
Assert.Equal(0, h.H);
Assert.Equal(0, h.S);
Assert.Equal(0, h.V);
// White // White
Bgra color = new Bgra(255, 255, 255, 255); Bgra color = new Bgra(255, 255, 255, 255);
Hsv hsv = color; Hsv hsv = color;
Assert.Equal(0, hsv.H); Assert.Equal(0, hsv.H);
Assert.Equal(0, hsv.S); Assert.Equal(0, hsv.S);
Assert.Equal(1, hsv.V); Assert.Equal(100, hsv.V);
// Dark moderate pink. // Dark moderate pink.
Bgra color2 = new Bgra(106, 64, 128, 255); Bgra color2 = new Bgra(106, 64, 128, 255);
@ -109,6 +118,51 @@ namespace ImageProcessor.Tests
Assert.Equal(320.6, hsv2.H, 1); Assert.Equal(320.6, hsv2.H, 1);
Assert.Equal(50, hsv2.S, 1); Assert.Equal(50, hsv2.S, 1);
Assert.Equal(50.2, hsv2.V, 1); Assert.Equal(50.2, hsv2.V, 1);
// Ochre.
Bgra color3 = new Bgra(34, 119, 204, 255);
Hsv hsv3 = color3;
Assert.Equal(30, hsv3.H, 1);
Assert.Equal(83.3, hsv3.S, 1);
Assert.Equal(80, hsv3.V, 1);
}
[Fact]
public void HsvToBgr()
{
// Dark moderate pink.
Hsv hsv = new Hsv(320.6f, 50, 50.2f);
Bgra bgra = hsv;
Assert.Equal(bgra.B, 106);
Assert.Equal(bgra.G, 64);
Assert.Equal(bgra.R, 128);
// Ochre
Hsv hsv2 = new Hsv(30, 83.3f, 80);
Bgra bgra2 = hsv2;
Assert.Equal(bgra2.B, 34);
Assert.Equal(bgra2.G, 119);
Assert.Equal(bgra2.R, 204);
// White
Hsv hsv3 = new Hsv(0, 0, 100);
Bgra bgra3 = hsv3;
Assert.Equal(bgra3.B, 255);
Assert.Equal(bgra3.G, 255);
Assert.Equal(bgra3.R, 255);
// Check others.
Random random = new Random(0);
for (var i = 0; i < 1000; i++)
{
Bgra bgra4 = new Bgra((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
Hsv hsb4 = bgra4;
Assert.Equal(bgra4, (Bgra)hsb4);
}
} }
} }
} }

Loading…
Cancel
Save