C#:优化图像像素操作

以图像阈值化为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# very slow solution
public static unsafe Bitmap ApplyThreshold(Bitmap scrBitmap, int lower_value, int upper_value)
{
    Color newColor = Color.Red;
    Bitmap newBitmap = new Bitmap(scrBitmap.Width, scrBitmap.Height);
    //Locking the bitmap's bits allows you to iterate through it's color-data many times faster than using GetPixel, using unsafe code.
    lock (_imageLock)
    {
        var data = scrBitmap.LockBits(new Rectangle(0, 0, scrBitmap.Width, scrBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
 
        var offset = data.Stride - scrBitmap.Width * 3;
        var p = (byte*)data.Scan0.ToPointer();
 
        for (var i = 0; i < scrBitmap.Height; i++)
        {
            for (var j = 0; j < scrBitmap.Width; j++, p += 3)
            {
                var v = (int)(p[0] + p[1] + p[2]) / 3;
                var c = Color.FromArgb(p[2], p[1], p[0]);
                if (v > upper_value || v < lower_value)
                    newBitmap.SetPixel(j, i, newColor);
                else
                    newBitmap.SetPixel(j, i, c);
            }
            p += offset;
        }
 
        scrBitmap.UnlockBits(data);
    }
 
    return newBitmap;
}
 
# speed up using mutiple threads
public static unsafe Bitmap ApplyThresholdParallel2(Bitmap scrBitmap, int lower_value, int upper_value)
{
    //Locking the bitmap's bits allows you to iterate through it's color-data many times faster than using GetPixel, using unsafe code.
    lock (_imageLock)
    {
        var data = scrBitmap.LockBits(new Rectangle(0, 0, scrBitmap.Width, scrBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        int bytesPerPixel = 3;
        int stride = data.Stride;
        var p = (byte*)data.Scan0.ToPointer();
 
        Task[] tasks = new Task[4];
        for (int i = 0; i < tasks.Length; i++)
        {
            int ii = i;
            tasks[i] = Task.Factory.StartNew(() =>
            {
                int minY = ii < 2 ? 0 : data.Height / 2;
                int maxY = ii < 2 ? data.Height / 2 : data.Height;
 
                int minX = ii % 2 == 0 ? 0 : data.Width / 2;
                int maxX = ii % 2 == 0 ? data.Width / 2 : data.Width;
 
                for (int y = minY; y < maxY; y++)
                {
                    byte* row = p + (y * data.Stride);
 
                    for (int x = minX; x < maxX; x++)
                    {
                        int bIndex = x * bytesPerPixel;
                        int gIndex = bIndex + 1;
                        int rIndex = bIndex + 2;
 
                        byte pixelR = row[rIndex];
                        byte pixelG = row[gIndex];
                        byte pixelB = row[bIndex];
 
                        int v = (pixelR + pixelG + pixelB) / 3;
 
                        if (v > upper_value || v < lower_value)
                        {
                            row[rIndex] = 255;
                            row[gIndex] = 0;
                            row[bIndex] = 0;
                        }
                    }
                }
            });
        }
 
        Task.WaitAll(tasks);
        scrBitmap.UnlockBits(data);
    }
 
    return scrBitmap;
}
 
 
# speed up using Parallel for
public static unsafe Bitmap ApplyThresholdParallel(Bitmap scrBitmap, int lower_value, int upper_value)
{
    var rect = new Rectangle(0, 0, scrBitmap.Width, scrBitmap.Height);
    Bitmap targetBmp = scrBitmap.Clone(rect, PixelFormat.Format24bppRgb);
    //Locking the bitmap's bits allows you to iterate through it's color-data many times faster than using GetPixel, using unsafe code.
    lock (_imageLock)
    {
        var data = targetBmp.LockBits(rect, ImageLockMode.ReadWrite, targetBmp.PixelFormat);
        int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(targetBmp.PixelFormat) / 8;
        int heightInPixels = data.Height;
        int widthInBytes = data.Width * bytesPerPixel;
        byte* PtrFirstPixel = (byte*)data.Scan0;
 
        Parallel.For(0, heightInPixels, y =>
        {
            byte* currentLine = PtrFirstPixel + (y * data.Stride);
            for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
            {
                int b = currentLine[x];
                int g = currentLine[x + 1];
                int r = currentLine[x + 2];
 
                var v = (b + g + r) / 3;
                if (v > upper_value || v < lower_value)
                {
                    currentLine[x] = (byte)0;
                    currentLine[x + 1] = (byte)0;
                    currentLine[x + 2] = (byte)255;
                }
            }
        });
 
        targetBmp.UnlockBits(data);
    }
 
    return targetBmp;
}

  

 

参考:

Fast Pixel Operations in .NET (With and Without unsafe)

Why the use of GetPixel and SetPixel is so inefficient!

Fast Image Processing in C#

 

posted @   小金乌会发光-Z&M  阅读(471)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示