Created
June 9, 2017 16:36
-
-
Save azyobuzin/0a0482c657b1124ac1a38755b0ea03c3 to your computer and use it in GitHub Desktop.
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.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