图像处理局部阈值

2015-03-09 11:14:52 TonyShengTan 阅读数 7320


学习DIP第57天
转载请标明本文出处:***http://blog.csdn.net/tonyshengtan ***,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro
更多图像处理机器学习内容请访问最新网站www.face2ai.com

#开篇废话
废话开始,今天说下区域阈值(局部阈值),前面介绍的阈值都是全局阈值,也就是阈值根据全局信息产生,而作用对象也是整幅图像的全部像素,而局部阈值的产生是一个中心像素c(x,y)c(x,y)的邻域的一些属性来计算出一个或多个阈值以及阈值的判别式。这句话比较难懂,举个例子,假设c的邻域R,根据邻域R计算出阈值T1,T2,T3.....TnT_1,T_2,T_3.....T_n我们可以表示成向量T=(T1,T2,T3....Tn)T=(T_1,T_2,T_3....T_n),设计阈值判别式Q(T,pixValue)Q(T,pixValue)其中pix_value的值就是像素c(x,y)c(x,y)的灰度值,判别式返回真假,真的话像素设置为亮,否则设置成暗。
#算法内容
内容迁移至:http://www.face2ai.com/DIP-7-7-灰度图像-图像分割-阈值处理之局部阈值/

http://www.tony4ai.com/DIP-7-7-灰度图像-图像分割-阈值处理之局部阈值/

2018-01-20 20:11:06 KYJL888 阅读数 3892

1 全局固定阈值分割
threshold(image, global, th, 255, CV_THRESH_BINARY_INV); 
一副图像包括目标、背景和噪声,设定某一阈值T将图像分成两部分:大于T的像素群和小于T的像素群。

捕获

在实际处理时候,为了显示需要一般用255表示背景,用0表示对象物。

由于实际得到的图像目标和背景之间不一定单纯地分布在两个灰度范围内,此时就需要两个或以上的阈值来提取目标。

捕获

图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰度级范围的图像。难点在于如何选择一个合适的阈值实现较好的分割。

 cv::threshold(image, global, th, 255, CV_THRESH_BINARY_INV);

  1. int main(int argc, char** argv)  
  2. {  
  3.     Mat image = imread("E:/VS2013/face/xuelian/png/1.png", CV_LOAD_IMAGE_GRAYSCALE);  
  4.     if (image.empty())  
  5.     {  
  6.         cout << "read image failure" << endl;  
  7.         return -1;  
  8.     }  
  9.   
  10.   
  11.     // 全局二值化  
  12.     int th = 100;//阈值  
  13.     Mat global;  
  14.     threshold(image, global, th, 255, CV_THRESH_BINARY_INV);  
  15.   
  16.   
  17.     // 局部二值化  
  18.   
  19.     int blockSize = 7;  
  20.     int constValue = 11;  
  21.     Mat local;  
  22.     adaptiveThreshold(image, local, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, blockSize, constValue);  
  23.   
  24.   
  25.     imshow("globalThreshold", global);  
  26.     imshow("localThreshold", local);  
  27.     waitKey(0);  
  28.   
  29.   
  30.     return 0;  
  31. }  


2 局部自适应阈值

adaptiveThreshold(image, local, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, blockSize, constValue);

   局部自适应阈值则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。这样做的好处在于每个像素位置处的二值化阈值不是固定不变的,而是由其周围邻域像素的分布来决定的。亮度较高的图像区域的二值化阈值通常会较高,而亮度较低的图像区域的二值化阈值则会相适应地变小。不同亮度、对比度、纹理的局部图像区域将会拥有相对应的局部二值化阈值。常用的局部自适应阈值有:1)局部邻域块的均值;2)局部邻域块的高斯加权和。

int main(int argc, char** argv)
{
	Mat image = imread("E:/VS2013/face/xuelian/png/1.png", CV_LOAD_IMAGE_GRAYSCALE);
	if (image.empty())
	{
		cout << "read image failure" << endl;
		return -1;
	}


	// 全局二值化
	int th = 100;//阈值
	Mat global;
	threshold(image, global, th, 255, CV_THRESH_BINARY_INV);


	// 局部二值化

	int blockSize = 7;
	int constValue = 11;
	Mat local;
	adaptiveThreshold(image, local, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, blockSize, constValue);


	imshow("globalThreshold", global);
	imshow("localThreshold", local);
	waitKey(0);


	return 0;
}

adaptiveThreshold(
const CvArr* src,    // 输入图像. 
CvArr* dst,    //  输出图像.
double max_value,   // max_value:使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值.
int adaptive_method=CV_ADAPTIVE_THRESH_MEAN_C,    // adaptive_method:自适应阈值算法使用:                                                                                                                    //CV_ADAPTIVE_THRESH_MEAN_C 或 CV_ADAPTIVE_THRESH_GAUSSIAN_C 
int threshold_type=CV_THRESH_BINARY, // threshold_type:取阈值类型:必须是CV_THRESH_BINARY或者                                                                                                         CV_THRESH_BINARY_INV
int block_size=3,//    block_size:用来计算阈值的象素邻域大小: 3, 5, 7, ...
double param=5 )//   param:与方法有关的参数。对方法 CV_ADAPTIVE_THRESH_MEAN_C 和                                                                                                   CV_ADAPTIVE_THRESH_GAUSSIAN_C,

 它是一个从均值或加权均值提取的常数(见讨论), 尽管它可以是负数。

对于max_value中的两个方式中的T是为每一个象素点单独计算的阈值,即每个像素点的阈值都是不同的,就是将该像素点周围B*B区域内的像素加权平均然后减去一个常数param,从而得到该点的阈值。b由block_size指定,常数由param1指定
对方法 CV_ADAPTIVE_THRESH_MEAN_C,先求出块中的均值,再减掉param。
对方法 CV_ADAPTIVE_THRESH_GAUSSIAN_C ,那么区域中(x,y)周围的像素根据高斯函数按照他们离中心点的距离进行加权计算, 再减掉param。



3.最大方差阈值

最大方差阈值的基本思想是:把直方图在某一阈值处分割成两组,当被分成的的两组之间方差最大时,决定阈值。

设图像的灰度值为0~m-1级,灰度值i的像素数为ni,此时我们得到像素总数:

捕获

然后用T将其分成两组C0={0~T-1},C1={T~m-1},各组的概率如下

捕获

平均值为:

捕获

其中:捕获是整体图像的灰度平均值,捕获是阈值为T时的灰度平均值,

所以全部采样的灰度平均值为:捕获

两组间的方差用下式求出:

捕获

从1~m-1之间改变T,求上式为最大时的T,Tmax即为我们需要的阈值。所以上式称为阈值选择函数。

 然后,基于上面的阈值对图像进行二值化处理,即可得到结果。

最大类间方差法(大津法,OTSU)

3自适应阈值算法(大津阈值法)

threshold(g_grayImage, g_ndstImage, 133, 255, THRESH_BINARY|THRESH_OTSU);
threshold(g_grayImage, g_ndstImage, 1, 255, THRESH_BINARY|THRESH_OTSU);

threshold(g_grayImage, g_ndstImage, 133, 255, THRESH_OTSU);

最大类间方差法是由日本学者大津(Nobuyuki Otsu)于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU。它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均
灰度记为μ,类间方差记为g。假设图像的背景较暗,并且图像的大小为M×N,图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:
      ω0=N0/ M×N                                                         (1)
      ω1=N1/ M×N                                                         (2)
      N0+N1=M×N                                                           (3)
      ω0+ω1=1                                                            (4)
      μ=ω0*μ0+ω1*μ1                                                   (5)
      g=ω0(μ0-μ)^2+ω1(μ1-μ)^2                                        (6)

将式(5)代入式(6),得到等价公式:
            g=ω0ω1(μ0-μ1)^2                                                  (7)

采用遍历的方法得到使类间方差最大的阈值T,即为所求。

由于,当图像在254或255灰度值上没有像素点时,求平均灰度时会出现0/0的情况,为避免抛出异常,可在当出现前景像素数为零时,跳出循环。

        

采用遍历的方法得到使类间方差最大的阈值,即为所求。

在大津法中,我们定义组内方差为

通过选择使得上述组内方差最小化时的阈值 t,就可以使得图像中的前景和背景尽可能的被区别开(假设我们将最终图像里被分开的两部分称为前景和背景)。w0w1分别是一个像素可能属于前景或背景的概率,而 σ 表示两个类别的方差。如果一个图像的直方图有L个等级(一般L=256),那么在给定阈值 t的情况下,w0w1分别定义为

大津展之证明最小化组内方差(intra-class variance)与最大化组间方差(inter-class variance)是等价的,于是有

又因为(其中 μ 表示均值或期望)

可以推出

这个证明仅仅涉及一些算术上的推导,我简单演示如下


otsu算法选择使类间方差最大的灰度值为阈值,具有很好的效果
算法具体描述见otsu论文,或冈萨雷斯著名的数字图像处理那本书
这里给出程序流程:
1、计算直方图并归一化histogram
2、计算图像灰度均值avgValue.
3、计算直方图的零阶w[i]和一级矩u[i]
4、计算并找到最大的类间方差(between-class variance)
variance[i]=(avgValue*w[i]-u[i])*(avgValue*w[i]-u[i])/(w[i]*(1-w[i]))
对应此最大方差的灰度值即为要找的阈值
5、用找到的阈值二值化图像

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;

//-----------------------------------【宏定义部分】-------------------------------------------- 
//  描述:定义一些辅助宏 
//----------------------------------------------------------------------------------------------
#define WINDOW_NAME "【Shi-Tomasi角点检测】"        //为窗口标题定义的宏 



//-----------------------------------【全局变量声明部分】--------------------------------------
//          描述:全局变量声明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage, g_grayImage,g_ndstImage;
int main(  )
{
	//【0】改变console字体颜色
	system("color 2F"); 
	//【1】载入源图像并将其转换为灰度图
	g_srcImage = imread("S (3).jpg");
	imshow("【原始图】", g_srcImage);
	cvtColor( g_srcImage, g_grayImage, CV_BGR2GRAY );
	imshow("【灰度图】", g_grayImage);
	//【方法1】全局固定阈值分割
	//threshold(g_grayImage, g_ndstImage, 133, 1, 1);	//更新效果图
	//imshow("threshold", g_ndstImage*255);

	//【方法2】局固自适应阈值分割
	//int blockSize = 13;  
	//int constValue =9;  
	//Mat local;  
	//adaptiveThreshold(g_grayImage, local, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, blockSize, constValue);    
	//imshow("localThreshold", local);
	//【方法3】大津法、Otus法阈值分割
	//threshold(g_grayImage, g_ndstImage, 133, 255, THRESH_OTSU);
	threshold(g_grayImage, g_ndstImage, 100, 255, THRESH_BINARY|THRESH_OTSU);	
	imshow("THRESH_OTSU", g_ndstImage);

	waitKey();
	return 0;
}

 PS说明    CV_THRESH_MASK        =7 //不工作问题

一. 关键函数介绍

下面就介绍OpenCV中对图像进行二值化的关键函数——cvThreshold()

函数功能:采用Canny方法对图像进行边缘检测

函数原型:

void cvThreshold(

  const CvArrsrc,

  CvArrdst,

  double threshold,

  double max_value,

  int threshold_type

);

函数说明:

第一个参数表示输入图像,必须为单通道灰度图。

第二个参数表示输出的边缘图像,为单通道黑白图。

第三个参数表示阈值

第四个参数表示最大值。

第五个参数表示运算方法。

OpenCVimgproc\types_c.h中可以找到运算方法的定义。

/* Threshold types */

enum

{

    CV_THRESH_BINARY      =0,  /* value = value > threshold ? max_value : 0       */

    CV_THRESH_BINARY_INV  =1,  /* value = value > threshold ? 0 : max_value       */

    CV_THRESH_TRUNC       =2,  /* value = value > threshold ? threshold : value   */

    CV_THRESH_TOZERO      =3,  /* value = value > threshold ? value : 0           */

    CV_THRESH_TOZERO_INV  =4,  /* value = value > threshold ? 0 : value           */

    CV_THRESH_MASK        =7,

    CV_THRESH_OTSU        =8  /* use Otsu algorithm to choose the optimal threshold value; combine the flag with one of the above CV_THRESH_* values */

};

注释已经写的很清楚了,因此不再用中文来表达了。

 




2018-12-23 13:18:06 lx_xin 阅读数 1980

基本概念

图像阈值分割是图像处理中最基本也是最常用的方法之一,主要用于将图像中的像素点划分为两个或者多个类别,从而得到便于处理的目标对象。

类别划分

按照阈值作用范围分:全局阈值分割,局部阈值分割;
按照阈值选取准则函数分:最大熵法,类间方差法,交叉熵法,最小误差法,模糊熵法;
按照阈值个数分:单阈值法和多阈值法;

2019-05-22 17:39:49 weixin_44403952 阅读数 898

全局阈值

全局阈值就是在整个图像中将灰度阈值设置成一个常数,全局阈值适合如下场景:图像背景的灰度值在整个图像中可合理的看作恒定,而且所有物体和背景都具有几乎相同的对比度,反应在直方图上就是 具有明显的双峰。在此种情况下,只要选择合适的阈值,使用固定的全局阈值往往会有比较好的效果。全局阈值选择的主要方法有:人工选择法、直方图选择法、迭代式阈值选择法、最大类间方差阈值(Otsu)。
在OpenCV中图像的二值化分割采用的是 cv2.threshold(src, thresh, maxval, type, dst=None)

  • 第一个参数src: 原图像
  • 第二个参数thresh: 分类的阈值
  • 第三个参数 maxval:高于阈值时选取的新的值
  • 第四个参数maxval: 就是 上述介绍的几个方法选择参数
    常用的有:• cv2.THRESH_BINARY(黑白二值)
    • cv2.THRESH_BINARY_INV(黑白二值反转)
    • cv2.THRESH_TRUNC (得到的图像为多像素值)
    • cv2.THRESH_TOZERO
    • cv2.THRESH_TOZERO_INV
  • 返回值(2个):第一个 retVal: 得到的阈值,第二个 就是阈值化后的图像
import cv2
import numpy as np
img = cv2.imread("sources/lena_1.tiff",0)
from matplotlib import pyplot as plt

ret , thresh = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV )
#ret , thresh = cv2.threshold(img,127,255,cv2.THRESH_OTSU )
cv2.imshow("1",thresh)
cv2.waitKey(0)

自适应阈值

在很多情况下,背景的灰度值并不是常数,物体和背景的对比度在图像中是变化的,此时一个图像中某个区域效果良好的阈值在其他区域可能会效果很差。在这种情况下,需要把灰度阈值取为一个随图像中位置缓慢变化的函数值,也就是自适应阈值。
自适应阈值主要的方法就是将图像进一步划分为子图像,并对不同的子图像使用不同的阈值进行分割。此方法的关键问题就是如何将图像进行细分和如何为得到的子图像估计阈值。
自适应阈值的经典算法是: 分水岭算法,详情参考博文:https://blog.csdn.net/dcrmg/article/details/52498440
OpenCV的自适应阈值函数为:
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)

  • 第一个参数src: 原图像
  • 第二个参数maxVal: 像素的最大值
  • 第三个参数 adaptiveMethod(有两种方法):
    cv2.ADAPTIVE_THRESH_MEAN_C:邻域内均值
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C:邻域内像素点加权和,权重为一个高斯窗口
    第四个参数thresholdType:有 cv2.THRESH_BINARY、cv2.THRESH_BINARY_INV两个
    第五个参数Block Size:规定的邻域大小
    第六个参数 C:设置阈值为 (均值 - C) 或者是 (加权值 - C)

返回值: 只有一个,就是二值化后的图像

import cv2
import numpy as np
img = cv2.imread("sources/lena_1.tiff",0)
from matplotlib import pyplot as plt

thresh = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,10)

cv2.imshow("1",thresh)
cv2.waitKey(0)

- 显示效果
------ 全局阈值
图1
-----局部阈值自适应调整
图2

  • 分析
    最后两个参数,是用来调整子图像大小(子图像边长只能为奇数) 以及 局部子图像阈值自适应调整。这两个参数用来改善二值化的图像的质量。当子图像的大小调整到原图像大小时,该方法退化为 全局阈值调整。