搜索 | 会员  
图片的黑白处理(二值化)
来源: 收集   作者:网友  日期:2009/10/9  类别:编程语言  主题:VB  编辑:德仔
原始圖片 ../../UploadFiles/Dezai200910091150
原始圖片
 
 
 黑白處理后圖片
 
 
 
 
 
部分处理代码:
 
 VB.NET code
  ……
  Dim ts2 As IThresholder = New GlobalMeanThreshold(inbmp)
  Dim tsBMP As New Bitmap(PictureBox1.Width, PictureBox1.Height)
  ts2.RenderToBitmap(tsBMP)
  PictureBox6.Image = tsBMP
  PictureBox6.Height = PictureBox1.Height
  PictureBox6.Width = PictureBox1.Width
  PictureBox6.Left = 0
  PictureBox6.Top = 0
  ……
 
  理论知识:
  灰度图像的二值化处理就是讲图像上的点的灰度置为0或255,也就是讲整个图像呈现出明显的黑白效果。即将256个亮度等级的灰度图像通过适当的阀值选取而获得仍然可以反映图像整体和局部特征的二值化图像。在数字图像处理中,二值图像占有非常重要的地位,特别是在实用的图像处理中,以二值图像处理实现而构成的系统是很多的,要进行二值图像的处理与分析,首先要把灰度图像二值化,得到二值化图像,这样子有利于再对图像做进一步处理时,图像的集合性质只与像素值为0或255的点的位置有关,不再涉及像素的多级值,使处理变得简单,而且数据的处理和压缩量小。为了得到理想的二值图像,一般采用封闭、连通的边界定义不交叠的区域。所有灰度大于或等于阀值的像素被判定为属于特定物体,其灰度值为255表示,否则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体区域。如果某特定物体在内部有均匀一致的灰度值,并且其处在一个具有其他等级灰度值的均匀背景下,使用阀值法就可以得到比较的分割效果。如果物体同背景的差别表现不在灰度值上(比如纹理不同),可以将这个差别特征转换为灰度的差别,然后利用阀值选取技术来分割该图像。动态调节阀值实现图像的二值化可动态观察其分割图像的具体结果。
  灰度图像的二值化没有固定的算法,根据图像区域中目标物体的不同而有不同的二值化算法.目前最主要的方法有:最大值法,平均值法,加权平均值法
  如下边代码:
 
 Private Function SetBitMapToBlackAndWhite(bmp As Bitmap) As Bitmap
  Try
  Dim height As Integer = bmp.Height
  Dim Width As Integer = bmp.Width
  Dim newBitMap As New Bitmap(Width, height)
  Dim pixel As Color
  For x As Integer = 0 To Width - 1
  For y As Integer = 0 To height - 1
  pixel = bmp.GetPixel(x, y)
  Dim r As Integer
  Dim g As Integer
  Dim b As Integer
  Dim Result As Integer
  Result = 0
  r = pixel.R
  g = pixel.G
  b = pixel.B
  Dim iType As Integer = 2
  Select Case iType
  Case 0
  '平均值法
  Result = (r + g + b) / 3
  Exit Select
  Case 1
  '最大值法
  If (r > g) Then
  Result = r
  Else
  Result = g
  End If
  If (Result > b) Then
  Result = Result
  Else
  Result = b
  End If
  Exit Select
  Case 2
  '加权平均值
  Result = CInt(0.7) * r + CInt(0.2) * g + CInt(0.1) * b
  Exit Select
  End Select
  newBitMap.SetPixel(x, y, Color.FromArgb(Result, Result, Result))
  Next
  Next
  Return newBitMap
  Catch ex As Exception
  Return Nothing
  End Try
  End Function
 
  该函数实现的简单的图像2值化。
  实际使用中最简单的二值化算法就是根据每个像素的灰度值做舍入处理,比如二值化阀值可以设置为0-255的中值127,但是这种的二值化没有根基图像的整体灰度值所在范围做考虑,所以效果很差.
  网上流传较广的是根据图像的直方图求取阀值:可以到网上搜索,我就不重复了
  介绍我的方法:
  图像数据处理函数:由于需要对图像进行指针操作,故使用C#编写源码
 public override unsafe void DoThresholding()
  {
  System.Drawing.Imaging.BitmapData indata = InBMP.LockBits(new Rectangle(0, 0, InBMP.Width, InBMP.Height),
  System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  byte* ptr = (byte*)indata.Scan0.ToPointer();
  Histogram his = new Histogram(indata, ptr, 0, 0, InBMP.Width, InBMP.Height);
  int mean = Math.Max((int)his.Mean, 0) * 3;
  //图像阀值
  int stride = indata.Stride;
  for (int x = this.Width - 1; x >= 0; x--)
  {
  for (int y = this.Height - 1; y >= 0; y--)
  {
  int average = (ptr[(y * stride) + (x * 3)] +
  ptr[(y * stride) + (x * 3) + 1] +
  ptr[(y * stride) + (x * 3) + 2]);
  //byte average = (byte)((ptr[(y * indata.Stride) + (x * 3) + 2]));
  if (average > mean)
  Data[x, y] = 255;
  else
  Data[x, y] = 0;
  }
  }
  InBMP.UnlockBits(indata);
  }
  }

  图像阀值的求取:
byte[] HistogramData;
  unsafe public Histogram(System.Drawing.Imaging.BitmapData indata, byte* ptr,
  int left, int top, int width, int height)
  {
  HistogramData = new byte[256];
  for (int i = 0; i < 256; i++)
  HistogramData[i] = 0;
  int right = Math.Min(left + width, indata.Width);
  int bottom = Math.Min(top + height, indata.Height);
  int stride = indata.Stride;
  for (int x = left; x < right; x++)
  {
  for (int y = top; y < bottom; y++)
  {
  HistogramData[ptr[(y * stride) + (x * 3)]] += 1;
  HistogramData[ptr[(y * stride) + (x * 3) + 1]] += 1;
  HistogramData[ptr[(y * stride) + (x * 3) + 2]] += 1;
  }
  }
  CalculateMean();
  CalculateMinMax();
  }
  unsafe public Histogram(byte* ptr, int stride, int imgWidth, int imgHeight,
  int left, int top, int width, int height)
  {
  HistogramData = new byte[256];
  int right = Math.Min(left + width, imgWidth);
  int bottom = Math.Min(top + height, imgHeight);
  for (int x = left; x < right; x++)
  {
  for (int y = top; y < bottom; y++)
  {
  byte average = (byte)((ptr[(y * stride) + (x * 3)] +
  ptr[(y * stride) + (x * 3) + 1] +
  ptr[(y * stride) + (x * 3) + 2]) / 3);
  HistogramData[average] += 1;
  }
  }
  CalculateMean();
  CalculateVariance();
  }
  public byte Mean;
  private int Sum;
  private int WeightedSum;
  private void CalculateMean()
  {
  int sum = 0;
  int weightedSum = 0;
  for (int i = 0; i < 256; i++)
  {
  sum += HistogramData[i];
  weightedSum += HistogramData[i] * i;
  }
  Sum = sum;
  WeightedSum = weightedSum;
  if (sum > 0)
  Mean = (byte)(weightedSum / sum);
  else
  Mean = 0;
  }
 
  生成黑白图像
 public unsafe void RenderToBitmap(Bitmap bmp)
  {
  if (Data == null)
  DoThresholding();
  System.Drawing.Imaging.BitmapData outdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
  System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  byte* ptr = (byte*)outdata.Scan0.ToPointer();
  for (int x = bmp.Width - 1; x >= 0; x--)
  {
  for (int y = bmp.Height - 1; y >= 0; y--)
  {
  if (this.Data[x, y] > 0)
  {
  ptr[(y * outdata.Stride) + (x * 3)] = this.Data[x, y];
  ptr[(y * outdata.Stride) + (x * 3) + 1] = this.Data[x, y];
  ptr[(y * outdata.Stride) + (x * 3) + 2] = this.Data[x, y];
  }
  else
  {
  ptr[(y * outdata.Stride) + (x * 3)] = 0;
  ptr[(y * outdata.Stride) + (x * 3) + 1] = 0;
  ptr[(y * outdata.Stride) + (x * 3) + 2] = 0;
  }
  }
  }
  bmp.UnlockBits(outdata);
  }

  考虑到图像处理速度问题,所有图像都锁定在内存中进行操作。这样比直接操作速度快了几倍。
 
德仔网尊重行业规范,每篇文章都注明有明确的作者和来源;德仔网的原创文章,请转载时务必注明文章作者和来源:德仔网;
头条那些事
大家在关注
广告那些事
我们的推荐
也许感兴趣的
干货