灰度图像的自动阈值分割(Otsu 法)

机器视觉领域许多算法都要求先对图像进行二值化。这种二值化操作阈值的选取非常重要。阈值选取的不合适,可能得到的结果就毫无用处。今天就来讲讲一种自动计算阈值的方法。这种方法被称之为Otsu法。发明人是个日本人,叫做Nobuyuki Otsu (大津展之)。 
简单的说,这种算法假设一副图像由前景色和背景色组成,通过统计学的方法来选取一个阈值,使得这个阈值可以将前景色和背景色尽可能的分开。或者更准确的说是在某种判据下最优。与数理统计领域的 fisher 线性判别算法其实是等价的。

otsu算法中这个判据就是最大类间方差 (intra-class variance or the variance within the class)。下面就来详细说说什么是 intra-class variance。

我们知道一副灰度图像,可以计算它的颜色平均值,或者更进一步。可以计算出灰度直方图。

比如下面的例子图片: 

这个图片拍摄的是一个条形码。在这个图中,前景色就是黑色的条形码,背景色是其余部分的灰色。那么我们可以计算出这个图像的灰度直方图。

 
图中那个大的峰是背景色的部分,小的峰是前景色。

灰度值的均值是 122. 我们称这个均值为 M。

现在任意选取一个灰度值 t,则可以将这个直方图分成前后两部分。我们称这两部分分别为 A 和 B。对应的就是前景色和背景色。这两部分各自的平均值成为 MA 和 MB。 
A 部分里的像素数占总像素数的比例记作 PA,B部分里的像素数占总像素数的比例记作 PB。 
Nobuyuki Otsu 给出的类间方差定义为:

ICV=PA∗(MA−M)2+PB∗(MB−M)2

那么这个最佳的阈值t 就是使得 ICV 最大的那个值。 
对于上面的测试图像,我们可以遍历 t 的各种取值,计算 ICV。之后可以画出这样的ICV 曲线(绿色线条): 

可以看出,ICV 取最值的点确实将前景色和背景色分开了。 
下面是个例子代码,用到了 Qt 的QImage。

int otsu(const QImage &image)
{
double hist[256];
normalizedHistogram(image, hist); double omega[256];
double mu[256]; omega[0] = hist[0];
mu[0] = 0;
for(int i = 1; i < 256; i++)
{
omega[i] = omega[i-1] + hist[i]; //累积分布函数
mu[i] = mu[i-1] + i * hist[i];
}
double mean = mu[255];// 灰度平均值
double max = 0;
int k_max = 0;
for(int k = 1; k < 255; k++)
{
double PA = omega[k]; // A类所占的比例
double PB = 1 - omega[k]; //B类所占的比例
double value = 0;
if( fabs(PA) > 0.001 && fabs(PB) > 0.001)
{
double MA = mu[k] / PA; //A 类的灰度均值
double MB = (mean - mu[k]) / PB;//B类灰度均值
value = PA * (MA - mean) * (MA - mean) + PB * (MB - mean) * (MB - mean);//类间方差 if (value > max)
{
max = value;
k_max = k;
}
}
//qDebug() <<k << " " << hist[k] << " " << value;
}
return k_max;
}
bool normalizedHistogram(const QImage &image, double hist[256])
{
for(int i = 0; i < 256; i++)
{
hist[i] = 0.0;
}
int height = image.height();
int width = image.width();
int N = height * width;
if(image.format() != QImage::Format_Indexed8)
{
return false;
}
for(int i = 0; i < height; i++)
{
const quint8 *pData = (const quint8 *)image.constScanLine(i);
for(int j = 0; j < width; j++)
{
++hist[ pData[j] ];
}
}
for(int i = 0; i < 256; i++)
{
hist[i] = hist[i] / N;
}
return true;
}

利用这个方法计算出的阈值做了二值化后得到图像如下:

可以看到效果很好。

Otsu 方法也不是万能的。当目标与背景的大小比例悬殊时,类间方差准则函数可能呈现双峰或多峰,此时效果不好。这时就要考虑其他的办法了。

其实,我们可以仔细观察 ICV 的计算公式。

ICV=PA∗(MA−M)2+PB∗(MB−M)2

这里面 PA 和 PB 相当于是个前景色和背景色部分做个加权。当前景色或背景色有一个区域很小时。比如 PA 非常的小。那么这时计算出来的 t 就会和 B 区域很接近,这时的分割效果就会比较差。我们可以对ICV的公式进行一点小小的改造。

ICV=PAα∗(MA−M)2+PBα∗(MB−M)2

这里的 α 可以取一个 0-1之间的值。比如上面的例子图片,如果我们取 α=0.8 计算出的效果会更好一些。当然这个 α 值就要全凭经验来定了。

灰度图像的自动阈值分割(Otsu 法)(转载)的更多相关文章

  1. 灰度图像的自动阈值分割(Otsu 法)

    关于otsu分割方法,这个文章讲的是最好的,清晰易懂,一看就是作者认真思考过的. 因为在看这个算法的时候我就想,如果一个很大的图像上,大部分像素值都在0 - 50范围内,但是有很小一块像素值在240的 ...

  2. python数字图像处理(11):图像自动阈值分割

    图像阈值分割是一种广泛应用的分割技术,利用图像中要提取的目标区域与其背景在灰度特性上的差异,把图像看作具有不同灰度级的两类区域(目标区域和背景区域)的组合,选取一个比较合理的阈值,以确定图像中每个像素 ...

  3. 七种常见阈值分割代码(Otsu、最大熵、迭代法、自适应阀值、手动、迭代法、基本全局阈值法)

    http://blog.csdn.net/xw20084898/article/details/17564957 一.工具:VC+OpenCV 二.语言:C++ 三.原理 otsu法(最大类间方差法, ...

  4. 【转】七种常见阈值分割代码(Otsu、最大熵、迭代法、自适应阀值、手动、迭代法、基本全局阈值法)

    http://blog.csdn.net/xw20084898/article/details/17564957 一.工具:VC+OpenCV 二.语言:C++ 三.原理 otsu法(最大类间方差法, ...

  5. 【图像算法】七种常见阈值分割代码(Otsu、最大熵、迭代法、自适应阀值、手动、迭代法、基本全局阈值法)

    图像算法:图像阈值分割 SkySeraph Dec 21st 2010  HQU Email:zgzhaobo@gmail.com    QQ:452728574 Latest Modified Da ...

  6. 自适应阈值分割—大津法(OTSU算法)C++实现

    大津法是一种图像灰度自适应的阈值分割算法,是1979年由日本学者大津提出,并由他的名字命名的.大津法按照图像上灰度值的分布,将图像分成背景和前景两部分看待,前景就是我们要按照阈值分割出来的部分.背景和 ...

  7. 第十四节,OpenCV学习(三)图像的阈值分割

    图像的阈值处理 图像的阈值分割:图像的二值化(Binarization) 阈值分割法的特点是:适用于目标与背景灰度有较强对比的情况,重要的是背景或物体的灰度比较单一,而且总可以得到封闭且连通区域的边界 ...

  8. python实现遥感图像阈值分割

    1.阈值分割 import os import cv2 import numpy as np import matplotlib.pyplot as plt from osgeo import gda ...

  9. 阈值分割与XLD轮廓拼接——第4讲

    一.阈值分割 阈值分割算子众多: threshold :这是最基本最简单的阈值算子. binary_threshold :它是自动阈值算子,自动选出暗(dark)的区域,或者自动选出亮(light)的 ...

随机推荐

  1. windows下phpstorm的快捷键

    ctrl+shift+n查找文件 ctrl+shift+f 在一个目录里查找一段代码(ctrl+f的升级版) ctr+shift+r 在一个目录里查找一段代码并替换(ctrl+r的升级版) CTRL+ ...

  2. IBatis.net动态SQL语句(六)

    在学习动态SQL语句之前,首先必须对条件查询有一定了解,先来学习如何向IBatis.Net的映射文件里传入参数. 一.条件查询 1.传递单个参数 如根据Id查询: <select id=&quo ...

  3. $lookup

    db.orders.aggregate([ { $lookup: { from: "inventory", localField: "item", foreig ...

  4. DotNetBar v12.9.0.0 Fully Cracked

    更新信息: http://www.devcomponents.com/customeronly/releasenotes.asp?p=dnbwf&v=12.9.0.0 如果遇到破解问题可以与我 ...

  5. bjui给出的一个标准应用的首页

    <!DOCTYPE html> <html lang="zh"> <head> <meta charset="utf-8&quo ...

  6. 预写式日志WAL

    Chapter 25. 预写式日志(Write-Ahead Logging (WAL)) Table of Contents 25.1. WAL 的好处 25.2. WAL 配置 25.3. 内部 预 ...

  7. Google 宣布支持中文邮箱地址

    Gmail 宣布,即日起开始支持非拉丁字符邮箱地址.也就是说,我们可以在 Gmail 中针对中文邮箱地址发送和接收邮件了. 全世界母语是拉丁字母语言的人类不超过全人类总数的一半,母语是英语的人数更少. ...

  8. Snagit 12 – 功能强的老牌截图软件

    老牌截图软件 Snagit 12 新版正式发布,相比旧版,Snagit 12 界面变得更加简洁,深色的配色增加了专业感,也更加舒服. 在功能上,Snagit 大幅简化了用户体验,不需要复杂的操作,只需 ...

  9. IOS UITableView的分隔线多出问题

    如题,有时显示UITableView多出部分在页面时,下面会显示处多出的行, 此时应该在UITableView初始化时设置为Group if (_tableView == nil) { _tableV ...

  10. lightoj1080 线段树

    //Accepted 6628 KB 520 ms //I a b 把a到b区间的二进制位去反,转化成a到b区间的数全部加1 //Q a 判断第a位的奇偶 #include <cstdio> ...