找到一篇PPT,对于基础知识点的总结比较好,需要自己去深挖知识。链接
一. 图片灰度化
灰度化有多种方式,可以根据自己的需求自己定制,这里列举三种常用的方式。
将彩色图像的R、G、B三分量的亮度之一作为灰度图像灰度值 Gray=B or Gray=G or Gray=R。最大值法:将彩×××像的R、G、B三分量亮度最大值作为灰度图像灰度值max(R,max(G,B)) 注意max函数一次只能比较两个参数;加权平均法:将彩×××像的R、G、B三分量以不同的权重进行加权平均。人眼对绿色敏感最高,对蓝色敏感最低,故采用心理学灰度公式:Gray= 0.114B+ 0.587G+ 0.299R 。即是Opencv的颜色空间转换函数 :CV_BGR2GRAY 加权平均法C#示例代码
public static Bitmap GetGrayImage(Bitmap image
)
{
Bitmap result
= image
.Clone() as Bitmap
;
Color c
= new Color();
int ret
;
for (int i
= 0; i
< image
.Width
; i
++)
{
for (int j
= 0; j
< image
.Height
; j
++)
{
c
= result
.GetPixel(i
, j
);
ret
= (int)(c
.R
* 0.299 + c
.G
* 0.587 + c
.B
* 0.114);
result
.SetPixel(i
, j
, Color
.FromArgb(ret
, ret
, ret
));
}
}
return result
;
}
二. 图片锐化
方法壹:将原图像与拉普拉斯模板相乘,再加上原图像。转载自
private void button13_Click(object sender
, EventArgs e
)
{
Bitmap myBitmap
= new Bitmap(pictureBox1
.Image
);
int[] Laplacian
= { -1, -1, -1, -1, 9, -1, -1, -1, -1 };
Color pixel
;
for (int i
= 1; i
< myBitmap
.Width
- 1; i
++)
{
for (int j
= 1; j
< myBitmap
.Height
- 1; j
++)
{
int red
= 0, green
= 0, blue
= 0;
int index
= 0;
for (int col
= -1; col
<= 1; col
++)
{
for (int row
= -1; row
<= 1; row
++)
{
pixel
= myBitmap
.GetPixel(i
+ row
, j
+ col
);
red
+= pixel
.R
* Laplacian
[index
];
green
+= pixel
.G
* Laplacian
[index
];
blue
+= pixel
.B
* Laplacian
[index
];
index
++;
}
}
if (red
> 255) red
= 255;
if (red
< 0) red
= 0;
if (green
> 255) green
= 255;
if (green
< 0) green
= 0;
if (blue
> 255) blue
= 255;
if (blue
< 0) blue
= 0;
myBitmap
.SetPixel(i
-1, j
-1, Color
.FromArgb((int)red
, (int)green
, (int)blue
));
}
}
pictureBox1
.Image
= myBitmap
;
}
方法贰:用当前像素减去周围八个像素的平均值得到差,得到的差值乘以锐化程度,再加原来的像素,得到新的当前像素值。在示例中,由于直接操作内存是不安全的,所以要加unsafe。(来自网络思路)
public static Bitmap KiSharpen(Bitmap b
, float val
)
{
if (b
== null)
{
return null;
}
int w
= b
.Width
;
int h
= b
.Height
;
try
{
Bitmap bmpRtn
= new Bitmap(w
, h
, PixelFormat
.Format24bppRgb
);
BitmapData srcData
= b
.LockBits(new Rectangle(0, 0, w
, h
), ImageLockMode
.ReadOnly
, PixelFormat
.Format24bppRgb
);
BitmapData dstData
= bmpRtn
.LockBits(new Rectangle(0, 0, w
, h
), ImageLockMode
.WriteOnly
, PixelFormat
.Format24bppRgb
);
unsafe
{
byte* pIn
= (byte*)srcData
.Scan0
.ToPointer();
byte* pOut
= (byte*)dstData
.Scan0
.ToPointer();
int stride
= srcData
.Stride
;
byte* p
;
for (int y
= 0; y
< h
; y
++)
{
for (int x
= 0; x
< w
; x
++)
{
if (x
== 0 || x
== w
- 1 || y
== 0 || y
== h
- 1)
{
pOut
[0] = pIn
[0];
pOut
[1] = pIn
[1];
pOut
[2] = pIn
[2];
}
else
{
int r1
, r2
, r3
, r4
, r5
, r6
, r7
, r8
, r0
;
int g1
, g2
, g3
, g4
, g5
, g6
, g7
, g8
, g0
;
int b1
, b2
, b3
, b4
, b5
, b6
, b7
, b8
, b0
;
float vR
, vG
, vB
;
p
= pIn
- stride
- 3;
r1
= p
[2];
g1
= p
[1];
b1
= p
[0];
p
= pIn
- stride
;
r2
= p
[2];
g2
= p
[1];
b2
= p
[0];
p
= pIn
- stride
+ 3;
r3
= p
[2];
g3
= p
[1];
b3
= p
[0];
p
= pIn
- 3;
r4
= p
[2];
g4
= p
[1];
b4
= p
[0];
p
= pIn
+ 3;
r5
= p
[2];
g5
= p
[1];
b5
= p
[0];
p
= pIn
+ stride
- 3;
r6
= p
[2];
g6
= p
[1];
b6
= p
[0];
p
= pIn
+ stride
;
r7
= p
[2];
g7
= p
[1];
b7
= p
[0];
p
= pIn
+ stride
+ 3;
r8
= p
[2];
g8
= p
[1];
b8
= p
[0];
p
= pIn
;
r0
= p
[2];
g0
= p
[1];
b0
= p
[0];
vR
= (float)r0
- (float)(r1
+ r2
+ r3
+ r4
+ r5
+ r6
+ r7
+ r8
) / 8;
vG
= (float)g0
- (float)(g1
+ g2
+ g3
+ g4
+ g5
+ g6
+ g7
+ g8
) / 8;
vB
= (float)b0
- (float)(b1
+ b2
+ b3
+ b4
+ b5
+ b6
+ b7
+ b8
) / 8;
vR
= r0
+ vR
* val
;
vG
= g0
+ vG
* val
;
vB
= b0
+ vB
* val
;
if (vR
> 0)
{
vR
= Math
.Min(255, vR
);
}
else
{
vR
= Math
.Max(0, vR
);
}
if (vG
> 0)
{
vG
= Math
.Min(255, vG
);
}
else
{
vG
= Math
.Max(0, vG
);
}
if (vB
> 0)
{
vB
= Math
.Min(255, vB
);
}
else
{
vB
= Math
.Max(0, vB
);
}
pOut
[0] = (byte)vB
;
pOut
[1] = (byte)vG
;
pOut
[2] = (byte)vR
;
}
pIn
+= 3;
pOut
+= 3;
}
pIn
+= srcData
.Stride
- w
* 3;
pOut
+= srcData
.Stride
- w
* 3;
}
}
b
.UnlockBits(srcData
);
bmpRtn
.UnlockBits(dstData
);
return bmpRtn
;
}
catch
{
return null;
}
}
方法叁:将像素从内存中拷贝出来操作完后再重新赋值,以达到对图像的快速读取和处理。(程序源自网络)
public static Bitmap SharpenImage(Bitmap bmp
)
{
int height
= bmp
.Height
;
int width
= bmp
.Width
;
Bitmap newbmp
= new Bitmap(width
, height
);
LockBitmap lbmp
= new LockBitmap(bmp
);
LockBitmap newlbmp
= new LockBitmap(newbmp
);
lbmp
.LockBits();
newlbmp
.LockBits();
Color pixel
;
int[] Laplacian
= { -1, -1, -1, -1, 9, -1, -1, -1, -1 };
for (int x
= 1; x
< width
- 1; x
++)
{
for (int y
= 1; y
< height
- 1; y
++)
{
int r
= 0, g
= 0, b
= 0;
int Index
= 0;
for (int col
= -1; col
<= 1; col
++)
{
for (int row
= -1; row
<= 1; row
++)
{
pixel
= lbmp
.GetPixel(x
+ row
, y
+ col
);
r
+= pixel
.R
* Laplacian
[Index];
g
+= pixel
.G
* Laplacian
[Index];
b
+= pixel
.B
* Laplacian
[Index];
Index
++;
}
}
r
= r
> 255 ? 255 : r
;
r
= r
< 0 ? 0 : r
;
g
= g
> 255 ? 255 : g
;
g
= g
< 0 ? 0 : g
;
b
= b
> 255 ? 255 : b
;
b
= b
< 0 ? 0 : b
;
newlbmp
.SetPixel(x
- 1, y
- 1, Color
.FromArgb(r
, g
, b
));
}
}
lbmp
.UnlockBits();
newlbmp
.UnlockBits();
return newbmp
;
}
public class LockBitmap
{
Bitmap source
= null;
IntPtr Iptr
= IntPtr
.Zero
;
BitmapData bitmapData
= null;
public byte[] Pixels
{ get; set; }
public int Depth
{ get; private set; }
public int Width
{ get; private set; }
public int Height
{ get; private set; }
public LockBitmap(Bitmap source
)
{
this.source
= source
;
}
public void LockBits()
{
try
{
Width
= source
.Width
;
Height
= source
.Height
;
int PixelCount
= Width
* Height
;
Rectangle rect
= new Rectangle(0, 0, Width
, Height
);
Depth
= System
.Drawing
.Bitmap
.GetPixelFormatSize(source
.PixelFormat
);
if (Depth
!= 8 && Depth
!= 24 && Depth
!= 32)
{
throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
}
bitmapData
= source
.LockBits(rect
, ImageLockMode
.ReadWrite
, source
.PixelFormat
);
int step
= Depth
/ 8;
Pixels
= new byte[PixelCount * step
];
Iptr
= bitmapData
.Scan0
;
Marshal
.Copy(Iptr
, Pixels
, 0, Pixels
.Length
);
}
catch (Exception ex
)
{
throw ex
;
}
}
public void UnlockBits()
{
try
{
Marshal
.Copy(Pixels
, 0, Iptr
, Pixels
.Length
);
source
.UnlockBits(bitmapData
);
}
catch (Exception ex
)
{
throw ex
;
}
}
public Color GetPixel(int x
, int y
)
{
Color clr
= Color
.Empty
;
int cCount
= Depth
/ 8;
int i
= ((y
* Width
) + x
) * cCount
;
if (i
> Pixels
.Length
- cCount
)
throw new IndexOutOfRangeException();
if (Depth
== 32)
{
byte b
= Pixels
[i
];
byte g
= Pixels
[i
+ 1];
byte r
= Pixels
[i
+ 2];
byte a
= Pixels
[i
+ 3];
clr
= Color
.FromArgb(a
, r
, g
, b
);
}
if (Depth
== 24)
{
byte b
= Pixels
[i
];
byte g
= Pixels
[i
+ 1];
byte r
= Pixels
[i
+ 2];
clr
= Color
.FromArgb(r
, g
, b
);
}
if (Depth
== 8)
{
byte c
= Pixels
[i
];
clr
= Color
.FromArgb(c
, c
, c
);
}
return clr
;
}
public void SetPixel(int x
, int y
, Color color
)
{
int cCount
= Depth
/ 8;
int i
= ((y
* Width
) + x
) * cCount
;
if (Depth
== 32)
{
Pixels
[i
] = color
.B
;
Pixels
[i
+ 1] = color
.G
;
Pixels
[i
+ 2] = color
.R
;
Pixels
[i
+ 3] = color
.A
;
}
if (Depth
== 24)
{
Pixels
[i
] = color
.B
;
Pixels
[i
+ 1] = color
.G
;
Pixels
[i
+ 2] = color
.R
;
}
if (Depth
== 8)
{
Pixels
[i
] = color
.B
;
}
}
}
三. 二值化
二值化:将图片各像素按照阈值设定为255(纯白)、0(纯黑)两种状态,对于自适应阈值的设定可以按照自己的需求进行修改。(示例来源网络,自己添加注释,如有错误请留言)
public static Bitmap DoThresholdings1(Bitmap img1
, int threshold
)
{
int w
= img1
.Width
;
int h
= img1
.Height
;
Bitmap dstBitmap
= new Bitmap(w
, h
, PixelFormat
.Format24bppRgb
);
int[] histogram
= new int[256];
int minGrayValue
= 255, maxGrayValue
= 0;
for (int i
= 0; i
< w
; i
++)
{
for (int j
= 0; j
< h
; j
++)
{
Color pixelColor
= img1
.GetPixel(i
, j
);
histogram
[pixelColor
.R
]++;
if (pixelColor
.R
> maxGrayValue
) maxGrayValue
= pixelColor
.R
;
if (pixelColor
.R
< minGrayValue
) minGrayValue
= pixelColor
.R
;
}
}
if (threshold
== -1)
{
int newThreshold
= (minGrayValue
+ maxGrayValue
) / 2;
for (int iterationTimes
= 0; threshold
!= newThreshold
&& iterationTimes
< 100; iterationTimes
++)
{
threshold
= newThreshold
;
int lP1
= 0;
int lP2
= 0;
int lS1
= 0;
int lS2
= 0;
for (int i
= minGrayValue
; i
< threshold
; i
++)
{
lP1
+= histogram
[i
] * i
;
lS1
+= histogram
[i
];
}
int mean1GrayValue
= (lP1
/ lS1
);
for (int i
= threshold
+ 1; i
< maxGrayValue
; i
++)
{
lP2
+= histogram
[i
] * i
;
lS2
+= histogram
[i
];
}
int mean2GrayValue
= (lP2
/ lS2
);
newThreshold
= (mean1GrayValue
+ mean2GrayValue
) / 2;
}
}
for (int i
=0; i
< w
; i
++)
{
for (int j
= 0; j
< h
; j
++)
{
Color pixelColor
= img1
.GetPixel(i
, j
);
if (pixelColor
.R
> threshold
)
{
dstBitmap
.SetPixel(i
, j
, Color
.FromArgb(255, 255, 255));
}
else
{
dstBitmap
.SetPixel(i
, j
, Color
.FromArgb(0, 0, 0));
}
}
}
return dstBitmap
;
}
全局平均值二值化:计算全图平均值,按照平均值进行二值化赋值。 具体例程如下,相对简单:
public static Bitmap GetBinaryzationImage1(Bitmap image
)
{
Bitmap result
= image
.Clone() as Bitmap
;
List
<int> tempList
= new List<int>();
for (int i
= 0; i
< result
.Width
; i
++)
{
for (int j
= 0; j
< result
.Height
; j
++)
{
tempList
.Add(result
.GetPixel(i
, j
).R
);
}
}
double average
= tempList
.Average();
Color color
= new Color();
for (int i
= 0; i
< result
.Width
; i
++)
{
for (int j
= 0; j
< result
.Height
; j
++)
{
color
= result
.GetPixel(i
, j
);
if ((color
.R
+ color
.G
+ color
.B
) / 3 > average
)
{
result
.SetPixel(i
, j
, Color
.White
);
}
else
{
result
.SetPixel(i
, j
, Color
.Black
);
}
}
}
return result
;
}
四. 图像边缘检测
Canny算子(程序如下,原理后续补齐)。网上找到一篇不错的博文。链接
public class Canny
{
public Bitmap _bitmap
;
public Canny(Bitmap _bitmap
)
{
this._bitmap
= _bitmap
;
}
public static Image CannyProcess(Bitmap src
, int highThreshould
, int lowThreshould
)
{
if (src
!= null)
{
int w
= src
.Width
;
int h
= src
.Height
;
byte[] temp
= Bitmap2Bytes(src
);
byte[] tempMask
= (byte[])temp
.Clone();
int[,] srcBytes
= new int[w
, h
];
for (int j
= 0; j
< h
; j
++)
{
for (int i
= 0; i
< w
; i
++)
{
srcBytes
[i
, j
] = (int)(tempMask
[i
* 4 + j
* w
* 4] * 0.114 +
tempMask
[i
* 4 + 1 + j
* w
* 4] * 0.587 +
tempMask
[i
* 4 + 2 + j
* w
* 4] * 0.299);
}
}
float gradientMax
= 100;
float[,] gradient
= new float[w
, h
];
byte[,] degree
= new byte[w
, h
];
GaussFilter(ref srcBytes
, w
, h
);
GetGradientDegree(srcBytes
, ref gradient
, ref degree
, ref gradientMax
, w
, h
);
NonMaxMini(gradient
, ref srcBytes
, gradientMax
, w
, h
, degree
);
TwoThreshouldJudge(highThreshould
, lowThreshould
, ref srcBytes
, w
, h
);
byte bytex
;
Bitmap dstBitmap
= new Bitmap(w
, h
, PixelFormat
.Format24bppRgb
);
for (int j
= 0; j
< h
; j
++)
{
for (int i
= 0; i
< w
; i
++)
{
bytex
= (byte)srcBytes
[i
, j
];
dstBitmap
.SetPixel(i
, h
- j
- 1, Color
.FromArgb(bytex
, bytex
, bytex
));
}
}
return dstBitmap
;
}
else
{
return null;
}
}
private static void GaussFilter(ref int[,] src
, int x
, int y
)
{
for (int j
= 1; j
< y
- 1; j
++)
{
for (int i
= 1; i
< x
- 1; i
++)
{
src
[i
, j
] = (4 * src
[i
, j
] + src
[i
- 1, j
- 1] + src
[i
+ 1, j
- 1] + src
[i
- 1, j
+ 1] + src
[i
+ 1, j
+ 1] + 2 * src
[i
, j
- 1] + 2 * src
[i
- 1, j
] + 2 * src
[i
, j
+ 1] + 2 * src
[i
+ 1, j
]) / 16;
}
}
}
private static void GetGradientDegree(int[,] srcBytes
, ref float[,] gradient
, ref byte[,] degree
, ref float GradientMax
, int x
, int y
)
{
gradient
= new float[x
, y
];
degree
= new byte[x
, y
];
int gx
, gy
;
int temp
;
double div
;
for (int j
= 1; j
< y
- 1; j
++)
{
for (int i
= 1; i
< x
- 1; i
++)
{
gx
= srcBytes
[i
+ 1, j
- 1] + 2 * srcBytes
[i
+ 1, j
] + srcBytes
[i
+ 1, j
+ 1] - srcBytes
[i
- 1, j
- 1] - 2 * srcBytes
[i
- 1, j
] - srcBytes
[i
- 1, j
+ 1];
gy
= srcBytes
[i
- 1, j
- 1] + 2 * srcBytes
[i
, j
- 1] + srcBytes
[i
+ 1, j
- 1] - srcBytes
[i
- 1, j
+ 1] - 2 * srcBytes
[i
, j
+ 1] - srcBytes
[i
+ 1, j
+ 1];
gradient
[i
, j
] = (float)Math
.Sqrt((double)(gx
* gx
+ gy
* gy
));
if (GradientMax
< gradient
[i
, j
])
{
GradientMax
= gradient
[i
, j
];
}
if (gx
== 0)
{
temp
= (gy
== 0) ? 0 : 90;
}
else
{
div
= (double)gy
/ (double)gx
;
if (div
< 0)
{
temp
= (int)(180 - Math
.Atan(-div
) * 180 / Math
.PI
);
}
else
{
temp
= (int)(Math
.Atan(div
) * 180 / Math
.PI
);
}
if (temp
< 22.5)
{
temp
= 0;
}
else if (temp
< 67.5)
{
temp
= 45;
}
else if (temp
< 112.5)
{
temp
= 90;
}
else if (temp
< 157.5)
{
temp
= 135;
}
else
temp
= 0;
}
degree
[i
, j
] = (byte)temp
;
}
}
}
private static void NonMaxMini(float[,] gradient
, ref int[,] srcBytes
, float GradientMax
, int x
, int y
, byte[,] degree
)
{
float leftPixel
= 0, rightPixel
= 0;
for (int j
= 1; j
< y
- 1; j
++)
{
for (int i
= 1; i
< x
- 1; i
++)
{
switch (degree
[i
, j
])
{
case 0:
leftPixel
= gradient
[i
- 1, j
];
rightPixel
= gradient
[i
+ 1, j
];
break;
case 45:
leftPixel
= gradient
[i
- 1, j
+ 1];
rightPixel
= gradient
[i
+ 1, j
- 1];
break;
case 90:
leftPixel
= gradient
[i
, j
+ 1];
rightPixel
= gradient
[i
, j
- 1];
break;
case 135:
leftPixel
= gradient
[i
+ 1, j
+ 1];
rightPixel
= gradient
[i
- 1, j
- 1];
break;
default:
break;
}
if ((gradient
[i
, j
] < leftPixel
) || (gradient
[i
, j
] < rightPixel
))
{
srcBytes
[i
, j
] = 0;
}
else
{
int xx
= (int)(255 * gradient
[i
, j
] / GradientMax
);
srcBytes
[i
, j
] = xx
;
}
}
}
}
private static void TwoThreshouldJudge(int highThreshold
, int lowThreshould
, ref int[,] srcBytes
, int x
, int y
)
{
for (int j
= 1; j
< y
- 1; j
++)
{
for (int i
= 1; i
< x
- 1; i
++)
{
if (srcBytes
[i
, j
] > highThreshold
)
{
srcBytes
[i
, j
] = 255;
}
else if (srcBytes
[i
, j
] < lowThreshould
)
{
srcBytes
[i
, j
] = 0;
}
else
{
if (srcBytes
[i
- 1, j
- 1] < highThreshold
&& srcBytes
[i
, j
- 1] < highThreshold
&& srcBytes
[i
+ 1, j
- 1] < highThreshold
&& srcBytes
[i
- 1, j
] < highThreshold
&& srcBytes
[i
+ 1, j
] < highThreshold
&& srcBytes
[i
- 1, j
+ 1] < highThreshold
&& srcBytes
[i
, j
+ 1] < highThreshold
&& srcBytes
[i
+ 1, j
+ 1] < highThreshold
)
{
srcBytes
[i
, j
] = 0;
}
else
srcBytes
[i
, j
] = 255;
}
}
}
}
public static byte[] Bitmap2Bytes(Bitmap b
)
{
MemoryStream ms
= new MemoryStream();
b
.Save(ms
, ImageFormat
.Bmp
);
byte[] bytes
= ms
.GetBuffer();
ms
.Close();
return bytes
;
}
public static Bitmap Bytes2Bitmap(byte[] bytes
, int w
, int h
)
{
MemoryStream ms1
= new MemoryStream(bytes
, 0, w
* 4 * h
);
Bitmap bm
= (Bitmap
)Image
.FromStream(ms1
);
ms1
.Close();
return bm
;
}
}