OpenCV—图像阈值

1、简单阈值

如同简单阈值的名字一样,这种处理方式也的确比较简单。当像素值高于阈值时,将该像素设为白色或者黑色。OpenCV中使用cv2.threshold()函数来实现。该函数的定义如下:

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

第一个参数是输入的图片,应该是灰度图;第二个图像是输出图像,第三个参数是阈值,第四个参数是当像素低于或者高于阈值时被设定的新值,第五个参数是类型。OpenCV官方文档中给出了下面几种类型:

 

我们来看下面的案例代码:

img = cv.imread(‘lalala.jpg‘,0)
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)
titles = [‘Original Image‘,‘BINARY‘,‘BINARY_INV‘,‘TRUNC‘,‘TOZERO‘,‘TOZERO_INV‘]
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],‘gray‘)
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

运行结果:

可以看到,经过处理后的图片被二值化了,但是在这种滤波方式中,整幅图片采用的是同一个阈值,这会存在一个问题:当图像上的不同部分亮度不同时,这种方式未必能够产生好的效果。我们接下来看第二种阈值。

2、自适应阈值

自适应阈值在图像上的不同部分采用不同的阈值。OpenCV中使用函数cv.adaptiveThreshold来实施这种阈值,该函数的定义如下:

cv.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst])

第一个参数仍然是源图像;第二个图像是当像素点的值高于或者低于阈值的情况下被设定的新值;第三个参数时是计算阈值的方式,有这样两种:

cv2.ADPTIVE_THRESH_MEAN_C:阈值取自相邻区域的平均值;cv2.ADPTIVE_THRESH_GAUSSIAN_C:阈值取值相邻区域的加权和,权重为一个高斯窗口。

第四个参数是阈值类型,有这样两种类型:

第五个参数:邻域的大小,用来计算阈值,应该是奇数,比如:3,5...

第六个参数C:计算阈值时用邻域的平均值或加权平均值减去这个C就能够得到阈值。

我们来看下面的案例代码:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread(‘lalala.jpg‘,0)
img = cv.medianBlur(img,5)
ret,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,            cv.THRESH_BINARY,11,2)
th3 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,            cv.THRESH_BINARY,11,2)
titles = [‘Original Image‘, ‘Global Thresholding (v = 127)‘,
            ‘Adaptive Mean Thresholding‘, ‘Adaptive Gaussian Thresholding‘]
images = [img, th1, th2, th3]
for i in range(4):
    plt.subplot(2,2,i+1),plt.imshow(images[i],‘gray‘)
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

程序结果:

从上图的对比中不难发现,采用了自适应阈值的二值化图像在图像的亮度分布不均匀的情况下能够表现出更好的效果。

3、OTSU‘S二值化

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

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

ω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),这就是类间方差
采用遍历的方法得到使类间方差g最大的阈值T,即为所求。也可以有下面这种理解:

当前景和背景的类内方差加权之和最小时,t为最优的阈值。

用python实现上述算法的代码如下:

img = cv.imread(‘lalala.jpg‘,0)
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)

blur = cv.GaussianBlur(img,(5,5),0)
# find normalized_histogram, and its cumulative distribution function
hist = cv.calcHist([blur],[0],None,[256],[0,256])
hist_norm = hist.ravel()/hist.max()
Q = hist_norm.cumsum()
bins = np.arange(256)
fn_min = np.inf
thresh = -1
for i in range(1,256):
    p1,p2 = np.hsplit(hist_norm,[i]) # probabilities
    q1,q2 = Q[i],Q[255]-Q[i] # cum sum of classes
    b1,b2 = np.hsplit(bins,[i]) # weights
    # finding means and variances
    m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2
    v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2
    # calculates the minimization function
    fn = v1*q1 + v2*q2
    if fn < fn_min:
        fn_min = fn
        thresh = i
# find otsu‘s threshold value with OpenCV function
ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)

th2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,       cv.THRESH_BINARY,11,2)
imgs=[img,thresh1,th2,otsu]

titles = [‘Original Image‘, ‘Global Thresholding (v = 127)‘,
          ‘Adaptive Mean Thresholding‘, ‘OTSU‘]
for i in range(4):
    plt.subplot(2,2,i+1),plt.imshow(imgs[i],‘gray‘)
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()
print("thresh_OTSU={},ret_OTSU={}".format(thresh,ret))

程序结果:

thresh_OTSU=178,ret_OTSU=177.0

可以看到,我们实现的函数计算出来的阈值与OpenCV自带的函数计算出来的结果是基本一致的。

原文地址:https://www.cnblogs.com/puheng/p/9239209.html

时间: 2024-10-10 22:59:19

OpenCV—图像阈值的相关文章

python opencv图像阈值处理

######################################################### 学习函数 cv2.threshold, cv2.adaptiveThreshold等等 简单阈值: 原理很直接,如果像素值大于一个阈值,那么久将它赋值一个数,否则赋值成另外一个数.函数使用cv2.threshold 第一参数是原图像,而且这个图像应该是一个灰度图像.第二个参数用来区分像素的阈值.第三个参数是像素值大于阈值时的上限.opencv提供了不同样式的阈值并且由第四个参数决定

opencv学习之路(13)、图像阈值化

一.图像阈值化简介 二.固定阈值 三.自适应阈值 1 #include<opencv2/opencv.hpp> 2 using namespace cv; 3 4 void main(){ 5 Mat src=imread("E://1.jpg",0);//以灰度模式读入 6 Mat dst; 7 //threshold(src,dst,100,255,CV_THRESH_BINARY); 8 //adaptiveThreshold(src,dst,255,CV_ADAPT

OpenCV实现图像阈值化

纯粹阅读,请移步OpenCV实现图像阈值化 效果图 源码 KqwOpenCVBlurDemo 阈值化是一种将我们想要在图像中分析的区域分割出来的方法. 我们把每个像素值都与一个预设的阈值做比较,再根据比较的结果调整像素值. 类似这样 Imgproc.threshold(src,src,100,255,Imgproc.THRESH_BINARY); 其中100是阈值,255是最大值(纯白色的值). 常量 名称 常量 二值阈值化 Imgproc.THRESH_BINARY 阈值化到零 Imgproc

OpenCV的阈值化函数threshold

在OpenCV中,threshold用来进行对图像(二维数组)的二值化阈值处理 通过查找OpenCV在线文档,发现存在很多函数: 其函数原型如下: 1. C版本的:函数原型: void cvThreshold( const CvArr* src, CvArr* dst, double threshold,double max_value, int threshold_type ); src,dst: 不必多说,其要求类型一致性: threshold:需要设置的阈值,当像素值大于某个数字时,设定一

OpenCV &mdash;&mdash; 图像局部与部分分割(一)

背景减除 一旦背景模型建立,将背景模型和当前的图像进行比较,然后减去这些已知的背景信息,则剩下的目标物大致就是所求的前景目标了 缺点 -- 该方法基于一个不长成立的假设:所有像素点是独立的 场景建模 新的前景(物体移动的新位置) -- 旧的前景 (物体离开后留下的"空洞")-- 背景 cvInitLineIterator()  和  CV_NEXT_LINE_POINT() 对任意直线上的像素进行采样 // 从视频的一行中读出所有像素的RGB值,收集这些数值并将其分成三个文件 #inc

opencv图像原地(不开辟新空间)顺时旋转90度

前一阵朋友碰到这么一道题:将图像原地顺时针旋转90度,不开辟新空间.此题看似平易(题目简短),仔细研究发现着实不容易.经过一番探索后,终于找到了正确的算法,但是当使用opencv实现时,有碰到了困难而且费了一番周折才找到问题所在. 首先,解决这个问题,先简化成原地90度旋转一M×N的矩阵A(注意不是N×N方阵).对于2×3的矩阵A = {1,2,3;4,5,6},其目标为矩阵B = {4,1;5,2;6,3}.因为是原地旋转,这里A和B应指向同一大小为6的内存空间. 这里有这样一个重要的导出公式

OpenCV &mdash;&mdash; 图像局部与分割(二)

分水岭算法 将图像中的边缘转化成"山脉",将均匀区域转化为"山谷" 分水岭算法首先计算灰度图像的梯度,这对山谷或没有纹理的盆地(亮度值低的点)的形成有效,也对山头或图像中没有主导线段的山脉(山脊对应的边缘)的形成有效.然后开始从用户指定点或算法得到的点开始"灌注"盆地知道这些区域连在一起.基于这样产生的标记就可以把区域合并到一起,合并后的区域又通过聚集的方式进行分割,好像图像被"填充"起来. cvWatershed 用 Inp

[转]OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

[OpenCV入门教程之十三]OpenCV图像金字塔:高斯金字塔.拉普拉斯金字塔与图片尺寸缩放 2014-05-18 18:58 36007人阅读 评论(54) 收藏 举报 本文章已收录于:  OpenCV知识库 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http

【OpenCV入门教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 这篇文章里,我们将一起探讨图像金字塔的一