【问题标题】:Creating 16-bit+ grayscale images in WPF在 WPF 中创建 16 位以上灰度图像
【发布时间】:2013-03-25 18:56:46
【问题描述】:

我想从我的 WPF 程序中的数据值创建一个 16 位灰度图像。目前我一直在研究使用带有 PixelFormats.Gray16 集的 WriteableBitmap。

但是我无法让它工作,微软页面 (http://msdn.microsoft.com/en-us/magazine/cc534995.aspx) 将 Gray16 格式列为不可通过 WriteableBitmap 写入,但是不建议如何以这种方式制作一个。

目前我的代码在一个循环中运行,其中i 表示图像高度,j 表示宽度,看起来像这样:

short dataValue = GetDataSamplePixelValue(myDataValue);

//the pixel to fill with this data value:
int pixelOffset = ((i * imageWidth) + j) * bytesPerPixel;

//set the pixel colour values:
pixels[pixelOffset] = dataValue;

我确实得到了一张图片,但它只是一堆垂直的黑白线条。如果只使用 8 位灰度数据,我没有问题(在这种情况下,在上面的示例中,short 更改为byte)。

有谁知道如何使用 WPF 创建每像素 16 位或更高灰度的图像?这张图片最终也需要保存。

非常感谢任何建议。

编辑

除此之外,我还进行了一些编辑,现在使用 Gray16 PixelFormat 获得了一个合理的图像。我很难判断它是否实际上是 16 位,因为图像程序的颜色计数为 256,我不确定这是否是因为图像受到 WPF 的约束,或者图像程序确实如此不支持它,因为显然许多图像程序忽略了低 8 位。现在我会坚持我所拥有的。

代码如下:

myBitmap = new WriteableBitmap((int)visualRect.Width, (int)visualRect.Height, 96, 96, PixelFormats.Gray16, null);

int bytesPerPixel = myBitmap.Format.BitsPerPixel / 8;
ushort[] pixels = new ushort[(int)myBitmap.PixelWidth * (int)myBitmap.PixelHeight];

//if there is a shift factor, set the background colour to white:
if (shiftFactor > 0)
{
    for (int i = 0; i < pixels.Length; i++)
    {
        pixels[i] = 255;
    }
}

//the area to be drawn to:
Int32Rect drawRegionRect = new Int32Rect(0, 0, (int)myBitmap.PixelWidth, (int)myBitmap.PixelHeight);

//the number of samples available at this line (reduced by one so that the picked sample can't lie beyond the array):
double availableSamples = myDataFile.DataSamples.Length - 1;

for (int i = 0; i < numDataLinesOnDisplay; i++)
{
    //the current line to use:
    int currentLine = ((numDataLinesOnDisplay - 1) - i) + startLine < 0 ? 0 : ((numDataLinesOnDisplay- 1) - i) + startLine;

    for (int j = 0; j < myBitmap.PixelWidth; j++)
    {                             
        //data sample to use:
        int sampleToUse = (int)(Math.Floor((availableSamples / myBitmap.PixelWidth) * j));

        //get the data value:
        ushort dataValue = GetDataSamplePixelValue(sampleToUse);

        //the pixel to fill with this data value:
        int pixelOffset = (((i + shiftFactor) * (int)myBitmap.PixelWidth) + j);

        //set the pixel colour values:
        pixels[pixelOffset] = dataValue;
    }
}

//copy the byte array into the image: 
int stride = myBitmap.PixelWidth * bytesPerPixel;
myBitmap.WritePixels(drawRegionRect, pixels, stride, 0);

在这个例子中 startLine 和 shiftFactor 已经设置好了,并且取决于用户从数据文件中的哪个点开始查看,shiftFactor 仅在数据文件小于屏幕的情况下为非零,在这种情况下,我我正在使用此值将图像垂直居中。

【问题讨论】:

  • 我没有看到任何证据表明 WriteableBitmap 会对 PixelFormats.Gray16 产生问题,您的问题到底是什么?一些处理图像并试图解释你得到的无效结果的代码怎么样?
  • 我不确定我是否理解; 2^16 给出 65536 种独特的颜色,2^8 给出 256。
  • 保存图像,加载保存的图像,并在代码中生成您自己的直方图,看看您是否真的获得了正确数量的颜色。如果您不能信任其他程序,请自己制作!

标签: c# wpf .net-4.0


【解决方案1】:

查找代码中的错误或显示完整代码

gray16 图像的下一个示例工作正常

  var width = 300;
  var height = 300;

  var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray16, null);
  var pixels = new ushort[width * height];
  for (var y = 0; y < height; ++y)
    for (var x = 0; x < width; ++x)
    {
      var v = (0x10000*2 * x/width + 0x10000 * 3 * y / height);
      var isMirror = (v / 0x10000) % 2 == 1;
      v = v % 0xFFFF;
      if (isMirror)
        v = 0xFFFF - v;

      pixels[y * width + x] = (ushort)v;
    }

  bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width *2, 0);

  var encoder = new PngBitmapEncoder();
  encoder.Frames.Add(BitmapFrame.Create(bitmap));
  using (var stream = System.IO.File.Create("gray16.png"))
    encoder.Save(stream);

【讨论】:

  • 这将写入 bin 文件夹,因此如果您在 Debug 中运行,您可能会在项目的 bin/Debug 中找到 gray16.png。 width*2 是写入水平行的“步幅”或缓冲区大小(以字节为单位)。在这种情况下,16bpp = 每像素 2 个字节。您需要宽度乘以每像素 2 字节来写入图像的一行,因此宽度 * 2。isMirror 和 0x10000 的数学运算有点令人困惑和不必要 - 您可以写任何数字 0-65535 (2^16-1 ) 并且您将在该像素处获得准确的灰度值。
【解决方案2】:

作为参考,屏幕不太可能显示 16 位灰度图像,而且 Windows 不太支持这种格式。例如,Windows XP 甚至无法在照片查看器中显示 16 位灰度图像,但 Windows 7+ 可以(我不确定 Vista,我没有)。

最重要的是,.NET 开放 TIF 方法不会加载 16 位灰度图像。

加载和保存 16 位灰度图像的解决方案,我一般会推荐用于 TIF 的解决方案是 LibTIFF。然后,您可以选择加载整个 TIF,或逐行加载,以及其他方法。我建议逐行加载它,因为这样您就可以只保留屏幕上可见的数据,因为现在一些 TIF 变得非常大,无法由单个数组保存。

所以最终,不要担心在屏幕上显示 16 位灰度,它可能会受到系统/显示器能力的限制,人眼无论如何也无法分辨这与 8 位之间的区别。但是,如果您需要加载或保存 16 位,请使用 LibTIFF。

【讨论】:

    猜你喜欢
    • 2015-06-29
    • 2017-08-21
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 2017-07-31
    • 2012-06-13
    • 2012-11-18
    • 1970-01-01
    相关资源
    最近更新 更多