当前位置: 首页 > article >正文

自适应二值化及伪影

一、自适应二值化

自适应二值化方法是针对全局阈值法(如 Otsu)在光照不均匀或局部对比度差异大的场景中效果不佳而提出的解决方案。它通过动态计算图像局部区域的阈值,实现更精准的二值化。以下是几种主流自适应二值化方法的详解:

1. 基于块的局部阈值法

原理

将图像划分为多个子块,每个子块独立计算阈值(如子块均值或高斯加权均值),并根据该阈值对块内像素进行二值化。

关键参数
  • 块大小(Block Size):子块的尺寸(通常为奇数,如 3×3、5×5)。
  • C 值:对计算出的阈值进行偏移(如减去 C 值,避免过分割)。
实现步骤
  1. 将图像分割为互不重叠的子块。
  2. 对每个子块计算统计量(如均值、高斯加权均值)。
  3. 根据统计量和 C 值生成局部阈值。
  4. 对每个子块内的像素进行二值化。
优缺点
  • 优点:适应局部光照变化。
  • 缺点:块边界可能出现伪影;块大小需手动调整。
OpenCV 示例
import cv2

# 读取灰度图
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# 自适应均值阈值
adaptive_mean = cv2.adaptiveThreshold(
    img, 255,
    cv2.ADAPTIVE_THRESH_MEAN_C,  # 均值法
    cv2.THRESH_BINARY,
    blockSize=11,  # 块大小
    C=2  # 偏移量
)

# 自适应高斯阈值
adaptive_gaussian = cv2.adaptiveThreshold(
    img, 255,
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C,  # 高斯加权法
    cv2.THRESH_BINARY,
    blockSize=11,
    C=2
)

2. Niblack 算法

原理

基于局部区域的均值和标准差动态计算阈值: (T = \mu + k \cdot \sigma)

  • \mu:子块均值
  • \sigma:子块标准差
  • k:调节系数(通常取 - 0.2~0.2)
改进点

通过引入标准差考虑局部对比度,适用于纹理复杂的图像。

实现步骤
  1. 计算每个子块的均值和标准差。
  2. 根据公式生成阈值。
  3. 二值化:像素值 > T → 255,否则 → 0。
优缺点
  • 优点:对纹理和噪声有更好的适应性。
  • 缺点:计算复杂度较高;k值需手动调参。
示例:
import cv2
import numpy as np
import matplotlib.pyplot as plt

def niblack(image, window_size=15, k=0.2):
    height, width = image.shape
    binary_image = np.zeros_like(image)

    half_window = window_size // 2

    for y in range(height):
        for x in range(width):
            # 计算局部区域的边界
            y_min = max(0, y - half_window)
            y_max = min(height, y + half_window + 1)
            x_min = max(0, x - half_window)
            x_max = min(width, x + half_window + 1)

            # 提取局部区域
            local_region = image[y_min:y_max, x_min:x_max]

            # 计算局部区域的均值和标准差
            mean = np.mean(local_region)
            std = np.std(local_region)

            # 计算阈值
            threshold = mean + k * std

            # 根据阈值进行二值化
            if image[y, x] > threshold:
                binary_image[y, x] = 255
            else:
                binary_image[y, x] = 0

    return binary_image

# 读取图像并转换为灰度图
image = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用Niblack算法进行二值化
binary_image = niblack(image, window_size=15, k=0.2)

# 显示原始图像和二值化后的图像
plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.imshow(image, cmap='gray')
plt.title('Original Image')
plt.axis('off')

plt.subplot(122)
plt.imshow(binary_image, cmap='gray')
plt.title('Niblack Binarization')
plt.axis('off')

plt.show()
代码解释
  1. 定义niblack函数:该函数接受图像、窗口大小和调节系数作为输入,返回二值化后的图像。
  2. 遍历图像的每个像素:对于每个像素,提取其局部区域,并计算该区域的均值和标准差。
  3. 计算阈值:根据 Niblack 算法的公式计算阈值。
  4. 根据阈值进行二值化:将像素值大于阈值的像素设为 255,小于阈值的像素设为 0。
  5. 返回二值化后的图像:将处理后的图像返回。
  6. 读取图像并进行二值化:使用cv2.imread函数读取图像,并调用niblack函数进行二值化。
  7. 显示原始图像和二值化后的图像:使用Matplotlib库显示原始图像和二值化后的图像

3. Sauvola 算法

原理

在 Niblack 算法基础上改进,解决低对比度区域过分割问题: (T = \mu \cdot \left(1 + k \cdot \left(\frac{\sigma}{R} - 1\right)\right))

  • R:最大可能的标准差(通常设为 128)
改进点
  • 引入R防止分母为零。
  • 通过\frac{\sigma}{R}归一化标准差,增强低对比度区域的鲁棒性。
实现步骤
  1. 计算子块的均值(\mu)和标准差(\sigma)
  2. 根据公式生成阈值。
  3. 二值化:像素值 > T → 255,否则 → 0。
优缺点
  • 优点:更适合低对比度场景(如老旧文档扫描)。
  • 缺点:计算复杂度高;需选择合适的k和R。

示例: 

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

def sauvola(image, window_size=15, k=0.2, R=128):
    height, width = image.shape
    binary_image = np.zeros_like(image)

    half_window = window_size // 2

    for y in range(height):
        for x in range(width):
            # 计算局部区域的边界
            y_min = max(0, y - half_window)
            y_max = min(height, y + half_window + 1)
            x_min = max(0, x - half_window)
            x_max = min(width, x + half_window + 1)

            # 提取局部区域
            local_region = image[y_min:y_max, x_min:x_max]

            # 计算局部区域的均值和标准差
            mean = np.mean(local_region)
            std = np.std(local_region)

            # 计算阈值
            threshold = mean * (1 + k * ((std / R) - 1))

            # 根据阈值进行二值化
            if image[y, x] > threshold:
                binary_image[y, x] = 255
            else:
                binary_image[y, x] = 0

    return binary_image

# 读取图像并转换为灰度图
image = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用Sauvola算法进行二值化
binary_image = sauvola(image, window_size=15, k=0.2, R=128)

# 显示原始图像和二值化后的图像
plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.imshow(image, cmap='gray')
plt.title('Original Image')
plt.axis('off')

plt.subplot(122)
plt.imshow(binary_image, cmap='gray')
plt.title('Sauvola Binarization')
plt.axis('off')

plt.show()
代码解释
  1. 定义sauvola函数:该函数接收图像、窗口大小、调节系数和最大可能的标准差作为输入,返回二值化后的图像。
  2. 遍历图像的每个像素:针对每个像素,提取其局部区域,并计算该区域的均值和标准差。
  3. 计算阈值:依据 Sauvola 算法的公式计算阈值。
  4. 根据阈值进行二值化:把像素值大于阈值的像素设为 255,小于阈值的像素设为 0。
  5. 返回二值化后的图像:将处理后的图像返回。
  6. 读取图像并进行二值化:使用cv2.imread函数读取图像,并调用sauvola函数进行二值化。
  7. 显示原始图像和二值化后的图像:使用Matplotlib库显示原始图像和二值化后的图像。

4. Bernsen 算法

原理

基于局部区域的最大和最小值计算阈值: (T = \frac{\text{max} + \text{min}}{2})

  • (\text{max} - \text{min} < \Delta),则强制二值化为背景。
关键参数
  • \Delta:对比度阈值(通常设为 15~30)。
实现步骤
  1. 计算子块内的最大和最小值。
  2. 若对比度不足(\text{max}-\text{min} < \Delta),设为背景。
  3. 否则,以均值为阈值二值化。
优缺点
  • 优点:计算速度快,适合实时处理。
  • 缺点:对噪声敏感,需结合降噪预处理。
示例:
import cv2
import numpy as np
import matplotlib.pyplot as plt


def bernsen(image, window_size=15, delta=15):
    """
    Bernsen算法实现
    :param image: 输入的灰度图像
    :param window_size: 局部窗口的大小,通常为奇数
    :param delta: 对比度阈值
    :return: 二值化后的图像
    """
    height, width = image.shape
    binary_image = np.zeros_like(image)
    half_window = window_size // 2

    for y in range(height):
        for x in range(width):
            # 计算局部区域的边界
            y_min = max(0, y - half_window)
            y_max = min(height, y + half_window + 1)
            x_min = max(0, x - half_window)
            x_max = min(width, x + half_window + 1)

            # 提取局部区域
            local_region = image[y_min:y_max, x_min:x_max]

            # 计算局部区域的最大值和最小值
            local_max = np.max(local_region)
            local_min = np.min(local_region)

            # 计算对比度
            contrast = local_max - local_min

            # 计算阈值
            threshold = (local_max + local_min) / 2

            if contrast < delta:
                # 若对比度小于阈值,则认为是低对比度区域,将该像素设为背景(这里设为0)
                binary_image[y, x] = 0
            else:
                # 根据阈值进行二值化
                if image[y, x] > threshold:
                    binary_image[y, x] = 255
                else:
                    binary_image[y, x] = 0

    return binary_image


# 读取图像并转换为灰度图
image = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用Bernsen算法进行二值化
binary_image = bernsen(image, window_size=15, delta=15)

# 显示原始图像和二值化后的图像
plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.imshow(image, cmap='gray')
plt.title('Original Image')
plt.axis('off')

plt.subplot(122)
plt.imshow(binary_image, cmap='gray')
plt.title('Bernsen Binarization')
plt.axis('off')

plt.show()
代码解释:
  1. 函数定义:定义了bernsen函数,该函数接收三个参数:输入的灰度图像image、局部窗口的大小window_size(通常为奇数)和对比度阈值delta
  2. 遍历图像像素:对于图像中的每个像素,提取其所在的局部区域。
  3. 计算局部区域的最值和对比度:计算局部区域的最大值local_max和最小值local_min,并计算它们的差值作为对比度contrast
  4. 计算阈值:通过(local_max + local_min) / 2计算阈值。
  5. 二值化处理
    • 如果对比度小于delta,认为该区域是低对比度区域,将该像素设为背景(这里设为 0)。
    • 否则,根据计算得到的阈值对该像素进行二值化处理。
  6. 读取图像并进行二值化:使用cv2.imread函数读取图像,并将其转换为灰度图。调用bernsen函数进行二值化处理。
  7. 显示结果:使用matplotlib库显示原始图像和二值化后的图像。
注意事项
  • window_sizedelta是可以调整的参数,你可以根据具体的图像特点进行调整,以获得更好的二值化效果。一般来说,window_size越大,算法对局部信息的考虑范围越广,但计算量也会相应增加;delta越大,对低对比度区域的判定越严格

5. 自适应 Otsu(局部 Otsu)

原理

将图像划分为多个子块,对每个子块单独应用 Otsu 算法计算阈值。

实现步骤
  1. 分块。
  2. 对每个子块计算 Otsu 阈值。
  3. 对每个子块内的像素二值化。
优缺点
  • 优点:结合 Otsu 的自适应性,处理复杂光照。
  • 缺点:计算量大;块边界可能不一致。
示例: 
import cv2
import numpy as np
import matplotlib.pyplot as plt

def adaptive_otsu(image, block_size=16):
    """
    自适应Otsu算法实现
    :param image: 输入的灰度图像
    :param block_size: 子块的大小
    :return: 二值化后的图像
    """
    height, width = image.shape
    binary_image = np.zeros_like(image)

    for y in range(0, height, block_size):
        for x in range(0, width, block_size):
            # 计算子块的边界
            y_end = min(y + block_size, height)
            x_end = min(x + block_size, width)

            # 提取子块
            sub_block = image[y:y_end, x:x_end]

            # 对当前子块应用Otsu算法
            _, sub_binary = cv2.threshold(sub_block, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

            # 将子块的二值化结果放回原图像对应的位置
            binary_image[y:y_end, x:x_end] = sub_binary

    return binary_image

# 读取图像并转换为灰度图
image = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用自适应Otsu算法进行二值化
binary_image = adaptive_otsu(image, block_size=16)

# 显示原始图像和二值化后的图像
plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.imshow(image, cmap='gray')
plt.title('Original Image')
plt.axis('off')

plt.subplot(122)
plt.imshow(binary_image, cmap='gray')
plt.title('Adaptive Otsu Binarization')
plt.axis('off')

plt.show()
代码解释:
  1. 函数定义:定义了adaptive_otsu函数,它接收两个参数,image是输入的灰度图像,block_size是子块的大小,默认值为 16。
  2. 遍历子块:通过嵌套的for循环遍历图像的每个子块。
  3. 提取子块:根据当前的坐标和子块大小,确定子块的边界,并提取出对应的子块。
  4. 应用 Otsu 算法:对提取的子块使用cv2.threshold函数结合cv2.THRESH_BINARY + cv2.THRESH_OTSU参数进行 Otsu 二值化处理。
  5. 更新二值化图像:将子块的二值化结果放回原图像对应的位置。
  6. 读取图像并进行二值化:使用cv2.imread函数读取图像,并将其转换为灰度图。调用adaptive_otsu函数进行自适应 Otsu 二值化处理。
  7. 显示结果:使用matplotlib库显示原始图像和二值化后的图像。
注意事项:
  • block_size是一个重要的参数,它会影响二值化的效果。较小的block_size可以更好地适应局部光照变化,但可能会增加计算量和出现块边界伪影;较大的block_size则可能导致对局部光照变化的适应性变差。你可以根据具体的图像特点调整该参数。

6. 方法对比与选择

方法适用场景计算复杂度调参难度
自适应均值 / 高斯均匀光照下的轻微对比度变化
Niblack纹理复杂、噪声较多的图像
Sauvola低对比度、老旧文档扫描
Bernsen实时处理、简单场景
自适应 Otsu全局光照不均、复杂背景

7. 优化技巧

  1. 预处理降噪:先进行高斯模糊或中值滤波,减少噪声对局部统计量的影响。
  2. 重叠分块:相邻子块部分重叠,减少边界伪影。
  3. 动态块大小:根据图像复杂度调整块大小(如使用多尺度分块)。
  4. 结合全局阈值:对整体均匀的区域使用全局阈值,局部复杂区域使用自适应方法。

总结

自适应二值化通过局部阈值解决全局阈值的局限性,适用于光照不均、对比度差异大的场景。选择方法时需权衡计算复杂度、调参难度和场景需求。实际应用中,常结合预处理(如降噪)和后处理(如形态学操作)提升效果。

二、伪影 

伪影(Artifact)是指在图像处理或信号采集过程中,由于算法局限性、硬件误差或噪声干扰等因素引入的非真实、异常的图像结构或像素值。在二值化处理中,伪影通常表现为图像中出现的不自然边缘、斑点或纹理,这些特征并非原始图像的真实内容。

二值化中伪影的常见表现

  1. 块边界伪影

    • 现象:图像被划分为多个子块后,块与块之间的阈值差异导致边界处出现明显的亮暗分割线。
    • 原因:基于块的局部阈值法(如自适应均值 / 高斯阈值)中,相邻子块的局部阈值不同,导致块边界像素的二值化结果不一致。
  2. 噪声放大伪影

    • 现象:图像中出现孤立的亮点或暗点,类似于椒盐噪声。
    • 原因:局部阈值计算受噪声影响(如 Niblack 算法中标准差的波动),导致部分噪声像素被错误分类。
  3. 纹理丢失或过分割伪影

    • 现象:图像中原本连续的纹理被分割成不连贯的区域,或低对比度区域被错误地全部设为背景。
    • 原因:阈值选择不当(如 Sauvola 算法中参数 k 设置不合理),或局部统计量未能准确反映图像内容。

伪影的主要成因

  1. 分块处理的局限性

    • 固定块大小无法适应图像内容的变化,导致边缘或纹理区域被错误分割。
  2. 局部统计量的不稳定性

    • 子块内像素分布不均(如包含噪声或复杂纹理)时,均值、标准差等统计量无法代表真实特征。
  3. 参数设置不合理

    • 如块大小、C 值、k 值等参数未根据图像特性调整,导致阈值计算偏离最优值。

减少伪影的方法

  1. 重叠分块

    • 相邻子块部分重叠(如 50% 重叠),通过插值或加权平均平滑块边界的阈值差异。
  2. 动态块大小

    • 根据图像复杂度自动调整块大小(如在纹理密集区域使用更小的块)。
  3. 预处理降噪

    • 使用高斯模糊、中值滤波等方法减少噪声对局部统计量的干扰。
  4. 后处理优化

    • 应用形态学操作(如闭运算)填补小孔或平滑边缘,或通过连通组件分析去除孤立噪声点。
  5. 改进阈值计算方法

    • 采用更鲁棒的局部统计量(如中值而非均值),或结合全局信息(如自适应 Otsu)。

示例说明

  • 基于块的局部阈值法伪影
    若图像中某块均值较高(如明亮区域),相邻块均值较低(如阴影区域),块边界的像素可能因阈值差异被错误二值化,形成明显的分界线。

  • Niblack 算法伪影
    当子块内存在噪声时,标准差 σ 增大,导致阈值 T 偏高,部分真实前景像素被误判为背景。

总结

伪影是二值化处理中常见的干扰,其成因与算法设计、参数选择及图像特性密切相关。通过优化分块策略、预处理降噪及后处理调整,可以有效减少伪影,提升二值化结果的质量。


http://www.kler.cn/a/585571.html

相关文章:

  • RabbitMQ消息持久化与Lazy模式对比分析
  • 每日一题——逆波兰表达式
  • DeepSeek API 客户端使用文档
  • Spring Boot对接twilio发送邮件信息
  • Docker 部署Spring boot + Vue(若依为例)
  • Linux系统下安装Gedit文本编辑器的完整指南
  • C++能力测试题
  • 深入 Python 网络爬虫开发:从入门到实战
  • 完善 Django 框架以实现传递视频、图片给算法模块进行识别分析
  • 防止手机验证码被刷:React + TypeScript 与 Node.js + Express 的全面防御策略
  • WebLogic XMLDecoder反序列化漏洞(CVE-2017-10271)深度解析与实战复现
  • JVM 的不同组成部分分别有什么作用?
  • URL 中的参数通常用于跟踪和传递信息,特别是在在线广告和营销活动中。
  • 鸿蒙初学者学习手册(HarmonyOSNext_API14)_UIContext(@ohos.arkui.UIContext (UIContext))
  • tensorflow与torch并行读取数据机制
  • 浅谈StarRocks数据库简介及应用
  • 阿里巴巴发布 R1-Omni:首个基于 RLVR 的全模态大语言模型,用于情感识别
  • ZooKeeper的五大核心作用及其在分布式系统中的关键价值
  • Redis 常用数据类型
  • 在 Qt 中自定义控件样式:使用 QProxyStyle 代理和修改绘制元素