图像处理 面积阈值_图像处理阈值化的阈值确定 - CSDN
精华内容
参与话题
  • 图像处理面积滤波

    2020-07-30 23:30:21
    图像处理中的高速面积滤波,根据二值图中连通域的面积滤除选定阈值范围之外的连通区域。采用堆栈处理方式,类似于matlab中的bwareaopen函数,速度很快。
  • 图像处理——连通域的长宽比

    千次阅读 2018-02-23 21:25:11
    3.对二值图像进行形态学处理,主要任务是去除连通域面积较小的区域以及降低筛选难度。4.利用bwlabel()函数对连通区域进行标记5.得到连通域的长宽比matlab实现程序:clear all;close all;clc I= imr...

    在此用矩形连通域为例

    当获取车牌位置信息时,连通域的长宽比是一种非常有效的辅助定位方法。

    大致步骤:

    1.输入图像获取灰度图像。

    2.选取合适的阈值将灰度图像转化为二值图像。

    3.对二值图像进行形态学处理,主要任务是去除连通域面积较小的区域以及降低筛选难度。

    4.利用bwlabel()函数对连通区域进行标记

    5.得到连通域的长宽比


    matlab实现程序:

    clear all;close all;clc
    I= imread('F:\matlab\MATLAB上机操作\源代码\Fig0903(a)(utk).tif');
    I = rgb2gray(RGB);
    threshold = graythresh(I);
    bw = bwareaopen(bw,50);%去除连通域面积小于50的区域(连通域面积与对象的像素数目不一定相等)
    se = strel('disk',2);
    bw = imclose(bw,se);%闭运算
    bw = imfill(bw,'holes');%填充
    ed=edge(bw);
    L = bwlabel(bw);%标记连通域
    L1 = bwlabel(ed);
    p=zeros(1,max(L1(:)));
    for i=1:max(L(:))%得到连通域的长宽比
    p(i)=sum(ed(L==i));
    [y,x]=find(L==i);
    x0=min(x(:));
    x1=max(x(:));
    y0=min(y(:));
    y1=max(y(:));
    bl=(x1-x0)/(y1-y0);
    disp(bl)
    end


    图像填充imfill()

    bw2=imfill(bw)该函数对二值图像进行填充操作,对于二维图像允许通过鼠标选择填充的点。

    [bw2,locations]=imfill(bw)  locations包含交互式选择时的点坐标。

    bw2=imfill(bw,’holes’)通过参数holes可以填充二值图像的空洞。

    连通域标记bwlabel()

    L=bwlabel(bw,n)该函数先对二值图像的连通区域进行标记,参数n为联通类型,可取值48,默认为8,即为8连通,函数返回值为标记矩阵和原来的二值图像有相同的大小

    [l,num]=bwlabel(bw,n)  num为连通域的数目。

    Bwlabel()返回的标记矩阵可以通过函数label2rgb()进行显示。


    展开全文
  • OpenCV图像处理——阈值处理/二值化 5.1 简介 该部分的学习内容是对经典的阈值分割算法进行回顾,图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和...

    OpenCV图像处理——阈值处理/二值化

    5.1 简介

    阈值处理是指剔除图像内像素值高于阈值或者低于阈值得像素点。例如,设定阈值为127,将图像内所有像素值大于127的像素点的值设为255;将图像内所有像素值小于127的像素点的值设为0。

    图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰度级范围的图像。它不仅可以极大的压缩数据量,而且也大大简化了分析和处理步骤,因此在很多情况下,是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域不具有这种一致属性。这样的划分可以通过从灰度级出发选取一个或多个阈值来实现。

    5.2 学习目标

    • 了解阈值分割基本概念

    • 理解最大类间方差法(大津法)、自适应阈值分割的原理

    • 掌握OpenCV框架下上述阈值分割算法API的使用

    5.3 内容介绍

    1、阈值处理

    2、最大类间方差法的原理

    3、自适应阈值处理

    4、OpenCV代码实践

    5.4 算法理论介绍

    5.4.1 阈值处理

    threshold函数

    OpenCV使用threshold函数实现阈值化处理。

    double cv::threshold ( InputArray src,
                           OutputArray dst,
                           double thresh,
                           double maxval,
                           int type )  

    参数:

    • src — 原图像,8或32位浮点类型的Mat。
    • dst — 输出图像,与原始图像具有相同大小和类型。
    • thresh — 要设定的阈值
    • maxval — 当type为THRESH_BINARY或者THRESH_BINARY_INV时,设定的最大值
    • type — 阈值分割的类型
      • THRESH_BINARY;二值化阈值处理:灰度值大于阈值的点,将其灰度值设定为最大值,灰度值小于或等于阈值的点,将其灰度值设定为0
      • THRESH_BINARY_INV;反二值化阈值处理:灰度值大于阈值的点,将其灰度值设定为0,灰度值小于或等于阈值的点,将其灰度值设定为最大值
      • THRESH_TRUNC;截断阈值化处理:灰度值大于阈值的点,将其灰度值设定为阈值,灰度值小于或等于阈值的点,其灰度值保持不变
      • THRESH_TOZERO;低阈值零处理:灰度值大于阈值的点,其灰度值保持不变,灰度值小于或等于阈值的点,将其灰度值设定为0
      • THRESH_TOZERO_INV;高阈值零处理:灰度值大于阈值的点,将其灰度值设定为0,灰度值小于或等于阈值的点,其灰度值保持不变

    如下表:

    类型 定义
    THRESH_BINARY dst(x,y)={maxval,src(x,y)>thresh0,otherdst(x,y)=\left\{\begin{array}{rcl}&maxval,&{src(x,y)>thresh}\\&0,&{other}\end{array}\right.
    THRESH_BINARY_INV dst(x,y)={0,src(x,y)>threshmaxval,otherdst(x,y)=\left\{\begin{array}{rcl}&0,&{src(x,y)>thresh}\\&maxval,&{other}\end{array}\right.
    THRESH_TRUNC dst(x,y)={thresh,src(x,y)>threshsrc(x,y),otherdst(x,y)=\left\{\begin{array}{rcl}&thresh,&{src(x,y)>thresh}\\&src(x,y),&{other}\end{array}\right.
    THRESH_TOZERO_INV dst(x,y)={0,src(x,y)>threshsrc(x,y),otherdst(x,y)=\left\{\begin{array}{rcl}&0,&{src(x,y)>thresh}\\&src(x,y),&{other}\end{array}\right.
    THRESH_TOZERO dst(x,y)={src(x,y),src(x,y)>thresh0,otherdst(x,y)=\left\{\begin{array}{rcl}&src(x,y),&{src(x,y)>thresh}\\&0,&{other}\end{array}\right.

    5.4.2 OTSU(大津法)

    使用threshold进行阈值处理时,需要自定义一个阈值,并以此阈值作为图像阈值处理的依据 。通常情况下对于色彩均衡的图像,直接将阈值设为127即可,但有时图像灰度级的分布是不均衡的,如果此时还将阈值设为127,那么阈值处理的结果就是失败的。所以需要找出图像的最佳的分割阈值。OTSU就是获得最佳阈值的方法。

    OTSU(大津法)是一种确定图像二值化分割阈值的算法,由日本学者大津于1979年提出。从大津法的原理上来讲,该方法又称作最大类间方差法,因为按照大津法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大。

    它被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,因此在数字图像处理上得到了广泛的应用。它是按图像的灰度特性,将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

    OTSU 是求图像全局阈值的最佳方法,适用于大部分需要求图像全局阈值的场合。

    缺点: 对图像噪声敏感;只能针对单一目标分割;当图像中的目标与背景的面积相差很大时,表现为直方图没有明显的双峰,或者两个峰的大小相差很大,分割效果不佳,或者目标与背景的灰度有较大的重叠时也不能准确的将目标与背景分开。导致这种现象出现的原因是该方法忽略了图像的空间信息,同时该方法将图像的灰度分布作为分割图像的依据,因而对噪声也相当敏感。所以,在实际应用中,总是将其与其他方法结合起来使用。

    图像直方图

    在这里插入图片描述

    效果:

    在这里插入图片描述

    图像直方图

    在这里插入图片描述

    效果:

    在这里插入图片描述

    OTSU求阈值过程:

    假设图像的像素个数为M×N。
    假设存在阈值T将图像所有像素分为两类C1(像素值小于T)和C2(像素值大于T)。
    假设图片背景较暗,则C1类为背景,C2类为前景。
    像素被分为C1和C2类的概率分别为p1、p2。
    图像中属于C1类的像素个数记作N1,其平均灰度μ1μ1;属于C2类的的像素个数记作N2,其平均灰度为μ2μ2
    图像的总平均灰度记为μμ,类间方差记为σ2\sigma^2

    因此有如下关系式:
    p1=N1/(M×N)p2=N2/(M×N)N1+N2=M×Np1+p2=1μ=μ1p1+μ2p2σ2=p1(μ1μ)2+p2(μ2μ)2 p1=N1/(M×N)\\ p2=N2/(M×N)\\ N1+N2=M×N\\ p1+p2=1\\ \mu=\mu1*p1+\mu2*p2\\ \sigma^2=p1*(\mu1-\mu)^2+p2*(\mu2-\mu)^2

    μ=μ1p1+μ2p2\mu=\mu1*p1+\mu2*p2带入类间方差公式,化简,可以得到:
    σ2=p1p2(μ1μ2)2 \sigma^2=p1p2*(\mu1-\mu2)^2

    LL为灰度级数,nin_{i}为灰度级为ii的像素点数

    MN=n1+n2+...+nL pi=ni/(MN)i=0Lpi=1MN=n_{1}+n_{2}+...+n_{L}\\ \ \\ p_{i}=n_{i}/(MN)\\ \sum\limits_{i=0}^{L}p_{i}=1

    小于或等于灰度级K的累加均值μk\mu_{k}为:

    μk=i=0kipi=i=0kini/(MN)p1=i=0kpi=i=0kni/(MN)\mu_{k}=\sum\limits_{i=0}^{k}ip_{i}=\sum\limits_{i=0}^{k}in_{i}/(MN)\\ p1=\sum\limits_{i=0}^{k}p_{i}=\sum\limits_{i=0}^{k}n_{i}/(MN)

    所以,
    μ1=i=0kipii=0kini=μk/p1 μ2=(μμk)/p2\mu_{1}=\frac{\sum\limits_{i=0}^{k}ip_{i}}{\sum\limits_{i=0}^{k}in_{i}}=\mu_{k}/p1\\ \ \\ \mu_{2}=(\mu-\mu_{k})/p2

    类间方差公式可以化为:
    σ2=(p1μμk)2p1(1p1)\sigma^2=\frac{(p1\mu-\mu_{k})^2}{p1(1-p1)}

    求得使σ2\sigma^2最大的灰度级K,就是OTSU的阈值。
    OTSU方法会遍历所有灰度级,找到最佳阈值。

    5.4.3 自适应阈值处理

    前面介绍了OTSU算法,但这算法还属于全局阈值法,即整张图片只有一个阈值。所以对于某些光照不均的图像,这种方法无法得到清晰有效的阈值分割结果图像,如下图:

    在这里插入图片描述
    显然,这样的阈值处理结果不是我们想要的,所以需要使用变化的阈值对图像进行分割,这种技术称为自适应阈值处理方式。它的思想不是计算全局图像的阈值,而是根据图像不同区域亮度分布,计算其局部阈值,所以对于图像不同区域,能够自适应计算不同的阈值,因此被称为自适应阈值法。

    确定局部阈值的方法:计算每个像素点周围临近区域的加权平均值获得阈值,并使用该阈值对该像素点进行处理。

    adaptiveThreshold函数

    OpenCV提供了adaptiveThreshold函数实现自适应阈值处理。

    void adaptiveThreshold(InputArray src, OutputArray dst, 
                           double maxValue,
                           int adaptiveMethod,
                           int thresholdType, 
                           int blockSize, double C)

    参数:

    • src — 原图像,8或32位浮点类型的Mat。必须为单通道灰度图像。
    • dst — 输出图像,与原始图像具有相同大小和类型。
    • maxValue — 像素最大值
    • adaptiveMethod — 自适应方法,只有两种:THRESH_BINARY 和THRESH_BINARY_INV。参考:cv::AdaptiveThresholdTypes
    • thresholdType — 阈值计算方式,有两种:
      • ADAPTIVE_THRESH_MEAN_C:计算方法是计算出领域内的像素平均值再减去C的值
      • ADAPTIVE_THRESH_GAUSSIAN_C:计算方法是计算出领域内像素的高斯均值再减去C的值
    • blockSize — 表示一个像素在计算阈值时使用的邻域尺寸,通常为3、5、7。
    • C — 常数,用均值或高斯计算阈值后,再减去C就是最终阈值。

    5.5 基于OpenCV的实现

    5.5.1 c++实现

    1、阈值处理

    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace cv;
    using namespace std;
    int main()
    {
     	//载入图像
     	Mat img = imread("D:\\yt\\picture\\threshold\\s.jpg");
     	if (img.empty())
     	{
      		cout << "Error: Could not load image" << endl;
      		return 0;
     	}
     	Mat gray;
     	cvtColor(img, gray, COLOR_BGR2GRAY);//先转为灰度图
     	Mat dst1,dst2,dst3,dst4,dst5;
     	threshold(gray, dst1, 127, 255, THRESH_BINARY);//二值化阈值处理
     	threshold(gray, dst2, 127, 255, THRESH_BINARY_INV);//反二值化阈值处理
     	threshold(gray, dst3, 127, 255, THRESH_TRUNC);//截断阈值化处理
     	threshold(gray, dst4, 127, 255, THRESH_TOZERO_INV);//超阈值零处理
     	threshold(gray, dst5, 127, 255, THRESH_TOZERO);//低阈值零处理
    	//显示图像
     	imshow("gray_image", gray);
     	imshow("THRESH_BINARY", dst1);
     	imshow("THRESH_BINARY_INV", dst2);
     	imshow("THRESH_TRUNC", dst3);
     	imshow("THRESH_TOZERO_INV", dst4);
     	imshow("THRESH_TOZERO", dst5);
     	waitKey(0);
     	return 0;
    }

    效果

    二值化阈值处理

    在这里插入图片描述

    反二值化阈值处理

    在这里插入图片描述

    截断阈值化处理

    在这里插入图片描述

    超阈值零处理

    在这里插入图片描述

    低阈值零处理

    在这里插入图片描述

    2、OTSU处理

    在OpenCV中,设定参数type为“THRESH_OTSU”即可实现OTSU方式的阈值分割。且设定阈值thresh为0。

    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace cv;
    using namespace std;
    int main()
    {
     	//载入图像
     	Mat img = imread("D:\\yt\\picture\\threshold\\s.jpg");
     	if (img.empty())
     	{
      		cout << "Error: Could not load image" << endl;
      		return 0;
     	}
     	Mat gray;
     	cvtColor(img, gray, COLOR_BGR2GRAY);//先转为灰度图
     	Mat dst;
     	threshold(gray, dst, 0, 255, THRESH_OTSU);//OTSU
     	
     	//显示图像
    	imshow("gray_image", gray);
     	imshow("THRESH_OTSU", dst);
     	waitKey(0);
     	return 0;
    }
    

    效果

    在这里插入图片描述

    3、自适应阈值处理

    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace cv;
    using namespace std;
    int main()
    {
     	//载入图像
     	Mat img = imread("D:\\yt\\picture\\threshold\\1.jpg");
     	if (img.empty())
     	{
      		cout << "Error: Could not load image" << endl;
      	return 0;
     	}
     	Mat gray;
     	cvtColor(img, gray, COLOR_BGR2GRAY);//先转为灰度图
     	Mat dst;
     	adaptiveThreshold(gray, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 7, 10);
     	//创建窗口,WINDOW_NORMAL使窗口可以自由调节大小
     	namedWindow("gray_image",WINDOW_NORMAL);
     	namedWindow("adaptiveThreshold", WINDOW_NORMAL);
     	//显示图像
     	imshow("gray_image", gray);
     	imshow("adaptiveThreshold", dst);
     	
     	waitKey(0);
     	return 0;
    }

    效果

    在这里插入图片描述

    进阶实现(根据原理自己实现)

    实现示例(c++)

    1、OTSU处理

    #include <iostream>
    #include <opencv2/core.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
     
    int Otsu(cv::Mat& src, cv::Mat& dst, int thresh){
    	const int Grayscale = 256;
    	int graynum[Grayscale] = { 0 };
    	int r = src.rows;
    	int c = src.cols;
    	for (int i = 0; i < r; ++i){
    		const uchar* ptr = src.ptr<uchar>(i);
    		for (int j = 0; j < c; ++j){        //直方图统计
    			graynum[ptr[j]]++;
    		}
    	}
     
        double P[Grayscale] = { 0 };   
    	double PK[Grayscale] = { 0 };
    	double MK[Grayscale] = { 0 };
    	double srcpixnum = r*c, sumtmpPK = 0, sumtmpMK = 0;
    	for (int i = 0; i < Grayscale; ++i){
    		P[i] = graynum[i] / srcpixnum;   //每个灰度级出现的概率
    		PK[i] = sumtmpPK + P[i];         //概率累计和 
    		sumtmpPK = PK[i];
    		MK[i] = sumtmpMK + i*P[i];       //灰度级的累加均值                                                                                                                                                                                                                                                                                                                                                                                                        
    		sumtmpMK = MK[i];
    	}
    	
    	//计算类间方差
    	double Var=0;
    	for (int k = 0; k < Grayscale; ++k){
    		if ((MK[Grayscale-1] * PK[k] - MK[k])*(MK[Grayscale-1] * PK[k] - MK[k]) / (PK[k] * (1 - PK[k])) > Var){
    			Var = (MK[Grayscale-1] * PK[k] - MK[k])*(MK[Grayscale-1] * PK[k] - MK[k]) / (PK[k] * (1 - PK[k]));
    			thresh = k;
    		}
    	}
     
    	//阈值处理
    	src.copyTo(dst);
    	for (int i = 0; i < r; ++i){
    	    uchar* ptr = dst.ptr<uchar>(i);
    		for (int j = 0; j < c; ++j){
    			if (ptr[j]> thresh)
    				ptr[j] = 255;
    			else
    				ptr[j] = 0;
    		}
    	}
    	return thresh;
    }
     
     
    int main(){
    	cv::Mat src = cv::imread("D:\\yt\\picture\\threshold\\1.jpg");
    	if (src.empty()){
    		return -1;
    	}
    	if (src.channels() > 1)
    		cv::cvtColor(src, src, CV_RGB2GRAY);
     
    	cv::Mat dst;
    	int thresh=0;
    	double t2 = (double)cv::getTickCount();
    	thresh=Otsu(src , dst, thresh); //Otsu
     
    	cv::namedWindow("src", CV_WINDOW_NORMAL);
    	cv::imshow("src", src);
    	cv::namedWindow("dst", CV_WINDOW_NORMAL);
    	cv::imshow("dst", dst);	
    	cv::waitKey(0);
    }

    2、自适应阈值处理

    #include <iostream>
    #include <opencv2/core.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
     
    enum adaptiveMethod{meanFilter,gaaussianFilter,medianFilter};
     
    void AdaptiveThreshold(cv::Mat& src, cv::Mat& dst, double Maxval, int Subsize, double c, adaptiveMethod method = meanFilter){
     
    	if (src.channels() > 1)
    		cv::cvtColor(src, src, CV_RGB2GRAY);
     
    	cv::Mat smooth;
    	switch (method)
    	{
    	case  meanFilter:
    		cv::blur(src, smooth, cv::Size(Subsize, Subsize));  //均值滤波
    		break;
    	case gaaussianFilter:
    		cv::GaussianBlur(src, smooth, cv::Size(Subsize, Subsize),0,0); //高斯滤波
    		break;
    	case medianFilter:
    		cv::medianBlur(src, smooth, Subsize);   //中值滤波
    		break;
    	default:
    		break;
    	}
     
    	smooth = smooth - c;
    	
    	//阈值处理
    	src.copyTo(dst);
    	for (int r = 0; r < src.rows;++r){
    		const uchar* srcptr = src.ptr<uchar>(r);
    		const uchar* smoothptr = smooth.ptr<uchar>(r);
    		uchar* dstptr = dst.ptr<uchar>(r);
    		for (int c = 0; c < src.cols; ++c){
    			if (srcptr[c]>smoothptr[c]){
    				dstptr[c] = Maxval;
    			}
    			else
    				dstptr[c] = 0;
    		}
    	}
     
    }
     
    int main(){
    	cv::Mat src = cv::imread("D:\\yt\\picture\\threshold\\1.jpg");
    	if (src.empty()){
    		return -1;
    	}
    	if (src.channels() > 1)
    		cv::cvtColor(src, src, CV_RGB2GRAY);
     
    	cv::Mat dst;
    	AdaptiveThreshold(src, dst, 255, 21, 10, meanFilter); 
    
     	cv::namedWindow("src", CV_WINDOW_NORMAL);
    	cv::imshow("src", src);
    	cv::namedWindow("dst", CV_WINDOW_NORMAL);
    	cv::imshow("dst", dst);
    	cv::waitKey(0);
    }

    5.5.2 python实现

    与c++不同,python中函数cv2.threshold的返回值有两个

    	retval,dst = cv2.threshold(src,thresh,maxval,type)
    • retval — 返回的阈值
    • dst — 阈值处理的输出图像

    二值化阈值处理

    import cv2
    if __name__ == "__main__":
        img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
        t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
        
        # 显示图像
        cv2.imshow("origin image", img)
        cv2.imshow("THRESH_BINARY", dst1)
    
        # 保存图像
        cv2.imwrite("D:/yt/picture/threshold/THRESH_BINARY.bmp", dst1)
        
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    效果

    在这里插入图片描述

    反二值化阈值处理

    import cv2
    if __name__ == "__main__":
        img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
        t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
        
        # 显示图像
        cv2.imshow("origin image", img)
        cv2.imshow("THRESH_BINARY_INV", dst1)
        
        # 保存图像
        cv2.imwrite("D:/yt/picture/threshold/THRESH_BINARY_INV.bmp", dst1)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    效果

    在这里插入图片描述

    截断阈值化处理

    import cv2
    if __name__ == "__main__":
        img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
        t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
    
        # 显示图像
        cv2.imshow("origin image", img)
        cv2.imshow("THRESH_TRUNC", dst1)
    
        # 保存图像
        cv2.imwrite("D:/yt/picture/threshold/THRESH_TRUNC.bmp", dst1)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    效果

    在这里插入图片描述

    超阈值零处理

    import cv2
    if __name__ == "__main__":
        img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
        t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
    
        # 显示图像
        cv2.imshow("origin image", img)
        cv2.imshow("THRESH_TOZERO_INV", dst1)
    
        # 保存图像
        cv2.imwrite("D:/yt/picture/threshold/THRESH_TOZERO_INV.bmp", dst1)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    效果

    在这里插入图片描述

    低阈值零处理

    import cv2
    if __name__ == "__main__":
        img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
        t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
    
        # 显示图像
        cv2.imshow("origin image", img)
        cv2.imshow("THRESH_TOZERO", dst1)
    
        # 保存图像
        cv2.imwrite("D:/yt/picture/threshold/THRESH_TOZERO.bmp", dst1)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    效果

    在这里插入图片描述

    OTSU处理

    在OpenCV中,给参数type多传递一个参数“THRESH_OTSU”即可实现OTSU方式的阈值分割。且设定阈值thresh为0。

    import cv2
    import numpy as np
    if __name__ == "__main__":
        img = cv2.imread('D:/yt/picture/threshold/tiffany.bmp')
        
        img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#原图像不是灰度图,必须先转换为灰度图
        #普通二值化阈值处理
        t1, dst1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
        #采用OTSU的处理
        t2, dst2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    
        # 创建窗口
        cv2.namedWindow("origin image",cv2.WINDOW_NORMAL)#cv2.WINDOW_NORMAL使窗口大小可调整
        cv2.namedWindow("THRESH_TOZERO",cv2.WINDOW_NORMAL)
        cv2.namedWindow("THRESH_OTSU",cv2.WINDOW_NORMAL)
        # 显示图像
        cv2.imshow("origin image", img)
        cv2.imshow("THRESH_TOZERO", dst1)
        cv2.imshow("THRESH_OTSU",dst2)
    
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    效果:

    在这里插入图片描述

    自适应阈值处理

    import cv2
    import numpy as np
    if __name__ == "__main__":
        img = cv2.imread('D:/yt/picture/threshold/computer.jpg')
        img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#原图像不是灰度图,必须先转换为灰度图
        #普通二值化阈值处理
        t1, dst1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
        #自适应阈值处理,采用均值计算阈值
        dst2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,5,3)
        #自适应阈值处理,采用高斯均值计算阈值
        dst3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,5,3)
    
        # 创建窗口
        cv2.namedWindow("origin image",cv2.WINDOW_NORMAL)#cv2.WINDOW_NORMAL使窗口大小可调整
        cv2.namedWindow("THRESH_BINARY",cv2.WINDOW_NORMAL)
        cv2.namedWindow("MEAN_C",cv2.WINDOW_NORMAL)
        cv2.namedWindow("GAUSSIAN_C", cv2.WINDOW_NORMAL)
        # 显示图像
        cv2.imshow("origin image", img)
        cv2.imshow("THRESH_BINARY", dst1)
        cv2.imshow("MEAN_C",dst2)
        cv2.imshow("GAUSSIAN_C", dst3)
    
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    效果

    在这里插入图片描述


    By: 胖虎

    博客:https://blog.csdn.net/qq_44957388

    关于Datawhale

    Datawhale是一个专注于数据科学与AI领域的开源组织,汇集了众多领域院校和知名企业的优秀学习者,聚合了一群有开源精神和探索精神的团队成员。Datawhale以“for the learner,和学习者一起成长”为愿景,鼓励真实地展现自我、开放包容、互信互助、敢于试错和勇于担当。同时Datawhale 用开源的理念去探索开源内容、开源学习和开源方案,赋能人才培养,助力人才成长,建立起人与人,人与知识,人与企业和人与未来的联结。

    展开全文
  • 图像处理时,受外界光线的干扰一般比较大,假如在阈值分割时采用固 定阈值,那么在环境改变时分割效果受影响极大,那么为了避免此影响就 必须采用动态阈值,自动求出合适的阈值进行分割。 本文的介绍几...

    在图像处理时,受外界光线的干扰一般比较大,假如在阈值分割时采用固


    定阈值,那么在环境改变时分割效果受影响极大,那么为了避免此影响就


    必须采用动态阈值,自动求出合适的阈值进行分割。
    本文的介绍几种主要的图像分割方法,并给出自动阈值分割的源代码




    图像分割是图像处理与计算机视觉领域低层次视觉中最为基础和重要的领域之一,它是对图像进行视觉分析和模式识别的基本前提.阈值法是一种传统的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术.已被应用于很多的领域。本文是在阅读大量国内外相关文献的基础上,对阈值分割技术稍做总结,分三个大类综述阈值选取方法,然后对阈值化算法的评估做简要介绍。


    1.引言
    所谓图像分割是指根据灰度、彩色、空间纹理、几何形状等特征把图像划分成若干个互不相交的区域,使得这些特征在同一区域内,表现出一致性或相似性,而在不同区域间表现出明显的不同[37].简单的讲,就是在一幅图像中,把目标从背景中分离出来,以便于进一步处理。图像分割是图像处理与计算机视觉领域低层次视觉中最为基础和重要的领域之一,它是对图像进行视觉分析和模式识别的基本前提.同时它也是一个经典难题,到目前为止既不存在一种通用的图像分割方法,也不存在一种判断是否分割成功的客观标准。
    阈值法是一种传统的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术.已被应用于很多的领域,例如,在红外技术应用中,红外无损检测中红外热图像的分割,红外成像跟踪系统中目标的分割;在遥感应用中,合成孔径雷达图像中目标的分割等;在医学应用中,血液细胞图像的分割,磁共振图像的分割;在农业工程应用中,水果品质无损检测过程中水果图像与背景的分割。在工业生产中,机器视觉运用于产品质量检测等等。在这些应用中,分割是对图像进一步分析、识别的前提,分割的准确性将直接影响后续任务的有效性,其中阈值的选取是图像阈值分割方法中的关键技术。


    2.阈值分割的基本概念
    图像阈值化分割是一种最常用,同时也是最简单的图像分割方法,它特别适用于目标和背景占据不同灰度级范围的图像[1]。它不仅可以极大的压缩数据量,而且也大大简化了分析和处理步骤,因此在很多情况下,是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域布局有这种一致属性。这样的划分可以通过从灰度级出发选取一个或多个阈值来实现。
    阈值分割法是一种基于区域的图像分割技术,其基本原理是:通过设定不同的特征阈值,把图像像素点分为若干类.常用的特征包括:直接来自原始图像的灰度或彩色特征;由原始灰度或彩色值变换得到的特征.设原始图像为f(x,y),按照一定的准则在f(x,y)中找到特征值T,将图像分割为两个部分,分割后的图像为


     
    若取 :b0=0(黑),b1=1(白),即为我们通常所说的图像二值化。


       
              (原始图像)    (阈值分割后的二值化图像)


    一般意义下,阈值运算可以看作是对图像中某点的灰度、该点的某种局部特性以及该点在图像中的位置的一种函数,这种阈值函数可记作
        T(x,y,N(x,y),f(x,y))
    式中,f(x,y)是点(x,y)的灰度值;N(x,y)是点(x,y)的局部邻域特性.根据对T的不同约束,可以得到3种不同类型的阈值[37],即
        点相关的全局阈值T=T(f(x,y))  
    (只与点的灰度值有关)
    区域相关的全局阈值T=T(N(x,y),f(x,y))  
    (与点的灰度值和该点的局部邻域特征有关)
        局部阈值或动态阈值T=T(x,y,N(x,y),f(x,y))
    (与点的位置、该点的灰度值和该点邻域特征有关)


    图像阈值化这个看似简单的问题,在过去的四十年里受到国内外学者的广泛关注,产生了数以百计的阈值选取方法[2-9],但是遗憾的是,如同其他图像分割算法一样,没有一个现有方法对各种各样的图像都能得到令人满意的结果,甚至也没有一个理论指导我们选择特定方法处理特定图像。
    所有这些阈值化方法,根据使用的是图像的局部信息还是整体信息,可以分为上下文无关(non-contextual)方法(也叫做基于点(point-dependent)的方法)和上下文相关(contextual)方法(也叫做基于区域(region-dependent)的方法);根据对全图使用统一阈值还是对不同区域使用不同阈值,可以分为全局阈值方法(global thresholding)和局部阈值方法(local thresholding,也叫做自适应阈值方法adaptive thresholding);另外,还可以分为双阈值方法(bilever thresholding)和多阈值方法(multithresholding)
    本文分三大类对阈值选取技术进行综述:
    1) 基于点的全局阈值方法;
    2) 基于区域的全局阈值方法
    3) 局部阈值方法和多阈值方法




    3.基于点的全局阈值选取方法
    3.1  p-分位数法
    1962年Doyle[10]提出的p-分位数法(也称p-tile法)可以说是最古老的一种阈值选取方法。该方法使目标或背景的像素比例等于其先验概率来设定阈值,简单高效,但是对于先验概率难于估计的图像却无能为力。
    例如,根据先验知识,知道图像目标与背景象素的比例为PO/PB,则可根据此条件直接在图像直方图上找到合适的阈值T,使得f(x,y)>=T的象素为目标,f(x,y)<T的象素为背景。


    3.2  迭代方法选取阈值[11]
    初始阈值选取为图像的平均灰度T0,然后用T0将图像的象素点分作两部分,计算两部分各自的平均灰度,小于T0的部分为TA,大于T0的部分为TB
    计算   ,将T1 作为新的全局阈值代替T0,重复以上过程,如此迭代,直至TK 收敛,即TK+1 =TK 
    经试验比较,对于直方图双峰明显,谷底较深的图像,迭代方法可以较快地获得满意结果。但是对于直方图双峰不明显,或图像目标和背景比例差异悬殊,迭代法所选取的阈值不如最大类间方差法。


    3.3  直方图凹面分析法
    从直观上说,图像直方图双峰之间的谷底,应该是比较合理的图像分割阈值,但是实际的直方图是离散的,往往十分粗糙、参差不齐,特别是当有噪声干扰时,有可能形成多个谷底。从而难以用既定的算法,实现对不同类型图像直方图谷底的搜索。
    Rosenfeld和Torre[12]提出可以构造一个包含直方图 的最小凸多边形 ,由集差 确定 的凹面。若 和 分别表示 与 在灰度级之处的高度,则 取局部极大值时所对应的灰度级可以作为阈值。也有人使用低通滤波的方法平滑直方图,但是滤波尺度的选择并不容易[13]。
    但此方法仍然容易受到噪声干扰,对不同类型的图像,表现出不同的分割效果。往往容易得到假的谷底。但此方法对某些只有单峰直方图的图像,也可以作出分割。如:
     


    3.4 最大类间方差法
    由Otsu[14]于1978年提出的最大类间方差法以其计算简单、稳定有效,一直广为使用。从模式识别的角度看,最佳阈值应当产生最佳的目标类与北京类的分离性能,此性能我们用类别方差来表征,为此引入类内方差 、类间方差 和总体方差 ,并定义三个等效的准则测量:
     ,  ,  .                 (3)
    鉴于计算量的考量,人们一般通过优化第三个准则获取阈值。此方法也有其缺陷,kittler和Illingworth[15]的实验揭示:当图像中目标与背景的大小之比很小时方法失效。
     在实际运用中,往往使用以下简化计算公式:
       (T) = WA(μa-μ)2  + Wb(μb-μ)2 
    其中, 为两类间最大方差,WA 为A类概率,μa为A类平均灰度,Wb 为B类概率,μb为B类平均灰度,μ为图像总体平均灰度。
    即阈值T将图像分成A,B两部分,使得两类总方差 (T)取最大值的T,即为最佳分割阈值。


    3.5 熵方法
    八十年代以来,许多学者将Shannon信息熵的概念应用于图像阈值化,其基本思想都是利用图像的灰度分布密度函数定义图像的信息熵,根据假设的不同或视角的不同提出不同的熵准则,最后通过优化该准则得到阈值。Pun[16]通过使后验熵的上限最大来确定阈值。Kapur等人[17]的方法假定目标和背景服从两个不同的概率分布 和 定义
                     (4)
    使得熵
                               (5)
    达到最大求得最佳阈值。
    此方法又称为KSW熵方法。


    3.6 最小误差阈值
    此方法来源于Bayes最小误差分类方法。
     
    Eb(T)是目标类错分到背景类的概率,Eo(T)是背景类错分到目标类的概率
    总的误差概率 E(T) = Eb(T) + Eo(T)
    使E(T)取最小值,即为最优分类方法。


    在Kittler和Illingworth[18]于1986年提出的最小误差法中,直方图被视为目标与背景混合集概率密度函数 的估计
                     (9)
    其中, 为先验概率, ,求解下列方程可得到Bayes最小误差阈值
                            (10)
    遗憾的是上式中 , 和 通常是未知的,Nakagawa和Rosenfeld[19]提倡用拟合方法从直方图中估计这些参数,但是算法相当复杂,不易实现。




    3.7 矩量保持法
    矩量保持(moment-preserving)法[20] ,即矩守恒阈值法,是1985年提出的,其基本思想是最佳的阈值应该使分割前后图像的矩量保持不变,由此可以得到一组矩量保持方程,求解该方程组就可以得到最佳阈值。


    3.8 模糊集方法
    模糊集理论较好的描述了人类视觉中的模糊性和随机性,因此在图像阈值化领域受到了广泛的关注。模糊集阈值化方法的基本思想是,选择一种S状的隶属度函数定义模糊集,隶属度为0.5的灰度级对应了阈值,当然在上述隶属度函数的表达式中阈值是一个未知的参数;然后在此模糊集上定义某种准则函数(例如整个图像的总体模糊度),通过优化准则函数来确定最佳阈值。
    Pal等[21]首先,他们把一幅具有 个灰度级的 图像看作一个模糊集 ,其中隶属函数 定义如下:
                   (11)
    参数 称之为交叉点(即 )。由此从图像 的空间 平面得到模糊特性 平面。然后,基于此模糊集定义了图像的线性模糊度 、二次模糊度 和模糊熵 ,使这三个量取最小值时的交叉点 即为最佳阈值。
    文献[21]指出模糊隶属度函数在该算法中的作用仅在于将图像由灰度数据空间转换为模糊空间 ,其函数的形式对增强结果几乎没有影响。这就使我们有理由使用一些形式简单的函数形式。例如国内学者发表的一种模糊阈值方法[22]:
     
    隶属度μ(x)表示灰度x具有明亮特性的程度,c为隶属函数窗宽,q对应隶属度为0.5的灰度级。设灰度级 的模糊率为:
      = min{μ(l),1-μ(l)}
    则得到整幅图像的模糊率[44] 
     
    其中,MN为图像尺寸,L为图像总灰度级, 图像中灰度为 的象素个数。
    对应于不同的q值,就可以计算出相应的图像模糊率,选取使得 最小的q值,作为图像分割的最佳阈值即可。


    3.9 小结
    对于基于点的全局阈值选取方法,除上述主要几种之外还许多,但大多都是以上述基本方法为基础,做出的改进方法或者对算法的优化,如使用递推方法以降低算法复杂性。
    例如在文献[42]中,提出一种使目标和背景差距最大的阈值求取方法,类似于最大类间方差阈值法。是它的一种简化算法。
    又如1984年Dunn等人[23]提出了均匀化误差阈值选取方法,这种方法实质上是要使将背景点误分为目标点的概率等于将目标点误分为背景点的概率。类似于最小误差阈值法。
    近年来有一些新的研究手段被引入到阈值选取中。比如人工智能,在文献[24] 中,描述了如何用人工智能的方法,寻找直方图的谷底点,作为全局阈值分割。其它如神经网络,数学形态学[39][46],小波分析与变换[40]等等。
    总的来说,基于点的全局阈值算法,与其它几大类方法相比,算法时间复杂度较低,易于实现,适合应用于在线实时图像处理系统。由于我的研究方向为机器视觉,所作的项目要求算法具有良好的实时性,因此针对基于点的全局阈值方法,阅读了较多的文献,在综述里叙述也相对比较详细。


    4 基于区域的全局阈值选取方法
    对一幅图像而言,不同的区域,比如说目标区域或背景区域,同一区域内的象素,在位置和灰度级上同时具有较强的一致性和相关性。
    而在上述基于点的全局阈值选取方法中,有一个共同的弊病,那就是它们实际上只考虑了直方图提供的灰度级信息,而忽略了图像的空间位置细节,其结果就是它们对于最佳阈值并不是反映在直方图的谷点的情况会束手无策,不幸我们通常遇到的很多图像恰恰是这种情况。另一方面,完全不同的两幅图片却可以有相同的直方图,所以即使对于峰谷明显的情况,这些方法也不能保证你得到合理的阈值。于是,人们又提出了很多基于空间信息的阈值化方法。
    可以说,局域区域的全局阈值选取方法,是基于点的方法,再加上考虑点领域内象素相关性质组合而成,所以某些方法常称为“二维xxx方法”。由于考虑了象素领域的相关性质,因此对噪声有一定抑止作用[41]。
    4.1 二维熵阈值分割方法[25]
    使用灰度级-局域平均灰度级形成的二维灰度直方图[43]进行阈值选取,这样就得到二维熵阈值化方法。
     
    (二维灰度直方图: 灰度-领域平均灰度)
    如图,在0区和1区,象素的灰度值与领域平均灰度值接近,说明一致性和相关性较强,应该大致属于目标或背景区域;2区和3区一致性和相关性较弱,可以理解为噪声或边界部分。二维熵阈值分割,就是选择(S,T)对,使得目标类和背景类的后验熵最大。(具体方法是一维熵阈值分割的推广,可参见上一节)
    Abutaleb[26],和Pal]结合Kapur]和Kirby的方法,分别提出了各自的二维熵阈值化方法,其准则函数都是使目标熵和背景熵之和最大化。Brink[27]的方法则是使这两者中的较小者最大化,该方法的计算复杂度为 ,后来有人改进为递推快速算法将时间复杂度降为 (其中 为最大灰度级数)。 


    4.2  简单统计法
    Kittler等人[28],[29]提出一种基于简单的图像统计的阈值选取方法。使用这种方法,阈值可以直接计算得到,从而避免了分析灰度直方图,也不涉及准则函数的优化。该方法的计算公式为
                             (19)
    其中, 
        
        
    因为e(x,y)表征了点(x,y)领域的性质,因此本方法也属于基于区域的全局阈值法。


    4.3  直方图变化法
    从理论上说,直方图的谷底是非常理想的分割阈值,然后在实际应用中,图像常常受到噪声等的影响而使其直方图上原本分离的峰之间的谷底被填充,或者目标和背景的峰相距很近或者大小差不多,要检测他们的谷底就很难了。
    在上一节基于点的全局阈值方法中,我们知道直方图凹面分析法的弊病是容易受到噪声干扰,对不同类型的图像,表现出不同的分割效果。往往容易得到假的谷底。这是由于原始的直方图是离散的,而且含噪声,没有考虑利用象素领域性质。
    而直方图变化法,就是利用一些象素领域的局部性质变换原始的直方图为一个新的直方图。这个新的直方图与原始直方图相比,或者峰之间的谷底更深,或者谷转变成峰从而更易于检测。这里的象素领域局部性质,在很多方法中经常用的是象素的梯度值。
     例如,由于目标区的象素具有一定的一致性和相关性,因此梯度值应该较小,背景区也类似。而边界区域或者噪声,就具有较大的梯度值。最简单的直方图变换方法,就是根据梯度值加权,梯度值小的象素权加大,梯度值大的象素权减小。这样,就可以使直方图的双峰更加突起,谷底更加凹陷。


    4.4 其它基于区域的全局阈值法
    松弛法利用邻域约束条件迭代改进线性方程系统的收敛特性,当用于图像阈值化时其思想是:首先根据灰度级按概率将像素分为“亮”和“暗”两类,然后按照领域像素的概率调整每个像素的概率,调整过程迭代进行,使得属于亮(暗)区域的像素“亮(暗)”的概率变得更大。
    其它还有许多方法利用灰度值和梯度值散射图,或者利用灰度值和平均灰度值散射图。


    5 局部阈值法和多阈值法


    5.1 局部阈值(动态阈值)
    当图像中有如下一些情况:有阴影,照度不均匀,各处的对比度不同,突发噪声,背景灰度变化等,如果只用一个固定的全局阈值对整幅图像进行分割,则由于不能兼顾图像各处的情况而使分割效果受到影响。有一种解决办法就是用与象素位置相关的一组阈值(即阈值使坐标的函数)来对图像各部分分别进行分割。这种与坐标相关的阈值也叫动态阈值,此方法也叫变化阈值法,或自适应阈值法。这类算法的时间复杂性可空间复杂性比较大,但是抗噪能力强,对一些用全局阈值不易分割的图像有较好的效果。
    例如,一幅照度不均(左边亮右边暗)的原始图像为:

    如果只选择一个全局阈值进行分割,那么将出现下面两种情况,都不能得到满意的效果。


                    
    (阈值低,对亮区效果好,则暗区差)          (阈值高,对暗区效果好,则亮区差)


    若使用局部阈值,则可分别在亮区和暗区选择不同的阈值,使得整体分割效果较为理性。


     
    (按两个区域取局部阈值的分割结果)
    进一步,若每个数字都用不同的局部阈值,则可达到更理想的分割效果。

    /************************************************************************/
    /* 全局阈值分割  自动求取阈值        */
    /************************************************************************/
    //自动求取阈值,增加对场景的适应性
    //只需求取一次,之后就可以一直使用
    #include<cv.h>
    #include <highgui.h>
    #include <iostream>
    #include <math.h>
    using namespace std;
    int main(){
    	IplImage * image,* image2;
    	image = cvLoadImage("E:\\image\\dowels.tif",0);
    	cvNamedWindow("image",1);
    	cvShowImage("image",image);
    	image2 = cvCreateImage(cvSize(image->width,image->height),image->depth,1);
    	double T = 0;
    	double dT0 = 1.0;//阈值求取结束标志
    	double dT = 255.0;
    
    	//求取平均灰度,作为阈值T的初始值T0
    	  int i, j;
         double T0 = 0,T1 = 0,T2 = 0;//初始阈值
    	 int count1,count2;
    	 unsigned char * ptr,*dst;
    	 for (i = 0 ; i< image->height ; i++)
    	 {
    		 for (j =0 ; j < image->width;j++)
    		 {
    			 ptr = (unsigned char *)image->imageData + i*image->widthStep + j;
    			 T0 += ((double)(*ptr))/image->width/image->height;
    		 }
    	 }
    	 cout<<"T0:     "<<T0<<endl;
    	 T = (int)(T0 + 0.5); 
    	 //计算T两侧的灰度平均值,然后把二者的均值赋值给T
    	 while (dT > dT0)
    	 {
    
    		 T1 = 0;
    		 T2 = 0;
    		 count1 = 0;
    		 count2 = 0;
    		 for (i = 0 ; i< image->height ; i++)
    		 {
    			 for (j =0 ; j < image->width;j++)
    			 {
    				 ptr = (unsigned char *)image->imageData + i*image->widthStep + j;
    				if (*ptr > T)
    				{
    					T1 += ((double)(*ptr))/image->width/image->height;
    					count1++;
    				} 
    				else if(*ptr < T)
    				{
    					T2 +=  ((double)(*ptr))/image->width/image->height;
    					count2++;
    				}
    			 }
    		 }
    
    		 T1 = T1*image->width*image->height/count1;
    		 T2 = T2*image->width*image->height/count2;
    		 dT = fabs(T - (T1 + T2)/2);
    		  
    		 cout<<"T1"<<T1<<endl;
    		 cout<<"T2"<<T2<<endl;
    		 cout<<"dT  " << dT<<endl;
    		 T = (T1 + T2)/2;
    		 cout<<"T:     "<<T<<endl;
    	 }
    	 
    
    
    	 //根据求取的阈值进行分割
    	 for (i = 0 ; i< image2->height ; i++)
    	 {
    		 for (j =0 ; j < image2->width;j++)
    		 {
    			 ptr = (unsigned char *)image->imageData + i*image->widthStep + j;
    			  dst = (unsigned char *)image2->imageData+i*image2->widthStep+j;
    			if (*ptr > T)
    			{
    				*dst = 255;
    			} 
    			else
    			{
    				*dst =0;
    			}
    		 }
    	 }
    
    	 cvNamedWindow("image2",1);
    	 cvShowImage("image2",image2);
    	 cvSaveImage("E:\\image\\dowels2.tif",image2);
    	 cvWaitKey(0);
    	 return 0;
    }




    5.1.1 阈值插值法
     首先将图像分解成系列子图,由于子图相对原图很小,因此受阴影或对比度空间变化等带来的问题的影响会比较小。然后对每个子图计算一个局部阈值(此时的阈值可用任何一种固定阈值选取方法)。通过对这些子图所得到的阈值进行插值,就可以得到对原图中每个象素进行分割所需要的合理阈值。这里对应每个象素的阈值合起来构成的一个曲面,叫做阈值曲面。


    5.1.2 水线阈值算法
    水线(也称分水岭或流域,watershed)阈值算法可以看成是一种特殊的自适应迭代阈值方法,它的基本思想是:初始时,使用一个较大的阈值将两个目标分开,但目标间的间隙很大;在减小阈值的过程中,两个目标的边界会相向扩张,它们接触前所保留的最后像素集合就给出了目标间的最终边界,此时也就得到了阈值。


    5.1.3 其它的局部阈值法
    文献[30]提出了一种基于阈值曲面的二维遗传算法。遗传算法是基于进化论中自然选择机理的、并行的、统计的随机化搜索方法,所以在图像处理中常用来确定分割阈值。
     文献[31] [32]中提出一种基于局部梯度最大值的插值方法。首先平滑图像,并求得具有局部梯度最大值的像素点,然后利用这些像素点的位置和灰度在图像上内插,得到灰度级阈值表面。
    除此之外,典型的局部阈值方法还有White和Rohrer[33]的加权移动平均阈值方法,Perez和Gonzalez[34]的适用于非均匀照射下图像的局部阈值方法以及Shio[35]的与照射无关的对比度度量阈值方法等。总的来说,这类算法的时间和空间复杂度都较大,但是抗噪能力强,对一些使用全局阈值法不宜分割的图像具有较好的效果。


    5.2 多阈值法
    很显然,如果图像中含有占据不同灰度级区域的几个目标,则需要使用多个阈值才能将它们分开。其实多域值分割,可以看作单阈值分割的推广,前面讨论的大部分阈值化技术,诸如Otsu的最大类间方差法, Kapur的最大熵方法、矩量保持法和最小误差法等等都可以推广到多阈值的情形。以下介绍另外几种多阈值方法。
    5.2.1 基于小波的多域值方法。
    小波变换的多分辨率分析能力也可以用于直方图分析[36],一种基于直方图分析的多阈值选取方法思路如下:首先在粗分辨率下,根据直方图中独立峰的个数确定分割区域的类数,这里要求独立峰应该满足三个条件:(1)具有一定的灰度范围;(2)具有一定的峰下面积;(3)具有一定的峰谷差。然后,在相邻峰之间确定最佳阈值,这一步可以利用多分辨的层次结构进行。首先在最低分辨率一层进行,然后逐渐向高层推进,直到最高分辨率。可以基于最小距离判据对在最低层选取的所有阈值逐层跟踪,最后以最高分辨率层的阈值为最佳阈值。
    5.2.2 基于边界点的递归多域值方法。
    这是一种递归的多阈值方法。首先,将象素点分为边界点和非边界点两类,边界点再根据它们的邻域的亮度分为较亮的边界点和较暗的边界点两类,然后用这两类边界点分别作直方图,取两个直方图中的最高峰多对应的灰度级作为阈值。接下去,再分别对灰度级高于和低于此阈值的像素点递归的使用这一方法,直至得到预定的阈值数。


    5.2.3 均衡对比度递归多域值方法。
    首先,对每一个可能阈值计算它对应于它的平均对比度
                                             
    其中, 是阈值为 时图像总的对比度, 是阈值 检测到的边界点的数目。然后,选择 的直方图上的峰值所对应的灰度级为最佳阈值。对于多阈值情形,首先用这种方法确定一个初始阈值,接着,去掉初始阈值检测到的边界点的贡献再做一次 的直方图,并依据新的直方图选择下一个阈值。这一过程可以这样一直进行下去,直到任何阈值的最大平均对比度小于某个给定的限制为止。


    6 阈值化算法评价简介
    尽管人们在图像分割方面做了许多研究工作,但由于尚无通用的分割理论,现已提出的分割算法大都是针对具体问题的,并没有一种适合于所有图像的通用的分割算法。另一方面,给定一个实际图像分割问题要选择合用的分割算法也还没有标准的方法。为解决这些问题需要研究对图像分割的评价问题。分割评价是改进和提高现有算法性能、改善分割质量和指导新算法研究的重要手段。
     然而,如同所有的图像分割方法一样,阈值化结果的评价是一个比较困难的问题。事实上对图像分割本身还缺乏比较系统的精确的研究,因此对其评价则更差一些。人们先后已经提出了几十个评价准则。这些准则中又有定性的,也有定量的;有分析算法的,也有检测实验结果的,文献[37]将它们大致分为13类。
    文献[4] 中选择摄影师、建筑物和模特三幅图像作为标准图像,并采用趋于一致性度量和形状参数对几种常用的全局阈值方法的分割结果进行了评价。结果表明对于这三幅图像,如果希望得到的二值图像比较均匀且目标的形状较好,推荐使用最大熵方法、矩量保持方法和最大类间方差法。
    文献[38] 中以磁盘及鹤模型作标准图像,在噪声条件下用错分概率、形状和均匀性度量作为标准评估了五种常见的整体阈值选取方法的性能。这五种方法是四元树方法、矩量保持法、最大类间方差法、最大熵方法和简单统计法。结果表明各种方法的性能不仅与所处理的图像有关,而且也和所选用的准则有关。该文献也指出,对于一般实时应用来说,可以选择最大类间方差方法和简单统计法。
    最后,评价的目的是为了能指导、改进和提高分割,如何把评价和分割应用联系起来尚有许多工作要做。一个可能的方法是结合人工智能技术,建立分割专家系统[45],以有效的利用评价结果进行归纳推理,从而把对图像的分割由目前比较盲目的试验阶段推进到系统地实现的阶段。

    转载于:https://www.cnblogs.com/libing64/archive/2012/02/11/2878732.html

    展开全文
  • 图像处理中我们常常需要提取目标图像中的ROI区域或这是某个形状,这样我们就需要观察对象的特征,根据它的特征去提取。 这次我们在下图中提取其中的圆 基本思路 1、二值化处理 2、形态学操作(开与闭)去除干扰 3...

    在图像处理中我们常常需要提取目标图像中的ROI区域或这是某个形状,这样我们就需要观察对象的特征,根据它的特征去提取。
    这次我们在下图中提取其中的圆
    在这里插入图片描述

    基本思路

    1、二值化处理
    2、形态学操作(开与闭)去除干扰
    3、提取轮廓,通过轮廓的面积大小与横纵比过滤
    4、获取目标图像数据并标记

    代码

    # include<opencv2\opencv.hpp>
    # include <iostream>
    # include <math.h>
    using namespace std;
    using namespace cv;
    Mat src, dst,binary;
    int main(int argc, char** argv) {
    	src = imread("E:/tuku/case003.png",IMREAD_GRAYSCALE);
    	if (src.empty()) {
    		cout << "can't find this picture...";
    		return -1;
    	}
    	Rect roi = Rect(30, 30, src.cols - 50, src.rows - 50);
    	Mat ROI = src(roi);
    	imshow("input", ROI);
    
    	//二值化
    	threshold(ROI, binary, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);
    	imshow("binary Image", binary);
    	//形态学操作(先闭操作再开操作)
    	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    	morphologyEx(binary, dst, MORPH_CLOSE, kernel);
    	imshow("colse Image", dst);
    
    	kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    	morphologyEx(dst, dst, MORPH_OPEN, kernel);
    	imshow("open Image", dst);
    	//提取轮廓
    	vector<vector<Point>> contours;
    	vector<Vec4i>hierachy;
    	findContours(dst, contours, hierachy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(-1,-1));
    
    	Mat ResultImage = Mat::zeros(ROI.size(), CV_8UC3);
    	//Mat circleImage = ROI.clone();
    	//cvtColor(circleImage, circleImage, COLOR_GRAY2BGR);
    	Point cc;
    	for (size_t t = 0; t < contours.size(); t++) {
    		//面积过滤
    		double area = contourArea(contours[t]);
    		if (area < 150)continue;
    		//横纵比过滤
    		Rect rect = boundingRect(contours[t]);//最小的外接矩形
    		float radio = float(rect.height) / float(rect.width);
    		if (radio<1.1&&radio>0.9) {
    		drawContours(ResultImage, contours, t, Scalar(0, 0, 255), -1, 8, Mat(), 0, Point());
    		printf("circle area :%f\n", area);
    		printf("circle length:%f\n", arcLength(contours[t], true));//周长计算
    		int x = rect.x + rect.width / 2;
    		int y = rect.y + rect.height / 2;
    		cc = Point(x, y);
    		circle(ResultImage, cc, 2, Scalar(0, 0, 255), 2, 8, 0);
    		printf("圆心坐标:(%d,%d)", x, y);
    		}
    	}
    	imshow("result Image", ResultImage);
    	Mat circleImage = ROI.clone();
    	cvtColor(circleImage, circleImage, COLOR_GRAY2BGR);
    	circle(circleImage, cc, 2, Scalar(0, 0, 255), 2, 8, 0);
    	imshow("Final Result Image", circleImage);
    	//detect circle
    	/*vector<Vec3f>Mycircle;
    	Mat grat_result;
    	cvtColor(ResultImage, grat_result, COLOR_GRAY2BGR);
    	HoughCircles(grat_result, Mycircle, HOUGH_GRADIENT, 1, 7, 50, 20, 23, 100);
    
    	Mat circleImage = ROI.clone();
    	cvtColor(circleImage, circleImage, COLOR_GRAY2BGR);
    	for (int i = 0; i < Mycircle.size(); i++) {
    		Vec3f circleInfo = Mycircle[i];
    		circle(circleImage, Point(circleInfo[0], circleInfo[1]), circleInfo[2], Scalar(0, 0, 255), 2, 8, 0);
    	}
    	imshow("Final Result Image", circleImage);*/
    	waitKey(0);
    	return 0;
    }
    

    效果图:
    在这里插入图片描述

    展开全文
  • 测试对象:骨头的DR照片 测试目标:找出骨头的轮廓,并且测出轮廓内部的面积大小。 采用方法:具体过程由下面的流程图详细说明。 以及每步操作的效果图如下所示:   图1 流程图 图2 原始图像
  • 图像分割阈值选取技术综述

    万次阅读 2006-02-27 09:39:00
    图像分割阈值选取技术综述中科院成都计算所 刘平 2004-2-26摘要 图像分割是图像处理与计算机视觉领域低层次视觉中最为基础和重要的领域之一,它是对图像进行视觉分析和模式识别的基本前提.阈值法是一种传统的...
  • 图像分割—灰度阈值分割

    千次阅读 2020-04-02 11:41:41
    阈值分割概念图像阈值分割具有直观和易于实现的特点,在图像分割应用中占有重要地位。许多情况下,图像$f(x,y)$由暗对象和亮对象这两类具有不同灰度级的区域组成,如报纸和书本。这种图像的亮暗部分可以在直方图中...
  • 为提高目标检测概率9针对复杂的地面目标红外亚图像9提出了一种以最大类间方差法为基础的自适应阈 值图像分割方法- 用分割出...均可得到正确的分割结果- 通过设置阈值运算的灰度取值范围9可大大减少计算量9节省处理时间-
  • 灰度直方图是一个离散函数,它表示图像每一个灰度级与该灰度级出现频率的对应关系。一般计算步骤: 1、统计各个灰度值的像素个数 2、根据统计表画出直方图 性质: 1、只反映该图像中不同灰度值出现的次数,并不...
  • @迭代法求取阈值进行图像分割 迭代法求取阈值进行图像分割 它的主要思想是:图像分割后的两部分A和B的均值和基本保持稳定。也就是说,随着迭代的进行,取 [mean(A)+mean(B)]/2 最终的收敛值作为分割阈值。 在这里有...
  • 十一,二值化图像处理(灰度图像的直方图,阈值计算和图像二值化,4领域收缩膨胀,8领域收缩膨胀,清除孤立点,细化等)。 十二,图像形状参数测量(标号法面积测量,labeling去除小面积粒子,标号法周长测量,...
  • 数字图像处理,实现图像增强(包括线性增强,邻域平均,中值滤波等),图像变换(平移,镜像等),二值,灰度图像腐蚀膨胀,开闭等,霍夫变换检测直线,大津阈值分割等,连通域个数及面积求取等
  • 十六、数字图像处理之图像分割

    万次阅读 多人点赞 2019-07-02 10:31:57
    图像分割(一)点、线和边缘检测(1)点检测(2)线检测(3)使用函数edge的边缘检测(二)使用霍夫变换的线检测(三)阈值处理(1...图像阈值处理(四)基于区域的分割(1)基本表达式(2)区域生长(3)区域分离和...
  • 这次采用matlab的库函数来进行膨胀和腐蚀,然后查找连通区域,对于每个连通区域,用红色方框框选其最大的范围。 膨胀:imdilate(); 腐蚀:imerode(); ...查找连通区域:...%通过matlab函数实现大面积图形查...
  • 图像处理与识别学习小结

    万次阅读 热门讨论 2009-08-31 23:14:00
    图像处理与识别学习小结 数字图像处理是对图像进行分析、加工、和处理,使其满足视觉、心理以及其他要求的技术。图像处理是信号处理在图像域上的一个应用。目前大多数的图像是以数字形式存储,因而图像处理很多情况...
  • 对一个图像的简单操作1.1 读取图像并转换为灰度图1.2 二值化处理 :大于阈值使用maxval(255)表示,小于阈值使用0表示1.3 腐蚀处理: 将图像中的高亮区域或白色部分进行缩减细化1.4 图像膨胀:将图像中的高亮区域或白色...
  • jupyter入门之图像处理

    千次阅读 2018-06-19 10:01:17
    scipy图片处理 导入库 import numpy as np ...# 图像处理库 import matplotlib.pyplot as plt %matplotlib inline # scipy.fftpack模块用来计算快速傅里叶变换 # 速度比传统傅里叶变换更快,是对之前...
  • 本文是对于Omar Banimelhem and Yahya Ahmed Yahya 发表论文《Multi-Thresholding Image Segmentation ...用遗传算法对图像进行多阈值分割(Multi-Thresholding Image Segmentation Using Genetic Algorithm) 摘要
1 2 3 4 5 ... 20
收藏数 7,548
精华内容 3,019
关键字:

图像处理 面积阈值