Python OpenCV 图像处理二值化,取经之旅第 7 天

如果你有想要交流的想法、技术,欢迎在评论区留言。

本篇博客继续学习 OpenCV 的二值化操作。

局部二值化方法

局部二值化方法也叫自适应阈值法。在上篇文章中,咱学会的是一种全局性的阈值,只需要设定一个阈值,整个图像都和这个阈值比较。
而自适应阈值可以看成一种局部性的阈值,通过设定一个区域大小,比较这个点与区域大小里面像素点 的平均值(或者其他特征)的大小关系确定这个像素点的情况。

说白了就是在一个范围内进行二值化操作。

方法的语法格式如下:

adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst]) -> dst

其中各参数如下:

  • src - 输入图像(8 位单通道图像)
  • maxValue - 使用 THRESH_BINARY 和 THRESH_BINARY_INV 的最大值
  • adaptiveMethod - 自适应阈值算法,平均 (ADAPTIVE_THRESH_MEAN_C)或高斯(ADAPTIVE_THRESH_GAUSSIAN_C)
  • thresholdType - 阈值类型,必须为 THRESH_BINARY 或 THRESH_BINARY_INV 的阈值类型
  • blockSize - 块大小(奇数且大于 1 )
  • C-常数,从平均值或加权平均值中减去的数。 通常情况下,这是正值,但也可能为零或负值

其中最关键的参数是 adaptiveMethod,具体内容如下:
adaptiveMethod:决定如何计算阈值

  • cv2.ADAPTIVE_THRESH_MEAN_C:阈值是邻域的平均值
  • cv2.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的加权和,其中权重是高斯窗口

当然在网络上还找到关于 blockSize 最好的一段解释。

在使用平均和高斯两种算法情况下,通过计算每个像素周围 blockSize x blockSize 大小像素块的加权均值并减去常量 C 即可得到自适应阈值。
如果使用平均的方法,则所有像素周围的权值相同;
如果使用高斯的方法,则每个像素周围像素的权值则根据其到中心点的距离通过高斯方程得到。

该方法的返回值是:dst - 经函数处理过的图像。

测试代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt


# 局部二值化
def local_threshold(src):
    old_img = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

    gray = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY)

    # 平均
    binary1 = cv2.adaptiveThreshold(
        gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 25, 10)

    # 高斯处理
    binary2 = cv2.adaptiveThreshold(
        gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 25, 10)

    # 建立一个 2 行 2 列的图
    plt.subplot(2, 2, 1)
    # 原图
    plt.imshow(old_img, 'gray')
    plt.title("old_img")

    # 灰度图
    plt.subplot(2, 2, 2)
    plt.imshow(gray, 'gray')
    plt.title("gray")

    # 添加第三幅图
    plt.subplot(2, 2, 3)
    plt.imshow(binary1, 'gray')
    plt.title("MEAN")

    # 添加第四幅图
    plt.subplot(2, 2, 4)
    plt.imshow(binary2, 'gray')
    plt.title("GAUSSIAN")

    plt.show()


if __name__ == "__main__":
    # 读取图像并转换为灰度图
    img = cv2.imread('7.jpg')
    cv2.imshow("aaa", img)
    local_threshold(img)

运行之后对比,发现局部二值化确实比全局二值化呈现效果好。

相同的效果,再把全局二值化得到的结果做一下对比。
代码如下,上篇博客已经做了相关说明,本文不再补充。

def global_threshold(src):
    # 手动二值化处理
    # 设置阈值大小 threshold
    thresh = 125
    # 设置超过阈值像素值的最大值
    maxval = 255
    # THRESH_BINARY:超过阈值为maxval,否则为0
    # THRESH_BINARY_INV:超过阈值为0,否则为maxval(相当于上个参数取反)
    # THRESH_TRUNC:超过阈值为thresh,低于阈值灰度值不变
    # THRESH_TOZERO:超过阈值灰度值不变,否则为0
    # THRESH_TOZERO_INV:超过阈值为0,低于阈值灰度值不变
    ret, thresh1 = cv2.threshold(src, thresh, maxval, cv2.THRESH_BINARY)
    ret, thresh2 = cv2.threshold(src, thresh, maxval, cv2.THRESH_BINARY_INV)
    ret, thresh3 = cv2.threshold(src, thresh, maxval, cv2.THRESH_TRUNC)
    ret, thresh4 = cv2.threshold(src, thresh, maxval, cv2.THRESH_TOZERO)
    ret, thresh5 = cv2.threshold(src, thresh, maxval, cv2.THRESH_TOZERO_INV)

    titles = ['original image', 'Binary',
              'binary-inv', 'trunc', 'tozero', 'tozero-inv']
    images = [src, 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()

局部二值化更好的描绘了图片的轮廓。

对于二值化的学习,橡皮擦一直在好奇这个东西用在哪里比较合适,由于接触时间短,毕竟才开始 7 天,我能想到的只有验证码识别和扫描图像识别了。

验证码处理

先对验证码进行处理,例如下面的验证码。


先使用全局的看一下二值化的结果。

20201230211713117[1].png

对比之后发现效果还可以,如果应用于 OCR 技术会大幅度提高识别率。但是如果我将验证码修改成下图中间区域,发现有的就无法正常识别了。

20201230211857569[1].png

对于上图二维码,进行局部二值化操作,看一下效果。


20201230212016406[1].png

如果验证码再次增加难度,例如下面的混合验证码,得到的结果又是怎样的呢?可以查阅一下。

20201230212115610[1].png

全局阈值确实容易丢失细节。


20201230212201593[1].png

扫描文件识别

如果你要处理一些扫描到电脑里的文件,二值化看起来也是非常棒的一种选择,可以自行试试,效果也是一样的。


20201230212604261[1].jpg

全局阈值进行二值化之后如下图所示。

20201230212629390[1].png

局部二值化处理之后的效果对比。


2020123021270951[1].png

上篇博客中的函数改造

在上篇博客中,用到计算像素点矩阵中的所有像素点的灰度值的平均值 avg,用来做阈值,今天在检索中发现了一个其他的函数,分享给大家。

# 自己求图像平均阈值,一个常见的办法
# 求出图像均值作为阈值来二值化
def custom_image(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    h, w = gray.shape[:2]
    # 将图像转一维数组,一行,w*h 列,转换维度要保证其 size 不变
    m = np.reshape(gray, [1, w*h])
    # 求平均值来当做阈值,来分割图像
    mean = m.sum() / (w*h)
    print("mean: ", mean)
    ret, binary = cv2.threshold(gray, mean, 255, cv2.THRESH_BINARY)
    plt.imshow(binary, 'gray')
    plt.title("binary")

    plt.show()

OpenCV 尾声

1 个小时又过去了,对 Python OpenCV 图像写入相关的知识点,你掌握了吗?

空闲之余,可以订阅橡皮擦的爬虫百例课程学习爬虫知识。

想学 Python 爬虫,可以订阅橡皮擦专栏哦~ 🈲🈲🈲🈲 点击发现惊喜 🈲🈲🈲🈲

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容