Skip to content

Commit 8f4d4c1

Browse files
committed
refactor colormap module
1 parent 13deead commit 8f4d4c1

13 files changed

Lines changed: 239 additions & 138 deletions

File tree

src/Spectrogram.MicrophoneDemo/FormAnalysis.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private void timer1_Tick(object sender, EventArgs e)
8484
using (Bitmap bmpIndexed = Image.Create(pixelValues, nextColIndex))
8585
using (Graphics gfx = Graphics.FromImage(bmp))
8686
{
87-
new Colormaps.Viridis().Apply(bmpIndexed);
87+
new Colormap.Viridis().Apply(bmpIndexed);
8888
gfx.DrawImage(bmpIndexed, 0, 0);
8989
gfx.DrawLine(Pens.White, nextColIndex, 0, nextColIndex, spec.Height);
9090
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using NuGet.Frameworks;
2+
using NUnit.Framework;
3+
using Spectrogram.Colormaps;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Text;
7+
8+
namespace Spectrogram.Tests
9+
{
10+
class ColormapTests
11+
{
12+
[Test]
13+
public void Test_Colormap_ExtendedFractionsReturnEdgeValues()
14+
{
15+
var cmap = Colormap.GetColormap();
16+
17+
Random rand = new Random(0);
18+
for (double frac = -3; frac < 3; frac += rand.NextDouble() * .2)
19+
{
20+
Console.WriteLine($"{frac}: {cmap.GetRGB(frac)}");
21+
22+
if (frac <= 0)
23+
Assert.AreEqual(cmap.GetRGB(0), cmap.GetRGB(frac));
24+
25+
if (frac >= 1)
26+
Assert.AreEqual(cmap.GetRGB(1.0), cmap.GetRGB(frac));
27+
}
28+
}
29+
30+
[Test]
31+
public void Test_Colormap_EveryColorIsImplementedAndUnique()
32+
{
33+
Colormap.Name[] cmapNames = (Colormap.Name[])Enum.GetValues(typeof(Colormap.Name));
34+
35+
byte pixelIntensity = 123;
36+
List<int> colorValuesSeen = new List<int>();
37+
38+
foreach (Colormap.Name cmapName in cmapNames)
39+
{
40+
Colormaps.Colormap cmap = Colormap.GetColormap(cmapName);
41+
int cmapInt = cmap.GetInt32(pixelIntensity);
42+
var cmapRGB = cmap.GetRGB(pixelIntensity);
43+
44+
Console.WriteLine($"{cmap}: value 123 RGB={cmapRGB}, Int32={cmapInt}");
45+
46+
Assert.That(colorValuesSeen, Has.No.Member(cmapInt));
47+
colorValuesSeen.Add(cmapInt);
48+
}
49+
}
50+
51+
[Test]
52+
public void Test_Colormap_IntegerColorsMatchRGBColors()
53+
{
54+
Colormap.Name[] cmapNames = (Colormap.Name[])Enum.GetValues(typeof(Colormap.Name));
55+
56+
byte pixelIntensity = 123;
57+
58+
foreach (Colormap.Name cmapName in cmapNames)
59+
{
60+
Colormaps.Colormap cmap = Colormap.GetColormap(cmapName);
61+
62+
var (r, g, b) = cmap.GetRGB(pixelIntensity);
63+
var colorFromRGB = System.Drawing.Color.FromArgb(255, r, g, b);
64+
65+
int cmapInt = cmap.GetInt32(pixelIntensity);
66+
var colorFromInt = System.Drawing.Color.FromArgb(cmapInt);
67+
68+
Console.WriteLine($"{cmap}: value 123 RGB={colorFromRGB}, Int32={cmapInt}");
69+
70+
Assert.AreEqual(colorFromRGB, colorFromInt);
71+
}
72+
}
73+
}
74+
}

src/Spectrogram.Tests/Quickstart.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,20 @@ public void Test_WholeFile_DefaultSettings()
1515
var spec = new Spectrogram(sampleRate: 44100, fftSize: 1 << 12, stepSize: 500, maxFreq: 3000);
1616
spec.Add(audio);
1717
spec.SaveImage("../../../../../dev/graphics/hal.png", intensity: .2);
18-
// TODO: colormap
18+
19+
Console.WriteLine(spec);
20+
}
21+
22+
[Test]
23+
public void Test_WholeFile_CustomColormap()
24+
{
25+
double[] audio = Read.WavInt16mono("../../../../../data/cant-do-that-44100.wav");
26+
27+
var spec = new Spectrogram(sampleRate: 44100, fftSize: 1 << 12, stepSize: 500, maxFreq: 3000);
28+
spec.Add(audio);
29+
spec.SetColormap(Colormap.Name.Inferno);
30+
spec.SaveImage("../../../../../dev/graphics/hal.png", intensity: .2);
31+
1932
Console.WriteLine(spec);
2033
}
2134
}

src/Spectrogram/Colormap.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace Spectrogram
6+
{
7+
public static class Colormap
8+
{
9+
public enum Name
10+
{
11+
Grayscale,
12+
Viridis,
13+
Cividis,
14+
Inferno,
15+
Magma,
16+
Plasma
17+
}
18+
19+
public static Colormaps.Colormap GetColormap(Name name = Name.Viridis)
20+
{
21+
switch (name)
22+
{
23+
case Name.Grayscale:
24+
return new Colormaps.Grayscale();
25+
case Name.Viridis:
26+
return new Colormaps.Viridis();
27+
case Name.Cividis:
28+
return new Colormaps.Cividis();
29+
case Name.Inferno:
30+
return new Colormaps.Inferno();
31+
case Name.Magma:
32+
return new Colormaps.Magma();
33+
case Name.Plasma:
34+
return new Colormaps.Plasma();
35+
default:
36+
throw new NotImplementedException();
37+
}
38+
}
39+
}
40+
}

src/Spectrogram/Colormaps/Cividis.cs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,17 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Drawing;
4-
using System.Drawing.Imaging;
53
using System.Text;
64

75
namespace Spectrogram.Colormaps
86
{
9-
public class Cividis : IColormap
7+
class Cividis : Colormap
108
{
11-
public string GetName()
9+
public override (byte r, byte g, byte b) GetRGB(byte value)
1210
{
13-
return "Cividis"; // TODO: use reflection
14-
}
15-
16-
public void Apply(Bitmap bmp)
17-
{
18-
ColorPalette pal = bmp.Palette;
19-
for (int i = 0; i < 256; i++)
20-
pal.Entries[i] = Color.FromArgb(255, RGB[i, 0], RGB[i, 1], RGB[i, 2]);
21-
bmp.Palette = pal;
22-
}
23-
24-
public (byte r, byte g, byte b) Lookup(int value)
25-
{
26-
value = Math.Max(value, 0);
27-
value = Math.Min(value, 255);
2811
return (RGB[value, 0], RGB[value, 1], RGB[value, 2]);
2912
}
3013

31-
// cividis
32-
public readonly byte[,] RGB =
14+
private readonly byte[,] RGB =
3315
{
3416
{0, 34, 78},
3517
{0, 35, 79},
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Drawing;
4+
using System.Drawing.Imaging;
5+
using System.Text;
6+
7+
namespace Spectrogram.Colormaps
8+
{
9+
public abstract class Colormap
10+
{
11+
// each colormap must implement its own lookup method
12+
public abstract (byte r, byte g, byte b) GetRGB(byte value);
13+
14+
public string GetName()
15+
{
16+
return GetType().Name;
17+
}
18+
19+
public override string ToString()
20+
{
21+
return $"Colormap {GetName()}";
22+
}
23+
24+
public (byte r, byte g, byte b) GetRGB(int value)
25+
{
26+
value = Math.Max(value, 0);
27+
value = Math.Min(value, 255);
28+
return GetRGB((byte)value);
29+
}
30+
31+
public (byte r, byte g, byte b) GetRGB(double fraction)
32+
{
33+
return GetRGB((int)(fraction * 255));
34+
}
35+
36+
public Color GetColor(byte value)
37+
{
38+
var (r, g, b) = GetRGB(value);
39+
return Color.FromArgb(255, r, g, b);
40+
}
41+
42+
public Color GetColor(int value)
43+
{
44+
var (r, g, b) = GetRGB(value);
45+
return Color.FromArgb(255, r, g, b);
46+
}
47+
48+
public Color GetColor(double fraction)
49+
{
50+
var (r, g, b) = GetRGB(fraction);
51+
return Color.FromArgb(255, r, g, b);
52+
}
53+
54+
public int GetInt32(byte value)
55+
{
56+
var (r, g, b) = GetRGB(value);
57+
Int32 argb = 255 << 24 | r << 16 | g << 8 | b;
58+
return argb;
59+
}
60+
61+
public int GetInt32(int value)
62+
{
63+
value = Math.Max(value, 0);
64+
value = Math.Min(value, 255);
65+
return GetInt32((byte)value);
66+
}
67+
68+
public int GetInt32(double fraction)
69+
{
70+
return GetInt32((int)(fraction * 255));
71+
}
72+
73+
public void Apply(Bitmap bmp)
74+
{
75+
ColorPalette pal = bmp.Palette;
76+
for (int i = 0; i < 256; i++)
77+
pal.Entries[i] = GetColor((byte)i);
78+
bmp.Palette = pal;
79+
}
80+
}
81+
}
Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,14 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Drawing;
4-
using System.Drawing.Imaging;
53
using System.Text;
64

75
namespace Spectrogram.Colormaps
86
{
9-
public class Grayscale : IColormap
7+
class Grayscale : Colormap
108
{
11-
public void Apply(Bitmap bmp)
9+
public override (byte r, byte g, byte b) GetRGB(byte value)
1210
{
13-
ColorPalette pal = bmp.Palette;
14-
for (int i = 0; i < 256; i++)
15-
pal.Entries[i] = Color.FromArgb(255, i, i, i);
16-
bmp.Palette = pal;
17-
}
18-
19-
public string GetName()
20-
{
21-
return "Grayscale";
22-
}
23-
24-
public (byte r, byte g, byte b) Lookup(int value)
25-
{
26-
return ((byte)value, (byte)value, (byte)value);
11+
return (value, value, value);
2712
}
2813
}
2914
}

src/Spectrogram/Colormaps/IColormap.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/Spectrogram/Colormaps/Inferno.cs

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,17 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Drawing;
4-
using System.Drawing.Imaging;
53
using System.Text;
64

75
namespace Spectrogram.Colormaps
86
{
9-
public class Inferno : IColormap
7+
class Inferno : Colormap
108
{
11-
public void Apply(Bitmap bmp)
9+
public override (byte r, byte g, byte b) GetRGB(byte value)
1210
{
13-
ColorPalette pal = bmp.Palette;
14-
for (int i = 0; i < 256; i++)
15-
pal.Entries[i] = Color.FromArgb(255, RGB[i, 0], RGB[i, 1], RGB[i, 2]);
16-
bmp.Palette = pal;
17-
}
18-
19-
public (byte r, byte g, byte b) Lookup(int value)
20-
{
21-
value = Math.Max(value, 0);
22-
value = Math.Min(value, 255);
2311
return (RGB[value, 0], RGB[value, 1], RGB[value, 2]);
2412
}
2513

26-
public string GetName()
27-
{
28-
return "Inferno";
29-
}
30-
31-
// inferno
32-
public readonly byte[,] RGB =
14+
private readonly byte[,] RGB =
3315
{
3416
{0, 0, 3},
3517
{0, 0, 4},
@@ -288,6 +270,5 @@ public string GetName()
288270
{251, 254, 161},
289271
{253, 255, 165},
290272
};
291-
292273
}
293274
}

0 commit comments

Comments
 (0)