【问题标题】:Displaying Portable Graymap (PGM) images in Universal Windows App (c#, XAML)在通用 Windows 应用程序(c#、XAML)中显示便携式灰度图 (PGM) 图像
【发布时间】:2017-04-03 16:16:53
【问题描述】:

我需要在我的通用 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 包的解决方案,但我很绝望,因此对任何解决方案持开放态度。

【问题讨论】:

    标签: c# xaml uwp bitmapimage pgm


    【解决方案1】:

    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;
        }
    

    【讨论】:

      【解决方案2】:

      我已经测试了您的代码并重现了您的问题。问题是WriteableBitmap不能被xmal控件完美加载。

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

      字节数组中的像素包含代表灰色阴影的 0-255 值。因此,您可以将 BitmapPixelFormat.Gray8 用作 BitmapPixelFormat。您云创建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 已上传。请检查。

      【讨论】:

      • 感谢 Nico 快速详细的回答。我还通过扩展 byte[] 以适应 BGRA 编码找到了自己的解决方案。我已经发布了代码作为答案;也许有人会觉得它有用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-10
      • 1970-01-01
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多