深入理解局部阈值的原理、实现与应用
局部阈值是一种自适应二值化方法,为图像的不同区域计算不同的阈值,适用于光照不均匀的图像处理。
局部阈值(也称为自适应阈值)是一种图像二值化技术,它为图像中的每个像素或每个局部区域计算不同的阈值,而不是使用单一的全局阈值。这种方法特别适用于光照不均匀或对比度变化较大的图像。
局部阈值的基本思想是:
常见的局部阈值方法包括:
自适应阈值的计算公式为:
T(x,y) = mean_of_neighbors - C
其中:
import cv2
import numpy as np
import matplotlib.pyplot as plt
def adaptive_threshold_builtin(image_path, method=cv2.ADAPTIVE_THRESH_MEAN_C, block_size=11, c=2):
"""
使用OpenCV内置函数实现自适应阈值
:param image_path: 输入图像路径
:param method: 自适应阈值方法
:param block_size: 邻域大小
:param c: 常数偏移量
:return: 原图和自适应阈值后的图像
"""
# 读取图像
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 应用自适应阈值
binary = cv2.adaptiveThreshold(gray, 255, method, cv2.THRESH_BINARY, block_size, c)
return img, cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
def manual_adaptive_threshold(image_path, block_size=11, c=2):
"""
手动实现自适应阈值算法
:param image_path: 输入图像路径
:param block_size: 邻域大小
:param c: 常数偏移量
:return: 二值化后的图像
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
img = img.astype(np.float64)
height, width = img.shape
radius = block_size // 2
# 初始化输出图像
binary = np.zeros_like(img)
# 计算每个像素的局部阈值
for i in range(radius, height - radius):
for j in range(radius, width - radius):
# 提取邻域
neighborhood = img[i-radius:i+radius+1, j-radius:j+radius+1]
# 计算邻域的均值
local_mean = np.mean(neighborhood)
# 计算阈值
threshold = local_mean - c
# 应用阈值
if img[i, j] > threshold:
binary[i, j] = 255
else:
binary[i, j] = 0
return cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_GRAY2BGR), cv2.cvtColor(binary.astype(np.uint8), cv2.COLOR_GRAY2BGR)
def gaussian_adaptive_threshold(image_path, block_size=11, c=2):
"""
高斯加权自适应阈值
:param image_path: 输入图像路径
:param block_size: 邻域大小
:param c: 常数偏移量
:return: 原图和高斯加权自适应阈值结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
img = img.astype(np.float64)
height, width = img.shape
radius = block_size // 2
# 创建高斯权重核
def create_gaussian_kernel(size, sigma):
kernel = np.zeros((size, size), dtype=np.float32)
center = size // 2
for i in range(size):
for j in range(size):
x, y = i - center, j - center
kernel[i, j] = np.exp(-(x**2 + y**2) / (2 * sigma**2))
return kernel / np.sum(kernel)
# 创建高斯核
gaussian_kernel = create_gaussian_kernel(block_size, block_size/6)
# 初始化输出图像
binary = np.zeros_like(img)
# 计算每个像素的局部阈值
for i in range(radius, height - radius):
for j in range(radius, width - radius):
# 提取邻域
neighborhood = img[i-radius:i+radius+1, j-radius:j+radius+1]
# 计算加权均值
local_mean = np.sum(neighborhood * gaussian_kernel)
# 计算阈值
threshold = local_mean - c
# 应用阈值
if img[i, j] > threshold:
binary[i, j] = 255
else:
binary[i, j] = 0
return cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_GRAY2BGR), cv2.cvtColor(binary.astype(np.uint8), cv2.COLOR_GRAY2BGR)
def bradley_adaptive_threshold(image_path, block_size=11, threshold_ratio=0.15):
"""
Bradley自适应阈值算法
:param image_path: 输入图像路径
:param block_size: 邻域大小
:param threshold_ratio: 阈值比率
:return: 原图和Bradley自适应阈值结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
img = img.astype(np.float64)
height, width = img.shape
radius = block_size // 2
# 初始化输出图像
binary = np.zeros_like(img)
# 计算每个像素的局部阈值
for i in range(radius, height - radius):
for j in range(radius, width - radius):
# 提取邻域
neighborhood = img[i-radius:i+radius+1, j-radius:j+radius+1]
# 计算邻域的均值
local_mean = np.mean(neighborhood)
# Bradley算法:如果像素值小于均值的一定比例,则设为背景
threshold = local_mean * (1 - threshold_ratio)
# 应用阈值
if img[i, j] > threshold:
binary[i, j] = 255
else:
binary[i, j] = 0
return cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_GRAY2BGR), cv2.cvtColor(binary.astype(np.uint8), cv2.COLOR_GRAY2BGR)
def compare_local_thresholding_methods(image_path):
"""
比较不同的局部阈值方法
:param image_path: 输入图像路径
:return: 原图和不同方法的二值化结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 不同的局部阈值方法
methods = {
'Adaptive Mean': cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2),
'Adaptive Gaussian': cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2),
'Otsu': cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],
'Manual Adaptive': manual_adaptive_threshold_from_array(img, 11, 2)
}
results = {}
for name, result in methods.items():
results[name] = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), results
def local_threshold_with_postprocessing(image_path):
"""
局部阈值结合后处理
:param image_path: 输入图像路径
:return: 原图、二值化结果和后处理结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 应用自适应阈值
binary = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
# 后处理:使用形态学操作去除噪声
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
# 开运算:先腐蚀后膨胀,去除小的噪声点
cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# 闭运算:先膨胀后腐蚀,填补小的孔洞
final_result = cv2.morphologyEx(cleaned, cv2.MORPH_CLOSE, kernel)
return (cv2.cvtColor(img, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(final_result, cv2.COLOR_GRAY2BGR))
def manual_adaptive_threshold_from_array(img_array, block_size=11, c=2):
"""
从数组输入实现手动自适应阈值算法
:param img_array: 输入图像数组
:param block_size: 邻域大小
:param c: 常数偏移量
:return: 二值化后的图像
"""
img = img_array.astype(np.float64)
height, width = img.shape
radius = block_size // 2
# 初始化输出图像
binary = np.zeros_like(img)
# 计算每个像素的局部阈值
for i in range(radius, height - radius):
for j in range(radius, width - radius):
# 提取邻域
neighborhood = img[i-radius:i+radius+1, j-radius:j+radius+1]
# 计算邻域的均值
local_mean = np.mean(neighborhood)
# 计算阈值
threshold = local_mean - c
# 应用阈值
if img[i, j] > threshold:
binary[i, j] = 255
else:
binary[i, j] = 0
return binary.astype(np.uint8)
def illumination_correction_local_threshold(image_path):
"""
结合光照校正的局部阈值
:param image_path: 输入图像路径
:return: 原图、光照校正结果和局部阈值结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 估计光照分量(使用大核模糊)
illumination = cv2.GaussianBlur(img, (41, 41), 0)
# 校正光照
corrected = img.astype(np.float32) / (illumination.astype(np.float32) + 1e-6) * 128
corrected = np.clip(corrected, 0, 255).astype(np.uint8)
# 应用局部阈值
binary = cv2.adaptiveThreshold(corrected, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
return (cv2.cvtColor(img, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(corrected, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR))
# 使用示例
if __name__ == "__main__":
# 注意:需要提供实际的图像路径
# img, result = adaptive_threshold_builtin('image.jpg')
# manual_img, manual_result = manual_adaptive_threshold('image.jpg', 11, 2)
# gaussian_img, gaussian_result = gaussian_adaptive_threshold('image.jpg', 11, 2)
# bradley_img, bradley_result = bradley_adaptive_threshold('image.jpg', 11, 0.15)
# comp_img, comp_results = compare_local_thresholding_methods('image.jpg')
# post_img, post_result, post_final = local_threshold_with_postprocessing('image.jpg')
# illum_img, illum_corrected, illum_result = illumination_correction_local_threshold('image.jpg')
pass