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
相关产品推荐
相关产品推荐

