在通用 Windows 应用程序(c#,XAML)中显示便携式灰度图 (PGM) 图像

Displaying Portable Graymap (PGM) images in Universal Windows App (c#, XAML)

我需要在我的通用 Windows 应用程序中显示 .pgm 图像。 XAML 图像控件不直接支持 .pgm 图像,所以我需要解决它。

互联网上有很多用 C# 打开 .pgm 文件的例子,但所有这些都依赖于使用通用 Windows 平台不支持的位图对象(System.Drawing 和System.Windows.Media 无法使用库。

我有读取图像宽度和高度并读取字节数组中像素的代码(包含代表灰度的值 0-255)。

下一步是使用任何最终可以传递给 XAML Image.Source 的对象从 byte[] 数组中绘制图像(并以合理的数量进行时间)。

我能做的最好的事情就是显示 this 但实际图片应该看起来像 this(出于某种原因,它显示图像 4 倍并且颜色错误)。

我使用的代码:

    public int width;
    public int height;
    public int maxVal; //255
    public byte[] pixels;

    public async Task<WriteableBitmap> ToWriteableBitmap()
    {
        WriteableBitmap writeableBitmap = new WriteableBitmap(width, height);
        using (Stream stream = writeableBitmap.PixelBuffer.AsStream())
        {
            await stream.WriteAsync(pixels, 0, pixels.Length);
        }
        return writeableBitmap;
    }

如果有关系,我还提供了我用来将 .pgm 文件读取到 PgmImage 对象的代码,但我很确定这工作正常:

    public static async Task<PgmImage> LoadFromFile(string file)
    {
        FileStream ifs = null;
        await Task.Run( () =>
        {
            Task.Yield();
            ifs = new FileStream(file, FileMode.Open, FileAccess.Read);
        });
        BinaryReader br = new BinaryReader(ifs);

        string magic = NextNonCommentLine(br);
        //if (magic != "P5")
        //    throw new Exception("Unknown magic number: " + magic);

        string widthHeight = NextNonCommentLine(br);
        string[] tokens = widthHeight.Split(' ');
        int width = int.Parse(tokens[0]);
        int height = int.Parse(tokens[1]);

        string sMaxVal = NextNonCommentLine(br);
        int maxVal = int.Parse(sMaxVal);

        byte[] pixels = new byte[height * width];
        for (int i = 0; i < height * width; i++)
        {
            pixels[i] = br.ReadByte();
        }
        return new PgmImage(width, height, maxVal, pixels);
    }

    static string NextAnyLine(BinaryReader br)
    {
        string s = "";
        byte b = 0; // dummy
        while (b != 10) // newline
        {
            b = br.ReadByte();
            char c = (char)b;
            s += c;
        }
        return s.Trim();
    }

    static string NextNonCommentLine(BinaryReader br)
    {
        string s = NextAnyLine(br);
        while (s.StartsWith("#") || s == "")
            s = NextAnyLine(br);
        return s;
    }

(这是一个稍微编辑过的版本:jamesmccaffrey.wordpress.com/2014/10/21/a-pgm-image-viewer-using-c)。 我应该提一下,我更喜欢不依赖于任何第三方库或 NuGet 包的解决方案,但我很绝望,因此对任何解决方案都持开放态度。

我已经测试了你的代码并重现了你的问题。问题是WriteableBitmap xmal控件无法完美加载

我尝试使用 SoftwareBitmap 来存储从 pgm 文件加载的 PgmByteData。而且效果很好。

字节数组中的像素包含表示灰色的值 0-255 shades.So,您可以将 BitmapPixelFormat.Gray8 用作 BitmapPixelFormat。您云创建 SoftwareBitmap 作为以下代码。

SoftwareBitmap softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Gray8, (int)width, (int)height);
            softwareBitmap.CopyFromBuffer(pgmByteData.AsBuffer());

Currently, the Image control only supports images that use BGRA8 encoding and pre-multiplied or no alpha channel. Before attempting to display an image, test to make sure it has the correct format, and if not, use the SoftwareBitmap static Convert method to convert the image to the supported format.

更多信息请参考Use SoftwareBitmap with a XAML Image control

if (softwareBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8 ||
    softwareBitmap.BitmapAlphaMode == BitmapAlphaMode.Straight)
{
    softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}

var source = new SoftwareBitmapSource();
await source.SetBitmapAsync(softwareBitmap);

// Set the source of the Image control
imageControl.Source = source;

code sample已上传。请检查。

WritableBitmap.PixelBuffer使用了RGBA颜色space这意味着每个像素在字节数组中用四个字节来描述,但是从PGM图像生成的数组只用一个字节来描述一个像素. 通过简单地将数组扩展 4 次(并将每个像素的 alpha 值设置为最大值 255),我设法获得了正确的显示。

public async Task<WriteableBitmap> ToWriteableBitmap()
    {
        WriteableBitmap writeableBitmap = new WriteableBitmap(width, height);

        byte[] expanded = new byte[pixels.Length * 4];
        int j = 0;
        for (int i = 0; i< pixels.Length; i++)
        {
            expanded[j++] = pixels[i];
            expanded[j++]= pixels[i];
            expanded[j++] = pixels[i];
            expanded[j++] = 255; //Alpha
        }

        using (Stream stream = writeableBitmap.PixelBuffer.AsStream())
        {
            await stream.WriteAsync(expanded, 0, expanded.Length);
        }
        return writeableBitmap;
    }