Skip to content

Instantly share code, notes, and snippets.

@azyobuzin
Created June 9, 2017 16:36
Show Gist options
  • Save azyobuzin/0a0482c657b1124ac1a38755b0ea03c3 to your computer and use it in GitHub Desktop.
Save azyobuzin/0a0482c657b1124ac1a38755b0ea03c3 to your computer and use it in GitHub Desktop.
using System;
using System.Threading.Tasks;
namespace Blockhash
{
public static class BlockHashImpl
{
public static byte[] ComputeHashQuick<TImage>(int bits, TImage image)
where TImage : IInputImage
{
if (bits % 4 != 0) throw new ArgumentException();
if (image.Width % bits != 0 || image.Height % bits != 0) throw new ArgumentException();
var blocks = new ulong[bits * bits];
var blockWidth = image.Width / bits;
var blockHeight = image.Height / bits;
Parallel.For(0, blocks.Length, blockIndex =>
{
var xStart = (blockIndex * blockWidth) % image.Width;
var xEnd = xStart + blockWidth;
var yStart = ((blockIndex * blockWidth) / image.Width) * blockHeight;
var yEnd = yStart + blockHeight;
ulong blockValue = 0;
for (var y = yStart; y < yEnd; y++)
{
for (var x = xStart; x < xEnd; x++)
{
var pixel = image.GetPixel(x, y);
blockValue += pixel.A == 0
? 255UL * 3
: (ulong)pixel.R + pixel.G + pixel.B + pixel.A;
}
}
blocks[blockIndex] = blockValue;
});
var h = 255 * 3 * (ulong)blockWidth * (ulong)blockHeight / 2;
var groupSize = bits * bits / 4;
Parallel.For(0, 4, groupIndex =>
{
var med = Median(blocks, groupSize * groupIndex, groupSize);
var blockStart = groupIndex * groupSize;
var blockEnd = blockStart + groupSize;
for (var i = blockStart; i < blockEnd; i++)
{
var block = blocks[i];
blocks[i] = block > med || (block == med && med > h) ? 1UL : 0;
}
});
var result = new byte[bits * bits / 8];
for (var i = 0; i < result.Length; i++)
{
uint b = 0;
for (var j = 0; j < 8; j++)
{
if (blocks[i * 8 + j] != 0)
b |= 1U << (7 - j);
}
result[i] = (byte)b;
}
return result;
}
private static ulong Median(ulong[] src, int startIndex, int length)
{
// TODO: もっと良さそうな方法
var working = new ulong[length];
Array.Copy(src, startIndex, working, 0, length);
Array.Sort(working);
return length % 2 == 0
? (working[length / 2] + working[length / 2 - 1]) / 2
: working[length / 2];
}
}
public struct Pixel
{
public byte R { get; set; }
public byte G { get; set; }
public byte B { get; set; }
public byte A { get; set; }
public Pixel(byte r, byte g, byte b, byte a)
{
this.R = r;
this.G = g;
this.B = b;
this.A = a;
}
}
public interface IInputImage
{
int Width { get; }
int Height { get; }
Pixel GetPixel(int x, int y);
}
public struct InputImage : IInputImage
{
public int Width { get; }
public int Height { get; }
public Span<ImageSharp.Rgba32> Pixels { get; }
public InputImage(ImageSharp.Image<ImageSharp.Rgba32> image)
{
this.Width = image.Width;
this.Height = image.Height;
this.Pixels = image.Pixels;
}
public Pixel GetPixel(int x, int y)
{
var color = this.Pixels[y * this.Width + x];
return new Pixel(color.R, color.G, color.B, color.A);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment