目标

  • 在本教程中,您将学习简单阈值,自适应阈值和Otsu阈值。
  • 你将学习函数cv.thresholdcv.adaptiveThreshold

简单阈值

在这里,问题直截了当。对于每个像素,应用相同的阈值。如果像素值小于阈值,则将其设置为0,否则将其设置为最大值。函数cv.threshold用于应用阈值。第一个参数是源图像,它应该是灰度图像。第二个参数是阈值,用于对像素值进行分类。第三个参数是分配给超过阈值的像素值的最大值。OpenCV提供了不同类型的阈值,这由函数的第四个参数给出。通过使用cv.THRESH_BINARY类型。所有简单的阈值类型为:

  • cv.THRESH_BINARY
  • cv.THRESH_BINARY_INV
  • cv.THRESH_TRUNC
  • cv.THRESH_TOZERO
  • cv.THRESH_TOZERO_INV

请通过类型的文档来观察区别。

该方法返回两个输出。第一个是使用的阈值,第二个输出是阈值后的图像

此代码比较了不同的简单阈值类型:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('gradient.png',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 xrange(6):
plt.subplot(2,3,i 1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()

注意

为了绘制多个图像,我们使用plt.subplot()函数。请查看matplotlib文档以获取更多详细信息。

该代码产生以下结果:

自适应阈值

在上一节中,我们使用一个全局值作为阈值。但这可能并非在所有情况下都很好,例如,如果图像在不同区域具有不同的光照条件。在这种情况下,自适应阈值阈值化可以提供帮助。在此,算法基于像素周围的小区域确定像素的阈值。因此,对于同一图像的不同区域,我们获得了不同的阈值,这为光照度变化的图像提供了更好的结果。

除上述参数外,方法cv.adaptiveThreshold还包含三个输入参数:

adaptiveMethod决定阈值是如何计算的:

cv.ADAPTIVE_THRESH_MEAN_C::阈值是邻近区域的平均值减去常数C

cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的高斯加权总和减去常数C

BLOCKSIZE确定附近区域的大小,C是从邻域像素的平均或加权总和中减去的一个常数。

下面的代码比较了光照变化的图像的全局阈值和自适应阈值:

结果:

Otsu的二值化

在全局阈值化中,我们使用任意选择的值作为阈值。相反,Otsu的方法避免了必须选择一个值并自动确定它的情况。

考虑仅具有两个不同图像值的图像(双峰图像),其中直方图将仅包含两个峰。一个好的阈值应该在这两个值的中间。类似地,Otsu的方法从图像直方图中确定最佳全局阈值。

为此,使用了cv.threshold作为附加标志传递。阈值可以任意选择。然后,算法找到最佳阈值,该阈值作为第一输出返回。

查看以下示例。输入图像为噪点图像。在第一种情况下,采用值为127的全局阈值。在第二种情况下,直接采用Otsu阈值法。在第三种情况下,首先使用5x5高斯核对图像进行滤波以去除噪声,然后应用Otsu阈值处理。了解噪声滤波如何改善结果。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('noisy2.png',0)
# 全局阈值
ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
# Otsu阈值
ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY cv.THRESH_OTSU)
# 高斯滤波后再采用Otsu阈值
blur = cv.GaussianBlur(img,(5,5),0)
ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY cv.THRESH_OTSU)
# 绘制所有图像及其直方图
images = [img, 0, th1,
img, 0, th2,
blur, 0, th3]
titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',
'Original Noisy Image','Histogram',"Otsu's Thresholding",
'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]
for i in xrange(3):
plt.subplot(3,3,i*3 1),plt.imshow(images[i*3],'gray')
plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])
plt.subplot(3,3,i*3 2),plt.hist(images[i*3].ravel(),256)
plt.title(titles[i*3 1]), plt.xticks([]), plt.yticks([])
plt.subplot(3,3,i*3 3),plt.imshow(images[i*3 2],'gray')
plt.title(titles[i*3 2]), plt.xticks([]), plt.yticks([])
plt.show()

结果:

Otsu的二值化如何实现?

本节演示了Otsu二值化的Python实现,以展示其实际工作方式。如果您不感兴趣,可以跳过此步骤。

由于我们正在处理双峰图像,因此Otsu的算法尝试找到一个阈值(t),该阈值将由关系式给出的加权类内方差最小化:

σw2(t)=q1(t)σ12(t)q2(t)σ22(t)
\sigma_w^2(t) = q_1(t)\sigma_1^2(t) q_2(t)\sigma_2^2(t)
σw2​(t)=q1​(t)σ12​(t)q2​(t)σ22​(t)

其中

q1(t)=∑i=1tP(i)&q2(t)=∑i=t1IP(i)
q_1(t) = \sum_{i=1}^{t} P(i) \quad \& \quad q_2(t) = \sum_{i=t 1}^{I} P(i)
q1​(t)=i=1∑t​P(i)&q2​(t)=i=t1∑I​P(i)

μ1(t)=∑i=1tiP(i)q1(t)&μ2(t)=∑i=t1IiP(i)q2(t)
\mu_1(t) = \sum_{i=1}^{t} \frac{iP(i)}{q_1(t)} \quad \& \quad \mu_2(t) = \sum_{i=t 1}^{I} \frac{iP(i)}{q_2(t)}
μ1​(t)=i=1∑t​q1​(t)iP(i)​&μ2​(t)=i=t1∑I​q2​(t)iP(i)​

σ12(t)=∑i=1t[i−μ1(t)]2P(i)q1(t)&σ22(t)=∑i=t1I[i−μ2(t)]2P(i)q2(t)
\sigma_1^2(t) = \sum_{i=1}^{t} [i-\mu_1(t)]^2 \frac{P(i)}{q_1(t)} \quad \& \quad \sigma_2^2(t) = \sum_{i=t 1}^{I} [i-\mu_2(t)]^2 \frac{P(i)}{q_2(t)}
σ12​(t)=i=1∑t​[i−μ1​(t)]2q1​(t)P(i)​&σ22​(t)=i=t1∑I​[i−μ2​(t)]2q2​(t)P(i)​

实际上,它找到位于两个峰值之间的t值,以使两个类别的差异最小。它可以简单地在Python中实现,如下所示:

img = cv.imread('noisy2.png',0)
blur = cv.GaussianBlur(img,(5,5),0)
# 寻找归一化直方图和对应的累积分布函数
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 xrange(1,256):
p1,p2 = np.hsplit(hist_norm,[i]) # 概率
q1,q2 = Q[i],Q[255]-Q[i] # 对类求和
b1,b2 = np.hsplit(bins,[i]) # 权重
# 寻找均值和方差
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
# 计算最小化函数
fn = v1*q1 v2*q2
if fn < fn_min:
fn_min = fn
thresh = i
# 使用OpenCV函数找到otsu的阈值
ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY cv.THRESH_OTSU)
print( "{} {}".format(thresh,ret) )

其他资源

  1. Digital Image Processing, Rafael C. Gonzalez

练习题

  1. Otsu的二值化有一些优化。您可以搜索并实现它。

欢迎关注磐创博客资源汇总站:

http://docs.panchuang.net/

欢迎关注PyTorch官方中文教程站:

http://pytorch.panchuang.net/

OpenCV中文官方文档:

http://woshicver.com/

OpenCV-Python 图像阈值 | 十五的更多相关文章

  1. 【OpenCV新手教程之十五】水漫金山:OpenCV漫水填充算法(Floodfill)

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.    文章链接: http://blog.csdn.net/poem_qianmo/article/details/28261997 作者:毛星云( ...

  2. Java基于opencv实现图像数字识别(五)—投影法分割字符

    Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...

  3. Python进阶(三十五)-Fiddler命令行和HTTP断点调试

    Python进阶(三十五)-Fiddler命令行和HTTP断点调试 一. Fiddler内置命令   上一节(使用Fiddler进行抓包分析)中,介绍到,在web session(与我们通常所说的se ...

  4. 孤荷凌寒自学python第八十五天配置selenium并进行模拟浏览器操作1

    孤荷凌寒自学python第八十五天配置selenium并进行模拟浏览器操作1 (完整学习过程屏幕记录视频地址在文末) 要模拟进行浏览器操作,只用requests是不行的,因此今天了解到有专门的解决方案 ...

  5. 孤荷凌寒自学python第七十五天开始写Python的第一个爬虫5

    孤荷凌寒自学python第七十五天开始写Python的第一个爬虫5 (完整学习过程屏幕记录视频地址在文末) 今天在上一天的基础上继续完成对我的第一个代码程序的书写. 直接上代码.详细过程见文末屏幕录像 ...

  6. 孤荷凌寒自学python第六十五天学习mongoDB的基本操作并进行简单封装4

    孤荷凌寒自学python第六十五天学习mongoDB的基本操作并进行简单封装4 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第十一天. 今天继续学习mongoDB的简单操作 ...

  7. 孤荷凌寒自学python第四十五天Python初学基础基本结束的下阶段预安装准备

     孤荷凌寒自学python第四十五天Python初学基础基本结束的下阶段预安装准备 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 今天本来应当继续学习Python的数据库操作,但根据过去我自 ...

  8. 孤荷凌寒自学python第三十五天python的文件操作之针对文件操作的os模块的相关内容

     孤荷凌寒自学python第三十五天python的文件操作之针对文件操作的os模块的相关内容 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 一.打开文件后,要务必记得关闭,所以一般的写法应当 ...

  9. 进击的Python【第十五章】:Web前端基础之DOM

    进击的Python[第十五章]:Web前端基础之DOM 简介:文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文档的编程接口.它给文档提供了一种结构化的表示 ...

随机推荐

  1. Spark基础全解析

    我的个人博客:https://www.luozhiyun.com/ 为什么需要Spark? MapReduce的缺陷 第一,MapReduce模型的抽象层次低,大量的底层逻辑都需要开发者手工完成. 第 ...

  2. 冒泡排序算法(C#、Java、Python、JavaScript、C、C++实现)

    一.介绍 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小.首字母从Z到A)错误就把他们交换过来. 走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排 ...

  3. MVC三层架构

    需求: 注册登录: # 知识补充: >> MVC模型: |-- M 模型: |-- V 视图: |-- >> 基本概念: |-- 层级之间的调用关系: |-- V层接收前台数据 ...

  4. 使用IDEA创建Maven整合SSM

    创建数据库 CREATE DATABASE `ssmbuild`; USE `ssmbuild`; DROP TABLE IF EXISTS `books`; CREATE TABLE `books` ...

  5. XSS_跨站脚本攻击

    前段时间在网上看到一个网址,好奇之下进去看了看.胜利的条件是你录入一个串,让其调用prompt(1) .发现里面有好多想不到的东西,今天终于悠闲了来这里说说XSS. XSS 原理 恶意攻击者往Web页 ...

  6. 前端每日实战:145# 视频演示如何用纯 CSS 创作一个电源开关控件

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/PdMyJd 可交互视频 此视频是可 ...

  7. java后台生成并下载二维码

    这个功能在项目开发中是很基础的,平时用到的也很多,这里简单记录一下,以便以后使用的时候参考 前提业务要求:前台页面展示数据,有下载按钮,点击下载,下载对应数据的二维码. 首先,在pom.xml文件中添 ...

  8. 分享一次C#调用Delphi编写Dll程序

    1.前言: 最近接手了一个项目需要和Delphi语言编写的一个系统进行一些接口的对接,数据在传输过程中采用Des加密方式,因为Delphi 平台的加密方式和C#平台的加密方式不互通,所以采用的方式是C ...

  9. 致远·面向人工智能-逐浪CMS v8.1.2全面发布[全球首个基于dotNET core3.0的中文CMS]

    原文:https://www.z01.com/down/3484.shtml 再远, 我都不会停息, 因为技术而生, 因为技术而强, 这是逐浪软件的命与根! 全新打造, 三百多项超级功能, 助你十分钟 ...

  10. 助力SpringBoot自动配置的条件注解ConditionalOnXXX分析--SpringBoot源码(三)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 如何分析SpringBoot源码模块及结构?--SpringBoot源码(二) 上一篇分析了SpringBoo ...