如何将原始图像像素复制到新图像?代码性能问题咨询
问题分析与解决方案
一、先纠正核心逻辑错误
你当前的代码里,给新图像img1的每个像素(i,j)赋值时,内层循环遍历了整个pixels集合——这完全是无效操作,因为循环结束后这个像素只会保留pixels最后一个元素的颜色,前面的赋值全被覆盖了。
如果你的需求是给新图像的每个像素随机使用原始图像的像素颜色,应该直接随机选取pixels里的一个元素,而非遍历所有元素。
二、性能优化(解决29万像素耗时问题)
GetPixel和SetPixel方法本身性能极差,每次调用都要做大量底层安全检查。对于大规模像素操作,必须用LockBits直接操作内存,能把速度提升几十甚至上百倍。
优化后的完整代码
private void GetSetPixelsRandom() { // 用using包裹Bitmap,确保资源自动释放 using (Bitmap img = new Bitmap(@"d:\downloaded images\test.gif")) { // 步骤1:一次性读取原始图像所有像素到字节数组 Rectangle rect = new Rectangle(0, 0, img.Width, img.Height); var bmpData = img.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, img.PixelFormat); IntPtr ptr = bmpData.Scan0; int bytes = Math.Abs(bmpData.Stride) * img.Height; byte[] rgbValues = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); img.UnlockBits(bmpData); // 转换为Color集合(也可以直接操作字节数组,省掉这一步) List<Color> pixels = new List<Color>(); int pixelSize = System.Drawing.Image.GetPixelFormatSize(img.PixelFormat) / 8; for (int i = 0; i < rgbValues.Length; i += pixelSize) { // 注意:24位RGB格式的通道顺序是BGR,需反转 Color color = Color.FromArgb(rgbValues[i + 2], rgbValues[i + 1], rgbValues[i]); pixels.Add(color); } // 步骤2:创建新图像并快速赋值 using (Bitmap img1 = new Bitmap(512, 512)) { Random rand = new Random(); Rectangle rect1 = new Rectangle(0, 0, img1.Width, img1.Height); var bmpData1 = img1.LockBits(rect1, System.Drawing.Imaging.ImageLockMode.WriteOnly, img1.PixelFormat); IntPtr ptr1 = bmpData1.Scan0; int bytes1 = Math.Abs(bmpData1.Stride) * img1.Height; byte[] rgbValues1 = new byte[bytes1]; int pixelSize1 = System.Drawing.Image.GetPixelFormatSize(img1.PixelFormat) / 8; for (int i = 0; i < rgbValues1.Length; i += pixelSize1) { // 随机选一个原始图像的像素颜色 Color randomColor = pixels[rand.Next(pixels.Count)]; // 按BGR顺序写入数组(32位格式需额外写入Alpha通道) rgbValues1[i] = randomColor.B; rgbValues1[i + 1] = randomColor.G; rgbValues1[i + 2] = randomColor.R; if (pixelSize1 == 4) { rgbValues1[i + 3] = randomColor.A; } } // 把处理后的字节数组写回图像内存 System.Runtime.InteropServices.Marshal.Copy(rgbValues1, 0, ptr1, bytes1); img1.UnlockBits(bmpData1); img1.Save(@"d:\downloaded images\test3.gif"); } } }
关键优化点说明
- 用LockBits替代GetPixel/SetPixel:直接操作内存数组,彻底避免了底层调用的性能开销,处理29万级像素毫无压力。
- 批量读写像素数据:把整个图像的像素一次性读到字节数组,处理完再一次性写回,大幅减少IO次数。
- 修正逻辑:用
Random.Next随机选取原始像素颜色,符合“将原始图像像素设置到新图像”的需求(如果你的需求不是随机,可根据实际规则调整取值逻辑)。 - 资源安全:所有Bitmap都用
using包裹,确保程序结束后自动释放内存,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

