OpenCV(python)一键入门--十二篇(二值化)

举报
拓佑豪 发表于 2021/07/27 16:57:33 2021/07/27
【摘要】 简单概要地介绍了图像二值化入门函数使用,包括OTSU,TRIANGLE的二值寻找方法

本系列OpenCV博客旨在以最简单的方式分享OpenCV知识,欢迎大家交流和提出指导意见


1:简单的二值化

import cv2 as cv
import numpy as np

src = cv.imread("ma.jpg")
cv.imshow("input", src)

# 转换为灰度图像
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
h, w = gray.shape

t = cv.mean(gray)[0]
#t , std = cv.meanStdDev(gray)
#提示:以上两种方式都可以,都是为了提取均值
#为了方便入门理解,第二行的“std”变量仅用于接收方差,没有其他用途
#区别:
#cv.mean返回一个向量数组,数组里面的值是标量,用【0】获取值
#cv.meanStdDev返回一个矩阵
#可以通过下面的 print语句查看差别

print("t value : ", t)

# 创建一个空图
binary = np.zeros((h, w), dtype=np.uint8)

for row in range(h):
    for col in range(w):
        pv = gray[row, col]#遍历像素,大于t则赋值255,否则为0

        if pv > t:
            binary[row, col] = 255
        else:
            binary[row, col] = 0

cv.imshow("binary", binary)

cv.waitKey(0)
cv.destroyAllWindows()

1)均值这一部分,在本系列博客第二篇:https://bbs.huaweicloud.com/blogs/285391有简单介绍,大致内容放在下图:

当然,除了均值,可以根据自己的需要调整这个 阈值t 的获取方式,根据需要自行调整即可,本案例用均值进行二值化

2)下边的for循环嵌套很好理解了,就是遍历原图像素,大于 阈值t 的像素赋值255,否则为0,。这就是简单的二值化分割。

效果如下:

2:二值化基本阈值操作

import cv2 as cv
import numpy as np
#二值化分割
#下列表示阈值分割方法
#
# THRESH_BINARY = 0       二值分割
# THRESH_BINARY_INV = 1   反向二值分割
# THRESH_TRUNC = 2        截断
# THRESH_TOZERO = 3       取零
# THRESH_TOZERO_INV = 4   反向取零
#

src = cv.imread("ma.jpg")
cv.imshow("input", src)

T = 127#这里直接给定阈值,不再设置别的计算方式

# 转换为灰度图像
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

#for循环输出五种分割方式
for i in range(5):
    ret, binary = cv.threshold(gray, T, 255, i)
    #返回127.0(得到的阈值) 以及二值化图像
    print(f"ret_{i}:{ret}\n")
    cv.imshow("binary_" + str(i), binary)

cv.waitKey(0)
cv.destroyAllWindows()

cv.threshold有两个返回值,返回的第一个ret就是得到的阈值值,第二个就是阈值化后的图像。 

cv.threshold中,对于BINARY方法,当图像中的灰度值大于127(也就是阈值T),其重置像素值为255.

所以,这里的cv.threshold函数原型为:

cv.threshold(灰度图,阈值,重置像素值,模式)

3:图像二值寻找算法——OTSU

我们前面说到,cv.threshold函数有两个返回值的
前面的代码在阈值的处理上,我们选择的阈值都是127,或者均值计算。那么实际情况下,有的图像可能阈值127不是最好的效果。

那这里我们需要算法自己去寻找到一个阈值,而Otsu’s就可以自己找到一个认为最好的阈值。并且Otsu’s非常适合于图像灰度直方图具有双峰的情况,他会在双峰之间找到一个值作为阈值,对于非双峰图像,可能并不是很好用。

经过Otsu’s得到的那个阈值就是函数cv.threshold的第一个参数。因为Otsu’s方法会产生一个阈值,那么函数cv.threshold的的第二个参数(设置阈值)就是0了,并且在cv.threshold的方法参数中还得加上语句cv.THRESH_OTSU。

双峰图像:就是图像的灰度统计图中可以明显看出只有两个波峰,比如下面一个图的灰度直方图就可以是双峰图:

下列是代码

import cv2 as cv
import numpy as np
#
# THRESH_BINARY = 0
# THRESH_BINARY_INV = 1
# THRESH_TRUNC = 2
# THRESH_TOZERO = 3
# THRESH_TOZERO_INV = 4
# THRESH_MASK = 5 不支持
# THRESH_OTSU = 6 不支持32位
# THRESH_TRIANGLE = 7 不支持32位

src = cv.imread("ma.jpg")
cv.imshow("input", src)

# 自动阈值分割 OTSU
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
#                          灰度图,阈值,重置像素值,模式
#                          OTSU--基于类内最小方差实现阈值寻找

print("ret :", ret)
cv.imshow("binary", binary)

cv.waitKey(0)
cv.destroyAllWindows()


4:图像二值寻找算法——TRIANGLE

前面说到,OTSU对于有两个波峰的直方图效果很好。如果该直方图只有一个波峰,TRIANGLE是个不错的选择

import cv2 as cv
import numpy as np

src = cv.imread("ma.jpg")
cv.imshow("input", src)

# 自动阈值分割 TRIANGLE
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE)
#                           函数原型请参考之前的说明
##                         有时候直方图只有一个波峰,就用这种triangle寻找阈值
print("ret :", ret)
cv.imshow("binary", binary)

cv.waitKey(0)
cv.destroyAllWindows()

由于手头没有合适的图像,只能展示如下:

补充:

关于cv.threshold参数:


 

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。