首页 文章

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

提问于
浏览
1

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

在互联网上有很多例子在c#中打开.pgm文件,但所有这些都依赖于使用通用Windows平台不支持的Bitmap对象(不能使用System.Drawing和System.Windows.Media库) .

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

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

我设法做的最好的是显示this,但实际的图片应该看起来像this(由于某种原因,它显示图像4x,颜色是错误的) .

我使用的代码:

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包的解决方案,但我绝望,因此对所有解决方案都持开放态度 .

2 回答

  • 0

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

    我试图使用 SoftwareBitmap 来存储从pgm文件加载的 PgmByteData . 它工作正常 .

    字节数组中的像素包含表示灰色阴影的值0-255 . 因此,您可以使用BitmapPixelFormat.Gray8作为 BitmapPixelFormat . 您将 Cloud 创建为 SoftwareBitmap ,如下面的代码所示 .

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

    目前,Image控件仅支持使用BGRA8编码和预乘或无alpha通道的图像 . 在尝试显示图像之前,请进行测试以确保其格式正确,如果没有,请使用SoftwareBitmap静态Convert方法将图像转换为支持的格式 .

    有关更多信息,请参阅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已上传 . 请检查 .

  • 0

    WritableBitmap.PixelBuffer使用RGBA颜色空间,这意味着每个像素在字节数组中用四个字节描述,但是从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;
        }
    

相关问题