| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 7350 人关注过本帖
标题:图片的黑白处理(二值化)
只看楼主 加入收藏
fairy4
Rank: 4
等 级:贵宾
威 望:10
帖 子:738
专家分:267
注 册:2007-11-1
结帖率:100%
收藏
 问题点数:0 回复次数:13 
图片的黑白处理(二值化)
原始圖片
 
图片附件: 游客没有浏览图片的权限,请 登录注册

黑白處理后圖片
 
图片附件: 游客没有浏览图片的权限,请 登录注册

原始圖片:
 
图片附件: 游客没有浏览图片的权限,请 登录注册

黑白處理后圖片:
 
图片附件: 游客没有浏览图片的权限,请 登录注册

部分处理代码:

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);
        }
考虑到图像处理速度问题,所有图像都锁定在内存中进行操作。这样比直接操作速度快了几倍。

附件为Threshold控件,引用到工程里面即可,
Threshold.rar (8.66 KB)
收到的鲜花
  • mxmkeep2008-11-20 10:17 送鲜花  3朵   附言:原创内容(学习中,希望楼主能继续发教程)
搜索更多相关主题的帖子: 图像黑白处理 
2008-10-15 20:21
软编小虫
Rank: 5Rank: 5
来 自:广西,南宁,马山
等 级:职业侠客
威 望:1
帖 子:160
专家分:324
注 册:2008-10-8
收藏
得分:0 
给的代码能全点吗?!
IThresholder 编译时不能通过,提示"未定义变量,所以也应把这变量定义写上呀.
2008-10-16 09:43
fairy4
Rank: 4
等 级:贵宾
威 望:10
帖 子:738
专家分:267
注 册:2007-11-1
收藏
得分:0 
[bo][un]软编小虫[/un] 在 2008-10-16 09:43 的发言:[/bo]

给的代码能全点吗?!
IThresholder 编译时不能通过,提示"未定义变量,所以也应把这变量定义写上呀.

你引用附件里面的DLL即可,在 Imports Threshold,就可以了

一个人只有一个心脏,却有两个心房。一个住着快乐;一个住着悲伤。不要笑得太大声,不然会吵醒旁边的悲伤
2008-10-16 11:42
风弄无声
Rank: 1
等 级:新手上路
帖 子:31
专家分:0
注 册:2008-1-30
收藏
得分:0 
写得很不错的!
我是初学者,有很多都不是看得很明白,请楼主能不能注解得详细一点?
2008-11-13 22:01
风弄无声
Rank: 1
等 级:新手上路
帖 子:31
专家分:0
注 册:2008-1-30
收藏
得分:0 
C#那部分能加上更详细点的注解吗?
2008-11-13 22:04
风弄无声
Rank: 1
等 级:新手上路
帖 子:31
专家分:0
注 册:2008-1-30
收藏
得分:0 
楼主,快点出现吧,我真的真的很迫切想弄明白这个C#的代码,拜托了!!!!!!
2008-11-14 21:23
fairy4
Rank: 4
等 级:贵宾
威 望:10
帖 子:738
专家分:267
注 册:2007-11-1
收藏
得分:0 
呵呵,可以给你全部源码,太长了,也太多,详细的注释我也累啊!

一个人只有一个心脏,却有两个心房。一个住着快乐;一个住着悲伤。不要笑得太大声,不然会吵醒旁边的悲伤
2008-11-15 11:17
fairy4
Rank: 4
等 级:贵宾
威 望:10
帖 子:738
专家分:267
注 册:2007-11-1
收藏
得分:0 
这里发表的代码量,不到实际代码的1/5,需要的话你发个邮件到phoenix4197@,我将全部源码转寄给你

一个人只有一个心脏,却有两个心房。一个住着快乐;一个住着悲伤。不要笑得太大声,不然会吵醒旁边的悲伤
2008-11-15 11:20
风弄无声
Rank: 1
等 级:新手上路
帖 子:31
专家分:0
注 册:2008-1-30
收藏
得分:0 
恩,楼主太好了,谢谢!
2008-11-15 20:19
风弄无声
Rank: 1
等 级:新手上路
帖 子:31
专家分:0
注 册:2008-1-30
收藏
得分:0 
LZ邮件已经发过去了,就等LZ发话了......
2008-11-15 20:31
快速回复:图片的黑白处理(二值化)
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.036146 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved