像素数据转换为图像

8
我有以这种格式列出的像素列表
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进行动态分配。 - greatmajestics
1
首先,最好使用List<byte>而不是ArrayList来避免装箱/拆箱。 - Vano Maisuradze
好的,我已经将它更改为字节数组,我正在从这些值中生成图像,但是它给了我“无效参数异常”。这是一个窗体应用程序。我曾经看到过一种方法可以根据给定的像素值形成图像,但我忘记了网站。 - greatmajestics
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()); } } - greatmajestics
@VanoMaisuradze 假设他正在调用一个返回值数组的第三方 API。他想假设元组中的数组条目是像素的红色、绿色、蓝色值。换句话说,回答这个问题。 - Ian Boyd
显示剩余3条评论
2个回答

12

更新

为了方便以后的参考,我想改进这个答案。

将像素存储为无符号 32 位整数数组(C#中的uint)可以更加方便(和高效)。这样可以一次性设置每个像素的四个通道(alpha、red、green 和 blue)。如果您不需要使用 alpha 通道(用于透明度),请参见下面代码片段中有关像素格式的注释。

现在,您可以将颜色表示为 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;”。 - starbeamrainbowlabs
哦,你还需要 using System.Drawing.Imaging; - starbeamrainbowlabs

5
首先,正如我在评论中所说,最好使用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()非常慢。 参见


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接