数字图像处理11:阈值分割(基本全局阈值处理,Otsu 的最佳全局阈值,图像平滑改善全局阈值处理,图像分块的可变阈值)

本文深入探讨了阈值分割技术在图像处理中的应用,包括全局阈值、Otsu阈值、图像平滑改进及可变阈值处理。通过不同场景和算法的对比,展示了如何根据图像特性选择合适的阈值分割方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

阈值分割

从背景中提取物体的一种明显方法是,选择一个将这些模式分开的阈值 T。然后,f(x,y)>Tf(x,y)>Tf(x,y)>T的任何点 (x, y) 称为个对象点,否则该点称为背景点。
灰度阈值的成功与否直接关系到可区分直方图模式的波谷的宽度和深度。

而影响波谷特性的关键因素有以下几点:

(1)波峰间的间隔 (波峰离得越远,分离这些模式的机会越大);
(2)图像中的噪声内容 (模式随噪声的增加而展宽);
(3)物体和背景的相对尺寸;
(4)光源的均匀性;
(5)图像反射特性的均匀性。

基本全局阈值处理

当物体和背景像素的灰度分布十分明显时,可以用适用于整个图像的单个 (全局) 阈值。

实验思想

在大多数应用中,通常图像之间有较大变化,即使全局阈值是种合适的方法,也需要有能对每幅图像自动估计阈值的算法。下面的迭代算法可用于这一目的:
1.为全局阈值 T 选择一个初始估计值。
2.使用用初始的 T 分割该图像。这将产生两组像素:
G1 由灰度值大于 T 的所有像素组成,G2 由所有小于等于 T 的像素组成。
3.对 G1 和 G2 的像素分别计算平均灰度值 (均值)m1 和 m2。
4.计算一个新的阈值: T = (m1 + m2)/2
5.重复步骤 2 到步骤 4,直到连续迭代中的 T 值间的差小于个预定义的参数 dT 为止。

代码

主函数:

%% 运行 main_Global.m 

clc;
clear;
close all;

%% 课本图 10.34
im = imread('Fig1038(a)(noisy_fingerprint).tif');   % 原始图像 uint8
% 得到图像的行数、列数、以及每像素的维数(防止出现RGB图像)
[line,row,v]=size(im);
im = im(:,:,1);
t0 = sum(im(:))/(line*row);
[im1,NK] = Global_Threshold(im,0,t0);
x = 1:256;

%% 将结果保存到当前目录下的result文件夹下
imwrite(im, sprintf('result/%s.jpg','2_im1'));
imwrite(im1, sprintf('result/%s.jpg','2_im2'));

%% 显示图像
figure(1); 
subplot(131); imshow(im); title('原图'); axis on
subplot(133); imshow(im1); title('全局阈值'); axis on
subplot(132); plot(x,NK); title('直方图'); axis on

功能函数:

function [im1,NK] = Global_Threshold(im,dT,T0)

[h,w] = size(im);

%% 得到直方图和累计直方图
[NK,CH] = myHisteq(im);

G1sum = 0;
G2sum = 0;
dTemp = 255;

while(dTemp<dT)
    for i = 1:T0
        G1sum = G1sum + (i-1)*NK(i);
    end
    G1Ave = G1sum/CH(T0);

    for i = T0+1:256
        G2sum = G2sum + (i-1)*NK(i);
    end
    G2Ave = G2sum/(CH(256)-CH(T0));
    
    temp = round((G1Ave+G2Ave)/2);
    dTemp = abs(temp - T0);
    T0 = temp;
end
im1 = zeros(h,w);
for i =1:h
    for j = 1:w
        if(im(i,j)>=T0)
            im1(i,j) = 1;
        end
    end
end
im1 = logical(im1);

实验结果

图一是原图像,图二是灰度直方图,图三是全局阈值的结果

用 Otsu 的最佳全局阈值

Otsu 方法是最佳的,因为它使得类间方差最大化。其基本思想是,适当的阈值化的类就其像素灰度值而言,应当是截然不同的,相反地,就其灰度值而言,给出最佳类间分离的阈值 将是最佳的阈值。除了其最佳性之外,Otsu 方法还有一个重要的特性,即它完全以在一幅图像的直方图上执行计算为基础,直方图是很容易得到的一维阵列。

实验思想

Otsu 算法计算步骤如下:
1.计算输人图像的归一化直方图。使用 pi,i=0,1,2,.,L-1 表示该直方图的各个分量。
2. 对于 k=0, 1,2,.,L-1,计算累积和 P1(k) 和 P2(k), 计算公式为:
P1(k)=∑i=0kpi=1−P2(k)P1(k) = \sum_{i=0}^{k} pi = 1 - P2(k)P1(k)=i=0kpi=1P2(k)
3.对于 k=0,1,2…,L-1, 计算累积均值 m1(k) 和 m2(k)。计算公式为:
m1(k)=1P1(k)∗∑i=0k(i∗pi)m1(k) = \frac{1}{P1(k)} * \sum_{i=0}^{k} (i*pi) m1(k)=P1(k)1i=0k(ipi) m2(k)=1P2(k)∗∑i=k+1L−1(i∗pi)m2(k) = \frac{1}{P2(k)} * \sum_{i=k+1}^{L-1} (i*pi)m2(k)=P2(k)1i=k+1L1(ipi)
4.计算全局灰度均值 mg。计算公式为:mg=∑i=0L−1(i∗pi)mg = \sum_{i=0}^{L-1} (i*pi) mg=i=0L1(ipi)
5.对于 k=0,1,2…,L-1, 计算类间方差b2(k)b^2(k)b2(k)。计算公式:
b2(k)=P1(k)∗(m1(k)−mg)2+P2(k)∗(m2(k)−mg)2 b^2(k) = P1(k)*(m1(k)-mg)^2 + P2(k)*(m2(k)-mg)^2 b2(k)=P1(k)(m1(k)mg)2+P2(k)(m2(k)mg)2
6.得到 Otsu 阈值 k,即使得 b2(k) 最大的 k 值。如果最大值不唯一,用相应检测到的各个值 k 的平均得到 k*。

代码

主函数:

%% 运行 main_Otsu.m 

clc;
clear;
close all;

%% 课本图 10.39
im = imread('Fig1039(a)(polymersomes).tif');   % 原始图像 uint8
% 得到图像的行数、列数、以及每像素的维数(防止出现RGB图像)
[line,row,v]=size(im);
im = im(:,:,1);

[im1,NK] = Global_Threshold(im,0,sum(im(:))/(line*row));
[im2,NK] = my_Otsu(im);
x = 1:256;

%% 将结果保存到当前目录下的result文件夹下
imwrite(im, sprintf('result/%s.jpg','3_im1'));
imwrite(im1, sprintf('result/%s.jpg','3_im2'));
imwrite(im2, sprintf('result/%s.jpg','3_im3'));

%% 显示图像
figure(1); 
subplot(221); imshow(im); title('原图'); axis on
subplot(222); plot(x,NK); title('直方图'); axis on
subplot(223); imshow(im1); title('全局阈值'); axis on
subplot(224); imshow(im2); title('otsu阈值'); axis on

功能函数:

function [im1,NK] = my_Otsu(im)

[h,w] = size(im);
%% 像素值从0255,下标从1256
%% 得到直方图和累计直方图
[NK,CH] = myHisteq(im);
%归一化
nK = NK/(h*w);
cH = CH/(h*w);

%% 计算P1k P2k
P1 = cH;
P2 = 1-cH;

%% 累计均值的计算,仅有255个级别,比像素点取值总数小1
m1 = zeros(255,1);
for k = 1:255
    for j = 1:k
        m1(k) = m1(k) + j*nK(j);
    end
    m1(k) = m1(k)/cH(k);
end
m2 = zeros(255,1);
for k = 1:255
    for j = k+1:256
        m2(k) = m2(k) + j*nK(j);
    end
    m2(k) = m2(k)/(cH(256)-cH(k));
end
%% 全局灰度均值
mG = 0;
for j = 1:256
    mG = mG + j*nK(j);
end
mG = mG/cH(256);
%% 计算类间方差
phi2 = zeros(255,1);
for k = 1: 255
    phi2(k) = P1(k)*(m1(k)-mG)^2 + P2(k)*(m2(k)-mG)^2;
end

%% 最优phi2值
m = max(phi2);

%% 防止存在相等的最优值
num = 0;
k = zeros(10,0);
for i = 1:255
    if(phi2(i) == m) 
        num = num+1;
        k(num) = i;
    end
end
Bestk = sum(k)/num;
%% 得到最优二值化图像
im1 = zeros(h,w);
for i =1:h
    for j = 1:w
        if(im(i,j)>=Bestk)
            im1(i,j) = 1;
        end
    end
end
im1 = logical(im1);  

实验结果

图一是原图像,图二是灰度直方图图三是全局阈值的结果,图四是 otsu 方法的结果

图像平滑改善全局阈值处理

这道题未在本次实验要求范围内,但给了这张图片,于是进行了下面的实验。

实验思想

噪声会将简单的阈值处理问题变得不可解决,当噪声不能在源头减少,并且阈值处理又是所以选择的分割方法时,那么通常能增强性能的一种技术是,在阈值处理之前平滑图像。

部分核心代码

主函数:

%% 运行 main_SmoothOtsu.m 

clc;
clear;
close all;

%% 课本图 10.39
im = imread('Fig1040(a)(large_septagon_gaussian_noise_mean_0_std_50_added).tif');   % 原始图像 uint8
% 得到图像的行数、列数、以及每像素的维数(防止出现RGB图像)
[line,row,v]=size(im);
im = im(:,:,1);
[im1,NK1] = my_Otsu(im);
im2 = myAverage(im);
[im3,NK2] = my_Otsu(im2);
x = 1:256;

%% 将结果保存到当前目录下的result文件夹下
imwrite(im, sprintf('result/%s.jpg','4_im1'));
imwrite(im1, sprintf('result/%s.jpg','4_im2'));
imwrite(im2, sprintf('result/%s.jpg','4_im3'));
imwrite(im3, sprintf('result/%s.jpg','4_im4'));

%% 显示图像
figure(1); 
subplot(231); imshow(im); title('原图'); axis on
subplot(232); plot(x,NK1); title('直方图'); axis on
subplot(233); imshow(im1); title('全局阈值'); axis on
subplot(234); imshow(im2); title('原图'); axis on
subplot(235); plot(x,NK2); title('直方图'); axis on
subplot(236); imshow(im3); title('全局阈值'); axis on

功能函数前面都已经给出过

function [img_2] = myAverage(img_1)

size_1 = size(img_1);
h = size_1(1);
w = size_1(2);
img_2 = zeros(h, w);

%%边缘延拓四行四列
a = img_1(2:-1:1,:);
b = img_1(h:-1:h-1,:);
img_1 = [a;img_1;b];
c = img_1(:,2:-1:1);
d = img_1(:,w:-1:w-1);
img_1 = double([c,img_1,d]);

%5X5均值模板
L = 1/25*[1 1,1,1 1;1,1,1 1 1;1,1,1 1 1;1,1,1 1 1;1,1,1 1 1];

for i= 1:h
    for j = 1:w
        im = [img_1(i,j) img_1(i,j+1) img_1(i,j+2) img_1(i,j+3) img_1(i,j+4);...
            img_1(i+1,j) img_1(i+1,j+1) img_1(i+1,j+2) img_1(i+1,j+3) img_1(i+1,j+4);...
            img_1(i+2,j) img_1(i+2,j+1) img_1(i+2,j+2) img_1(i+2,j+3) img_1(i+2,j+4);...
            img_1(i+3,j) img_1(i+3,j+1) img_1(i+3,j+2) img_1(i+3,j+3) img_1(i+3,j+4);...
            img_1(i+4,j) img_1(i+4,j+1) img_1(i+4,j+2) img_1(i+4,j+3) img_1(i+4,j+4);...
            ];
        img_2(i,j) = round(sum(sum(L.*im)));
    end
end
img_2 =uint8(img_2);
end

实验结果

图一是原图像,图二是灰度直方图,图三是图一 otsu 阈值处理的结果

图四是滤波后图像,图五是灰度直方图,图六是图四 otsu 阈值处理的结果

图像分块的可变阈值

由于噪声或者光照不均匀等等一系列问题,导致无法直接进行阈值分割,图像平滑和边缘信息有益于阈值处理,但是在更经常的情况下,上述的两种做法效果不明显,只能使用可变阈值来进行解决。

实验思想

可变阈值处理最简单的方法之一是,把一幅图像分成不重叠的矩形。这种方法用于补偿光照和或反射的不均匀性。选择的矩形要足够小,以便每个矩形的光照都近似是均匀的。

代码

主函数:

%% 运行 main_divideOtsu.m 

clc;
clear;
close all;

%% 课本图 10.39
im = imread('Fig1046(a)(septagon_noisy_shaded).tif');   % 原始图像 uint8
% 得到图像的行数、列数、以及每像素的维数(防止出现RGB图像)
[line,row,v]=size(im);
im = im(:,:,1);

[im1,NK] = Global_Threshold(im,0,sum(im(:))/(line*row));
[im2,NK] = my_Otsu(im);
[im3,im4] = my_divideOtsu(im);
x = 1:256;

%% 将结果保存到当前目录下的result文件夹下
imwrite(im, sprintf('result/%s.jpg','5_im1'));
imwrite(im1, sprintf('result/%s.jpg','5_im2'));
imwrite(im2, sprintf('result/%s.jpg','5_im3'));
imwrite(im3, sprintf('result/%s.jpg','5_im4'));
imwrite(im4, sprintf('result/%s.jpg','5_im5'));

%% 显示图像
figure(1); 
subplot(231); imshow(im); title('原图'); axis on
subplot(232); plot(x,NK); title('直方图'); axis on
subplot(233); imshow(im1); title('全局阈值'); axis on
subplot(234); imshow(im2); title('otsu阈值'); axis on
subplot(235); imshow(im3); title('划分区域'); axis on
subplot(236); imshow(im4); title('可变阈值'); axis on

功能函数:

function [imd,imv] = my_divideOtsu(im)
[h,w] = size(im);
h1 = floor(h/2);
w1 = floor(w/3);
im1 = im(1:h1,1:w1);
im2 = im(1:h1,w1+1:2*w1);
im3 = im(1:h1,2*w1+1:w);
im4 = im(h1+1:h,1:w1);
im5 = im(h1+1:h,w1+1:2*w1);
im6 = im(h1+1:h,2*w1+1:w);
imd = im;
imd(h1,:) = 255;
imd(:,w1) = 255;
imd(:,2*w1) = 255;

[im1,n] = my_Otsu(im1);
[im2,n] = my_Otsu(im2);
[im3,n] = my_Otsu(im3);
[im4,n] = my_Otsu(im4);
[im5,n] = my_Otsu(im5);
[im6,n] = my_Otsu(im6);
imv = [im1,im2,im3;im4,im5,im6];

实验结果

图一是原图像,图二是灰度直方图,图三是图一全局阈值处理的结果

图四是图一 otsu 阈值处理的结果,图五是图像分块,图六是分块图像 otsu 阈值处理的结果

实验小结

在上面的图像分割中,通过不同的阈值处理方法,得到良好的图像分割结果。不难发现, 对于不同的噪声环境和光照影响等等,一种单一的阈值分割方法往往无法出色的完成任务。
在今后的实验中,要合理的根据自己的图像样本来选择合理的算法。例如是否需要对图像进行平滑去噪,是否需要图像分区来使光照近似均匀。不仅要掌握上面的处理方法,也明白了为什么对上面的图像进行这样的处理和分析。

### Otsu法动态阈值分割的实现 Otsu算法是一种基于全局阈值图像分割方法,能够自动计算最佳阈值以最大化类间方差。然而,在某些复杂场景下,单一的全局阈值可能无法满足需求,因此引入了动态阈值的概念[^1]。 #### 动态阈值分割基本概念 动态阈值分割是指根据不同区域的特点分别设置不同的阈值来进行图像分割的方法。这种方法尤其适合于局部特性差异较大的图像。在动态阈值分割中,通常会结合局部统计信息(如均值、方差等),或者利用滑动窗口的方式逐块计算阈值[^2]。 #### 结合Otsu算法的动态阈值分割 为了实现动态阈值分割Otsu方法,可以采用分块策略或自适应调整机制。以下是具体思路: 1. **分块处理** 将整个图像划分为若干个小块,每一块独立应用Otsu算法来计算各自的最优阈值。这样可以根据局部特征灵活调整分割效果。 2. **滑动窗口技术** 使用大小固定的滑动窗口遍历整幅图像,针对每个窗口内的像素分布重新计算Otsu阈值。此方法更精细但也增加了计算成本。 3. **加权融合** 对于边界重叠部分或其他特殊情况,可以通过加权平均等方式平滑过渡,减少不连续现象的影响。 下面是一个简单的Python代码示例展示如何基于OpenCV库实现带分块功能的动态Otsu阈值分割: ```python import cv2 import numpy as np def dynamic_otsu_threshold(image, block_size=64): height, width = image.shape[:2] result = np.zeros_like(image) for y in range(0, height, block_size): for x in range(0, width, block_size): # 提取子块 sub_image = image[y:y+block_size, x:x+block_size] # 计算当前子块的最佳Otsu阈值 _, thresholded_block = cv2.threshold(sub_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 应用于结果图 result[y:y+block_size, x:x+block_size] = thresholded_block return result if __name__ == "__main__": img_gray = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE) segmented_img = dynamic_otsu_threshold(img_gray) cv2.imshow('Dynamic Otsu Thresholding Result', segmented_img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 该程序定义了一个`dynamic_otsu_threshold`函数,接受输入灰度图片并返回经过动态Otsu阈值分割后的二值化图像。注意这里假设所有操作都在单通道灰阶模式下完成[^3]。 #### 性能提升与注意事项 尽管动态阈值分割提高了灵活性和适用范围,但它也带来了额外的时间开销。如果追求效率,则需考虑优化手段比如降低分辨率预处理或是简化模型参数估计过程。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值