Last active
February 5, 2019 15:41
-
-
Save equinox2k/7319939bf0e2d390bff0720f8e4990bd to your computer and use it in GitHub Desktop.
ImageSharpNormalMap.cs
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 SixLabors.ImageSharp; | |
| using SixLabors.ImageSharp.PixelFormats; | |
| using System.Numerics; | |
| namespace TestNormal | |
| { | |
| class Program | |
| { | |
| public enum EdgeDetectMethod | |
| { | |
| Sobel, | |
| Scharr | |
| } | |
| public static Vector3 GetPixelRGB(ref Image<Rgba32> originalImage, int xPos, int yPos) | |
| { | |
| xPos = Math.Min(Math.Max(xPos, 0), originalImage.Width - 1); | |
| yPos = Math.Min(Math.Max(yPos, 0), originalImage.Height - 1); | |
| var result = originalImage[xPos, yPos].ToVector4(); | |
| return new Vector3(result.X, result.Y, result.Z); | |
| } | |
| public static Vector4 GetPixelRGBA(ref Image<Rgba32> originalImage, int xPos, int yPos) | |
| { | |
| xPos = Math.Min(Math.Max(xPos, 0), originalImage.Width - 1); | |
| yPos = Math.Min(Math.Max(yPos, 0), originalImage.Height - 1); | |
| var result = originalImage[xPos, yPos].ToVector4(); | |
| return new Vector4(result.X, result.Y, result.Z, result.W); | |
| } | |
| public static float GetPixelFloat(ref Image<Rgba32> originalImage, int xPos, int yPos) | |
| { | |
| Vector3 result = GetPixelRGB(ref originalImage, xPos, yPos); | |
| return ((result.X / 255.0f) + (result.Y / 255.0f) + (result.Z / 255.0f)) / 3; | |
| } | |
| public static Image<Rgba32> NormalMap(Image<Rgba32> originalImage, float bias, bool emboss, EdgeDetectMethod edgeDetectMethod) | |
| { | |
| var imageResult = new Image<Rgba32>(originalImage.Width, originalImage.Height); | |
| const float rIntensity = 0.1f; // can be changed to make more defined although bias can also have similar effect | |
| const float gIntensity = 0.1f; // can be changed to make more defined although bias can also have similar effect | |
| float invertR = emboss ? -rIntensity : rIntensity; | |
| float invertG = emboss ? -gIntensity : gIntensity; | |
| for (int y = 0; y < originalImage.Height; y++) | |
| { | |
| for (int x = 0; x < originalImage.Width; x++) | |
| { | |
| float tl = Math.Abs(GetPixelFloat(ref originalImage, x - 1, y + 1)); | |
| float l = Math.Abs(GetPixelFloat(ref originalImage, x - 1, y)); | |
| float bl = Math.Abs(GetPixelFloat(ref originalImage, x - 1, y - 1)); | |
| float t = Math.Abs(GetPixelFloat(ref originalImage, x, y + 1)); | |
| float b = Math.Abs(GetPixelFloat(ref originalImage, x, y - 1)); | |
| float tr = Math.Abs(GetPixelFloat(ref originalImage, x + 1, y + 1)); | |
| float r = Math.Abs(GetPixelFloat(ref originalImage, x + 1, y)); | |
| float br = Math.Abs(GetPixelFloat(ref originalImage, x + 1, y - 1)); | |
| float dx; | |
| float dy; | |
| if (edgeDetectMethod == EdgeDetectMethod.Sobel) | |
| { | |
| dx = tl + l * 2.0f + bl - tr - r * 2.0f - br; | |
| dy = tl + t * 2.0f + tr - bl - b * 2.0f - br; | |
| } | |
| else | |
| { | |
| dx = tl * 3.0f + l * 10.0f + bl * 3.0f - tr * 3.0f - r * 10.0f - br * 3.0f; | |
| dy = tl * 3.0f + t * 10.0f + tr * 3.0f - bl * 3.0f - b * 10.0f - br * 3.0f; | |
| } | |
| var normal = Vector3.Normalize(new Vector3(dx * invertR, dy * invertG, 1.0f - ((bias - 0.01f) / 100.0f))); | |
| var result = new Vector4((normal.X * 0.5f) + 0.5f, (normal.Y * 0.5f) + 0.5f, normal.Z, 1.0f); | |
| imageResult[x, y] = new Rgba32(result.X, result.Y, result.Z, result.W); | |
| } | |
| } | |
| return imageResult; | |
| } | |
| public static Image<Rgba32> EmbossOverlay(Image<Rgba32> originalImage, bool emboss) | |
| { | |
| float[] kernel = { +2, 0, 0, 0, -1, 0, 0, 0, -1 }; | |
| Vector2[] offsets = { | |
| new Vector2(-1, 1), | |
| new Vector2(0, 1), | |
| new Vector2(1, 1), | |
| new Vector2(-1, 0), | |
| new Vector2(0, 0), | |
| new Vector2(1, 0), | |
| new Vector2(-1, -1), | |
| new Vector2(0, -1), | |
| new Vector2(1, -1) | |
| }; | |
| var imageResult = new Image<Rgba32>(originalImage.Width, originalImage.Height); | |
| for (int y = 0; y < originalImage.Height; y++) | |
| { | |
| for (int x = 0; x < originalImage.Width; x++) | |
| { | |
| var sum = new Vector4(0); | |
| for (int i = 0; i < 9; i++) | |
| { | |
| Vector4 temp = GetPixelRGBA(ref originalImage, x + (int)offsets[i].X, y + (int)offsets[i].Y); | |
| sum += temp * kernel[i]; | |
| } | |
| var result = new Rgba32(sum.X + 0.5f, sum.Y + 0.5f, sum.Z + 0.5f, 1.0f); | |
| float grey = ((result.R + result.G + result.B) / (3 * 255.0f)); | |
| float level = (grey - 0.5f) * 2.0f; | |
| float value1 = emboss ? 0.0f : 1.0f; | |
| float value2 = emboss ? 1.0f : 0.0f; | |
| if (level > 0) | |
| { | |
| imageResult[x, y] = new Rgba32(value1, value1, value1, Math.Abs(level)); | |
| } | |
| else | |
| { | |
| imageResult[x, y] = new Rgba32(value2, value2, value2, Math.Abs(level)); | |
| } | |
| } | |
| } | |
| return imageResult; | |
| } | |
| public static Image<Rgba32> Emboss(Image<Rgba32> originalImage, bool emboss, bool greyscale) | |
| { | |
| float[] kernel = { +2, 0, 0, 0, -1, 0, 0, 0, -1 }; | |
| Vector2[] offsets = { | |
| new Vector2(-1, 1), | |
| new Vector2(0, 1), | |
| new Vector2(1, 1), | |
| new Vector2(-1, 0), | |
| new Vector2(0, 0), | |
| new Vector2(1, 0), | |
| new Vector2(-1, -1), | |
| new Vector2(0, -1), | |
| new Vector2(1, -1) | |
| }; | |
| var imageResult = new Image<Rgba32>(originalImage.Width, originalImage.Height); | |
| for (int y = 0; y < originalImage.Height; y++) | |
| { | |
| for (int x = 0; x < originalImage.Width; x++) | |
| { | |
| var sum = new Vector4(0); | |
| for (int i = 0; i < 9; i++) | |
| { | |
| Vector4 temp = GetPixelRGBA(ref originalImage, x + (int)offsets[i].X, y + (int)offsets[i].Y); | |
| sum += temp * kernel[i]; | |
| } | |
| var result = new Rgba32(sum.X + 0.5f, sum.Y + 0.5f, sum.Z + 0.5f, 1.0f); | |
| if (greyscale) | |
| { | |
| byte grey = (byte)((result.R + result.G + result.B) / 3); | |
| result = new Rgba32(grey, grey, grey, 255); | |
| } | |
| if (!emboss) | |
| { | |
| result = new Rgba32((byte)Math.Abs(255 - result.R), (byte)Math.Abs(255 - result.G), (byte)Math.Abs(255 - result.B), 255); | |
| } | |
| imageResult[x, y] = result; | |
| } | |
| } | |
| return imageResult; | |
| } | |
| //static void Main(string[] args) | |
| //{ | |
| // float bias = 50; | |
| // bool emboss = true; | |
| // using (var image = Image.Load("/Users/ptribe/Desktop/input.png")) | |
| // using (Image<Rgba32> imageResult = Process(image, bias, emboss, EdgeDetectMethod.Scharr)) | |
| // { | |
| // imageResult.Save("/Users/ptribe/Desktop/outrput.png"); | |
| // } | |
| //} | |
| static void Main(string[] args) | |
| { | |
| float bias = 50; | |
| bool emboss = true; | |
| bool greyscale = true; | |
| using (var image = Image.Load("/Users/ptribe/Desktop/G0001373_Twin_Greenery_-_Shower_Invite/assets/APT_FLT57V_FRT.png")) | |
| { | |
| using (Image<Rgba32> imageResult = NormalMap(image, bias, emboss, EdgeDetectMethod.Sobel)) | |
| { | |
| imageResult.Save("/Users/ptribe/Desktop/G0001373_Twin_Greenery_-_Shower_Invite/test2.png"); | |
| } | |
| using (Image<Rgba32> imageResult2 = Emboss(image, emboss, greyscale)) | |
| { | |
| imageResult2.Save("/Users/ptribe/Desktop/G0001373_Twin_Greenery_-_Shower_Invite/test3.png"); | |
| } | |
| using (Image<Rgba32> imageResult2 = EmbossOverlay(image, emboss)) | |
| { | |
| imageResult2.Save("/Users/ptribe/Desktop/G0001373_Twin_Greenery_-_Shower_Invite/test4.png"); | |
| } | |
| } | |
| } | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example Input

Example output normal map

Example output emboss

Example output emboss overlay
