You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 05:07:46