从一种 PixelFormat 转换为另一种 WPF
Convert from one PixelFormat to an other WPF
我知道这听起来微不足道,但我目前已经卡住了。
我有各种格式的图像作为输入。 Gray16、Gray8、Indexed8 等。我需要做的是将它们转换为 RGB24 格式,到目前为止我已经完成了这个
public WriteableBitmap ConvertToRgb(WriteableBitmap source)
{
<-- create the new bitmap of the same width/height as the source with specific RGB24 format-->
var newBitmap = new WriteableBitmap(source.PixelWidth, source.PixelHeight, source.DpiX, source.DpiY, PixelFormats.Rgb24, source.Palette);
<-- create my pixel array -->
byte[] pixels = new byte[
source.PixelHeight * source.PixelWidth *
source.Format.BitsPerPixel / 8];
<-- copy my original pixels into the array) -->
source.CopyPixels(pixels, source.PixelWidth * source.Format.BitsPerPixel / 8, 0);
<-- write the pixels into the new image -->
newBitmap.WritePixels(
new Int32Rect(0, 0, newBitmap.PixelWidth, newBitmap.PixelHeight),
pixels,
newBitmap.PixelWidth * newBitmap.Format.BitsPerPixel/8,
0);
return newBitmap;
}
此方法抛出异常“缓冲区大小不足”
我猜它与我的 WritePixels 参数之一有关。但我不知道缓冲区的正确大小是多少。我找不到太多文档。任何帮助都会很好。
我使用的转换方法不是确定的,如果您知道更好的转换方法请告诉我。
首先:RGB 图像的默认格式(违反直觉)是 ARGB 的 BGR24 或 BGRA32。这是因为系统的字节序 (see here for why this happens)
其次:当你使用这种方法时,你根本不会转换PixelFormat。您只需获取所有像素,将它们转换为字节数组并将这些字节用于新的像素格式——这将完全不同地解释字节。因此,如果您的源图像是 BGRA 并且您转换为 RGB24,则第一个源像素的蓝色分量将被解释为第一个目标像素的红色分量,依此类推 - 第一个源 alpha 字节将被解释为第一个第二个像素的红色字节(不完全正确,但更容易理解。正确的映射是 a>b, r>g, g>r, r>b2... 因为颜色是如何映射到与它们是如何命名的)。这也是您的缓冲区大小不匹配的原因 - 因为不同的像素格式需要不同的字节数。
WriteableBitmap 的CopyPixels 和WritePixels 完全不知道底层的PixelFormat,完全忽略它。这仅在源和目标具有完全相同的 PixelFormat 和 Palette 或如果您手动重新计算和重新排列缓冲区中的所有字节时才有效。
您要找的东西要简单得多:它叫做 FormatConvertedBitmap
。这是一个 BitmapSource,专门用于更改图像的 PixelFormat。下面这行应该完成将所有内容转换为正确的 RGBA32 图像的工作(仅对 RGB 使用 Bgr24)。
var rgba32 = new FormatConvertedBitmap(source, PixelFormats.Bgra32, source.Palette,0);
经过多次尝试,我找到了答案。
public WriteableBitmap ConvertToRgb(WriteableBitmap source)
{
var newBitmap = new WriteableBitmap(source.PixelWidth, source.PixelHeight, source.DpiX, source.DpiY, PixelFormats.Rgb24, null);
byte[] pixels = new byte[
source.PixelHeight * source.PixelWidth *
source.Format.BitsPerPixel / 8];
source.CopyPixels(pixels, source.PixelWidth * source.Format.BitsPerPixel / 8, 0);
byte[] newPixels = new byte[newBitmap.PixelWidth * newBitmap.PixelHeight * newBitmap.Format.BitsPerPixel / 8];
var pixelCounter = 0;
var colorArray = source.Palette != null ? source.Palette.Colors.ToArray() : BitmapPalettes.Gray256.Colors.ToArray();
foreach (var pixel in pixels)
{
newPixels[pixelCounter] = colorArray[pixel].R;
pixelCounter++;
newPixels[pixelCounter] = colorArray[pixel].G;
pixelCounter++;
newPixels[pixelCounter] = colorArray[pixel].B;
pixelCounter++;
}
newBitmap.WritePixels(
new Int32Rect(0, 0, newBitmap.PixelWidth, newBitmap.PixelHeight),
newPixels,
newBitmap.PixelWidth * newBitmap.Format.BitsPerPixel / 8,
0);
return newBitmap;
}
基本上问题出在我猜的 Stride 和 newPixel 数组维度中的某个地方。这是一个完全有效的转换函数。
我知道这听起来微不足道,但我目前已经卡住了。
我有各种格式的图像作为输入。 Gray16、Gray8、Indexed8 等。我需要做的是将它们转换为 RGB24 格式,到目前为止我已经完成了这个
public WriteableBitmap ConvertToRgb(WriteableBitmap source)
{
<-- create the new bitmap of the same width/height as the source with specific RGB24 format-->
var newBitmap = new WriteableBitmap(source.PixelWidth, source.PixelHeight, source.DpiX, source.DpiY, PixelFormats.Rgb24, source.Palette);
<-- create my pixel array -->
byte[] pixels = new byte[
source.PixelHeight * source.PixelWidth *
source.Format.BitsPerPixel / 8];
<-- copy my original pixels into the array) -->
source.CopyPixels(pixels, source.PixelWidth * source.Format.BitsPerPixel / 8, 0);
<-- write the pixels into the new image -->
newBitmap.WritePixels(
new Int32Rect(0, 0, newBitmap.PixelWidth, newBitmap.PixelHeight),
pixels,
newBitmap.PixelWidth * newBitmap.Format.BitsPerPixel/8,
0);
return newBitmap;
}
此方法抛出异常“缓冲区大小不足”
我猜它与我的 WritePixels 参数之一有关。但我不知道缓冲区的正确大小是多少。我找不到太多文档。任何帮助都会很好。
我使用的转换方法不是确定的,如果您知道更好的转换方法请告诉我。
首先:RGB 图像的默认格式(违反直觉)是 ARGB 的 BGR24 或 BGRA32。这是因为系统的字节序 (see here for why this happens)
其次:当你使用这种方法时,你根本不会转换PixelFormat。您只需获取所有像素,将它们转换为字节数组并将这些字节用于新的像素格式——这将完全不同地解释字节。因此,如果您的源图像是 BGRA 并且您转换为 RGB24,则第一个源像素的蓝色分量将被解释为第一个目标像素的红色分量,依此类推 - 第一个源 alpha 字节将被解释为第一个第二个像素的红色字节(不完全正确,但更容易理解。正确的映射是 a>b, r>g, g>r, r>b2... 因为颜色是如何映射到与它们是如何命名的)。这也是您的缓冲区大小不匹配的原因 - 因为不同的像素格式需要不同的字节数。
WriteableBitmap 的CopyPixels 和WritePixels 完全不知道底层的PixelFormat,完全忽略它。这仅在源和目标具有完全相同的 PixelFormat 和 Palette 或如果您手动重新计算和重新排列缓冲区中的所有字节时才有效。
您要找的东西要简单得多:它叫做 FormatConvertedBitmap
。这是一个 BitmapSource,专门用于更改图像的 PixelFormat。下面这行应该完成将所有内容转换为正确的 RGBA32 图像的工作(仅对 RGB 使用 Bgr24)。
var rgba32 = new FormatConvertedBitmap(source, PixelFormats.Bgra32, source.Palette,0);
经过多次尝试,我找到了答案。
public WriteableBitmap ConvertToRgb(WriteableBitmap source)
{
var newBitmap = new WriteableBitmap(source.PixelWidth, source.PixelHeight, source.DpiX, source.DpiY, PixelFormats.Rgb24, null);
byte[] pixels = new byte[
source.PixelHeight * source.PixelWidth *
source.Format.BitsPerPixel / 8];
source.CopyPixels(pixels, source.PixelWidth * source.Format.BitsPerPixel / 8, 0);
byte[] newPixels = new byte[newBitmap.PixelWidth * newBitmap.PixelHeight * newBitmap.Format.BitsPerPixel / 8];
var pixelCounter = 0;
var colorArray = source.Palette != null ? source.Palette.Colors.ToArray() : BitmapPalettes.Gray256.Colors.ToArray();
foreach (var pixel in pixels)
{
newPixels[pixelCounter] = colorArray[pixel].R;
pixelCounter++;
newPixels[pixelCounter] = colorArray[pixel].G;
pixelCounter++;
newPixels[pixelCounter] = colorArray[pixel].B;
pixelCounter++;
}
newBitmap.WritePixels(
new Int32Rect(0, 0, newBitmap.PixelWidth, newBitmap.PixelHeight),
newPixels,
newBitmap.PixelWidth * newBitmap.Format.BitsPerPixel / 8,
0);
return newBitmap;
}
基本上问题出在我猜的 Stride 和 newPixel 数组维度中的某个地方。这是一个完全有效的转换函数。