wabbajack/Wabbajack.Hashing.PHash/Image.cs

126 lines
4.0 KiB
C#
Raw Normal View History

2021-09-27 12:42:46 +00:00
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using BCnEncoder.Decoder;
using BCnEncoder.Encoder;
using BCnEncoder.ImageSharp;
using BCnEncoder.Shared;
using BCnEncoder.Shared.ImageFiles;
using Shipwreck.Phash;
using Shipwreck.Phash.Imaging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Wabbajack.DTOs.Texture;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
2021-10-23 16:51:17 +00:00
namespace Wabbajack.Hashing.PHash;
public class ImageLoader
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
public static async ValueTask<ImageState> Load(AbsolutePath path)
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
await using var fs = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
return await Load(fs);
}
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public static async ValueTask<ImageState> Load(Stream stream)
{
var decoder = new BcDecoder();
var ddsFile = DdsFile.Load(stream);
var data = await decoder.DecodeToImageRgba32Async(ddsFile);
var format = ddsFile.dx10Header.dxgiFormat == DxgiFormat.DxgiFormatUnknown
? ddsFile.header.ddsPixelFormat.DxgiFormat
: ddsFile.dx10Header.dxgiFormat;
var state = new ImageState
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
Width = data.Width,
Height = data.Height,
Format = (DXGI_FORMAT) format
};
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
data.Mutate(x => x.Resize(512, 512, KnownResamplers.Welch).Grayscale(GrayscaleMode.Bt601));
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
var hash = ImagePhash.ComputeDigest(new ImageBitmap(data));
state.PerceptualHash = new DTOs.Texture.PHash(hash.Coefficients);
return state;
}
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public static float ComputeDifference(DTOs.Texture.PHash a, DTOs.Texture.PHash b)
{
return ImagePhash.GetCrossCorrelation(
new Digest {Coefficients = a.Data},
new Digest {Coefficients = b.Data});
}
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public static async Task Recompress(AbsolutePath input, int width, int height, DXGI_FORMAT format,
AbsolutePath output,
CancellationToken token)
{
var inData = await input.ReadAllBytesAsync(token);
await using var outStream = output.Open(FileMode.Create, FileAccess.Write);
await Recompress(new MemoryStream(inData), width, height, format, outStream, token);
}
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public static async Task Recompress(Stream input, int width, int height, DXGI_FORMAT format, Stream output,
CancellationToken token, bool leaveOpen = false)
{
var decoder = new BcDecoder();
var ddsFile = DdsFile.Load(input);
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
if (!leaveOpen) await input.DisposeAsync();
var data = await decoder.DecodeToImageRgba32Async(ddsFile, token);
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
data.Mutate(x => x.Resize(width, height, KnownResamplers.Welch));
var encoder = new BcEncoder
{
OutputOptions =
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
Quality = CompressionQuality.Balanced,
GenerateMipMaps = true,
Format = ToCompressionFormat(format),
FileFormat = OutputFileFormat.Dds
2021-09-27 12:42:46 +00:00
}
2021-10-23 16:51:17 +00:00
};
var file = await encoder.EncodeToDdsAsync(data, token);
file.Write(output);
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
if (!leaveOpen)
await output.DisposeAsync();
}
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public static CompressionFormat ToCompressionFormat(DXGI_FORMAT dx)
{
return dx switch
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
DXGI_FORMAT.BC1_UNORM => CompressionFormat.Bc1,
DXGI_FORMAT.BC2_UNORM => CompressionFormat.Bc2,
DXGI_FORMAT.BC3_UNORM => CompressionFormat.Bc3,
DXGI_FORMAT.BC4_UNORM => CompressionFormat.Bc4,
DXGI_FORMAT.BC5_UNORM => CompressionFormat.Bc5,
DXGI_FORMAT.BC7_UNORM => CompressionFormat.Bc7,
_ => throw new Exception($"Cannot re-encode texture with {dx} format, encoding not supported")
};
}
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public class ImageBitmap : IByteImage
{
private readonly Image<Rgba32> _image;
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public ImageBitmap(Image<Rgba32> image)
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
_image = image;
2021-09-27 12:42:46 +00:00
}
2021-10-23 16:51:17 +00:00
public int Width => _image.Width;
public int Height => _image.Height;
public byte this[int x, int y] => _image[x, y].R;
2021-09-27 12:42:46 +00:00
}
}