C# BitmapSource小节
在C#中,处理位图(Bitmap)图像数据时,理解像素格式(PixelFormat)、步幅(stride)和像素数据复制是非常重要的。
BitmapSource常用属性与方法
PixelFormat——Defines a pixel format for images and pixel-based surfaces.即定义图像和基于像素表面的像素格式。
Stride——(步幅)是图像数据中每一行像素所占用的字节数。这个值不一定等于图像宽度乘以每个像素的字节数,因为出于内存对齐的考虑,有些图像格式可能会在行尾添加额外的填充字节(padding bytes)。
PixelFormats——表示受支持像素格式的集合。PixelFormat
是一个静态类,描述了位图中每个像素的颜色深度和格式。例如,PixelFormats.
Bgra32表示每个像素占用32位,其中包含了透明度(Alpha)、红色(Red)、绿色(Green)和蓝色(Blue)四个分量。
CopyPixels——将位图像素数据复制到具有从指定偏移量开始的指定跨距的像素数组中。(C#中对于BitmapSource像素数据的复制,提供了此方法。)
应用示例
int pointIndex = 0;
List<int> nodes = [];
private void Build3DModelFromTiffData(MeshGeometry3D mesh, BitmapSource bitmap, int layerIndex, bool layer = false)
{
// 获取图像的宽度和高度
int width = bitmap.PixelWidth;
int height = bitmap.PixelHeight;
PixelFormat pixelFormat = bitmap.Format;
int stride = (bitmap.PixelWidth * pixelFormat.BitsPerPixel + 7) / 8;
int bufferSize = stride * bitmap.PixelHeight;
byte[] pixels = new byte[bufferSize];
bitmap.CopyPixels(pixels, stride, 0);
int minX = int.MaxValue;
int maxX = int.MinValue;
int minY = int.MaxValue;
int maxY = int.MinValue;
int index1 = 0;
int index2 = 0;
int index3 = 0;
int index4 = 0;
// 遍历图像的每个像素,根据像素信息构建 3D 模型
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int offset = y * stride + x * 4;
byte blue = pixels[offset];
byte green = pixels[offset + 1];
byte red = pixels[offset + 2];
//byte alpha = pixels[offset + 3];
if (blue == 0 && green == 0 && red == 0)
{
continue;
}
Point3D point = new(x, y, layerIndex);
minX = Math.Min(minX, x);
maxX = Math.Max(maxX, x);
minY = Math.Min(minY, y);
maxY = Math.Max(maxY, y);
if (minX == x && minY == y)
{
index1 = pointIndex;
}
if (minX == x && maxY == y)
{
index2 = pointIndex;
}
if (maxX == x && minY == y)
{
index3 = pointIndex;
}
if (maxX == x && maxY == y)
{
index4 = pointIndex;
}
pointIndex++;
// 将点添加到 MeshGeometry3D 中
mesh.Positions.Add(point);
}
}
mesh.TriangleIndices.Add(index1);
mesh.TriangleIndices.Add(index3);
mesh.TriangleIndices.Add(index2);
mesh.TriangleIndices.Add(index2);
mesh.TriangleIndices.Add(index3);
mesh.TriangleIndices.Add(index4);
if (layer)
{
nodes.Add(index1);
nodes.Add(index2);
nodes.Add(index3);
nodes.Add(index4);
}
}
注意事项
在上述示例中有一些关注点,在开发时需要注意:
- stride的求取要注意取整;示例中为了保证stride为整数操作为 (bitmap.PixelWidth * pixelFormat.BitsPerPixel + 7) / 8 ,因为1字节=8位,所以要将位数除以8来得到字节数。加7是为了实现向上取整的效果,比如如果每个像素占用3位,那么
(3 * width + 7) / 8
将确保每像素至少占用1个字节。 - RGBA的存储并不是按RGBA的先后顺序来的,而是B、G、R、A;这个一定要注意,上述代码中如下:
int offset = y * stride + x * 4; byte blue = pixels[offset]; byte green = pixels[offset + 1]; byte red = pixels[offset + 2]; //byte alpha = pixels[offset + 3];