Skip to content

Instantly share code, notes, and snippets.

@equinox2k
Last active February 5, 2019 15:41
Show Gist options
  • Select an option

  • Save equinox2k/7319939bf0e2d390bff0720f8e4990bd to your computer and use it in GitHub Desktop.

Select an option

Save equinox2k/7319939bf0e2d390bff0720f8e4990bd to your computer and use it in GitHub Desktop.
ImageSharpNormalMap.cs
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");
}
}
}
}
}
@equinox2k
Copy link
Author

equinox2k commented Feb 5, 2019

Example Input
apt_flt57v_frt

Example output normal map
test2

Example output emboss
test3

Example output emboss overlay
test4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment