【问题标题】:Pixel data to Image像素数据到图像
【发布时间】:2022-02-19 23:15:21
【问题描述】:

我有这种格式的列表像素

ArrayList list = new ArrayList();
list.add(Red);
list.add(Blue);
list.add(Green);

我有很多这样的价值观。我想将数组列表转换为图像。。尝试了很多,但没有找到任何合适的材料。

编辑:这可能会有所帮助:

List<byte> pic = new List<byte>(); 
for (int j = 0; j < headdata.Count; j++)
{ 
    if(headdata.ElementAt(j).getHead() == i) 
    { 
        pic.Add((byte)headdata.ElementAt(j).getRed());
        pic.Add((byte)headdata.ElementAt(j).getGreen());
        pic.Add((byte)headdata.ElementAt(j).getBlue()); 
    }
} 

【问题讨论】:

  • 为什么要用ArrayList来存储像素数据?
  • 我正在对图像进行聚类,所以我从一开始就不知道图像簇的确切大小,如果我在大型数据集上运行一次循环会花费很多时间.. 所以我使用 arraylist 进行动态分配
  • 您的项目类型是什么? WPF, Windows Form,...
  • 您是否对每个像素使用了 ArrayList?
  • 那么,首先,最好使用 List 而不是 ArrayList 以避免装箱/拆箱。

标签: c#


【解决方案1】:

更新

我想我会改进这个答案以供将来参考。

将像素存储为无符号 32 位整数数组(C#uint)会更方便(且性能更高)。这允许一次性设置每个像素的 4 个通道(alpha、红色、绿色和蓝色)。如果您对使用 alpha 通道(用于透明度)不感兴趣,请参阅下面代码 sn-p 中有关像素格式的注释。

您现在表示颜色的方式是 32 位数字,例如 0xff0000,您可能会认为它类似于 CSS 十六进制颜色值。

// Image dimensions.
var width = 640;
var height = 480;

// Array to contain pixel data, with each element representing one pixel.
var pixelBuffer = new uint[width * height];

// When creating the bitmap, the operating system needs to be told the memory
// address of the pixel data array created above. This memory address can be
// found using a pinned GC handle, which also tells the GC (garbage collector)
// that it cannot move the array in memory after this point (as that would
// change the memory address).
//
// Note that the handle prevents the GC from freeing the object automatically,
// so remember to call the handle's `Free` method once finished (see below). 
var pixelBufferHandle = GCHandle.Alloc(pixelBuffer, GCHandleType.Pinned);

// Create the bitmap using a constructor that takes a pointer to the array of
// pixel data:
//
// The `width` and `height` parameters are the image dimensions in pixels.
// These should match the values used above.
//
// The `stride` parameter specifies the number of _bytes_ required for each row
// of image data. Calculate this by multiplying the image's width by the number
// of bytes per pixel (here that's `sizeof(uint)`).
//
// The `format` parameter specifies how the colour data of each pixel is stored
// in the pixel data array:
//    - For 32-bit RGB colour, use `Format32bppPArgb`. This signifies that the
//      alpha channel is present (i.e. each pixel is 4 bytes) but should be
//      ignored. Each pixel will be 100% opaque, and the alpha can be zero.
//    - For 32-bit ARGB colour, use `Format32bppArgb`. Each pixel will be drawn
//      opaque, semi-transparent, or fully transparent based on the alpha.
//
// The `scan0` parameter takes a pointer to the pixel data - use the GC handle
// created above to acquire the address of the array object.
var bitmap = new Bitmap(
    width:  width,
    height: height,
    stride: width * sizeof(uint),
    format: PixelFormat.Format32bppPArgb,
    scan0:  pixelBufferHandle.AddrOfPinnedObject()
);

// At this point, the pixel data array can be freely manipulated independently
// of the bitmap. Each time the bitmap is draw, it will reflect the current
// content of the pixel data array.

// Once finished...

// Free the GC handle, allowing the GC to free the object automatically.
gchPixels.Free();

您可以创建用于在特定坐标处读取和写入像素的函数:

uint GetPixel(int x, int y) => pixels[x + y * width];
void SetPixel(int x, int y, uint argb) => pixels[x + y * width] = argb;

正如starbeamrainbowlabs 指出的那样:

GCHandleBitmapPixelFormat 分别在 System.Runtime.InteropServicesSystem.DrawingSystem.Drawing.Imaging 中找到。

原始答案如下...

有一个很好的方法可以做到这一点,但它要求您使用数组或字节而不是列表。

考虑一下:

// Define the width and the height of the image
int width = 100;
int height = 100;

/// Create an array of bytes consisting of the area's worth of pixels
/// with 3 bytes of data each.
byte[] pixels = new byte[width * height * 3];

/* ... Code for making the image etc. here ... */

// Create the bitmap.
// GCHandle requires System.Runtime.InteropServices
/// PixelFormat requires System.Drawing.Imaging.
Bitmap bmp = new Bitmap(width, height, width * 3, PixelFormat.Format24bppRgb,
                        GCHandle.Alloc(pixels, GCHandleType.Pinned).AddrOfPinnedObject());

如果您想将其更改为 ARGB,只需将“3”的所有实例更改为“4”, 并使用 PixelFormat.Format32bppArgb。

正如我所说,这要求您使用数组而不是列表,但这 只要您知道图像的宽度和高度,应该不会太难。 在数组中设置单个像素的一个好方法是(也许做一个方法 为此):

pixels[x + y * width]     = R;
pixels[x + y * width + 1] = G;
pixels[x + y * width + 2] = B;

这种方法通常也很快,如果你需要的话 做。

【讨论】:

  • 注意:您需要在文件顶部包含 using System.Runtime.InteropServices; 才能使其正常工作。
  • 哦,你也需要using System.Drawing.Imaging;
【解决方案2】:

首先,正如我在评论中所说,最好使用 List 而不是 ArrayList 以避免装箱/拆箱。

然后,您需要 bpp(每像素位数)和 widthheight 来创建图像。

如果你有所有这些值,那么你可以得到起始像素索引:

int i = ((y * Width) + x) * (depth / 8);

例如:

    List<byte> bytes = new List<byte>(); // this list should be filled with values
    int width = 100;
    int height = 100;
    int bpp = 24;

    Bitmap bmp = new Bitmap(width, height);

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {   
            int i = ((y * width) + x) * (bpp / 8);
            if(bpp == 24) // in this case you have 3 color values (red, green, blue)
            {
                 // first byte will be red, because you are writing it as first value
                 byte r = bytes[i]; 
                 byte g = bytes[i + 1]; 
                 byte b = bytes[i + 2]; 
                 Color color = Color.FromArgb(r, g, b);
                 bmp.SetPixel(x, y, color); 
            }

        }
    }

您可以使用其他库更快地创建位图和写入值。 (SetPixel() 很慢。See

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 1970-01-01
    • 2017-03-22
    • 2013-06-06
    • 2013-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多