c#设置像素颜色的更快方法

c#设置像素颜色的更快方法,c#,image,pointers,noise-generator,C#,Image,Pointers,Noise Generator,我选择了一个使用setPixel()创建噪波贴图的项目。这个应用程序的大部分运行时间都花在setPixel()函数中,因此我希望提高函数的执行速度 我对此做了一些研究,发现: int index = x + (y * Width); int col = color.ToArgb(); if (this.Bits == null) { this.Bits = new Int32[Width * Height]; } Bits[index] = col; 已被推荐为一种更快的方法。但是

我选择了一个使用
setPixel()
创建噪波贴图的项目。这个应用程序的大部分运行时间都花在
setPixel()
函数中,因此我希望提高函数的执行速度

我对此做了一些研究,发现:

int index = x + (y * Width);
int col = color.ToArgb();

if (this.Bits == null)
{
    this.Bits = new Int32[Width * Height];
}

Bits[index] = col;
已被推荐为一种更快的方法。但是,这会生成一个完全黑色的图像

我不完全理解图像处理和内存指针是如何工作的,以便能够完全理解代码并将其重构为更好的东西

以下是实施前的原始代码:

            unsafe
            {
                var scan0 = (byte*)Iptr;
                int bitmapStride = Stride;
                int bitmapPixelFormatSize = Depth / 8;

                index = (bitmapStride * y) + (x * bitmapPixelFormatSize);
                if (bitmapPixelFormatSize == 4)
                {
                    scan0[index + 3] = color.A;
                    scan0[index + 2] = color.R;
                    scan0[index + 1] = color.G;
                    scan0[index] = color.B;

                }
                else if (bitmapPixelFormatSize == 1)
                {
                    scan0[index] = color.R;

                }
                else
                {
                    scan0[index + 2] = color.R;
                    scan0[index + 1] = color.G;
                    scan0[index] = color.B;

                }
            }
Iptr
只是一个IntPtr

Stride
是一个int,我能找到的唯一设置该值的地方是
Stride=(像素计数/高度)*(深度/8)

x
是宽度

y
是高度


我能解释一下原始代码块中发生了什么,并能帮助理解如何将其转换为执行速度更快的代码块吗,目前,由于宽度*高度的嵌套for循环,完成此功能需要大约500000毫秒。

注意:以下信息最初由Bob Powell创建。原来的链接不再起作用,所以我从位于的Internet存档中复制了此信息。它有点长,但我认为它值得保存

我不确定这是否能直接回答你的问题,但也许能帮助你找到解决办法


使用锁定位方法访问图像数据

通过直接访问像素数据阵列,而不是依赖GetPixel和SetPixel或其他方法,可以加速许多图像处理任务甚至文件类型转换,例如从每像素32位到每像素8位

您会注意到.NET是一个托管代码系统,它通常使用托管数据,因此我们不再经常需要访问存储在内存中的字节,当托管数据访问速度太慢时,图像操作是为数不多的几次之一,因此我们需要再次深入研究查找数据和操作数据的棘手问题

在开始讨论本主题之前,我只想提醒您,用于访问任何非托管数据的方法将因编写程序所用的语言而异。C#开发人员有机会通过不安全的关键字和指针直接访问内存中的数据。VisualBasic程序员应该通过封送处理类方法访问这些数据,这也可能会显示少量的性能损失

锁定您的位

位图类提供锁定位和相应的解锁位方法,使您能够修复内存中位图像素数据数组的一部分,直接访问它,最后用修改后的数据替换位图中的位。LockBits返回一个BitmapData类,该类描述锁定数组中数据的布局和位置

BitmapData类包含以下重要属性:

  • Scan0固定数据阵列内存中的地址
  • 步长一行像素数据的宽度(以字节为单位)。这个宽度 是的像素尺寸的倍数,或者可能是次倍数 图像和可能会被填充以包含更多的字节。我将 请尽快解释原因
  • PixelFormat数据的实际像素格式。这很重要 用于查找正确的字节
  • 宽度锁定图像的宽度
  • 高度锁定图像的高度
Scan0和Stride与内存中数组的关系如图1所示

Stride属性(如图1所示)以字节为单位保存一行的宽度。然而,行的大小可能不是像素大小的精确倍数,因为为了提高效率,系统确保将数据打包到从四字节边界开始并填充到四字节倍数的行中。这意味着,例如,17像素宽的24位每像素图像的跨距为52。每行中使用的数据将占用317=51字节,1字节的填充将每行扩展到52字节或134字节。17像素宽的4BppIndexed图像的步幅为12。其中九个字节,或者更准确地说是八个半字节,将包含数据,并且该行将用另外3个字节填充到4个字节的边界

如上所述,行的数据承载部分根据像素格式布置。包含RGB数据的每像素24位图像将每3个字节有一个新像素,每像素32位RGBA每4个字节有一个新像素。必须仔细处理每字节包含多个像素的像素格式,例如每像素索引4位和每像素索引1位,以便所需像素不会与同一字节中的相邻像素混淆

查找正确的字节。

因为步幅是一行的宽度,所以要索引任何给定的行或Y坐标,可以将步幅乘以Y坐标以获得特定行的开头。在行中查找正确的像素可能更困难,这取决于了解像素格式的布局。以下示例显示如何访问给定像素格式的特定像素

  • Format32BppArgb给定X和Y坐标,第一个 像素中的元素是Scan0+(y*stride)+(x*4)。这就指向了 蓝色字节。以下三个字节包含绿色、红色和alpha 字节
  • Format24BppRgb给定X和Y坐标,第一个 像素中的元素是Scan0+(yStride)+(x3)。这就指向了 蓝色字节,后跟
    BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);
    
    int PixelSize=4;
    
    for(int y=0; y<bmd.Height; y++)
    {
        byte* row = (byte *)bmd.Scan0+(y*bmd.Stride);
        for(int x = 0; x<bmd.Width; x++)
        {
            row[x * PixelSize] = 255;
        }
    }
    
    Dim x As Integer
    Dim y As Integer
    Dim PixelSize As Integer = 4
    
    Dim bmd As BitmapData = bm.LockBits(new Rectangle(0, 0, 10, 10), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat)
    
    For y = 0 To bmd.Height - 1
        For x = 0 To bmd.Width - 1
            Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + (4 * x) , 255)
        Next
    Next
    
    byte* p=(byte*)bmd.Scan0.ToPointer();
    
    int index=y*bmd.Stride+(x>>3);
    
    byte mask=(byte)(0x80>>(x&0x7));
    
    if(pixel)
        p[index]|=mask;
    else
        p[index]&=(byte)(mask^0xff);
    
    Dim mask As Byte = 128 >> (x And 7)
    
    Dim offset As Integer = (y * bmd.Stride) + (x >> 3)
    
    Dim currentPixel As Byte = Marshal.ReadByte(bmd.Scan0, offset)
    
    If pixel = True Then
        Marshal.WriteByte(bmd.Scan0, offset, currentPixel Or mask)
    Else
        Marshal.WriteByte(bmd.Scan0, offset, CByte(currentPixel And (mask Xor 255)))
    End If
    
    int offset = (y * bmd.Stride) + (x >> 1);
    
    byte currentByte = ((byte *)bmd.Scan0)[offset];
    
    if((x&1) == 1)
    {
        currentByte &= 0xF0;
        currentByte |= (byte)(colorIndex & 0x0F);
    }
    else
    {
        currentByte &= 0x0F;
        currentByte |= (byte)(colorIndex << 4);
    }
    
    ((byte *)bmd.Scan0)[offset]=currentByte;
    
    Dim offset As Integer = (y * bmd.Stride) + (x >> 1)
    
    Dim currentByte As Byte = Marshal.ReadByte(bmd.Scan0, offset)
    
    If (x And 1) = 1 Then
        currentByte = currentByte And &HF0
        currentByte = currentByte Or (colorIndex And &HF)
    Else
        currentByte = currentByte And &HF
        currentByte = currentByte Or (colorIndex << 4)
    End If
    
    Marshal.WriteByte(bmd.Scan0, offset, currentByte)
    
    Dim bmd As BitmapData = bm.LockBits(New Rectangle(0, 0, 10, 10), ImageLockMode.ReadWrite, bm.PixelFormat)
    
        ' do operations here
    
    bm.UnlockBits(bmd)