本文是《编写高效的C#图像处理程序——我的实验》的后续实验。
昨天,在wuya的提醒下,仔细检查了下测试代码。发现存在2个问题:
(1)实验对EmguCV和OpenCV不公平,因为,其它测试都是进行处理图像这一个过程,而EmguCV和OpenCV在处理图像之间,需要将Bitmap转换为其内部图像格式IplImage这一过程。因此,今天的测试将这个转换过程提前,不计入执行时间。
(2)翻看OpenCV的实现代码,发现,它执行的是以下算法:
代码
1
static
CvStatus CV_STDCALL
2
icvBGRx2Gray_8u_CnC1R(
const
uchar
*
src,
int
srcstep,
3
uchar
*
dst,
int
dststep, CvSize size,
4
int
src_cn,
int
blue_idx )
5
{
6
int
i;
7
srcstep
-=
size.width
*
src_cn;
8
9
if
( size.width
*
size.height
>=
1024
)
10
{
11
int
*
tab
=
(
int
*
)cvStackAlloc(
256
*
3
*
sizeof
(tab[
0
]) );
12
int
r
=
0
, g
=
0
, b
=
(
1
<<
(csc_shift
-
1
));
13
for
( i
=
0
; i
<
256
; i
++
)
14
{
15
tab[i]
=
b;
16
tab[i
+
256
]
=
g;
17
tab[i
+
512
]
=
r;
18
g
+=
cscGg;
19
if
(
!
blue_idx )
20
b
+=
cscGb, r
+=
cscGr;
21
else
22
b
+=
cscGr, r
+=
cscGb;
23
}
24
25
for
( ; size.height
--
; src
+=
srcstep, dst
+=
dststep )
26
{
27
for
( i
=
0
; i
<
size.width; i
++
, src
+=
src_cn )
28
{
29
int
t0
=
tab[src[
0
]]
+
tab[src[
1
]
+
256
]
+
tab[src[
2
]
+
512
];
30
dst[i]
=
(uchar)(t0
>>
csc_shift);
31
}
32
}
33
}
34
else
35
{
36
for
( ; size.height
--
; src
+=
srcstep, dst
+=
dststep )
37
{
38
for
( i
=
0
; i
<
size.width; i
++
, src
+=
src_cn )
39
{
40
int
t0
=
src[blue_idx]
*
cscGb
+
src[
1
]
*
cscGg
+
src[blue_idx
^
2
]
*
cscGr;
41
dst[i]
=
(uchar)CV_DESCALE(t0, csc_shift);
42
}
43
}
44
}
45
return
CV_OK;
46
}
这一算法有什么特点呢?
第一,它不进行浮点计算。它首先将浮点数乘于一个Scale(2的N次幂),转换为整数,计算后再经过 >> 计算,消去这个Scale
第二,对于大图像,它使用的是查表方式而不是乘法方式来计算灰度的。
鉴于此,我改进了C#的实现代码,对小图像,依然采用浮点计算:
*to = (Byte)(p->Red * rCoeff + p->Green * gCoeff + p->Blue * bCoeff);
对大图像,则使用查表计算:
代码
int
*
rCache
=
stackalloc
int
[
256
];
int
*
gCache
=
stackalloc
int
[
256
];
int
*
bCache
=
stackalloc
int
[
256
];
const
int
shift
=
1
<<
10
;
int
rShift
=
(
int
)(rCoeff
*
shift);
int
gShift
=
(
int
)(gCoeff
*
shift);
int
bShift
=
shift
-
rShift
-
gShift;
int
r
=
0
, g
=
0
, b
=
0
;
for
(
int
i
=
0
; i
<
256
; i
++
)
{
rCache[i]
=
r;
gCache[i]
=
g;
bCache[i]
=
b;
r
+=
rShift;
g
+=
gShift;
b
+=
bShift;
}
while
(p
!=
end)
{
*
to
=
(Byte)((rCache[p
->
Red]
+
gCache[p
->
Green]
+
bCache[p
->
Red])
>>
10
);
p
++
;
to
++
;
}
这样,保证对比的是同一算法。今天的实验,主要比较下面五种图像灰度化方法:
(1)EmguCV实现:
代码
1
///
<summary>
2
///
使用EmguCv处理图像
3
///
</summary>
4
private
static
void
ProcessImageWithEmgucv(Image
<
Bgr, Byte
>
imageSource)
5
{
6
//
灰度
7
Image
<
Gray, Byte
>
imageGrayscale
=
imageSource.Convert
<
Gray, Byte
>
();
8
}
(2)OpenCV/PInvoke实现:
代码
1
///
<summary>
2
///
使用Open Cv P/Invoke处理图像
3
///
</summary>
4
unsafe
private
static
void
ProcessImageWithOpencv(IntPtr ptrSource, Size size)
5
{
6
IntPtr ptrGrayscale
=
CvInvoke.cvCreateImage(size, IPL_DEPTH.IPL_DEPTH_8U,
1
);
7
CvInvoke.cvCvtColor(ptrSource, ptrGrayscale, COLOR_CONVERSION.CV_BGR2GRAY);
8
}
9
(3)BitmapData实现:
代码
1
///
<summary>
2
///
将指定图像转换成灰度图
3
///
</summary>
4
///
<param name="bitmapSource">
源图像支持3通道或者4通道图像,支持Format24bppRgb、Format32bppRgb和Format32bppArgb这3种像素格式
</param>
5
///
<returns>
返回灰度图,如果转化失败,返回null。
</returns>
6
private
static
Bitmap Grayscale(Bitmap bitmapSource)
7
{
8
Bitmap bitmapGrayscale
=
null
;
9
if
(bitmapSource
!=
null
&&
(bitmapSource.PixelFormat
==
PixelFormat.Format24bppRgb
||
bitmapSource.PixelFormat
==
PixelFormat.Format32bppArgb
||
bitmapSource.PixelFormat
==
PixelFormat.Format32bppRgb))
10
{
11
int
width
=
bitmapSource.Width;
12
int
height
=
bitmapSource.Height;
13
Rectangle rect
=
new
Rectangle(
0
,
0
, width, height);
14
bitmapGrayscale
=
new
Bitmap(width, height, PixelFormat.Format8bppIndexed);
15
//
设置调色板
16
ColorPalette palette
=
bitmapGrayscale.Palette;
17
for
(
int
i
=
0
; i
<
palette.Entries.Length; i
++
)
18
palette.Entries[i]
=
Color.FromArgb(
255
, i, i, i);
19
bitmapGrayscale.Palette
=
palette;
20
BitmapData dataSource
=
bitmapSource.LockBits(rect, ImageLockMode.ReadOnly, bitmapSource.PixelFormat);
21
BitmapData dataGrayscale
=
bitmapGrayscale.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
22
byte
b, g, r;
23
int
strideSource
=
dataSource.Stride;
24
int
strideGrayscale
=
dataGrayscale.Stride;
25
unsafe
26
{
27
byte
*
ptrSource
=
(
byte
*
)dataSource.Scan0.ToPointer();
28
byte
*
ptr1;
29
byte
*
ptrGrayscale
=
(
byte
*
)dataGrayscale.Scan0.ToPointer();
30
byte
*
ptr2;
31
if
(bitmapSource.PixelFormat
==
PixelFormat.Format24bppRgb)
32
{
33
for
(
int
row
=
0
; row
<
height; row
++
)
34
{
35
ptr1
=
ptrSource
+
strideSource
*
row;
36
ptr2
=
ptrGrayscale
+
strideGrayscale
*
row;
37
for
(
int
col
=
0
; col
<
width; col
++
)
38
{
39
b
=
*
ptr1;
40
ptr1
++
;
41
g
=
*
ptr1;
42
ptr1
++
;
43
r
=
*
ptr1;
44
ptr1
++
;
45
*
ptr2
=
(
byte
)(
0.114
*
b
+
0.587
*
g
+
0.299
*
r);
46
ptr2
++
;
47
}
48
}
49
}
50
else
//
bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb
51
{
52
for
(
int
row
=
0
; row
<
height; row
++
)
53
{
54
ptr1
=
ptrSource
+
strideGrayscale
*
row;
55
ptr2
=
ptrGrayscale
+
strideGrayscale
*
row;
56
for
(
int
col
=
0
; col
<
width; col
++
)
57
{
58
b
=
*
ptr1;
59
ptr1
++
;
60
g
=
*
ptr1;
61
ptr1
++
;
62
r
=
*
ptr1;
63
ptr1
+=
2
;
64
*
ptr2
=
(
byte
)(
0.114
*
b
+
0.587
*
g
+
0.299
*
r);
65
ptr2
++
;
66
}
67
}
68
}
69
}
70
bitmapGrayscale.UnlockBits(dataGrayscale);
71
bitmapSource.UnlockBits(dataSource);
72
}
73
return
bitmapGrayscale;
74
}
(4)我自己的实现,Argb32Image,采用浮点计算:
代码
1
using
System;
2
using
System.Collections.Generic;
3
using
System.Runtime.InteropServices;
4
using
System.Text;
5
6
namespace
Orc.SmartImage
7
{
8
public
class
UnmanagedMemory
<
T
>
: IDisposable
9
where
T :
struct
10
{
11
public
Int32 ByteCount {
get
;
private
set
; }
12
public
Int32 Length {
get
;
private
set
; }
13
public
IntPtr Start {
get
;
private
set
; }
14
public
Int32 SizeOfType {
get
;
private
set
; }
15
16
public
UnmanagedMemory(Int32 length)
17
{
18
Length
=
length;
19
SizeOfType
=
SizeOfT();
20
ByteCount
=
SizeOfType
*
length;
21
Start
=
Marshal.AllocHGlobal(ByteCount);
22
}
23
24
public
void
Dispose()
25
{
26
Dispose(
true
);
27
GC.SuppressFinalize(
this
);
28
}
29
30
protected
virtual
void
Dispose(
bool
disposing)
31
{
32
if
(
false
==
disposed)
33
{
34
disposed
=
true
;
35
Marshal.FreeHGlobal(Start);
36
}
37
}
38
39
private
bool
disposed;
40
41
~
UnmanagedMemory()
42
{
43
Dispose(
false
);
44
}
45
46
private
Int32 SizeOfT()
47
{
48
return
Marshal.SizeOf(
typeof
(T));
49
}
50
}
51
}
52
53
54
using
System;
55
using
System.Collections.Generic;
56
using
System.Drawing;
57
using
System.Text;
58
59
namespace
Orc.SmartImage.UnmanagedObjects
60
{
61
public
struct
Argb32
62
{
63
public
Byte Alpha;
64
public
Byte Red;
65
public
Byte Green;
66
public
Byte Blue;
67
}
68
69
public
class
Argb32Image : UnmanagedMemory
<
Argb32
>
70
{
71
private
unsafe
Argb32
*
m_pointer;
72
73
public
unsafe
Argb32
*
Pointer {
get
{
return
m_pointer; } }
74
75
public
unsafe
Argb32Image(
int
length)
76
:
base
(length)
77
{
78
m_pointer
=
(Argb32
*
)
this
.Start;
79
}
80
81
public
unsafe
Argb32
this
[
int
index]
82
{
83
get
{
return
*
(m_pointer
+
index); }
84
set
{
*
(m_pointer
+
index)
=
value; }
85
}
86
87
public
Grayscale8Image ToGrayscaleImage()
88
{
89
return
ToGrayscaleImage(
0.299
,
0.587
,
0.114
);
90
}
91
92
public
unsafe
Grayscale8Image ToGrayscaleImage(
double
rCoeff,
double
gCoeff,
double
bCoeff)
93
{
94
Grayscale8Image img
=
new
Grayscale8Image(
this
.Length);
95
Argb32
*
p
=
Pointer;
96
Byte
*
to
=
img.Pointer;
97
Argb32
*
end
=
p
+
Length;
98
99
while
(p
!=
end)
100
{
101
*
to
=
(Byte)(p
->
Red
*
rCoeff
+
p
->
Green
*
gCoeff
+
p
->
Blue
*
bCoeff);
102
p
++
;
103
to
++
;
104
}
105
return
img;
106
}
107
108
public
unsafe
static
Argb32Image CreateFromBitmap(Bitmap map)
109
{
110
if
(map
==
null
)
throw
new
ArgumentNullException(
"
map
"
);
111
112
Argb32Image img
=
new
Argb32Image(map.Width
*
map.Height);
113
114
Argb32
*
p
=
img.Pointer;
115
116
for
(
int
row
=
0
; row
<
map.Height; row
++
)
117
{
118
for
(
int
col
=
0
; col
<
map.Width; col
++
)
119
{
120
Color c
=
map.GetPixel(col, row);
121
p
->
Alpha
=
c.A;
122
p
->
Red
=
c.R;
123
p
->
Green
=
c.G;
124
p
->
Blue
=
c.B;
125
p
++
;
126
}
127
}
128
129
return
img;
130
}
131
}
132
}
133
(5)我自己的实现,Rgb24Image,对小图像采用浮点计算,对大图像采用查表计算:
代码
1
using
System;
2
using
System.Collections.Generic;
3
using
System.Drawing;
4
using
System.Text;
5
6
namespace
Orc.SmartImage.UnmanagedObjects
7
{
8
public
struct
Rgb24
9
{
10
public
Byte Red;
11
public
Byte Green;
12
public
Byte Blue;
13
}
14
15
public
class
Rgb24Image : UnmanagedMemory
<
Rgb24
>
16
{
17
private
unsafe
Rgb24
*
m_pointer;
18
19
public
unsafe
Rgb24
*
Pointer {
get
{
return
m_pointer; } }
20
21
public
unsafe
Rgb24Image(
int
length)
22
:
base
(length)
23
{
24
m_pointer
=
(Rgb24
*
)
this
.Start;
25
}
26
27
public
unsafe
Rgb24
this
[
int
index]
28
{
29
get
{
return
*
(m_pointer
+
index); }
30
set
{
*
(m_pointer
+
index)
=
value; }
31
}
32
33
public
Grayscale8Image ToGrayscaleImage()
34
{
35
return
ToGrayscaleImage(
0.299
,
0.587
,
0.114
);
36
}
37
38
public
unsafe
Grayscale8Image ToGrayscaleImage(
double
rCoeff,
double
gCoeff,
double
bCoeff)
39
{
40
Grayscale8Image img
=
new
Grayscale8Image(
this
.Length);
41
Rgb24
*
p
=
Pointer;
42
Byte
*
to
=
img.Pointer;
43
Rgb24
*
end
=
p
+
Length;
44
45
if
(Length
<
1024
)
46
{
47
while
(p
!=
end)
48
{
49
*
to
=
(Byte)(p
->
Red
*
rCoeff
+
p
->
Green
*
gCoeff
+
p
->
Blue
*
bCoeff);
50
p
++
;
51
to
++
;
52
}
53
}
54
else
55
{
56
int
*
rCache
=
stackalloc
int
[
256
];
57
int
*
gCache
=
stackalloc
int
[
256
];
58
int
*
bCache
=
stackalloc
int
[
256
];
59
60
const
int
shift
=
1
<<
10
;
61
int
rShift
=
(
int
)(rCoeff
*
shift);
62
int
gShift
=
(
int
)(gCoeff
*
shift);
63
int
bShift
=
shift
-
rShift
-
gShift;
64
65
int
r
=
0
, g
=
0
, b
=
0
;
66
for
(
int
i
=
0
; i
<
256
; i
++
)
67
{
68
rCache[i]
=
r;
69
gCache[i]
=
g;
70
bCache[i]
=
b;
71
r
+=
rShift;
72
g
+=
gShift;
73
b
+=
bShift;
74
}
75
76
while
(p
!=
end)
77
{
78
*
to
=
(Byte)((rCache[p
->
Red]
+
gCache[p
->
Green]
+
bCache[p
->
Red])
>>
10
);
79
p
++
;
80
to
++
;
81
}
82
}
83
return
img;
84
}
85
86
public
unsafe
static
Rgb24Image CreateFromBitmap(Bitmap map)
87
{
88
if
(map
==
null
)
throw
new
ArgumentNullException(
"
map
"
);
89
90
Rgb24Image img
=
new
Rgb24Image(map.Width
*
map.Height);
91
92
Rgb24
*
p
=
img.Pointer;
93
94
for
(
int
row
=
0
; row
<
map.Height; row
++
)
95
{
96
for
(
int
col
=
0
; col
<
map.Width; col
++
)
97
{
98
Color c
=
map.GetPixel(col, row);
99
p
->
Red
=
c.R;
100
p
->
Green
=
c.G;
101
p
->
Blue
=
c.B;
102
p
++
;
103
}
104
}
105
106
return
img;
107
}
108
}
109
}
110
测试代码如下:
代码
1
using
System;
2
using
System.Collections.Generic;
3
using
System.Runtime.InteropServices;
4
using
System.Diagnostics;
5
using
System.Linq;
6
using
System.Text;
7
using
System.Drawing;
8
using
System.Drawing.Imaging;
9
using
Orc.SmartImage;
10
using
Emgu.CV;
11
using
Emgu.CV.Structure;
12
using
Emgu.CV.CvEnum;
13
using
Orc.SmartImage.Gpu;
14
using
Orc.SmartImage.UnmanagedObjects;
15
16
namespace
Orc.SmartImage.PerformanceTest
17
{
18
public
class
PerformanceTestCase0
19
{
20
public
static
String Test(Bitmap src,
int
count)
21
{
22
Argb32Image img32
=
Argb32Image.CreateFromBitmap(src);
23
Rgb24Image img24
=
Rgb24Image.CreateFromBitmap(src);
24
Image
<
Bgr, Byte
>
imageSource
=
new
Image
<
Bgr,
byte
>
(src);
25
IntPtr ptrSource
=
Marshal.AllocHGlobal(Marshal.SizeOf(
typeof
(MIplImage)));
26
Marshal.StructureToPtr(imageSource.MIplImage, ptrSource,
true
);
27
Size size
=
imageSource.Size;
28
StringBuilder sb
=
new
StringBuilder();
29
Stopwatch sw
=
new
Stopwatch();
30
31
DoSomething();
32
33
sw.Reset();
34
sw.Start();
35
for
(
int
i
=
0
; i
<
count; i
++
)
36
ProcessImageWithEmgucv(imageSource);
37
sw.Stop();
38
sb.AppendLine(
"
EmguCV:
"
+
sw.ElapsedMilliseconds.ToString());
39
40
DoSomething();
41
sw.Reset();
42
sw.Start();
43
for
(
int
i
=
0
; i
<
count; i
++
)
44
ProcessImageWithOpencv(ptrSource, imageSource.Size);
45
sw.Stop();
46
sb.AppendLine(
"
OpenCV P/Invoke:
"
+
sw.ElapsedMilliseconds.ToString());
47
48
DoSomething();
49
sw.Reset();
50
sw.Start();
51
for
(
int
i
=
0
; i
<
count; i
++
)
52
Grayscale(src);
53
sw.Stop();
54
sb.AppendLine(
"
BitmapData:
"
+
sw.ElapsedMilliseconds.ToString());
55
56
DoSomething();
57
sw.Reset();
58
sw.Start();
59
for
(
int
i
=
0
; i
<
count; i
++
)
60
img24.ToGrayscaleImage();
61
sw.Stop();
62
sb.AppendLine(
"
RgbImage24[use table]:
"
+
sw.ElapsedMilliseconds.ToString());
63
64
DoSomething();
65
sw.Reset();
66
sw.Start();
67
for
(
int
i
=
0
; i
<
count; i
++
)
68
img32.ToGrayscaleImage();
69
sw.Stop();
70
sb.AppendLine(
"
ArgbImage32:
"
+
sw.ElapsedMilliseconds.ToString());
71
72
return
sb.ToString();
73
}
74
75
private
static
int
[] DoSomething()
76
{
77
int
[] data
=
new
Int32[
20000000
];
78
for
(
int
i
=
0
; i
<
data.Length; i
++
)
79
{
80
data[i]
=
i;
81
}
82
return
data;
83
}
84
85
///
<summary>
86
///
使用EmguCv处理图像
87
///
</summary>
88
private
static
void
ProcessImageWithEmgucv(Image
<
Bgr, Byte
>
imageSource)
89
{
90
//
灰度
91
Image
<
Gray, Byte
>
imageGrayscale
=
imageSource.Convert
<
Gray, Byte
>
();
92
}
93
94
///
<summary>
95
///
使用Open Cv P/Invoke处理图像
96
///
</summary>
97
unsafe
private
static
void
ProcessImageWithOpencv(IntPtr ptrSource, Size size)
98
{
99
IntPtr ptrGrayscale
=
CvInvoke.cvCreateImage(size, IPL_DEPTH.IPL_DEPTH_8U,
1
);
100
CvInvoke.cvCvtColor(ptrSource, ptrGrayscale, COLOR_CONVERSION.CV_BGR2GRAY);
101
}
102
103
104
105
///
<summary>
106
///
将指定图像转换成灰度图
107
///
</summary>
108
///
<param name="bitmapSource">
源图像支持3通道或者4通道图像,支持Format24bppRgb、Format32bppRgb和Format32bppArgb这3种像素格式
</param>
109
///
<returns>
返回灰度图,如果转化失败,返回null。
</returns>
110
private
static
Bitmap Grayscale(Bitmap bitmapSource)
111
{
112
Bitmap bitmapGrayscale
=
null
;
113
if
(bitmapSource
!=
null
&&
(bitmapSource.PixelFormat
==
PixelFormat.Format24bppRgb
||
bitmapSource.PixelFormat
==
PixelFormat.Format32bppArgb
||
bitmapSource.PixelFormat
==
PixelFormat.Format32bppRgb))
114
{
115
int
width
=
bitmapSource.Width;
116
int
height
=
bitmapSource.Height;
117
Rectangle rect
=
new
Rectangle(
0
,
0
, width, height);
118
bitmapGrayscale
=
new
Bitmap(width, height, PixelFormat.Format8bppIndexed);
119
//
设置调色板
120
ColorPalette palette
=
bitmapGrayscale.Palette;
121
for
(
int
i
=
0
; i
<
palette.Entries.Length; i
++
)
122
palette.Entries[i]
=
Color.FromArgb(
255
, i, i, i);
123
bitmapGrayscale.Palette
=
palette;
124
BitmapData dataSource
=
bitmapSource.LockBits(rect, ImageLockMode.ReadOnly, bitmapSource.PixelFormat);
125
BitmapData dataGrayscale
=
bitmapGrayscale.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
126
byte
b, g, r;
127
int
strideSource
=
dataSource.Stride;
128
int
strideGrayscale
=
dataGrayscale.Stride;
129
unsafe
130
{
131
byte
*
ptrSource
=
(
byte
*
)dataSource.Scan0.ToPointer();
132
byte
*
ptr1;
133
byte
*
ptrGrayscale
=
(
byte
*
)dataGrayscale.Scan0.ToPointer();
134
byte
*
ptr2;
135
if
(bitmapSource.PixelFormat
==
PixelFormat.Format24bppRgb)
136
{
137
for
(
int
row
=
0
; row
<
height; row
++
)
138
{
139
ptr1
=
ptrSource
+
strideSource
*
row;
140
ptr2
=
ptrGrayscale
+
strideGrayscale
*
row;
141
for
(
int
col
=
0
; col
<
width; col
++
)
142
{
143
b
=
*
ptr1;
144
ptr1
++
;
145
g
=
*
ptr1;
146
ptr1
++
;
147
r
=
*
ptr1;
148
ptr1
++
;
149
*
ptr2
=
(
byte
)(
0.114
*
b
+
0.587
*
g
+
0.299
*
r);
150
ptr2
++
;
151
}
152
}
153
}
154
else
//
bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb
155
{
156
for
(
int
row
=
0
; row
<
height; row
++
)
157
{
158
ptr1
=
ptrSource
+
strideGrayscale
*
row;
159
ptr2
=
ptrGrayscale
+
strideGrayscale
*
row;
160
for
(
int
col
=
0
; col
<
width; col
++
)
161
{
162
b
=
*
ptr1;
163
ptr1
++
;
164
g
=
*
ptr1;
165
ptr1
++
;
166
r
=
*
ptr1;
167
ptr1
+=
2
;
168
*
ptr2
=
(
byte
)(
0.114
*
b
+
0.587
*
g
+
0.299
*
r);
169
ptr2
++
;
170
}
171
}
172
}
173
}
174
bitmapGrayscale.UnlockBits(dataGrayscale);
175
bitmapSource.UnlockBits(dataSource);
176
}
177
return
bitmapGrayscale;
178
}
179
180
}
181
}
182
在每种测试方法运行之前,执行DoSomething(),清空CPU的高速缓存。
测试程序采用WinForm编写,界面如下:
可输入执行次数。目前,我机器有800M空闲物理内存。下面进行测试:
图片1,分辨率205×581,32位图像,执行50次,结果——
EmguCV:48
OpenCV P/Invoke:30
BitmapData:103
RgbImage24[use table]:38
ArgbImage32:147
再按一下运行,结果——
EmguCV:49
OpenCV P/Invoke:31
BitmapData:107
RgbImage24[use table]:37
ArgbImage32:74
图片2,分辨率864×486,24位图像,执行50次,结果——
EmguCV:131
OpenCV P/Invoke:108
BitmapData:376
RgbImage24[use table]:132
ArgbImage32:258
第二次,
EmguCV:131
OpenCV P/Invoke:107
BitmapData:382
RgbImage24[use table]:130
ArgbImage32:257
图片3,1280×720,24位图像,执行50次,结果——
EmguCV:262
OpenCV P/Invoke:239
BitmapData:822
RgbImage24[use table]:288
ArgbImage32:570
第二次,
EmguCV:280
OpenCV P/Invoke:244
BitmapData:819
RgbImage24[use table]:289
ArgbImage32:568
下面是那张最大的图片 5000×6000的24位图像,执行5次,结果——
内存耗完了(上面的测试都未释放内存),没得到结果。关闭程序,再重新打开,将次数减少为1次,结果——
EmguCV:226
OpenCV P/Invoke:153
BitmapData:553
RgbImage24[use table]:189
ArgbImage32:609
第二次,
EmguCV:155
OpenCV P/Invoke:152
BitmapData:565
RgbImage24[use table]:184
ArgbImage32:745
==================================
下面进行总结(对昨天的结论进行修正):
(1)算法相同、数据结构相同的情况下,C#(采用指针和未托管内存)程序的性能大概是使用P/Invoke调用C程序的1/1.2~1/1.25倍。这个性能降低是可接受的(至少对我而言),并不是很大的性能降低。考虑到GC,性能会再打点折扣,就算是1/1.3吧。
(2)影响C#程序性能的主要因素是所使用的数据结构和算法。上面的测试,同样的功能,采用不同的数据结构和算法,性能差别是2~3倍。有时候可以产生1000倍以上的差异。
==================================
推论:
如果能够认同25%的性能损失,使用C#/非托管内存的优势主要是:快速开发,快速编译,快速测试,维护性好,同时,在非性能关键部分可以使用庞大的library。
==================================
为什么做这一系列实验?
我是想测试一下C#到底适合不适合做图像处理。目前我有几个选择:
(1)matlab。可以快速开发,然很难部署。
(2)OpenCV。速度很快,开发很慢。并且,OpenCV所提供的算法还是很有限。有时需要设计自己的算法,在OpenCV基础上扩展很慢。
(3)EmguCV。它封装了OpenCV。如果只使用既有功能,可以快速开发。一旦扩展,同样问题很大。
(4)AForge.Net。它提供了大量的适用的方法,但是我总感觉还是不好用,并且性能有问题。这个性能问题应该是所使用的算法问题。
所以,还不如写自己的纯C#图像库。因为一般来说一个应用,就只涉及那几个算法,把那几个算法实现就可以了。在C#下实现几个算法再进行研究和扩展,我感觉比在OpenCV下去研究现有算法,再进行扩展还要省事些。只要肯损失25%的性能。而我目前规划了一系列C#图像处理的文章,正好通过这个实验去寻找C#图像处理的最底层的数据结构。而未来多核机器+GPU的广泛应用,这个25%的性能损失问题不大。