解决 C# GetPixel 和 SetPixel 效率问题

在对Bitmap图片操作的时候,有时需要用到获取或设置像素颜色方法:GetPixel 和 SetPixel,

如果直接对这两个方法进行操作的话速度很慢,这里我们可以通过把数据提取出来操作,然后操作完在复制回去可以加快访问速度

其实对Bitmap的访问还有两种方式,一种是内存法,一种是指针法

1、内存法

  这里定义一个类LockBitmap,通过把Bitmap数据拷贝出来,在内存上直接操作,操作完成后在拷贝到Bitmap中

复制代码
        public class LockBitmap
        {
            Bitmap source = null;
            IntPtr Iptr = IntPtr.Zero;
            BitmapData bitmapData = null;

            public byte[] Pixels { get; set; }
            public int Depth { get; private set; }
            public int Width { get; private set; }
            public int Height { get; private set; }

            public LockBitmap(Bitmap source)
            {
                this.source = source;
            }

            /// <summary>
            /// Lock bitmap data
            /// </summary>
            public void LockBits()
            {
                try
                {
                    // Get width and height of bitmap
                    Width = source.Width;
                    Height = source.Height;

                    // get total locked pixels count
                    int PixelCount = Width * Height;

                    // Create rectangle to lock
                    Rectangle rect = new Rectangle(0, 0, Width, Height);

                    // get source bitmap pixel format size
                    Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);

                    // Check if bpp (Bits Per Pixel) is 8, 24, or 32
                    if (Depth != 8 && Depth != 24 && Depth != 32)
                    {
                        throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
                    }

                    // Lock bitmap and return bitmap data
                    bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                                 source.PixelFormat);

                    // create byte array to copy pixel values
                    int step = Depth / 8;
                    Pixels = new byte[PixelCount * step];
                    Iptr = bitmapData.Scan0;

                    // Copy data from pointer to array
                    Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            /// <summary>
            /// Unlock bitmap data
            /// </summary>
            public void UnlockBits()
            {
                try
                {
                    // Copy data from byte array to pointer
                    Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);

                    // Unlock bitmap data
                    source.UnlockBits(bitmapData);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            /// <summary>
            /// Get the color of the specified pixel
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <returns></returns>
            public Color GetPixel(int x, int y)
            {
                Color clr = Color.Empty;

                // Get color components count
                int cCount = Depth / 8;

                // Get start index of the specified pixel
                int i = ((y * Width) + x) * cCount;

                if (i > Pixels.Length - cCount)
                    throw new IndexOutOfRangeException();

                if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
                {
                    byte b = Pixels[i];
                    byte g = Pixels[i + 1];
                    byte r = Pixels[i + 2];
                    byte a = Pixels[i + 3]; // a
                    clr = Color.FromArgb(a, r, g, b);
                }
                if (Depth == 24) // For 24 bpp get Red, Green and Blue
                {
                    byte b = Pixels[i];
                    byte g = Pixels[i + 1];
                    byte r = Pixels[i + 2];
                    clr = Color.FromArgb(r, g, b);
                }
                if (Depth == 8)
                // For 8 bpp get color value (Red, Green and Blue values are the same)
                {
                    byte c = Pixels[i];
                    clr = Color.FromArgb(c, c, c);
                }
                return clr;
            }

            /// <summary>
            /// Set the color of the specified pixel
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <param name="color"></param>
            public void SetPixel(int x, int y, Color color)
            {
                // Get color components count
                int cCount = Depth / 8;

                // Get start index of the specified pixel
                int i = ((y * Width) + x) * cCount;

                if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha
                {
                    Pixels[i] = color.B;
                    Pixels[i + 1] = color.G;
                    Pixels[i + 2] = color.R;
                    Pixels[i + 3] = color.A;
                }
                if (Depth == 24) // For 24 bpp set Red, Green and Blue
                {
                    Pixels[i] = color.B;
                    Pixels[i + 1] = color.G;
                    Pixels[i + 2] = color.R;
                }
                if (Depth == 8)
                // For 8 bpp set color value (Red, Green and Blue values are the same)
                {
                    Pixels[i] = color.B;
                }
            }
        }
复制代码

  使用:先锁定Bitmap,然后通过Pixels操作颜色对象,最后释放锁,把数据更新到Bitmap中

复制代码
            string file = @"C:\test.jpg";
            Bitmap bmp = new Bitmap(Image.FromFile(file));
            
            LockBitmap lockbmp = new LockBitmap(bmp);
            //锁定Bitmap,通过Pixel访问颜色
            lockbmp.LockBits();

            //获取颜色
            Color color = lockbmp.GetPixel(10, 10);

            //从内存解锁Bitmap
            lockbmp.UnlockBits();
复制代码

 2、指针法

  这种方法访问速度比内存法更快,直接通过指针对内存进行操作,不需要进行拷贝,但是在C#中直接通过指针操作内存是不安全的,所以需要在代码中加入unsafe关键字,在生成选项中把允许不安全代码勾上,才能编译通过

  这里定义成PointerBitmap类

复制代码
            public class PointBitmap
            {
                Bitmap source = null;
                IntPtr Iptr = IntPtr.Zero;
                BitmapData bitmapData = null;

                public int Depth { get; private set; }
                public int Width { get; private set; }
                public int Height { get; private set; }

                public PointBitmap(Bitmap source)
                {
                    this.source = source;
                }

                public void LockBits()
                {
                    try
                    {
                        // Get width and height of bitmap
                        Width = source.Width;
                        Height = source.Height;

                        // get total locked pixels count
                        int PixelCount = Width * Height;

                        // Create rectangle to lock
                        Rectangle rect = new Rectangle(0, 0, Width, Height);

                        // get source bitmap pixel format size
                        Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);

                        // Check if bpp (Bits Per Pixel) is 8, 24, or 32
                        if (Depth != 8 && Depth != 24 && Depth != 32)
                        {
                            throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
                        }

                        // Lock bitmap and return bitmap data
                        bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                                     source.PixelFormat);

                        //得到首地址
                        unsafe
                        {
                            Iptr = bitmapData.Scan0;
                            //二维图像循环
                            
                        }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }

                public void UnlockBits()
                {
                    try
                    {
                        source.UnlockBits(bitmapData);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }

                public Color GetPixel(int x, int y)
                {
                    unsafe
                    {
                        byte* ptr = (byte*)Iptr;
                        ptr = ptr + bitmapData.Stride * y;
                        ptr += Depth * x / 8;
                        Color c = Color.Empty;
                        if (Depth == 32)
                        {
                            int a = ptr[3];
                            int r = ptr[2];
                            int g = ptr[1];
                            int b = ptr[0];
                            c = Color.FromArgb(a, r, g, b);
                        }
                        else if (Depth == 24)
                        {
                            int r = ptr[2];
                            int g = ptr[1];
                            int b = ptr[0];
                            c = Color.FromArgb(r, g, b);
                        }
                        else if (Depth == 8)
                        {
                            int r = ptr[0];
                            c = Color.FromArgb(r, r, r);
                        }
                        return c;
                    }
                }

                public void SetPixel(int x, int y, Color c)
                {
                    unsafe
                    {
                        byte* ptr = (byte*)Iptr;
                        ptr = ptr + bitmapData.Stride * y;
                        ptr += Depth * x / 8;
                        if (Depth == 32)
                        {
                            ptr[3] = c.A;
                            ptr[2] = c.R;
                            ptr[1] = c.G;
                            ptr[0] = c.B;
                        }
                        else if (Depth == 24)
                        {
                            ptr[2] = c.R;
                            ptr[1] = c.G;
                            ptr[0] = c.B;
                        }
                        else if (Depth == 8)
                        {
                            ptr[2] = c.R;
                            ptr[1] = c.G;
                            ptr[0] = c.B;
                        }
                    }
                }
            }
复制代码

 使用方法这里就不列出来了,跟上面的LockBitmap类似

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您的问题是如何使用多线程对像素点进行灰度化操作的C#实现,并且解决线程安全问题。 对于像素点灰度化的操作,可以使用C#提供的`Bitmap`类来实现。而多线程的实现可以使用`Parallel.For`方法来实现。为了解决线程安全问题,可以使用`lock`语句来保证多个线程不会同时访问同一个`Bitmap`对象。 具体实现步骤如下: 1. 将像素点的灰度化操作封装成一个函数。 ```c# private static void GrayPixel(Bitmap bmp, int x, int y) { // 获取像素点的颜色值 Color color; lock (bmp) { color = bmp.GetPixel(x, y); } // 计算像素点的灰度值 int gray = (int)(color.R * 0.299 + color.G * 0.587 + color.B * 0.114); // 更新像素点的值为灰度值 lock (bmp) { bmp.SetPixel(x, y, Color.FromArgb(gray, gray, gray)); } } ``` 2. 在主函数中,使用`Parallel.For`方法启动多线程对图像的像素点进行灰度化操作。 ```c# static void Main(string[] args) { // 读取图像 Bitmap bmp = new Bitmap("test.jpg"); // 获取图像的宽度和高度 int width = bmp.Width; int height = bmp.Height; // 启动多线程 Parallel.For(0, height, y => { for (int x = 0; x < width; x++) { GrayPixel(bmp, x, y); } }); // 显示灰度化后的图像 bmp.Save("gray.jpg"); } ``` 上述代码中,在`GrayPixel()`函数中使用了`lock`语句来保证多个线程不会同时访问同一个`Bitmap`对象。同时,在`Parallel.For`方法中,每个线程只会处理自己分配的像素点,不会造成线程之间的干扰。 最后,保存灰度化后的图像。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值