Last active
January 12, 2022 18:38
-
-
Save brianmed/e77e1149fef4569d53ac3a3d5923c582 to your computer and use it in GitHub Desktop.
Convert RGBA32 image to Grayscale via ILGPU in C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>net5.0</TargetFramework> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="ILGPU" Version="0.10.1" /> | |
</ItemGroup> | |
</Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.IO; | |
using System.Runtime.InteropServices; | |
using System.Threading.Tasks; | |
using ILGPU; | |
using ILGPU.Runtime; | |
// https://sigma.software/about/media/gp-gpu-computing-c | |
// https://e2eml.school/convert_rgb_to_grayscale.html | |
namespace ILGpuGrayscale | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
string imagePath = args[0]; | |
UInt32[] pixels = MemoryMarshal.Cast<byte, UInt32>(File.ReadAllBytes(imagePath)).ToArray(); | |
Stopwatch cpuTotalStopWatch = new Stopwatch(); | |
Stopwatch gpuTotalStopWatch = new Stopwatch(); | |
cpuTotalStopWatch.Start(); | |
foreach (int _ in Enumerable.Range(0, 10)) | |
{ | |
Stopwatch applyStopWatch = new Stopwatch(); | |
applyStopWatch.Start(); | |
TplImageFilter.Apply(pixels, TplImageFilter.Grayscale); | |
applyStopWatch.Stop(); | |
Console.WriteLine($"\tCPU: {applyStopWatch.Elapsed} [{cpuTotalStopWatch.Elapsed}]"); | |
} | |
cpuTotalStopWatch.Stop(); | |
Console.WriteLine($"\tCPU: {cpuTotalStopWatch.Elapsed}"); | |
using (IlGpuFilter ilGpuFilter = new()) | |
{ | |
gpuTotalStopWatch.Start(); | |
foreach (int _ in Enumerable.Range(0, 10)) | |
{ | |
Stopwatch applyStopWatch = new Stopwatch(); | |
applyStopWatch.Start(); | |
UInt32[] translatedPixels = ilGpuFilter.Apply(pixels, ilGpuFilter.KernelGrayscale); | |
applyStopWatch.Stop(); | |
Console.WriteLine($"\tCUDA: {applyStopWatch.Elapsed} [{gpuTotalStopWatch.Elapsed}]"); | |
} | |
gpuTotalStopWatch.Stop(); | |
Console.WriteLine($"\tCUDA: {gpuTotalStopWatch.Elapsed}"); | |
} | |
} | |
} | |
public class TplImageFilter | |
{ | |
public static UInt32[] Apply(UInt32[] pixels, Func<UInt32, UInt32> filter) | |
{ | |
Parallel.For(0, pixels.Length, i => pixels[i] = filter(pixels[i])); | |
return pixels; | |
} | |
public static UInt32 Grayscale(UInt32 pixel) | |
{ | |
byte red = (byte)(pixel & 0x0000_00FF); | |
byte green = (byte)((pixel >> 8) & 0x0000_00FF); | |
byte blue = (byte)((pixel >> 16) & 0x0000_00FF); | |
byte alpha = (byte)((pixel >> 24) & 0x0000_00FF); | |
byte gray = (byte)((red * 0.299) + (green * 0.587) + (blue * 0.114)); | |
return (UInt32)(gray << 0) + (UInt32)(gray << 8) + (UInt32)(gray << 16) + (UInt32)(alpha << 24); | |
} | |
} | |
public class IlGpuFilter : IDisposable | |
{ | |
public readonly Action<ILGPU.Index1, ArrayView<UInt32>> KernelGrayscale; | |
private readonly Accelerator Accelerator; | |
public IlGpuFilter() | |
{ | |
Accelerator = Accelerator.Create(new Context(), Accelerator.Accelerators.First(a => a.AcceleratorType == AcceleratorType.Cuda)); | |
KernelGrayscale = Accelerator.LoadAutoGroupedStreamKernel<ILGPU.Index1, ArrayView<UInt32>>(ApplyGrayscale); | |
} | |
private static void ApplyGrayscale( | |
ILGPU.Index1 index, | |
ArrayView<UInt32> pixels) | |
{ | |
pixels[index] = Grayscale(pixels[index]); | |
} | |
public UInt32[] Apply(UInt32[] pixels, Action<ILGPU.Index1, ArrayView<UInt32>> kernel) | |
{ | |
using (MemoryBuffer<UInt32> buffer = this.Accelerator.Allocate<UInt32>(pixels.Length)) | |
{ | |
buffer.CopyFrom(pixels, 0, ILGPU.Index1.Zero, pixels.Length); | |
kernel(buffer.Length, buffer.View); | |
// Wait for the kernel to finish... | |
Accelerator.Synchronize(); | |
return buffer.GetAsArray(); | |
} | |
} | |
public static UInt32 Grayscale(UInt32 pixel) | |
{ | |
byte red = (byte)((pixel >> 0) & 0x0000_00FF); | |
byte green = (byte)((pixel >> 8) & 0x0000_00FF); | |
byte blue = (byte)((pixel >> 16) & 0x0000_00FF); | |
byte alpha = (byte)((pixel >> 24) & 0x0000_00FF); | |
byte gray = (byte)((byte)(red * 0.299) + (byte)(green * 0.587) + (byte)(blue * 0.114)); | |
return (UInt32)(gray << 0) + (UInt32)(gray << 8) + (UInt32)(gray << 16) + (UInt32)(alpha << 24); | |
} | |
public void Dispose() | |
{ | |
this.Accelerator?.Dispose(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment