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

C#中从Bitmap优化创建LinkedPix集合的技术问询

图像处理LinkedPix关联性能优化瓶颈突破思路

需求背景

  • 正在进行图像处理操作,使用LinkedPix类(因已有超100处引用无法重构,仅可改为struct)
  • 核心需求:从Bitmap生成所有LinkedPix集合,像素需关联上下左右四个邻接像素(边缘像素关联3个、角落像素关联2个)
  • 预加载完成的像素数组为imWrap.Arr,无需实时构建

现有实现代码

LinkedPix类定义

public class LinkedPix
{
    public Point P;

    public override string ToString()
    {
        return P.ToString() + " " + Links.Count;
    }

    public HashSet<LinkedPix> Links;
    public SegObject MemberOfObject { get; set; }
    public bool IsMemberOfObject => MemberOfObject != null;

    private void Init()
    {
        MemberOfObject = null;
        Links = new HashSet<LinkedPix>();
    }

    public LinkedPix(int x, int y)
    {
        Init();
        P = new Point(x, y);
    }

    public LinkedPix(Point p)
    {
        Init();
        P = p;
    }

    public LinkedPix RotateCopy(double theta_radians)
    {
        //Assumes that 0,0 is the origin
        LinkedPix LP2 = new LinkedPix(new Point(
            (int)(P.X * Math.Cos(theta_radians) - P.Y * Math.Sin(theta_radians)),
            (int)(P.Y * Math.Cos(theta_radians) + P.X * Math.Sin(theta_radians))));
        return LP2;
    }

    public void LinksRemove(LinkedPix ToRemove, bool TwoWay = false)
    {
        //Remove from each others
        if (TwoWay) ToRemove.Links.Remove(this);
        Links.Remove(ToRemove);
    }

    /// <summary>
    /// Removes this from each of its links and removes these links
    /// </summary>
    internal void LinksCrossRemove()
    {
        foreach (LinkedPix tPix in Links) tPix.LinksRemove(this, false);
        Links = new HashSet<LinkedPix>();
    }
}

关联逻辑实现

Dictionary<Point, LinkedPix> PointDict = new Dictionary<Point, LinkedPix>();
void EstablishLinks(int xi, int yi, LinkedPix Pixi)
{
    if (imWrap.Arr[xi, yi] > threshold)
    {
        Point pi = new Point(xi, yi);
        LinkedPix Pix2 = GetOrAdd(pi);
        Pixi.Links.Add(Pix2);
        Pix2.Links.Add(Pixi);
    }
}

LinkedPix Pix; Point p;
int height = imWrap.Height;
for (int y = 0; y < height - 1; y++)
{
    if (Slice && y % Slice_ModulusFactor < 1) continue; //This is for Axon Degeneration to break up the axons
    for (int x = 0; x < height - 1; x++)
    {
        if (Slice && x % Slice_ModulusFactor < 1) continue;
        if (imWrap.Arr[x, y] > threshold)
        {
            Pixels++;
            if (CreateLinks)
            {
                p = new Point(x, y);
                Pix = GetOrAdd(p);
                if (y + 1 < height) EstablishLinks(x, y + 1, Pix);
                if (x + 1 < height) EstablishLinks(x + 1, y, Pix);
            }
        }
    }
}

public LinkedPix GetOrAdd(Point p)
{
    if (PointDict.ContainsKey(p))
    {
        return PointDict[p];
    }
    else
    {
        LinkedPix LP = new LinkedPix(p);
        PointDict.Add(p, LP);
        return LP;
    }
}

已尝试优化方案

  • 改用ConcurrentDictionary并行执行:哈希算法开销导致耗时反而增加
  • 改用Queue等集合:最终仍需转换为HashSet,无性能收益
  • 多种异步实现方式:性能提升甚微或无提升

当前各步骤耗时

GetOrAdd: 4.190254371881144 ticks
LinksAdd: 1.7282694093881512 ticks
LinksAddTwo: 1.6570632131524925 ticks
PointCreateTime: 0.20380008184398646 ticks
ArrayCheck: 0.3061780882729328 ticks

核心优化思路

1. 替换Dictionary为数组索引,彻底消除哈希开销

GetOrAdd是最大耗时项,直接用二维数组存储LinkedPix(或struct),利用坐标直接索引:

  • 预创建LinkedPix[,] pixArray = new LinkedPix[imWrap.Width, imWrap.Height]
  • 遍历像素时,若imWrap.Arr[x,y] > threshold,直接实例化pixArray[x,y] = new LinkedPix(x,y),无需字典查找
  • 邻接像素直接通过pixArray[x±1,y]、pixArray[x,y±1]获取,完全避免哈希计算和字典操作

2. 优化LinkedPix的Links存储结构

  • 若仅需存储最多4个邻接像素,用固定大小数组LinkedPix[] Links = new LinkedPix[4]替代HashSet,避免HashSet的扩容、哈希计算开销
  • 遍历邻接时直接检查数组非空元素,比HashSet的Contains/Add操作快数倍
  • 若改为struct,使用readonly struct+引用类型字段封装Links,避免不必要的值类型拷贝

3. 消除重复关联操作

当前代码中同一对邻接像素会被处理两次(比如(x,y)和(x+1,y)会在(x,y)遍历和(x+1,y)遍历各处理一次):

  • 仅在遍历到(x,y)时,处理右侧(x+1,y)和下侧(y+1,x)的关联,避免重复添加
  • 这样每个邻接对只处理一次,减少一半的Links.Add操作

4. 预分配内存减少GC开销

  • 若保留HashSet,提前为每个LinkedPix的Links预分配容量(比如预分配4个元素),避免动态扩容
  • 批量实例化LinkedPix时,利用对象池复用实例(若改为struct则无需此操作)

5. 循环优化与边界检查简化

  • 修正x循环的边界判断(当前代码x循环用了height,疑似笔误,应改为imWrap.Width)
  • 把Slice的判断提前到外层循环,减少内层循环的条件分支:
    if (Slice)
    {
        for (int y = Slice_ModulusFactor; y < height; y += Slice_ModulusFactor)
        {
            for (int x = Slice_ModulusFactor; x < width; x += Slice_ModulusFactor)
            {
                // 处理像素逻辑
            }
        }
    }
    else
    {
        // 正常遍历逻辑
    }
    

内容的提问来源于stack exchange,提问作者jdmneon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:12:26