C#:基于坐标矩阵高效填充大图像的优化方案咨询
问题
我有一张尺寸为8192×8192的大图像img,以及三个尺寸为4096×4096的矩阵:
- 矩阵
X:存储目标像素的x坐标 - 矩阵
Y:存储目标像素的y坐标 - 矩阵
RGB:存储对应的RGB像素值
需求是遍历上述矩阵的每个元素,将大图像img中对应(x,y)位置的像素设置为对应的RGB值。示例:若X[0,0]=500、Y[0,0]=200且RGB[0,0]=(255,0,0),则设置大图像[500,200]的像素值为(255,0,0)。
目前已实现的代码如下:
public void Test() { Image<Bgr, byte> img = new(8196, 8192); //Parameter int size = 4096; Image<Gray, int> X = new(size, size); Image<Gray, int> Y = new(size, size); Image<Bgr, byte> rgb = new(size, size); //Timer Stopwatch cs = new Stopwatch(); cs.Start(); Parallel.For(0, size, i => { for (int j = 0; j < size; j++) { int xTarget = (int)X[i, j].Intensity; int yTarget = (int)Y[i, j].Intensity; Bgr bgrTarget = rgb[i, j]; img[xTarget, yTarget] = bgrTarget; } }); cs.Stop(); MessageBox.Show(cs.ElapsedMilliseconds.ToString()); }
请问是否存在更高效的实现方式?
高效实现方案
以下几种方式可显著提升代码执行效率:
1. 直接操作图像原始数据缓冲区
EmguCV的Image类提供Data属性,可直接访问底层像素数组,避免索引器[i,j]访问时的封装开销,同时消除临时对象创建(如原代码中的Bgr实例)。
示例代码:
public void OptimizedTest() { Image<Bgr, byte> img = new(8192, 8192); // 修正原代码中宽度与需求不符的问题 int size = 4096; Image<Gray, int> X = new(size, size); Image<Gray, int> Y = new(size, size); Image<Bgr, byte> rgb = new(size, size); Stopwatch cs = new Stopwatch(); cs.Start(); // 获取各图像的原始数据数组 byte[] imgData = img.Data; int[] xData = X.Data; int[] yData = Y.Data; byte[] rgbData = rgb.Data; int imgWidth = img.Width; int imgChannels = img.NumberOfChannels; int totalPixels = size * size; Parallel.For(0, totalPixels, idx => { int xTarget = xData[idx]; int yTarget = yData[idx]; // 校验坐标合法性,避免越界错误 if (xTarget < 0 || xTarget >= imgWidth || yTarget < 0 || yTarget >= img.Height) return; // 计算RGB值的起始索引 int rgbBaseIdx = idx * imgChannels; byte b = rgbData[rgbBaseIdx]; byte g = rgbData[rgbBaseIdx + 1]; byte r = rgbData[rgbBaseIdx + 2]; // 计算目标图像中像素的起始索引 int imgBaseIdx = (yTarget * imgWidth + xTarget) * imgChannels; imgData[imgBaseIdx] = b; imgData[imgBaseIdx + 1] = g; imgData[imgBaseIdx + 2] = r; }); cs.Stop(); MessageBox.Show(cs.ElapsedMilliseconds.ToString()); }
优势:跳过类型转换、属性封装等冗余操作,直接操作内存数组,大幅降低单像素处理的开销。
2. 优化并行调度粒度
原代码用Parallel.For遍历行(共4096个任务),调度开销较高。改为直接遍历所有像素总数量(16777216个),或按更大的块划分任务,可减少线程调度次数,提升并行效率。
3. 使用unsafe代码进一步提速(极端性能场景)
若对性能要求极高,可使用unsafe代码直接操作指针,消除数组索引计算的开销:
public unsafe void UnsafeOptimizedTest() { Image<Bgr, byte> img = new(8192, 8192); int size = 4096; Image<Gray, int> X = new(size, size); Image<Gray, int> Y = new(size, size); Image<Bgr, byte> rgb = new(size, size); Stopwatch cs = new Stopwatch(); cs.Start(); fixed (byte* imgPtr = img.Data) fixed (int* xPtr = X.Data) fixed (int* yPtr = Y.Data) fixed (byte* rgbPtr = rgb.Data) { int imgWidth = img.Width; int totalPixels = size * size; Parallel.For(0, totalPixels, idx => { int xTarget = xPtr[idx]; int yTarget = yPtr[idx]; if (xTarget < 0 || xTarget >= imgWidth || yTarget < 0 || yTarget >= img.Height) return; // 直接操作指针赋值 byte* srcPixel = rgbPtr + idx * 3; byte* dstPixel = imgPtr + (yTarget * imgWidth + xTarget) * 3; *dstPixel = *srcPixel; *(dstPixel + 1) = *(srcPixel + 1); *(dstPixel + 2) = *(srcPixel + 2); }); } cs.Stop(); MessageBox.Show(cs.ElapsedMilliseconds.ToString()); }
注意:需在项目属性中启用「允许不安全代码」选项。
内容的提问来源于stack exchange,提问作者Fedour Traktor
相关产品推荐
相关产品推荐

