在UWP / C#中进行图像散列,图像在缩放和灰化后水平重复

时间:2017-07-29 13:37:21

标签: c# xaml image-processing hash uwp

我正在关注图像散列this tutorial

到目前为止,我已经实现了以下目标:

代码:

    private async Task<ImageSource> ProcessImageAsync(StorageFile ImageFile)
    {
        if (ImageFile == null)
            throw new ArgumentNullException("ImageFile cannot be null.");

        //The new size of processed image.
        const int side = 300; //300 is for clarity. Should be 8 or 16 px.

        //Initialize bitmap transformations to be applied to the image.
        var transform = new BitmapTransform() { ScaledWidth = side, ScaledHeight = side, InterpolationMode = BitmapInterpolationMode.Cubic };

        //Get image pixels.
        var stream = await ImageFile.OpenStreamForReadAsync();
        var decoder = await BitmapDecoder.CreateAsync(stream.AsRandomAccessStream());
        var pixelData = await decoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
        var pixels = pixelData.DetachPixelData();

        //Initialize writable bitmap.
        var wBitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
        await wBitmap.SetSourceAsync(stream.AsRandomAccessStream());

        //Make it gray
        var grayBitmapBuffer = await ConvertToGrayAsync(wBitmap);

        //Create a software bitmap from the writable bitmap's pixel buffer.
        var sBitmap = SoftwareBitmap.CreateCopyFromBuffer(wBitmap.PixelBuffer, BitmapPixelFormat.Bgra8, side, side, BitmapAlphaMode.Premultiplied);

        //Create software bitmap source.
        var sBitmapSource = new SoftwareBitmapSource();
        await sBitmapSource.SetBitmapAsync(sBitmap);

        return sBitmapSource;
    }

    private async Task<IBuffer> ConvertToGrayAsync(WriteableBitmap srcBitmap)
    {
        // Get the source bitmap pixels
        byte[] srcPixels = new byte[4 * srcBitmap.PixelWidth * srcBitmap.PixelHeight];

        using (Stream pixelStream = srcBitmap.PixelBuffer.AsStream())
        {
            await pixelStream.ReadAsync(srcPixels, 0, srcPixels.Length);
        }

        // Create a destination bitmap and pixels array
        WriteableBitmap dstBitmap = new WriteableBitmap(srcBitmap.PixelWidth, srcBitmap.PixelHeight);
        byte[] dstPixels = new byte[4 * dstBitmap.PixelWidth * dstBitmap.PixelHeight];


        for (int i = 0; i < srcPixels.Length; i += 4)
        {
            double b = (double)srcPixels[i] / 255.0;
            double g = (double)srcPixels[i + 1] / 255.0;
            double r = (double)srcPixels[i + 2] / 255.0;

            byte a = srcPixels[i + 3];

            double e = (0.21 * r + 0.71 * g + 0.07 * b) * 255;
            byte f = Convert.ToByte(e);

            dstPixels[i] = f;
            dstPixels[i + 1] = f;
            dstPixels[i + 2] = f;
            dstPixels[i + 3] = a;

        }

        // Move the pixels into the destination bitmap
        using (Stream pixelStream = dstBitmap.PixelBuffer.AsStream())
        {
            await pixelStream.WriteAsync(dstPixels, 0, dstPixels.Length);
        }
        dstBitmap.Invalidate();

        // Display the new bitmap
        return dstBitmap.PixelBuffer;
    }

这是用户选择的原始图像: Original image

这是我的代码产生的输出: Processed image

它正在变得非常正确。但我不明白为什么图像会水平重复?

以下是呈现图像的XAML代码:

<Image x:Name="myImage" Stretch="None" />

设置图像源的C#代码:

StorageFile userPickedFile = ...; //code ignored.
myImage.Source = await ProcessImageAsync(userPickedFile);

我做错了什么?关于处理过的图像看起来有点可疑..我从这里迷失了......我应该如何继续Hashing?有什么帮助吗?

1 个答案:

答案 0 :(得分:2)

您的ConvertToGrayAsync方法应该只返回转换后的WriteableBitmap。循环内的像素转换代码也可以简化,并且该方法不需要在源和目标缓冲区上操作。它也可以适当地操纵像素值。

private async Task<WriteableBitmap> ConvertToGrayAsync(WriteableBitmap srcBitmap)
{
    var pixels = srcBitmap.PixelBuffer.ToArray();

    for (int i = 0; i < pixels.Length; i += 4)
    {
        var b = pixels[i];
        var g = pixels[i + 1];
        var r = pixels[i + 2];
        var f = (byte)(0.21 * r + 0.71 * g + 0.07 * b);
        pixels[i] = f;
        pixels[i + 1] = f;
        pixels[i + 2] = f;
    }

    var dstBitmap = new WriteableBitmap(srcBitmap.PixelWidth, srcBitmap.PixelHeight);

    using (var pixelStream = dstBitmap.PixelBuffer.AsStream())
    {
        await pixelStream.WriteAsync(pixels, 0, pixels.Length);
    }

    return dstBitmap;
}

以下方法加载具有预定义大小的WriteableBitmap:

private async Task<WriteableBitmap> LoadWriteableBitmapAsync(
    StorageFile file, int width, int height)
{
    using (var fileStream = await file.OpenReadAsync())
    using (var memoryStream = new InMemoryRandomAccessStream())
    {
        var decoder = await BitmapDecoder.CreateAsync(fileStream);
        var transform = new BitmapTransform
        {
            ScaledWidth = (uint)width,
            ScaledHeight = (uint)height,
            InterpolationMode = BitmapInterpolationMode.Cubic
        };
        var pixelData = await decoder.GetPixelDataAsync(
            BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, transform,
            ExifOrientationMode.RespectExifOrientation,
            ColorManagementMode.ColorManageToSRgb);
        var pixels = pixelData.DetachPixelData();

        var encoder = await BitmapEncoder.CreateAsync(
            BitmapEncoder.PngEncoderId, memoryStream);

        encoder.SetPixelData(
            BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight,
            (uint)width, (uint)height, 96, 96, pixels);

        await encoder.FlushAsync();
        memoryStream.Seek(0);

        var bitmap = new WriteableBitmap(width, height);
        await bitmap.SetSourceAsync(memoryStream);
        return bitmap;
    }
}

您的ProcessImageAsync方法现在看起来像这样:

private async Task<WriteableBitmap> ProcessImageAsync(
    StorageFile file, int width, int height)
{
    return await ConvertToGrayAsync(
        await LoadWriteableBitmapAsync(file, width, height));
}