Mathworks-工程与科学的图像处理笔记-全-

Mathworks 工程与科学的图像处理笔记(全)

1:专业领域概述 🖼️

在本节课中,我们将要学习图像处理在工程与科学领域的广泛应用及其重要性,并了解MathWorks提供的系列课程结构。

从图像中提取信息对于众多应用至关重要。

这些应用包括诊断医疗状况、研究气候变化的影响、设计自主系统以及改善农业。

无论您身处哪个领域,分析图像都是当今许多工作所必需的技能。这就是MathWorks创建《工程与科学图像处理》系列课程的原因。

无论您是图像处理的新手,还是正在寻找新工具来提高工作效率,这个专业课程都适合您。😊,因为您将使用MATLAB来快速执行图像处理任务。

MATLAB专为工程师和科学家设计,包含许多应用程序,使您能够快速测试不同方法并可视化结果。这些应用程序会自动生成代码,以便您可以复现和扩展您的工作。

该系列课程分为三个部分。在第一门课程中,您将扎实地学习如何处理数字图像。

您将学习如何创建常见的图像调整,以及如何隔离感兴趣区域以进行进一步分析。

例如,您将使用颜色信息来识别成熟的蓝莓。

并从卫星图像中计算冰川融化的量。在第二门课程中,您将解决图像分割中的常见挑战。

例如,您将学习减少噪声的技术,以及分离重叠的物体,确保您只隔离相关信息。

重要的是。

您将分析找到的区域,计算诸如面积和方向等属性。通常,您需要将图像处理步骤应用于许多图像,或者可能需要分析视频。

在第三门课程中,您将自动化您的算法以处理数千张图像,从而节省时间和精力。

当您拥有大量图像时,目视检查所有图像是不可行的。您将练习分析结果,以识别需要进一步调查的异常值。

在系列课程结束时,您将把新学到的技能应用到一个最终项目中:在嘈杂的视频中检测汽车以分析交通模式。😊。

您甚至能够处理一些复杂情况,例如当物体被部分遮挡时。最后,您将创建一个视频,其中包含在检测到的车辆驶过时围绕它们的边界框。

图像处理是一项需求量很大的职业技能。无论您是开发自主系统、诊断疾病还是研究宇宙,这个系列课程都将帮助您取得成功。

那么,让我们开始吧。


本节课中我们一起学习了图像处理在多个关键领域(如医疗、气候、自动驾驶和农业)的核心作用,了解了MathWorks《工程与科学图像处理》系列课程的三部分结构:数字图像基础、图像分割挑战以及算法自动化,并明确了掌握这些技能对职业发展的重要性。

2:图像处理导论 🎼

欢迎学习《图像处理导论》。在数据驱动日益重要的世界中,从图像中提取信息在许多应用中变得越来越关键。在接下来的几个模块中,你将学习如何对各种类型的图像数据进行定量的科学分析。

请看这两张相隔两年拍摄的冰架卫星图像。在本课程结束时,你将能够分离出蓝色的融水区域并计算其面积,从而估算冰架随时间的变化情况。

课程首先介绍数字图像。你将学习图像是如何被捕获、存储并导入到 MATLAB 中的。

接下来,你将学习对图像进行计算,例如旋转、调整大小以及通过平均来降低噪声。为了从图像中提取有用信息(比如这条裂缝的尺寸),你通常需要隔离出感兴趣的区域。

你所采用的具体方法取决于图像的类型及其属性。

你将学习基于灰度强度或颜色信息来分离图像中区域的不同技术。

当然,并非所有图像在原始状态下都适合直接处理。你通常需要先对它们进行调整。为了让特别暗或特别亮的图像中的细节更容易看清,你将学习几种改善图像对比度的方法以及它们各自的适用场景。

本课程中使用的图像旨在代表你在自己领域可能遇到的情况。

在学习示例的过程中,请花时间将这些技术应用到你自己的图像上。如果结果不是最佳,也无需担心。在后续的专题课程中,你将基于本课程学到的技能,学习更高级的分割方法、处理整个批次的图像。

以及分割视频文件。学习本课程不需要任何图像处理经验。但是,你应该熟悉 MATLAB 的基础知识。如果你是 MATLAB 新手,我们建议你先完成 MATLAB Onramp,这是一个免费的两小时入门教程。

我们将在讨论区随时为你提供帮助。

祝你学习顺利。

3:什么是数字图像 📸

在本节课中,我们将要学习数字图像的基本概念,包括其构成、类型、常见文件格式以及影响图像质量的因素。理解这些基础知识是进行后续图像处理的第一步。


数字图像处理被用于解决许多科学与工程应用中的挑战性问题。但在使用 MATLAB 处理图像之前,图像必须先被捕获并以数字格式存储。

数字图像的捕获与构成

数字相机使用电子光感受器。这些感受器利用光敏材料将入射光波转换为电信号,并将其记录为数字强度值。

人类将强度感知为亮度,亮度由光的振幅决定。单色传感器仅记录用于捕获灰度图像的强度值。

为了创建彩色图像,需要使用多个传感器来记录三种不同波长下的入射光强度,人类将其感知为红、绿和蓝。这些颜色分量被称为通道,它们可以根据各自的强度重新组合以形成原始颜色。

光感受器通常排列成矩形网格。网格上单个感受器捕获的强度被称为一个像素,像素是数字图像的最小组成单位。用于构成图像的像素数量被称为图像分辨率

分辨率通常表示为像素总数或垂直与水平方向上的像素数量。通常,更高分辨率的图像可以捕获更多视觉信息,从而获得更好的质量。影响图像质量的其他因素包括传感器效率、对焦以及曝光量(即允许到达传感器的光量)。

科学图像与三维图像

科学图像也使用人眼不可见但光感受器可检测的波长(包括紫外线和红外线)进行捕获。但这些强度信息仍然可以转换为人眼可见的波长。

除了相机,图像数据也可以由其他类型的仪器生成,这些仪器使用图像来表示非视觉信息。例如,天气图是使用雷达信号生成的。这里,检测到的反射信号量取决于多种因素,包括降水量的多少。医疗专业人员则通过拍摄X光图像或使用磁共振成像来可视化人体结构。

在这些专业领域,当测量是在一个体积上进行时,通常需要处理三维图像。与摄影图像类似,三维像素(称为体素)位于矩形网格上。查看三维图像中的所有信息具有挑战性,因此通常选择高维数据的切片,这些切片对应于在特定平面或表面上进行的测量。

另一种你可能熟悉的三维图像类型是视频,因为二维图像帧可以沿着代表时间的额外维度按顺序堆叠。

图像文件格式与压缩

数字图像数据的类型如此之多,存储它们的方式也多种多样就不足为奇了。其中一些文件格式你可能已经熟悉,而另一些除非你在使用它们的领域工作,否则可能不熟悉。

为什么有这么多格式?一个原因是图像数据可能具有不同的维度,以及它们代表的是单张图像还是一系列帧。另一个原因是每种格式使用不同的压缩技术。

大多数文件格式使用压缩来减少存储图像所需的内存。这是因为传感器记录的信息(称为原始图像)需要大量数据来存储,即使是低分辨率图像也是如此。而图像数据集通常由数千或数百万张图像或视频帧组成。压缩的目标是尝试用最少的内存存储最多的视觉信息。

压缩算法可分为两种主要类型:无损压缩允许精确再现原始图像,但文件大小的减少通常有限;而有损压缩能显著节省内存,但在压缩过程中会丢失视觉信息,因此压缩后的图像接近但不完全等同于原始图像。

图像文件类型之间的另一个区别是文件中可能包含的附加数据。例如,视频数据通常与音频数据结合为多媒体文件。图像、视频和多媒体文件还包含称为元数据的附加文本信息。元数据可以包括图像拍摄日期和位置、使用的设备、图像描述、视频帧率等信息,具体取决于图像类型和文件标准。

如何选择文件格式

面对如此多的格式,你可能想知道应该使用哪种格式。重要的考虑因素包括压缩与质量之间的权衡、你正在处理的图像数量、图像是二维还是三维,以及你打算执行的图像处理任务。在处理大量图像或执行计算机视觉和深度学习任务时,还必须考虑硬件限制,例如可用内存或处理能力。

然而请注意,除非你计划捕获自己的图像或愿意在格式之间进行转换,否则这个选择通常已经为你做好了。正如你将在下一课中看到的,一旦图像数据被导入 MATLAB,无论原始格式如何,大多数图像数据都以类似的方式表示。


在本节课中,我们一起学习了数字图像的基本原理,包括其通过像素和通道的构成方式、分辨率和质量的影响因素、科学图像与三维图像(如体素和视频)的概念,以及各种图像文件格式和压缩技术(无损与有损)的作用。理解这些概念是有效进行图像处理的重要基础。

4:在MATLAB中表示图像

在本节课中,我们将要学习如何将图像导入MATLAB,并理解图像数据是如何以数值数组的形式表示的。我们还将学习如何从图像数组中提取像素强度值,以及如何使用图像查看器应用来探索图像数据。


🖼️ 导入与显示图像

科学家、工程师和其他专业人员使用MATLAB执行常见的图像处理任务。但在处理图像之前,必须先将图像导入MATLAB。

要导入图像数据,可以使用 imread 函数。该函数将图像数据作为数值数组返回,并存储在一个新变量中。请注意,您的图像文件应位于MATLAB的当前文件夹或搜索路径中。

代码示例:

% 导入图像
imageData = imread('your_image.jpg');

导入图像后,可以使用 imshow 函数来查看图像。

代码示例:

% 显示图像
imshow(imageData);

🔢 理解图像数据表示

仔细观察输出,您会发现导入的图像被表示为一个整数数组。数组的总行数和总列数对应于图像的分辨率,而数组中的每个值代表一个像素的强度。

您可能还注意到,图像数据的类型是 uint8(8位无符号整数),这与MATLAB通常用来表示大多数数值数据的 double(双精度浮点数)类型不同。

double 变量可以表示广泛的数值,包括负值和小数值。而 uint8 变量只能表示0到255之间的整数集合。

那么,为什么不使用 double 类型来存储图像数据呢?为了理解原因,我们可以提取一个像素值进行比较。

代码示例:

% 提取特定位置的像素值(例如第100行,第50列)
pixelValue_uint8 = imageData(100, 50);

% 将其转换为double类型
pixelValue_double = double(pixelValue_uint8);

% 使用whos函数查看变量信息(内存占用)
whos pixelValue_uint8 pixelValue_double

一个 double 值所需的内存是 uint8 值的8倍。对于单个值,这种差异可能无关紧要,但当乘以典型图像数组中大量的元素时,这种差异就变得非常显著。对于高分辨率的彩色图像尤其如此。


🌈 处理彩色图像

彩色图像同样使用 imreadimshow 函数加载和查看。

灰度图像和彩色图像数据的主要区别在于,彩色图像的每个像素现在有三个强度值,分别对应红色、绿色和蓝色通道。

存储三个强度值的需求意味着彩色图像数组是三维的,其中每个颜色的强度值存储在第三维度上。

代码示例:

% 访问单个像素的某个颜色通道强度(例如第100行,第50列的红色通道)
redIntensity = colorImageData(100, 50, 1); % 1代表红色通道

% 提取单个像素的所有三个颜色强度
allChannels = colorImageData(100, 50, :);

% 提取整个绿色通道(所有像素的绿色强度)
greenChannel = colorImageData(:, :, 2); % 2代表绿色通道
imshow(greenChannel); % 显示为灰度图像

在上面的代码中,冒号 : 是“该维度所有值”的简写。提取出的绿色通道是一个二维数组,可以可视化为灰度图像。亮区表示绿色强度高的像素,暗区表示绿色强度低的像素。


🔍 使用图像查看器交互探索

仅凭强度值预测亮度或颜色,或仅凭图像估计强度值,都具有挑战性。因此,使用图像查看器应用同时检查图像和强度值会很有帮助。

使用 imtool 函数,并以图像变量或文件名作为输入,即可加载图像。

代码示例:

% 在图像查看器中打开图像
imtool(imageData);

该应用会自动预览图像,并提供一种交互方式来同时检查图像和数据。例如:

  • 将光标悬停在图像上可显示像素位置和强度值。
  • 点击标尺工具可测量两个感兴趣点之间的像素距离。
  • 使用缩放按钮放大区域,使用平移按钮在图像中导航。

要调查特定区域的像素值,可以从“工具”菜单中选择“像素区域”。这将在图像选定区域上打开一个强度值覆盖层。

如果您想将注意力限制在单个区域,可以使用裁剪按钮并根据需要调整选框。结果将生成一个新图像,可以进一步分析或导出为变量。


📝 总结

本节课中我们一起学习了:

  1. 如何使用 imreadimshow 函数加载和显示图像。
  2. 灰度图像和彩色图像在MATLAB中如何被表示为整数数组(uint8),以及这种表示方式在内存效率上的优势。
  3. 如何通过提供行、列和通道值,或使用冒号运算符,从数组中提取图像数据。
  4. 如何使用图像查看器应用交互式地探索图像数据,包括检查像素值、测量距离和裁剪区域。

掌握这些基础知识是进行后续图像处理操作的关键第一步。

05:处理图像数据

在本节课中,我们将学习如何对图像数据进行基本操作,包括算术运算、数据类型转换、色彩空间转换、几何变换以及如何保存处理结果。

图像以数字矩阵的形式存储。这意味着处理图像数据与处理其他矩阵和数组类似。例如,裁剪灰度图像等同于访问矩阵的一个子集。然而,需要注意大多数图像的数据类型是 uint8。这种类型占用内存较少,但如果不小心处理,可能会导致一些意想不到的错误。

算术运算与数据类型转换

上一节我们提到了图像作为矩阵的本质,本节中我们来看看对图像进行算术运算时需要注意的关键问题。一个常见的图像处理任务是将多张图像进行平均,这通常可以提高信噪比。让我们通过平均这些猎户座星云的图像来练习处理图像数据。

要创建平均图像,只需将图像相加,然后除以图像总数。

average_image = (image1 + image2 + image3 + image4 + image5) / 5;

然而,结果看起来并不正确。与单张图像相比,平均后的图像中心区域显得均匀,整体也更暗。这是为什么呢?

请注意,这些图像的数据类型是 uint8uint8 数据类型只能存储 0 到 255 之间的整数值。这虽然节省内存,但存在限制。例如,在 uint8 数据类型中计算 200 + 200,结果是 255,而不是 400。这个操作导致数据饱和,达到了该数据类型的最大值 255。

这解释了为什么平均图像的中心看起来均匀,但同时整体变暗。在进行加法运算时,许多像素值饱和到了最大值 255。随后将这些像素值除以 5,结果变成了 51。

因此,在对图像进行算术运算之前,应使用 im2double 函数将其转换为 double 数据类型。此函数还会将值重新缩放到 0 到 1 之间,这是许多图像处理算法的标准。

image_double = im2double(image_uint8);
average_image_double = (im2double(image1) + im2double(image2) + ... ) / 5;

转换为 double 类型后得到的平均图像,给出了预期的结果。

我们可以使用 montage 函数来比较平均图像与其中一张原始图像。

montage({single_image, average_image_double});

要使用此函数,请在花括号内提供以逗号分隔的图像列表。此处的差异很难看清,但放大后可以看到,平均图像中星云的噪声更少。

请注意,double 数据类型所需的内存远多于 uint8 数据类型。可以使用 im2uint8 函数转换回 uint8 类型。

average_image_uint8 = im2uint8(average_image_double);

像求平均这样的计算,需要你显式地在数据类型之间转换。然而,Matlab 中的大多数图像处理函数会为你自动完成此操作。

转换为灰度图像

接下来,我们看看如何简化图像。将彩色图像转换为灰度图像就是一个例子,函数内部会自动完成所有必要的数据类型转换。

灰度图像占用内存更少,处理速度更快,并且可用于更广泛的图像处理任务。

gray_image = rgb2gray(color_image);

你可能会想,转换为灰度是否会丢失信息?答案是肯定的,但通常颜色信息并非必需。假设你要计算这张图像中的星星数量,你只需要亮度信息,而这已由灰度强度值捕获。

让我们比较一下灰度图像与彩色图像的大小。所有这些变量都相当大,其中彩色图像所需的内存是灰度图像的三倍。

调整图像尺寸与旋转

降低图像分辨率可以减少所需内存。使用 imresize 函数来调整图像尺寸。此函数有两种使用方式。

第一种是提供一个缩放因子。例如,以下代码将图像缩小到原始分辨率的 75%。

resized_image = imresize(original_image, 0.75);

第二种方法是指定输出图像的行数和列数。

resized_image = imresize(original_image, [new_rows, new_cols]);

如果你的应用要求图像具有特定尺寸,请使用第二种方法。需要注意的是,如果 X 和 Y 方向的缩放因子不同,调整大小后的图像将会失真。

旋转图像也很常见,目的是确保多张图像或不同设置下的图像具有相同的对齐方式。使用 imrotate 函数旋转图像,输入角度单位为度。

rotated_image = imrotate(original_image, angle);

负角度表示顺时针旋转。请注意,旋转后图像的大小变大了,因为需要额外的行和列来为旋转后的图像腾出空间。如果希望输出图像与原始图像大小相同,请使用 ‘crop’ 选项。

rotated_image_cropped = imrotate(original_image, angle, ‘crop’);

保存处理后的图像

最后,我们来学习如何保存处理结果。要保存处理后的图像,请使用 imwrite 函数。

第一个输入参数是要保存的图像变量名,第二个输入参数是使用的文件名。写入文件时,将使用文件名扩展名中指定的文件格式。

imwrite(image_to_save, ‘output_image.png’);

以上代码创建一个 PNG 文件。PNG 文件使用无损压缩,但文件大小大于 JPEG 等有损压缩格式。这也是 JPEG 压缩被广泛使用的原因之一。

保存 JPEG 时,可以指定从 1 到 100 的图像质量。数值越高,图像还原越准确。但请注意,即使质量因子为 100,仍然使用的是有损压缩。

imwrite(image_to_save, ‘output_image.jpg’, ‘Quality’, 90);

你会注意到这三种图像格式的文件大小存在显著差异。建议尝试重复本视频中的分析,并打开图像查看图像质量是否存在明显差异。

总结

本节课中我们一起学习了图像数据处理的基础操作。我们了解了在对 uint8 类型图像进行算术运算前转换为 double 类型的重要性,掌握了使用 rgb2gray 进行色彩空间转换,使用 imresizeimrotate 进行几何变换,以及使用 imwrite 保存图像并选择不同格式和质量参数的方法。这些是进行更复杂图像处理前必须掌握的基本技能。

06:灰度图像分割 🖼️

在本节课中,我们将学习如何从灰度图像中分割出感兴趣的区域。分割是图像处理中的关键步骤,它允许我们分离和分析图像中的特定对象。我们将重点介绍三种分割方法:全局阈值法、自适应阈值法和多级阈值法。


全局阈值分割

上一节我们介绍了分割的基本概念,本节中我们来看看如何使用全局阈值进行分割。全局阈值法为整个图像选择一个单一的强度值,所有像素根据此值被分为前景或背景。

许多图像处理应用需要分析图像中的对象,例如测量这些对象的大小。在图像中隔离感兴趣区域的过程称为分割。最常见的分割形式是二值分割,其中像素被标记为属于前景或背景。

以下是一个使用全局阈值分割混凝土裂缝图像的步骤:

  1. 读取图像并将其转换为灰度图。
  2. 使用 imbinarize 函数自动选择最佳阈值进行二值化。
  3. 二值化结果是一个逻辑矩阵,其中 0(假/黑色)代表背景,1(真/白色)代表前景。
  4. 由于我们关注的是裂缝(暗区域),而非混凝土(亮区域),因此需要使用逻辑非运算符 ~ 反转黑白图像。
  5. 反转后,白色前景像素代表裂缝。

核心操作代码如下:

% 读取并转换为灰度图像
img = imread('concrete.jpg');
grayImg = rgb2gray(img);
% 自动二值化
BW = imbinarize(grayImg);
% 反转掩码,使裂缝为白色
BW_inverted = ~BW;

现在,您可以使用二值图像(掩码)来计算各种属性,例如前景像素的总数。使用 nnz 函数(非零元素数量)可以实现这一点。在这种情况下,裂缝覆盖了图像中近8000个像素。

二值图像被称为掩码,因为您可以将其覆盖到原始图像上,以屏蔽掉假(黑色)区域。具体操作是:首先创建图像变量的副本,然后使用二值图像进行逻辑索引,将所有掩码不为真的像素设置为零强度。

您可能想知道阈值是如何确定的。imbinarize 的默认行为是使用 Otsu方法,该方法基于像素强度的分布寻找阈值。您可以使用 graythresh 函数找到这个值,它返回一个介于0和1之间的值,乘以255可转换为强度值。

Otsu方法通常效果很好,但并非总是如此。例如,在处理细裂缝图像时,代表裂缝的像素数量很少,而混凝土主导了图像并具有明暗区域,因此Otsu方法倾向于分离混凝土的区域。在这种情况下,指定您自己的阈值可能是更好的方法。

您可以使用 imbinarize 的第二个输入参数来指定阈值。为了快速测试不同的值,添加一个数字滑块控件会很有帮助。


自适应阈值分割

上一节我们介绍了全局阈值的应用与局限,本节中我们来看看自适应阈值法。在某些应用中,全局应用的阈值无法捕获您想要的所有对象。

以一张米粒图像为例。直接使用全局阈值进行二值化后,图像底部的部分米粒缺失了。这是因为图像存在光照不均的问题,导致底部的一些米粒强度低于顶部的背景。因此,单个阈值不适用于整个图像,阈值需要根据局部平均强度而变化。

这被称为自适应阈值分割。您可以通过将方法指定为 imbinarize 函数的第二个输入参数来执行自适应阈值分割。

核心操作代码如下:

% 对光照不均的图像使用自适应阈值
BW_adaptive = imbinarize(grayImg, ‘adaptive’);

使用自适应阈值得到的结果看起来好得多,现在所有米粒都被掩码捕获了。虽然一些额外的背景像素被标记为前景,但您将在后续课程中学习处理此问题的技术。


多级阈值分割

分割并不总是二值的。一些应用需要多个标签,例如这张手臂的X光图像。图像中有四个明确定义的区域:背景、组织、骨骼和金属。

要处理这种情况,我们需要使用多级阈值。使用 multithresh 函数可以找到多个阈值。Otsu方法仍用于确定阈值,但现在您必须将阈值数量指定为第二个输入参数。在本例中,我们需要三个阈值来分割四个区域。

multithresh 的输出是一个包含阈值值的向量。要创建多标签分割图像,请将图像和阈值向量传递给 imquantize 函数。这将返回一个标签矩阵,其中包含代表每个像素所属区域的整数。

核心操作代码如下:

% 寻找多个阈值
numRegions = 4;
thresholds = multithresh(grayImg, numRegions-1);
% 根据阈值量化图像,生成标签矩阵
labelMatrix = imquantize(grayImg, thresholds);
% 显示标签矩阵,使用完整值范围
imshow(labelMatrix, [])

从这个标签矩阵中,您仍然可以隔离单个区域。为此,创建一个新掩码变量,并让其存储与某个标签对应的逻辑数组。例如,要隔离骨骼(第三个标签),可以这样做:

boneMask = labelMatrix == 3;


总结 🎯

本节课中,我们一起学习了三种从灰度图像中分割感兴趣区域的工具:

  1. 全局阈值分割:为整个图像应用单一阈值,适用于前景和背景对比度明显的图像。
  2. 自适应阈值分割:根据图像局部区域的特性动态调整阈值,适用于光照不均的图像。
  3. 多级阈值分割:使用多个阈值将图像分割为两个以上的区域,适用于包含多种不同强度对象的复杂图像。

您现在拥有多种工具,可以用于从灰度图像中分割出感兴趣的区域。请花些时间使用其他图像练习这些技术,并在论坛中分享您的结果。

07:色彩空间导论 🎨

在本节课中,我们将要学习色彩空间的基本概念,了解为何有时RGB色彩空间不适用于颜色分割任务,并探索HSV等替代色彩空间如何帮助我们更有效地处理颜色信息。

概述

色彩空间是描述和表示颜色的数学模型。最广为人知的是RGB(红、绿、蓝)色彩空间,它通过混合三种原色的强度来产生各种颜色。然而,在某些应用场景下,如识别成熟水果或处理不同光照条件下的物体,RGB空间会显得力不从心。本节将介绍RGB的局限性,并引导你认识其他更适用于特定任务的色彩空间。

RGB色彩空间的挑战

上一节我们介绍了图像处理的基本概念,本节中我们来看看使用RGB色彩空间进行颜色分割时可能遇到的困难。

考虑使用成像技术来确定采摘成熟水果(例如这些蓝莓)的最佳时机。你可能会猜测,可以通过观察RGB色彩空间中的蓝色通道来识别蓝色的成熟蓝莓。

但事实上,蓝色成熟蓝莓和绿色未成熟蓝莓的B(蓝色)通道数值非常接近。

类似的情况,以及阴影和光照的差异,使得在RGB色彩空间中进行颜色阈值分割变得困难。以下照片展示了同一停车标志在一天中不同时间的样子。请注意所有三个RGB分量值都发生了显著变化。

让我们看看未成熟蓝莓的颜色,以理解其原因。仅看这三种颜色(红、绿、蓝分量),你可能不会猜到它们组合起来会呈现绿色。

色彩空间转换的必要性

不同的颜色是通过改变红、绿、蓝三个颜色分量的强度产生的。将一个颜色分量值向其最大值移动,会增加该颜色的贡献度。

除了RGB,你可以使用其他替代色彩空间。这类似于笛卡尔坐标系中的点可以用其他坐标系(如球坐标系)来描述。最重要的是,一个坐标系中点之间复杂的关系,在另一个坐标系中描述起来可能简单得多。

例如,仅改变点P的θ值,就相当于在笛卡尔坐标系中改变所有三个值(x, y, z)。

HSV色彩空间简介

一个常用的色彩空间是HSV(色相、饱和度、明度)。与RGB色彩空间类似,HSV色彩空间也使用三个分量来构建颜色。

以下是HSV的三个分量定义:

  • 色相 (Hue):对应色轮上的角度,决定颜色的基本类型(如红、黄、蓝)。
  • 饱和度 (Saturation):决定颜色的纯度或强度。饱和度越低,颜色越接近灰色。
  • 明度 (Value):决定颜色的明暗程度。

回到蓝莓图像的案例。在HSV色彩空间中,你可以通过仅限制色相值来分离出蓝色。


这在RGB空间中要困难得多,因为在该空间中所有三个分量都会发生变化。

在MATLAB中操作色彩空间

在MATLAB中,你可以使用 rgb2hsv 函数将图像从RGB色彩空间转换到HSV色彩空间。

hsv_image = rgb2hsv(rgb_image);

请注意,RGB值通常是整数(如0-255),而转换后的HSV值是介于0和1之间的小数。

使用 hsv2rgb 函数可以转换回RGB色彩空间。得到的RGB图像仍然是 double 类型。

rgb_image_restored = hsv2rgb(hsv_image);

其他色彩空间

除了HSV,还有其他替代方案。例如:

  • YCbCr色彩空间:常用于视频设备。
  • LAB色彩空间:常用于照片编辑。

MATLAB也提供了在这些色彩空间之间进行转换的函数。由于很难预先知道哪种色彩空间最适合你的具体应用,因此你需要进行实验,尝试不同的色彩空间。

总结与预告

本节课中我们一起学习了色彩空间的基础知识。我们认识到RGB色彩空间在颜色分割上的局限性,并介绍了HSV色彩空间如何通过分离色相、饱和度和明度来更直观地处理颜色。我们还了解了在MATLAB中进行色彩空间转换的基本函数。

在下一个视频中,你将认识一个颜色阈值分割应用程序,它将帮助你在不同的色彩空间中对图像进行分割。



08:彩色图像阈值处理 🎨


概述

在本节中,我们将学习如何使用颜色信息对图像进行分割。颜色分割能提供重要信息,例如识别哪些蓝莓已经成熟。然而,分割彩色图像通常需要一些尝试和调整,因为选择合适的颜色空间和阈值往往具有挑战性。我们将通过使用Color Thresholder应用程序,快速尝试不同的分割方法。


导入图像与启动应用程序

首先,导入蓝莓图像。接着,导航至应用程序选项卡。请注意,这里提供了多种与图像处理相关的应用程序,包括本系列课程中将探索的几个。现在,打开Color Thresholder应用程序。


选择颜色空间

打开应用程序后,第一步是加载图像。在本例中,我们从工作区加载图像。接下来,系统会提示选择用于分割的颜色空间。通常,HSV和LAB颜色空间是良好的起点。我们的目标是将蓝色蓝莓从图像的其他部分分离出来。在HSV点云中,蓝色似乎分离得较好,因此我们从HSV开始尝试。


调整阈值

请注意,HSV的三个通道由右侧的滑块表示。您可以调整滑块的范围,并立即看到阈值处理对图像的影响。我们尝试通过移动H值的阈值,仅包含看起来是蓝色的部分,以分离成熟的蓝莓。

结果看起来很有希望。我们只看到蓝色部分透过掩膜显示出来。但如何确保没有意外移除一些感兴趣的区域呢?


检查分割结果

应用程序提供了调整背景不透明度和颜色的控件。掩膜为假的区域显示为不透明,这有助于判断阈值是否合适。您还可以查看二值掩膜。这通常有助于发现分割中的小缺陷,这些缺陷可能需要在后续处理中解决。

例如,一些小区域看起来是被叶子遮挡的成熟蓝莓,而其他区域则不是。稍后,您将学习如何优化初始掩膜并分析这些区域。


尝试其他颜色空间

HSV颜色空间在此图像上效果良好。但在分割其他图像时,您可能需要尝试多种颜色空间。选择新的颜色空间以尝试另一种方法。您之前的工作会被保留,以便比较结果。


导出结果与生成函数

一旦对结果满意,您可以将分割后的图像和掩膜导出到工作区进行进一步分析。您可能会想,是否需要每次都这样做,或者如何确保重现相同的结果。通过导出自动生成的函数,您可以记录所采取的步骤,并获得一个可应用于其他图像的函数。

建议为函数起一个描述性的名称,然后保存它。


实践与分享

现在,您已经完成了这个示例,可以导入自己的彩色图像,并尝试在不同的颜色空间中进行分割。在论坛中发布您的结果,并分享您采用的方法。


总结

在本节中,我们一起学习了如何使用Color Thresholder应用程序对彩色图像进行阈值分割。我们探讨了如何选择合适的颜色空间、调整阈值、检查分割结果,并导出函数以便重复使用。通过实践,您可以更有效地利用颜色信息进行图像分割。

09:常见图像调整 📊

在本节课中,我们将学习如何通过调整图像的对比度来优化其视觉效果。我们将从像素层面入手,了解图像直方图的概念,并学习两种关键的调整方法:直方图拉伸与直方图均衡化。这些技术能有效改善图像中难以看清的细节。

课程概述

欢迎来到本课程的最后一周。本周,我们将深入到像素级别进行图像处理。

到目前为止,你已经学会了调整图像大小、旋转和裁剪图像。你也学会了在不同色彩空间之间转换,以及将图像转换为灰度图。然而,所有这些操作都是对整个图像整体进行的。

在本模块中,你将学习如何可视化并调整图像的对比度,通过改变单个像素值来使图像整体光照更均匀。

理解图像对比度与直方图

上一节我们回顾了已学的整体图像操作,本节中我们来看看图像对比度的核心概念。

图像对比度指的是图像中暗部与亮部区域之间的差异。

图像直方图有助于可视化像素亮度值的分布情况。

在检查图像直方图之后,你将通过拉伸直方图以使用全部范围,或通过重塑直方图以突出图像的特定细节,来改变单个像素的值。

以下是两种主要的调整方法:

  • 直方图拉伸:扩展像素值的分布范围,以增强整体对比度。
  • 直方图均衡化:重新分布像素值,使直方图更均匀,从而增强局部对比度并突出细节。

这可以改善曝光过度或曝光不足图像中难以看清的细节。

在本模块结束时,你将运用所学的所有技能完成一次最终测验。

那么,让我们开始吧。

总结

本节课中我们一起学习了图像对比度的概念及其重要性。我们介绍了图像直方图作为分析像素亮度分布的工具,并探讨了通过直方图拉伸和直方图均衡化来调整图像对比度的两种核心方法。掌握这些技能,你将能够有效改善图像的视觉效果,揭示更多隐藏的细节。

10:图像对比度与直方图 📊

在本节课中,我们将要学习图像对比度的概念,并了解如何使用直方图这一工具来可视化图像的像素强度分布。理解这些基础知识是进行后续图像处理操作的关键。

什么是图像对比度? 🎼

图像对比度指的是图像中暗部区域与亮部区域之间的差异。一张对比度良好的图像,其前景物体与背景物体之间有清晰的区分。

而对比度差的图像,物体之间的区分度则较低。

就像这张曝光不足的图片一样。不过请放心,图像的细节信息仍然存在。

对于图像对比度的“好”与“坏”,并没有一个严格的技术定义。通过比较几幅图像中像素的强度,我们可以直观地感受一幅图像是否已经“准备就绪”,可以直接用于分析。

还是说它需要进行一些调整。

虽然本视频主要关注灰度图像,但所介绍的技术同样可以逐通道地应用于彩色图像。

高对比度图像示例 🌾

让我们来看一个简单的例子。这张米粒的图像具有很好的对比度。较亮的米粒在深色背景上清晰地凸显出来,并且每粒米的边缘都有明确的界定。

观察单个像素的强度值可以展示局部对比度。你可以看到从米粒像素(值高于200)到背景像素(值接近100)之间存在明显的变化。

理解图像直方图 📈

图像直方图是一种可以一次性查看所有像素值的方法。这张图显示了图像中每个可能的强度值(从0到255)分别对应多少个像素。

对于这张米粒图像,直方图上有几个峰值。右侧的峰值代表米粒中的像素,而其他峰值则与背景相关。值得注意的是,图像底部三分之一的背景比其余部分的背景要暗得多。

低对比度图像分析 ⚽

现在,让我们再看一下那张曝光不足的桌上足球台图片。

像这样对比度差的图像,在应用其他图像处理技术之前,通常需要进行调整。

其直方图显示,几乎所有的像素值都集中在强度范围的低端。为了看清图像中的细节,我们可以改变像素值,将直方图拉伸到整个强度范围内。这种线性缩放是调整图像对比度最简单的方法之一。

调整的结果是得到一幅改善后的图像,之前被隐藏的细节现在清晰可见。

总结与预告 🔮

本节课中,我们一起学习了图像对比度的概念,并探索了如何使用直方图来分析图像的像素强度分布。我们看到了高对比度图像与低对比度图像在直方图上的显著差异。

在下一个视频中,你将学习如何在Matlab中执行这种线性对比度拉伸以及其他更复杂的图像调整操作。

11:改善图像对比度 🖼️

在本节课中,我们将学习如何改善图像的对比度。这对于从医学诊断到自动驾驶的广泛应用都至关重要。我们将通过分析图像直方图,并应用不同的技术来增强图像细节。

分析图像直方图

要针对特定图像选择有效的方法,首先查看图像的直方图会很有帮助。直方图显示了图像中像素强度的分布情况。

根据直方图信息,可以选择一种对比度调整技术。如果调整后的图像效果足够好,那么任务就完成了。如果效果不理想,可以尝试新的技术或组合多种技术,反复迭代直到获得满意的结果。

灰度图像对比度增强

本节中,我们将使用直方图拉伸直方图形状重塑来改善灰度图像的对比度。

直方图拉伸

让我们以这张脚踝X光片为例。图像看起来很暗,脚踝几乎看不清。

使用 imhist 函数查看像素强度的分布。在这个16位图像中,像素值集中在直方图的左侧,最大强度仅为13000,而最大可能强度超过60000。图像对比度低是因为它没有充分利用整个像素范围。

为了解决这个问题,可以使用 imadjust 函数拉伸直方图,以利用完整的强度范围。新的直方图使用了完整的数值范围,并且在更高强度处有了更多的像素。

查看调整后的图像,这是一个巨大的改进,脚踝清晰可见。然而,肌肉中的软组织仍然非常暗,而脚踝关节又太亮。

直方图均衡化

让我们再看一下拉伸后的直方图。大多数像素仍然具有非常低的强度值。更均匀地分布像素强度可能会揭示更多细节。

在整个直方图范围内重新分配像素强度的过程称为直方图均衡化,可以使用 histeq 函数执行。现在,直方图被分成64个区间,像素强度以新的直方图大致平坦的方式进行分布。

查看新图像,较暗的区域变得更亮,但代价是增加了噪声。此外,骨骼和软组织都缺乏细节,因为图像整体被提亮,而局部变化却丢失了。

自适应直方图均衡化

那么,我们如何在不放大噪声的情况下执行局部均衡化呢?与其尝试均衡整个图像的直方图,不如将其分成多个小块。每个小块分别进行均衡化,然后混合在一起形成最终图像。这被称为自适应直方图均衡化

可以使用 adapthisteq 函数执行自适应直方图均衡化。大多数像素仍然具有低强度。让我们将其与原始图像进行比较。骨骼现在稍微更清晰一些,但亮度仍然不足以分辨细节。

组合技术

到目前为止,我们已经尝试了三种技术来改善对比度:直方图拉伸、直方图均衡化和自适应直方图均衡化。但我们仍然对结果不完全满意。相反,让我们尝试组合这些技术。

具有拉伸直方图的图像是目前效果最好的。也许这张图像中较暗的区域可以通过自适应直方图均衡化变得更亮。

让我们结合这些方法:首先拉伸原始直方图,然后用 adapthisteq 重塑它。得到的图像具有出色的对比度,骨骼和关节都清晰可见。

彩色图像对比度增强

到目前为止,我们调整了灰度图像的对比度。但对于许多应用(如自动驾驶)来说,彩色图像的对比度调整至关重要。

那么,如何改善彩色图像的对比度呢?由于对比度低,可以从直方图拉伸开始。然而,当你这样做时,它会返回一个错误,因为对比度调整函数期望一个二维图像,而彩色图像由三个不同的颜色通道组成。

RGB图像的亮度与所有三个通道都有关联。因此,如果不改变图像的颜色,就无法改变图像的亮度。相反,需要一个将亮度与颜色分离的色彩空间,例如 HSV

在HSV色彩空间中,V(明度)通道对应于图像的亮度。

让我们在MATLAB中尝试一下。将图像转换为HSV色彩空间。提取V通道并查看其直方图。注意大多数像素具有低强度。

由于你发现组合算法在灰度图像中很有效,将其应用于V通道以改善直方图。用新的V通道替换原始HSV图像的V通道,并将图像转换回RGB。

现在,让我们将其与原始图像进行比较。对比度得到了极大改善,同时保留了颜色,这非常好。这将使自动驾驶汽车能够在夜间检测到行人并停车。

总结

本节课中,我们一起学习了如何应用三种最常见的对比度调整技术。MATLAB中还包含许多其他用于调整图像对比度的函数,你可以自行探索。现在,你已经准备好将对比度调整技术应用到自己的图像中了。

12:课程总结 🎉

在本节课中,我们将对《工程与科学图像处理简介》课程进行总结,回顾已学知识,并展望后续课程内容。

恭喜你完成了《图像处理简介》课程。你已经打下了一个良好的基础,并且能够完成相当多的工作。

你学习了不同类型的数字图像,以及它们在 MATLAB 中的表示方式。

上一节我们介绍了图像的基础表示,本节中我们来看看如何转换这些表示以更好地访问数据。

你改变了图像的表示方式,使数据更易于处理。这包括在不同色彩空间之间转换,有时通过转换为灰度图来完全移除颜色信息。

图像分割使你能够将数据从背景中分离出来。

有时,这种分割足以让你分析数据并得出结论,正如你在分析冰川图像时所看到的那样。通过将这一想法扩展到多阈值分割,你成功地将肌肉和骨骼与人工植入物分离开来。

通过利用颜色信息,你创建了一个函数,能够识别成熟的蓝莓和那些还需要时间生长的蓝莓。

最后,查看和操作图像直方图使你的数据更加清晰。

在整个学习过程中,你遇到了一些困难,部分结果可能并不完全令人满意。

你需要学习更高级的图像处理技能来克服这些挑战。梅根,我们在下一门课程中将学习什么?谢谢阿曼达。在课程二中,你将学习强大的技术,以从图像中提取数据。

以下是课程二将涵盖的核心内容:

  • 空间滤波和形态学操作将改善你的分割效果。
  • 但分割通常只是第一步。你还将学习计算分割区域的属性,以便得出结论。

图像处理是一个广阔的领域,但你已经掌握了基础知识,并准备好开始将它们应用到自己的项目中。

加入我们的下一门课程,将你的知识提升到新的水平。

本节课中我们一起回顾了图像处理简介课程的核心内容,包括图像表示、色彩空间转换、阈值与颜色分割以及直方图分析。同时,我们也预览了下一阶段将学习的空间滤波、形态学和区域属性分析等进阶技能。

12:专业概述2

在本节课中,我们将学习图像处理在多个关键应用领域的重要性,并了解MathWorks推出的《工程与科学图像处理》专项课程的整体结构与学习路径。

从图像中提取信息对于众多应用至关重要。

这些应用包括诊断医疗状况、研究气候变化的影响、设计自主系统以及改进农业技术。

无论您身处哪个领域,分析图像都是当今许多工作所必需的技能。因此,MathWorks创建了《工程与科学图像处理》专项课程。

无论您是图像处理的新手,还是正在寻找新工具来提高工作效率,这个专项课程都适合您。这是因为您将使用MATLAB来快速执行图像处理任务。

MATLAB专为工程师和科学家设计,包含许多应用程序,使您能够快速测试不同方法并可视化结果。这些应用程序会自动生成代码,以便您能够复现和扩展您的工作。

该专项课程分为三门课程。

课程一:数字图像处理基础

在课程一中,您将扎实地学习如何处理数字图像。您将学习如何创建常见的图像调整,以及如何隔离感兴趣区域以进行进一步分析。

以下是课程一中的两个实践示例:

  • 利用颜色信息识别成熟的蓝莓。
  • 根据卫星图像计算冰川融化的量。

课程二:图像分割技术

上一节我们介绍了图像处理的基础操作,本节中我们来看看图像分割。在课程二中,您将解决图像分割中的常见挑战。

以下是课程二将涵盖的核心技术:

  • 学习减少噪声的技术。
  • 学习分离重叠对象的技术。

这些技术确保您只隔离出相关信息。重要的是,您将分析所找到的区域,并计算其属性。

以下是您将学习计算的典型属性:

  • 面积area = regionprops(binaryImage, ‘Area’)
  • 方向orientation = regionprops(binaryImage, ‘Orientation’)

课程三:算法自动化

通常,您需要将图像处理步骤应用于许多图像,或者可能需要分析视频。

在课程三中,您将自动化您的算法以处理成千上万的图像,从而节省时间和精力。

当您拥有大量图像时,目视检查所有图像是不可行的。您将练习分析结果,以识别需要进一步调查的异常值。

最终项目与应用

在专项课程结束时,您将应用新技能完成一个最终项目:在嘈杂的视频中检测汽车以分析交通模式。

您甚至能够处理一些复杂情况,例如当物体被部分遮挡时。最后,您将创建一个视频,在检测到的车辆驶过时为其添加边界框。

图像处理是一项需求量很大的职业技能。无论您是开发自主系统、诊断疾病还是研究宇宙,这个专项课程都将帮助您取得成功。

本节课中我们一起学习了图像处理的核心价值、专项课程的三门课程结构(基础、分割、自动化)以及最终项目。现在,让我们开始学习吧。

课程2:图像分割、滤波与区域分析 🎯

在本课程中,我们将学习如何应用空间滤波器来优化图像分割,修复分割掩码中的瑕疵,并对分割出的区域进行量化分析。通过本课程,你将能够从图像中提取出有价值的信息。


到目前为止,你已经在图像处理方面打下了坚实的基础。你已经在不同的场景中实践了这些技能,处理过从骨骼到蓝莓等各种对象。

你现在可以改善图像对比度,并分离出你最感兴趣的区域。然而,你也发现并非每张图像都易于分割。例如,这张图像中非常不均匀的背景使得将裂缝从混凝土中分离出来变得困难。

上一节我们回顾了基础分割方法面临的挑战,本节中我们来看看如何应用空间滤波器来解决这些问题。

空间滤波器使用相邻像素的加权和来修改某个像素的值。其核心公式可以表示为:

新像素值 = Σ (邻域像素值 × 对应权重)

更广泛地说,滤波器通常用于去除噪声和检测图像的有用部分,例如边缘。


在通过阈值法进行分割时,图像常常会存在缺陷。

以下是常见的分割瑕疵类型:

  • 区域内部存在孔洞。
  • 区域边界不平滑或有毛刺。
  • 存在小的、孤立的噪声点。


在本课程中,你将学习如何优化掩码以修复这些不完美之处。

这将使你能在生成初始掩码后,填补空隙并移除伪影。


我假设你学习本课程不仅仅是为了创作漂亮的黑白图像。你可能对你分割出的区域有进一步的问题。例如,你可能想确定其中有多少个对象、它们的大小或位置。

在完成本课程后,你将掌握从图像中分割并提取有用信息的技能。


如果你有任何问题,请在论坛上发帖,我们很乐意提供帮助。现在,让我们开始吧。


本节课中我们一起学习了课程2的总体目标:通过空间滤波改善分割效果,使用形态学操作修复分割掩码,并对最终分割出的区域进行量化分析,从而从图像中提取出有意义的测量数据。

13:使用空间滤波降噪 🎼

在本节课中,我们将要学习空间滤波的基本概念,并了解如何利用它来去除图像中的噪声,从而改善图像质量,为后续的分析和分割步骤打下基础。

调整图像的对比度可以凸显出原本难以看清的重要细节。

然而,同样的调整在使裂纹与混凝土之间的差异变得更加明显的同时,由噪声引起的细微变化也会变得更加显著。处理噪声是科学与工程领域中的一个常见问题。

在接下来的内容中,你将学习空间滤波,以及它如何用于去除图像中的噪声。

什么是图像噪声?

图像中的噪声是指任何使得图像主体更难以被区分的干扰因素。

在这张混凝土裂纹图像中,混凝土的纹理导致了像素值的变化。当这些差异通过对比度调整被放大后,它们可能会变得和裂纹本身一样显著。一种降低噪声的方法是应用空间滤波。

空间滤波原理

上一节我们介绍了噪声的概念,本节中我们来看看空间滤波如何工作。空间滤波根据一个像素周围邻域内其他像素的值来改变该像素的值。

一个滤波器就像一个掩模矩阵。当它覆盖在图像上时,会围绕一个像素创建一个窗口或邻域。中心像素的值会根据邻域内其他像素的值进行修改。

例如,一个平均滤波器会取邻域内像素的平均值,并将其赋值给中心像素。然后滤波器移动到下一个像素,并重复此过程。这使得空间滤波成为一种滑动窗口操作。

取邻域内像素的平均值,可以减少像素与像素之间的差异。

以下是创建和使用平均滤波器的关键步骤:

  • 指定窗口大小:默认的 3x3 滤波器尺寸适用于低分辨率图像,或当你只想包含最近邻像素时。对于高分辨率图像或需要包含更大区域信息的情况,可以增大滤波器尺寸。
  • 处理图像边界:在边界附近,窗口的一部分会延伸到图像外部。你需要为外部区域提供值,或者说对图像边界进行填充。

以下是几种常见的边界填充选项:

  • 使用最近边界像素的值。
  • 在边界处镜像图像。
  • 直接使用一个固定值。

非线性滤波:中值滤波

除了取平均值,你还可以通过取窗口内像素值的中位数来进行非线性滤波。

这可以减少与邻域像素差异极大的像素的影响,同时有助于保留边缘信息。

在 MATLAB 中实践

现在,让我们使用 MATLAB 对裂纹图像应用一个平均滤波器。

要创建一个 3x3 的平均滤波器,可以使用 fspecial 函数。指定滤波器类型和大小。这里,F 是一个 3x3 的平均滤波器。imfilter 函数将滤波器应用于图像。默认行为是用零值填充边界。

使用 montage 来显示原始图像和滤波后的图像,然后运行代码。

裂纹变得略微模糊,但背景看起来更加平滑。

为了观察这对分割效果的影响,让我们调整滤波后图像的对比度并使用 imbinarize 进行二值化,然后显示分割结果。

与原始分割结果相比,这有了相当大的改进。

降低噪声减少了那些在对比度调整过程中被放大的背景变化。

总结

本节课中我们一起学习了空间滤波是什么,以及它如何用于去除图像中的噪声。你也看到了如何在 MATLAB 中创建和使用空间滤波器。在后续课程中,你将会看到这一强大图像处理技术的其他用途。

14:边缘检测

在本节课中,我们将要学习图像处理中的边缘检测技术。边缘检测用于识别图像中不同物体或区域之间的边界,这对于图像分割、物体识别和形状分析至关重要。

概述

上一节我们介绍了阈值分割,它可以将图像分为前景和背景。然而,有时一个前景区域可能包含多个重叠的物体。边缘检测则专注于隔离物体之间的边界。通过从分割后的图像中移除这些边缘,可以将重叠的区域分离开来。此外,边缘也常用于寻找特定的形状或模式,例如角点或圆形。

边缘检测的基本原理

边缘检测需要一系列操作,包括对图像应用多个空间滤波器,以及对结果进行组合与处理。

大多数边缘检测算法都基于能够近似计算图像梯度的空间滤波器。Sobel滤波器是最简单的此类滤波器,被包括Sobel方法在内的许多算法所采用。

以下是Sobel滤波器的核心概念:

  • 梯度计算:Sobel滤波器通过比较中心像素邻域内像素的强度来计算梯度。公式上,它通过卷积核来近似计算图像在水平和垂直方向上的导数。
  • 边缘指示:计算出的梯度值中,较大的正值或负值可能指示边缘的存在。
  • 均匀区域接近零的值则表示该区域强度较为均匀。

例如,一个强调水平边缘的Sobel滤波器核可能如下所示(代码表示):

% 水平Sobel滤波器核
H = [-1 -2 -1; 0 0 0; 1 2 1];

应用该滤波器的转置(H')则会强调垂直边缘。将水平和垂直梯度结合起来,就能显示出所有潜在的边缘。

Sobel方法会从水平和垂直梯度中计算出梯度的幅度方向。梯度幅度图像显示了边缘的强度。最后,对该幅度图像应用一个阈值,以移除梯度值较低的区域(非边缘),并将高梯度区域细化至一个像素的宽度。这些步骤的具体实现细节因算法而异。

在MATLAB中实践边缘检测

边缘检测过程包含许多步骤,但在MATLAB中,你可以通过一次函数调用就完成它们。让我们尝试使用edge函数来处理一张真实硬币的图片,以将硬币从背景中分离出来。

这张高分辨率图像捕捉到了硬币表面的许多细节。由于我们只关心硬币的外边缘,这些内部细节可以被视为噪声。与处理背景噪声类似,在边缘检测前对图像进行轻微模糊处理可以提高输出质量。

高斯模糊常用于边缘检测前的预处理。高斯滤波器比均值滤波器更重视中心像素的权重,这可以在保留较强边缘的同时,移除较小的细节。在MATLAB中,imgaussfilt函数可以直接模糊图像,而无需单独创建和应用滤波器。高斯函数默认的标准差是0.5,但对于更高分辨率的图像,你可能需要尝试更大的值。

将模糊后的图像传递给edge函数,会得到一个硬币边缘的二值图像。默认使用的是Sobel方法。与图像分割一样,可以调整阈值值来根据需要保留或移除更多边缘。edge函数可以返回它计算出的阈值,你可以将其作为调整的起点。让我们稍微提高阈值:

% 示例:使用Sobel方法进行边缘检测并调整阈值
blurredImg = imgaussfilt(coinImage, 2); % 应用高斯模糊,标准差为2
[BW, threshold] = edge(blurredImg, 'sobel'); % 获取默认阈值
adjustedBW = edge(blurredImg, 'sobel', threshold * 1.2); % 将阈值提高20%

提高阈值后,硬币内部的一些细节被移除了,但一些较微弱的硬币边缘也可能丢失。这需要在保留主要边缘和抑制噪声之间进行权衡。

其他边缘检测方法

Canny方法是另一种流行的算法。它使用与Sobel方法相同的滤波器,但对输出进行了不同的处理和阈值化。它通常能比Sobel方法检测到更多的边缘细节,这在某些应用场景中非常有用。

edge函数还包含许多其他流行的边缘检测方法,如Prewitt、Roberts和Laplacian of Gaussian (LoG)。

特定应用:检测圆形物体

利用边缘来寻找圆形或近似圆形的物体是一个常见的应用。MATLAB为此提供了专用函数。

imfindcircles函数通过寻找圆形边缘来分割图像。随后,可以使用viscircles函数将检测到的边缘叠加显示在原图上。

% 示例:使用 imfindcircles 检测圆形
[centers, radii] = imfindcircles(blurredImg, [20 100], 'ObjectPolarity', 'dark');
imshow(coinImage);
viscircles(centers, radii, 'EdgeColor', 'b'); % 用蓝色圆圈标出检测到的硬币

总结

本节课中我们一起学习了边缘检测的核心知识。你了解了Sobel边缘检测方法背后的基本思想,包括梯度计算和阈值处理。你也看到了高斯滤波器如何在保留主要边缘的同时移除微小细节。最后,你学会了如何运用MATLAB内置的函数(如edge, imgaussfilt, imfindcircles)来应用这些技术,从而有效地从图像中提取边界信息。

15:利用形态学优化分割 🧩

在本节课中,我们将学习如何使用形态学操作来优化图像分割的结果。分割后的掩码(mask)常常会包含我们不想要的孔洞或孤立的噪声点,形态学提供了一套强大的工具来修正这些问题。

概述:分割的挑战与形态学的引入

当我们进行图像分割时,例如对下图中的拼图块使用灰度阈值法,得到的初始掩码往往并不完美。

初始掩码中,我们想要的真实像素区域可能存在我们不希望的间隙或孔洞。

同时,在背景区域也可能存在我们不想要的、孤立的真实像素点(噪声)。

解决这些问题的有力工具被称为形态学

形态学基础:膨胀与腐蚀

形态学操作类似于空间图像滤波。它通过一个在原始图像上移动的像素邻域(称为结构元素)来为每个像素分配新的值。

形态学有两个基本操作。第一个是膨胀,它将邻域内的最大值赋给新像素。第二个是腐蚀,它将邻域内的最小值赋给新像素。

在二值图像(掩码)中,这可以简化为:

  • 膨胀:如果结构元素覆盖的区域内有任何像素为真(True/1),则新像素为真。
  • 腐蚀:如果结构元素覆盖的区域内有任何像素为假(False/0),则新像素为假。

形态学强调改变结构元素的大小形状,这允许你对图像中不同大小或形状的物体产生不同的影响。

让我们仔细观察拼图块的掩码,看看这些操作如何影响它。

膨胀操作详解

使用一个简单的3x3方形结构元素。

对于二值图像,膨胀操作会扩展现有的真实像素区域。结构元素的大小和形状会影响结果。改变结构元素的大小或形状,会影响图像的哪些区域被改变,以及改变的程度。

腐蚀操作详解

腐蚀操作则相反,它会缩减现有的真实像素区域。同样,其效果与结构元素的大小和形状成比例。

组合操作:闭运算与开运算

有时,单独使用膨胀或腐蚀就足以修复掩码。但在本例中,单独应用其中任何一个操作,在解决部分问题的同时,也会使其他问题恶化。按顺序组合使用这些操作通常能获得更好的结果。

闭运算:填充内部孔洞

在本例中,一个包含两种操作的序列可以专门填充拼图块内部的孔洞。

以下是操作步骤:

  1. 膨胀:使用一个足够大的结构元素对掩码进行膨胀,以完全填充拼图块内部的间隙。这填充了间隙,但也在边界周围增加了额外的真实像素,并放大了我们不想要的噪声点。
  2. 腐蚀:使用相同的结构元素对膨胀后的结果进行腐蚀。这个操作移除了边界周围额外增加的像素,也缩小了被放大的噪声点,同时保持了拼图块内部已被填充的孔洞。

原理:膨胀完全闭合了拼图块内部的间隙,创建了一个比结构元素更大的真实像素区域。然而,在拼图块边缘产生的多余部分以及小噪声点的放大区域,其边缘仍然与假像素相邻,处于结构元素的作用范围内。因此,随后的腐蚀能够反转这些变化,而不会影响拼图块内部已闭合的间隙。

这个先膨胀后腐蚀(使用相同结构元素)的序列被称为形态学闭运算。关键是要记住,此操作会闭合能够被结构元素覆盖的真实像素区域之间的间隙,而掩码的其余部分保持不变。

开运算:消除外部噪声

回到我们的例子,经过闭运算后,仍然存在我们不想要的孤立真实像素点(噪声)。你可能会想,应该存在一种对偶的方法来消除这些小簇的真实像素,同时基本保持大的拼图块不变。你是对的。

只需反转步骤,你就得到了所谓的形态学开运算

以下是操作步骤:

  1. 腐蚀:使用一个足够大的结构元素对掩码进行腐蚀,以覆盖(即消除)那些小的噪声点。
  2. 膨胀:使用相同的结构元素对腐蚀后的结果进行膨胀,以恢复拼图块的边缘。

开运算序列通过移除能够被结构元素覆盖的小的真实像素区域,从而扩大了假像素区域。较大的拼图块基本保持不变。

效果对比与总结

将经过闭运算和开运算后的最终掩码与初始掩码进行比较。

可以看到,我们成功地填充了拼图块内部,并消除了不想要的噪声点,同时没有改变拼图块本身的轮廓。

将这个掩码叠加到原始图像上,可以看到它提供了非常准确的分割结果。

本节课总结:我们一起学习了二值形态学的核心概念与技术,包括膨胀腐蚀以及它们的组合开运算闭运算。这些工具能有效优化分割掩码,填充目标内部的孔洞或消除外部的孤立噪声。

接下来,你将学习如何在MATLAB中使用一个应用程序来轻松尝试这些操作并可视化结果。

第6章:使用图像分割器应用

在本节课中,我们将学习如何使用MATLAB的图像分割器应用来快速测试、评估和修改不同的掩膜优化方法。我们将从简单的阈值分割开始,探索形态学操作和填充孔洞等优化技术,并最终生成可复用的分割函数,以评估其在其他图像上的表现。


对于像这张混凝土裂缝图像,你已经看到简单的阈值分割就能产生不错的分割效果。

然而,仅靠阈值分割得到的掩膜通常需要进一步优化。

具体的优化步骤序列通常无法提前预知。在本视频中,你将学习如何使用图像分割器应用来快速测试、评估和修改不同的掩膜优化方法。你还将学习如何为每种方法生成可复用的分割函数。最后,你将评估每个函数在新图像上的表现。

你可以在MATLAB应用菜单的“图像处理与计算机视觉”部分找到图像分割器应用。

要开始使用,你需要导入一张图像。你可以导入图像文件或MATLAB工作区中的图像。在本例中,之前看到的图像已存在于工作区并已转换为灰度图。

当你加载新图像时,系统会提示你是否要调整图像以使用完整的亮度范围。这有时有助于阈值分割和掩膜优化,但此处我们不需要。

一旦图像加载到应用中,你会看到几个创建初始掩膜的选项。本视频我们将重点介绍阈值分割,但你也应该探索其他选项。

根据你的应用,其他方法可能更合适。点击“阈值”按钮,你将看到叠加在图像上的初始掩膜。

展开“方法”菜单,默认选项是自动选择的全局阈值。“自适应阈值”对于分割光照不均匀的图像非常有用。“手动”选项允许你在其他选项不适用时选择自己的全局阈值。在本例中,全局阈值选项已足够。

点击“创建掩膜”按钮后,优化掩膜、调整其显示方式以及导出结果的选项变为可用。前景对象通常是明亮的,因此要将裂缝本身作为前景,请在优化掩膜选项中使用“反转掩膜”按钮。

有时,以二值形式查看掩膜更容易发现其中的缺陷。

为此,点击“显示二值图像”按钮。如你所见,这个掩膜需要一些优化。

让我们开始优化。首先选择“形态学”操作,看看有哪些可用选项。

为了消除前景中的这个孔洞,“闭运算”会起作用。圆盘形是结构元素的一个好选择。选择足够大的半径以覆盖孔洞。接下来,为了去除多余的前景像素,“开运算”将是最佳选择。同样,你只需要一个足够大的结构元素。

这个分割结果看起来相当不错,尽管它漏掉了底部附近较小的裂缝部分。

让我们导出结果。要保存结果,点击“导出”,然后选择将掩膜和掩膜图像导出到工作区,或者生成一个函数以将分割步骤应用于其他图像。在本例中,我们生成一个函数。函数代码本身组织良好且有注释,但你可能需要选择一个唯一的名称。此外,当你使用灰度阈值分割作为初始掩膜时,通常建议添加一个灰度转换步骤,以确保即使输入图像原本是彩色的,函数也能正常工作。

要尝试其他方法,你可以点击历史记录中的早期步骤,然后从那里简单地选择不同的步骤。

或者,要在尝试另一种方法的同时保留当前分割,请点击“新建分割”。你可以选择从头开始,或者通过选择“克隆分割”从当前选定的步骤开始新的分割。

也许存在另一种形状用于开运算,可以在不消除小裂缝的情况下消除多余的前景像素。要查看应用中的可用选项,请展开“形状”菜单。

你可以尝试不同的形状和大小,而无需立即应用它们。

例如,一个足够高的矩形可以去除多余的前景。然而,我们同时也消除了小裂缝部分。请记住,结构元素的形状将决定它影响图像的哪些部分。在这里,一个45度角的线段可以适应小裂缝,同时仍能穿过多余的前景像素区域。

应用此掩膜,再次点击“显示二值图像”按钮以查看其叠加在图像上的效果。

你可以看到,这个操作很好地分割了这张图像中的裂缝。让我们也将此导出为一个函数。同样,为函数起一个唯一的名称并添加灰度转换是个好主意。

现在,你可能已经注意到“反转掩膜”旁边有一个名为“填充孔洞”的选项。我们一直在使用形态学闭运算来填充初始的孔洞,但如果使用这个选项会怎样呢?为了找出答案,让我们在“反转掩膜”步骤克隆分割并尝试它。

这个操作填充了中心孔洞,完全没有影响多余的前景。你也可以通过先反转图像,然后使用“填充孔洞”来尝试消除多余的前景。

再次点击“填充孔洞”,然后重新反转。

这个结果不错。小裂缝仍然存在。“填充孔洞”选项不需要你选择合适结构元素来闭合孔洞。然而,由于孔洞被定义为未连接到边界,我们确实遗漏了一些小的伪影。

让我们也导出这个函数,并像之前一样添加灰度转换和唯一名称。

现在,让我们在一张新图像上测试三种不同的分割方法,看看它们是否有效。第一个分割函数似乎效果很好。第二个也有效,但你可以看到在这种情况下,线段在边缘处的效果并不理想。最后,第三个遗漏了相当一部分裂缝。当你需要分割许多图像时,通常需要考虑权衡。

一种在单张图像上表现很好的方法可能无法推广到其他图像。在本例中,方法1可能是最好的。尽管它遗漏了一小段裂缝,但它很好地捕捉到了两条大裂缝。在后续课程中,你将学习如何快速测试分割函数在大量图像上的表现。

除了这里看到的,图像分割器应用中还有许多其他选项和设置值得探索。要了解更多关于其他选项的功能或如何最有效地使用它们,只需点击右上角的问号,然后点击“图像分割器应用入门”,或关注本视频下方的链接。


在本节课中,我们一起学习了如何使用图像分割器应用从阈值分割开始,通过形态学操作和填充孔洞等技术优化掩膜。我们探索了不同结构元素的影响,并学会了生成和导出可复用的分割函数。最后,我们评估了不同方法在新图像上的表现,理解了在分割多张图像时需要在精度和通用性之间进行权衡。

16:组合多个掩膜 🎭

在本节课中,我们将学习如何通过组合多个掩膜来实现更精确的图像分割。有时,单个掩膜无法满足我们的分割需求,这时就需要使用逻辑运算符来合并多个掩膜。

概述

有时,单个掩膜无法产生理想的分割结果。例如,从一张图像中分割出所有彩色芯片。

然而,通过组合多个掩膜,我们可以实现所需的分割效果。在本视频中,你将学习如何使用逻辑运算符(如 ANDOR 以及 NOT)来处理这种情况。

问题分析

由于芯片是圆形的,分割它们的一个好方法是使用图像分割器应用中的“查找圆形”工具。

调整大小和灵敏度可以分割所有黄色芯片,但无法分割其他颜色的芯片。这是因为前景极性设置为“亮”。

将前景极性更改为“暗”可以分割所有剩余颜色的芯片。因此,我们需要为每种极性设置生成一个分割函数,以便在脚本中创建掩膜。

创建掩膜

首先,打开一个脚本并加载图像。接下来,使用从应用中生成的函数为两种前景极性创建二值掩膜。

% 加载图像
img = imread('chips_image.png');

% 使用应用生成的函数创建掩膜
mask_bright = segmentChips_bright(img);
mask_dark = segmentChips_dark(img);

组合掩膜

要组合二值掩膜,可以使用逻辑运算符 ANDORNOT。在本例中,我们需要使用 OR 运算符来获取图像中任一掩膜为真的部分。

% 使用 OR 运算符组合掩膜
combined_mask = mask_bright | mask_dark;

现在,让我们检查结果。

看起来这个掩膜应该能够分割所有芯片。

应用掩膜

接下来,我们看看将掩膜应用到图像上的效果。

首先,创建图像的副本。然后,应用掩膜。使用 repmat 函数将二值掩膜复制到所有三个颜色平面。

% 创建图像副本
img_copy = img;

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/mathworks-engi-sci-imgproc/img/668197527a29e0ce81365028085972fa_16.png)

% 应用掩膜
mask_3d = repmat(combined_mask, [1, 1, 3]);
img_masked = img_copy .* uint8(mask_3d);

效果很好。每个芯片都被这个掩膜成功分割。

复杂掩膜组合

在你的应用中,可能需要组合两个以上的掩膜,并且它们之间的关系可能更复杂。但你可以始终将组合分解为单个运算符对。

例如,如果需要组合三个掩膜 ABC,可以使用以下方式:

% 组合三个掩膜
final_mask = (A & B) | C;

通过这种方式,你可以灵活地处理各种复杂的掩膜组合需求。

总结

在本节课中,我们一起学习了如何通过组合多个掩膜来实现更精确的图像分割。我们使用逻辑运算符 OR 将两个掩膜合并,从而成功分割了所有彩色芯片。我们还探讨了如何将复杂掩膜组合分解为简单的运算符对,以便更好地处理实际应用中的需求。

17:使用聚类进行图像分割 🧩

在本节课中,我们将要学习一种非二值化的图像分割方法——聚类。我们将探讨如何利用聚类技术,根据像素的颜色属性将图像分割成多个不同的区域,从而一次性标记出图像中的多个对象。

概述

分割并不总是二值的。某些应用需要在同一张图像中标记多个对象。例如,在下图中,如果我们希望将每种颜色的芯片单独分割出来,就需要一种能够生成多标签掩码的方法。

一种可能的方法是使用颜色阈值应用程序为每种颜色创建一个二值掩码,然后将它们组合成一个多标签掩码。

然而,这种方法可能有些繁琐。

聚类方法简介

上一节我们介绍了多标签分割的需求,本节中我们来看看一种能直接生成多标签掩码的方法:聚类。聚类是一种数学技术,用于将具有相似属性的数据分组。在图像处理中,我们可以根据像素的颜色坐标(例如在HSV颜色空间中的值)将图像聚类成不同的区域。

与颜色阈值处理类似,在聚类时,使用替代颜色空间(如HSV)通常是有利的。在本例中,HSV空间似乎能最好地区分芯片的颜色坐标。

以下是转换图像到HSV颜色空间的代码:

hsvImage = rgb2hsv(rgbImage);

执行K均值聚类

接下来,聚类算法需要知道图像中要标记多少个颜色簇。图像中有五种颜色的芯片、一支黑色笔和一个木色桌面,因此我们最初尝试使用7个标签。

imsegkmeans函数将对图像执行K均值聚类。请注意,输入图像的数据类型需要是single

以下是执行聚类的代码:

numLabels = 7;
[labelMatrix, ~] = imsegkmeans(single(hsvImage), numLabels);

函数的输出是一个像素标签矩阵。矩阵中的每个元素都是一个数字,指定了对应像素被分配到的簇。

为了将标签矩阵与原始图像进行比较,可以使用label2rgb函数:

coloredLabels = label2rgb(labelMatrix);
imshow(coloredLabels);

分析初步聚类结果

查看标签矩阵后发现,桌面像素被分到了三个不同的簇中,并且并非每种颜色的芯片都获得了唯一的标签。

K均值聚类使用点之间的距离作为相似性度量,将颜色坐标数据划分为K个簇。在本例中,可以看到桌面的许多像素分布在一定的亮度范围内,这个“体积”被分割成了多个簇。

然而,我们注意到属于笔和桌面的像素,其饱和度都低于彩色芯片的像素。

结合阈值处理优化分割

因此,我们可以利用这一点,先将桌面和笔从图像中分割出去,然后再对黑色背景上的彩色芯片进行聚类。

看起来,饱和度阈值约为0.75时,可以很好地分离桌面、笔和彩色芯片。

让我们将这一步骤添加到脚本中。饱和度值位于HSV图像的第二个坐标平面中。

以下是创建饱和度掩码并应用的代码:

% 假设 hsvImage 是之前转换好的HSV图像
saturationMask = hsvImage(:, :, 2) > 0.75;

% 创建图像副本并应用掩码
filteredHsvImage = hsvImage;
% 将掩码应用到所有三个颜色坐标平面
filteredHsvImage(repmat(~saturationMask, [1, 1, 3])) = 0;

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/mathworks-engi-sci-imgproc/img/f87a20428373ba2335fbe358c4def066_10.png)

% 为了视觉检查结果,转换为RGB显示
filteredRgbImage = hsv2rgb(filteredHsvImage);
imshow(filteredRgbImage);

看起来操作正确。

对过滤后的图像进行聚类

现在,让我们复制之前的聚类代码。

更新代码以使用我们刚刚创建的图像,并将标签数量改为6(对应五种芯片颜色和黑色背景)。

以下是更新后的聚类代码:

numLabelsRefined = 6;
[labelMatrixRefined, ~] = imsegkmeans(single(filteredHsvImage), numLabelsRefined);
coloredLabelsRefined = label2rgb(labelMatrixRefined);
imshow(coloredLabelsRefined);

现在,每种芯片颜色似乎都有了唯一的标签。

总结

本节课中我们一起学习了使用聚类进行图像分割。总的来说,聚类是一种将图像分割成几个不同颜色区域的有效方法。

以下是关键步骤的总结:

  1. 转换颜色空间:通常先将图像转换到更适合分离目标颜色的空间(如HSV)。
  2. 确定簇数量:根据图像中不同区域的数量设定K值。
  3. 执行聚类:使用imsegkmeans函数生成标签矩阵。
  4. 可视化和分析:使用label2rgbimshow检查聚类结果。
  5. 结合其他方法:如本例所示,有时需要将聚类与其他分割方法(如阈值处理)结合使用,以获得最佳结果。

您可能会发现,使用特定的颜色空间有助于聚类算法。并且,如本例所示,您可能需要将聚类与其他分割方法结合使用,以实现精确的多对象分割。

18:计算与应用区域属性 🧩

在本节课中,我们将学习如何计算图像分割后各个区域的属性,并利用这些属性来筛选和分析特定区域。这是从分割走向实际应用的关键一步。


在许多应用中,图像分割只是实现更大目标的一个步骤。

分割完成后,通常需要进一步分析这些区域,通过计算诸如面积周长形状位置等属性,或者利用这些属性来分离出特定的区域。

使用区域分析器应用程序

上一节我们介绍了分割的基本概念,本节中我们来看看如何分析分割出的区域。首先,打开 Region Analyzer 应用程序。

你可以在MATLAB的“Apps”选项卡中找到它,它与其他图像处理和计算机视觉应用程序归为一组。接下来,将分割得到的掩膜图像加载到该应用程序中。

加载后,你会注意到右侧会自动填充每个区域的属性列表。你可以使用菜单来添加或删除需要查看的属性。在这张图像中,存在包含一、二、三个拼图块的区域。

以下是可能帮助我们区分不同区域的一些属性:

  • 面积:区域内的像素总数。
  • 周长:区域边界的长度。
  • 质心:区域中心点的坐标。
  • 边界框:能完全包围区域的最小矩形。

让我们选择几个可能有助于区分不同区域的属性。包含更多拼图块的区域应该具有更大的面积,我们来验证一下。

请注意,在属性列表中选择特定行时,掩膜图像中对应的区域会高亮显示。此时,按面积对属性表进行排序会很有帮助。

通过排序可以清楚地看到:

  • 两个包含三个拼图块的区域,其面积均大于 20000 像素。
  • 包含两个拼图块的区域,其面积大约在 1200018000 像素之间。
  • 面积在 900010000 像素之间的区域对应单个拼图块。
  • 最后,两个面积很小(约20多像素)的区域是掩膜中的小噪点。

因此,面积属性可以用来分离包含不同数量拼图块的区域。

筛选特定区域

现在,让我们来分离出包含三个相连拼图块的区域。操作步骤如下:

  1. 点击筛选下拉菜单,确保选中“面积”属性。
  2. 将范围的下限值增加到 20000 像素。

很好,这样就筛选出了所有包含三个拼图块的区域,其他区域都被过滤掉了。通过区域属性进行筛选,可以让你专注于感兴趣的目标,也常用于去除小的噪点,正如我们刚才所做的那样。

导出与自动化分析

为了将此工作流程重复应用于当前掩膜或类似的掩膜,可以使用“导出函数”选项。

生成的函数使用 regionprops 函数来应用在应用程序中选择的筛选条件,并生成新的掩膜。对于每个筛选操作,都指定了属性和范围。例如,为了给面积范围的上限增加一些缓冲,可以将括号中的第二个数字增加到类似 28000 的值。

该函数还会返回计算出的属性本身。一个由逗号分隔的属性列表会告知 regionprops 函数以默认的结构体表格形式返回它们。若想从该函数获得表格输出,取消注释相应的代码行。

给函数起一个有意义且独特的名称是个好习惯。

在代码中应用区域属性

区域分析器应用程序是快速了解区域信息和进行筛选的便捷工具。而 regionprops 函数能够返回更多信息,请查阅文档以获取所有可用属性的完整列表和描述。

例如,你可能见过像下图这样的图像,其中使用边界框来高亮显示感兴趣的区域。让我们看看如何创建它。

首先,在脚本中准备好原始图像和掩膜。

% 加载原始图像和掩膜
originalImage = imread('puzzle_image.jpg');
load('segmented_mask.mat'); % 假设掩膜已保存

接下来,应用之前生成的函数到掩膜上,以筛选出三个拼图块的区域。

% 应用筛选函数
filteredMask = filterRegionsByArea(originalMask, [20000, 28000]);

然后,使用 regionprops 函数获取每个区域的边界框坐标。

% 计算边界框属性
stats = regionprops(filteredMask, 'BoundingBox');
boundingBoxes = vertcat(stats.BoundingBox); % 将结构体数组合并

最后,使用 insertShape 函数,结合边界框的坐标,在原始图像上绘制矩形。通常指定颜色并调整线宽有助于提高可视性。

% 在原始图像上绘制边界框
annotatedImage = insertShape(originalImage, 'Rectangle', boundingBoxes, ...
                             'Color', 'green', 'LineWidth', 3);
imshow(annotatedImage);

本节课中我们一起学习了如何使用 Region Analyzer 应用程序代码 来计算区域属性。我们利用区域属性对掩膜进行了筛选,并生成了函数以重复此分析流程。最后,我们还使用区域属性(如边界框)在图像中高亮显示了特定的目标对象。掌握这些技能,能帮助你从分割结果中提取有价值的信息,并推进到后续的分析或决策步骤。

19:综合应用指南 🧩

在本节中,我们将总结并整合之前学到的图像分割技术,讨论每种方法的适用场景,并提供一个清晰的决策流程,帮助初学者在面对具体图像分割任务时,能选择并组合最有效的工具。

概述

你已经学习了多种图像分割工作流中的技术。本节将总结这些技术,并讨论每种方法在何时使用最为合适。图像分割通常是一个迭代过程,需要根据图像的具体特性(如对比度、颜色、形状)和最终目标,灵活选择和组合不同的方法。

已掌握的技术回顾

上一节我们介绍了多种创建和优化掩膜的方法。以下是你在工作流中可能使用过的核心技术总结:

  • 掩膜创建方法
    • 在灰度图像上使用全局阈值法自适应阈值法
    • 在彩色图像上使用颜色阈值法聚类法
    • 使用边缘检测形状检测(如圆形查找)算法。

  • 预处理步骤
    • 使用空间滤波图像平均等方法在创建掩膜前降低噪声

  • 掩膜优化方法
    • 使用形态学操作(如开运算、闭运算)来修复掩膜中的孔洞或移除小瑕疵。
    • 根据区域属性(如面积、形状)对掩膜进行过滤。
    • 当单个掩膜效果不佳时,使用逻辑运算(如与、或)组合多个掩膜

如何选择合适的分割方法

面对如此多的工具,你可能会疑惑如何为自己的图像数据选择合适的方法。通常需要尝试多种方案,但一个好的起点是:确定最能清晰区分前景与背景的属性

以下是基于图像特性的方法选择指南:

  • 如果图像前景与背景之间存在明显的强度对比,那么灰度阈值法很可能效果良好。
    • 公式:mask = image > threshold
  • 如果图像还存在光照不均的情况自适应阈值法可以调整局部变化,是更好的选择。
  • 如果前景与背景在颜色上差异显著,那么颜色阈值法聚类法是很好的选择。
    • 通常,使用颜色阈值应用程序来评估不同的色彩空间并找到分割图像的最佳坐标会很有帮助。
  • 如果前景对象可以由圆形度等形状特征定义,那么基于边缘的算法(如imfindcircles)是合适的。
    • 注意:使用此类算法前,通常需要先应用一些预处理,如空间滤波。

预处理与迭代优化

无论哪种方法看起来最有希望,如果已知图像数据含有大量噪声,提前进行降噪处理通常是个好主意。这可以改善初始掩膜的质量,并减少后续精修的需要。

不过,你需要试验滤波器的尺寸。滤波器过强可能会从结果掩膜中消除不太明显的前景区域

图像处理常常涉及试错。因此,不要将所有精力都花在降噪上。如果在工作流后期遇到问题,你随时可以返回此步骤进行调整。

掩膜的精修策略

无论采用哪种方法,最终得到的初始掩膜通常都需要进一步精修。以下是两种常用的精修工具:

  1. 形态学操作:此技术非常灵活,因为你可以改变结构元素的大小和形状。
    • 用于闭合前景中的孔洞移除小的伪影
    • 代码示例(MATLAB):mask_closed = imclose(mask, strel('disk', 5));
  2. 区域属性过滤:如果你的掩膜前景具有明显的属性(如特定大小或形状),按区域属性过滤是另一个有效的精修工具。
    • 例如,只保留面积大于某个阈值的区域。

组合策略与迭代思维

最终,你可能会发现需要结合使用多种方法,例如组合多个掩膜,或者返回到工作流的早期步骤进行调整

请记住,图像分割通常是一个迭代过程。尝试多种方法,并始终专注于你的最终目标。随着经验积累,你将学会哪些技术最适合你的图像数据。

总结

本节课中,我们一起学习了如何综合运用图像分割工作流中的各项技术。关键要点包括:根据图像特性(强度、颜色、形状)选择初始分割方法;利用预处理(如降噪)改善输入质量;灵活使用形态学操作和区域属性过滤来精修掩膜;并理解图像分割是一个需要反复试验和调整的迭代过程。通过实践,你将能更高效地为自己的工程与科学应用构建可靠的分割方案。

20:分析三维图像 🧠

在本节课中,我们将学习如何将二维图像处理的概念应用到三维图像上。具体内容包括:分割三维体数据、使用形态学操作优化掩膜、合并多个掩膜以及执行区域分析。

三维成像是医学、地质学和微生物学等多个领域的重要组成部分。与处理二维图像类似,在分析三维图像数据之前,通常也需要先对其进行检查。

查看三维数据

上一节我们介绍了三维分析的重要性,本节中我们来看看如何查看数据。使用 Volume Viewer 应用程序可以从任意视角查看三维数据,并更好地理解整个体数据中的体素强度。

分割三维图像

与二维图像一样,也有专门的应用程序来帮助分割三维图像。Volume Segmenter 应用程序能让你快速尝试不同的分割算法,导入自定义算法,并可视化结果。

在本视频中,你将使用 Volume Segmenter 应用程序,首先在这张 MRI 扫描图像中将脑组织与颅骨和头皮分离,然后从脑组织中分离出白质。

导入图像并开始分割

让我们首先将三维图像导入应用程序。应用程序提供了基于体数据或基于切片的算法来分割三维图像的选项。这里我们将使用基于切片的分割方法。

为了在所有切片中分割出感兴趣区域,需要为算法提供一个起点。如果你有现成的掩膜可以加载。然而,由于这里的大脑很容易区分,手绘区域是一个很好的起点。即使区域不完美也没关系,只需要大致隔离出大脑即可。

应用活动轮廓算法

现在,由于大脑、颅骨和头皮具有清晰、平滑的轮廓,使用活动轮廓算法来优化这个掩膜。

效果看起来不错。让我们将这个结果应用到包含脑组织的其余切片上。看起来第一个包含脑组织的切片是第 26 片。

因此,我们将从起始切片 78 到切片 26 应用我们的算法。

看起来我们的算法在这些切片上很好地分割了大脑。在另一个方向上,最后一个包含脑组织的切片似乎是 155。

所以,让我们也从切片 78 到 155 应用我们的算法。嗯,看起来在这里效果不是很好。可以看到在这个范围内出现了相当多的伪影。

使用形态学优化掩膜

观察单个切片和三维显示,你可以尝试为这个范围开发不同的算法,但也可以使用三维图像的形态学操作来优化掩膜。

所以,让我们试试看。使用腐蚀,然后是膨胀,配合合适大小的结构元素,可以消除伪影,但不会影响大脑。

使用算法参数按钮来调整盘形结构元素的大小。让我们尝试半径为 10,并像在二维情况下一样,将近似值设置为 0。由于你尚不确定这是否有效,可以在当前切片上测试操作以查看结果。

看起来结构元素需要再大一点才能消除所有伪影。

你可以使用 Ctrl + C 组合键撤销之前的腐蚀操作。

现在,让我们尝试半径为 13。

这起作用了。现在,让我们检查一下它对其他有伪影的切片是否同样有效。

首先,切片 116 到 155。

还不错。好的,现在在另一个方向上,看起来我们需要从切片 114 回到切片 96 腐蚀伪影。在执行基于切片的形态学操作时,注意不要在同一切片上重复操作。

很好。现在,你需要膨胀掩膜,以恢复被腐蚀移除的脑组织区域。

使用相同的结构元素设置,并将切片范围设置为从 96 到 155。这看起来非常好。伪影消失了,掩膜看起来基本上捕获了所有的脑组织。

三维显示证实我们成功地分割了大脑。

导出并组合掩膜

让我们将此掩膜导出到工作区,使用逻辑数据类型,以便将其与下一个掩膜组合来分离白质。

注意,体素强度在白质、灰质和脑脊液之间存在局部变化。这意味着自适应阈值是分割白质与其他脑组织的一个有前景的选择。

让我们试一试。

这看起来相当不错。掩膜捕获了灰质和脑脊液,但也捕获了头皮和其他伪影。不过,当我们将其与大脑掩膜进行逻辑组合时,这些将被消除。

为此,让我们也将这个掩膜导出到工作区。

为了分离白质,使用 AND 逻辑运算符组合全脑掩膜和自适应阈值掩膜。因为自适应阈值掩膜捕获了白质以外的组织,使用 NOT 逻辑运算符将其反转。

在应用程序中打开生成的掩膜,确认我们只分离出了白质。

执行区域分析

最后,与二维情况类似,你可以计算各种掩膜区域属性。例如,让我们使用 regionprops3 函数计算白质的体积。

我们需要对结果求和,因为白质掩膜中有多个区域。

接下来,计算此次扫描中的总脑体积。

最后,找出大脑中白质的比例。根据我们的分割,这个大脑大约有 60% 是白质。

Matlab 中还有许多其他用于处理和分析体数据的函数。请查阅文档,探索你可以用三维图像做的一切。

总结

本节课中,我们一起学习了如何分析三维图像。我们使用 Volume Segmenter 应用程序分割了 MRI 脑部扫描数据,通过活动轮廓算法和形态学操作(腐蚀与膨胀)优化了分割结果,并利用逻辑运算组合掩膜来精确分离白质。最后,我们使用 regionprops3 函数计算了白质的体积和比例,完成了从数据查看、分割到定量分析的全过程。

课程二:课程总结与展望

在本节课中,我们将对已完成的课程二进行总结,并展望后续课程的学习方向。

恭喜你完成课程二的学习,掌握了新的技能,并开启了新的分析途径。你学习了应用空间滤波器来降低噪声和寻找边缘。随后,你通过应用形态学操作来闭合间隙和移除小瑕疵,从而优化了已有的图像掩膜。对于难以分割的图像,你应用了高级分割方法,例如组合多个掩膜和使用聚类技术。

为了评估分割结果,你通过计算区域属性来对分割效果进行了量化分析。

你甚至将新掌握的技能应用到了二维和三维图像的处理中。

掌握了这些知识,你已经准备好开始处理图像,并解决自己遇到的实际工程与科学问题了。

到目前为止,你已将上述技术应用于单个或小批量的图像处理中。

但是,如果你需要处理大量图像,该怎么办?想必你不会愿意手动逐张分割成百上千张图像。

那么,我们接下来该学习什么呢?这是一个非常好的问题。

在本专业系列的下一门课程中,你将学习如何将已掌握的技能应用于大规模图像集。你将使用图像批处理器应用程序和数据存储,对数量庞大、无法逐一分析的图像组进行自动化处理。你将在MATLAB中检查处理结果,并评估不同处理方案之间的权衡。你甚至会将图像处理技能应用于视频分析。最后,你将在一个最终项目中,综合运用本系列课程中学到的所有知识。

欢迎加入下一门课程的学习,你将学会自动化图像分析,并在更大规模上应用你的知识。

21:专业领域概述

在本节课中,我们将学习图像处理在多个专业领域中的核心应用,并了解MathWorks提供的专项课程如何帮助工程师和科学家掌握这项关键技能。

从图像中提取信息对于广泛的应用至关重要。

这些应用包括诊断医疗状况、研究气候变化的影响、设计自主系统以及改进农业。

无论您身处哪个领域,分析图像都是当今许多工作的必备技能。这就是MathWorks创建《工程与科学图像处理》专项课程的原因。

无论您是图像处理的新手,还是正在寻找新工具来提高工作效率,这个专项课程都适合您。这是因为您将使用MATLAB来快速执行图像处理任务。

MATLAB专为工程师和科学家设计,包含许多应用程序,使您能够快速测试不同方法并可视化结果。这些应用程序会自动生成代码,以便您能够复现和扩展您的工作。

该专项课程分为三门课程。

课程一:数字图像处理基础

在课程一中,您将扎实地学习如何处理数字图像。您将学习如何创建常见的图像调整,以及如何隔离感兴趣区域以进行进一步分析。

以下是课程一中的两个应用示例:

  • 使用颜色信息识别成熟的蓝莓。
  • 根据卫星图像计算冰川融化量。

课程二:图像分割技术

上一节我们介绍了图像处理的基础操作,本节中我们来看看图像分割。在课程二中,您将解决图像分割中的常见挑战。

以下是课程二将涉及的关键技术:

  • 学习减少噪声的技术。
  • 学习分离重叠对象的技术。

这些技术确保您只隔离相关信息。重要的是,您将分析找到的区域,计算诸如面积方向等属性。

课程三:算法自动化

通常,您需要将图像处理步骤应用于许多图像,或者可能需要分析视频。

在课程三中,您将自动化您的算法以处理数千张图像,从而节省时间和精力。

当您拥有大量图像时,目视检查所有图像是不可行的。您将练习分析结果以识别异常值,供进一步调查。

最终项目:综合应用

在专项课程结束时,您将运用新技能完成一个最终项目:在嘈杂的视频中检测汽车以分析交通模式。

您甚至能够处理复杂情况,例如当对象被部分遮挡时。最后,您将创建一个视频,其中包含在检测到的车辆驶过时围绕它们的边界框。

图像处理是一项需求量很大的职业技能。无论您是开发自主系统、诊断疾病还是研究宇宙,这个专项课程都将帮助您取得成功。

本节课中我们一起学习了图像处理在工程与科学中的重要性,以及MathWorks专项课程的三部分核心内容:从基础处理、分割技术到算法自动化。现在,让我们开始学习吧。

22:自动化图像处理 🚀

在本课程中,我们将学习如何将之前掌握的图像处理技能自动化,以处理和分析大量图像或视频序列,而无需手动操作每一张图片。

概述 📋

欢迎来到本专项学习的最后一门课程。至此,你已经掌握了一系列处理和分图像析的技能,例如调整图像对比度、应用空间滤波器、从背景中分割目标、使用形态学改善掩膜,以及计算区域属性。你已将这些步骤应用于少量图像。但如果需要分析大量图像呢?例如,混凝土图像数据集包含数百张图片。在本课程中,你将学习自动化图像处理步骤,使其能处理一系列图像,而非一次仅处理一张。

自动化方法 🛠️

上一节我们回顾了已掌握的技能,本节中我们来看看实现自动化的两种主要方法。

你将练习使用以下两种核心方法:

以下是两种主要的自动化处理方法:

  1. 图像批处理器应用程序:此工具使你能够快速查看图像处理步骤的结果。
  2. 图像数据存储:当需要将分析整合到你的工作中时,此方法提供了更高的灵活性。

处理挑战与解决方案 🔍

当处理大量图像集合时,逐一进行视觉检查通常不可行。因此,需要一种方法来识别有问题的图像以供进一步调查。

在本课程中,你将学习分析图像区域,以识别具有异常特征的图像。

视频处理应用 🎥

你还会将这些技术应用于视频处理,将其视为一系列图像帧。

课程项目与实践 🚗

课程结束时,你将把新技能应用到一个真实世界的项目中。

设想尝试分析一条道路全天的交通模式。你的任务将是处理这段视频并统计每一帧中的汽车数量。

请务必利用论坛提问并分享你发现的任何有趣结果。我们将在学习过程中为你提供帮助。

总结 ✨

本节课中,我们一起学习了自动化图像处理的重要性,介绍了两种核心的自动化方法(图像批处理器和图像数据存储),探讨了处理大批量图像时识别异常的策略,并了解了如何将自动化技术应用于视频分析。最后,我们预览了即将进行的真实项目挑战。祝你好运!

23:使用图像批处理器应用程序 🚀

在本节课中,我们将学习如何利用图像批处理器应用程序,自动化处理和分析大量图像。我们将以混凝土裂缝图像数据集为例,演示如何将分割与分析步骤结合,实现高效的工作流。


概述

面对包含数百或数千张图像的数据集,手动处理每张图像是不现实的。为了评估混凝土裂缝的严重程度,我们需要先对每张图像进行分割,然后从二值图像中计算裂缝的属性。本节课将指导您如何将这个过程自动化。


工作流程自动化

自动化工作流程可以整合为两个主要步骤。

首先,测试并优化分割函数,确保其在大多数图像上都能良好工作。

然后,将分割函数与分析函数结合,以实现整个工作流程的自动化。


测试分割函数

开始之前,您需要检查分割函数是否有效。

您可以手动对每张新图像重复运行该函数,但这个过程很快就会变得繁琐。一个更好的选择是使用图像批处理器应用程序。

打开应用程序后,加载包含所有混凝土图像的文件夹。

接着,选择您的分割函数。处理数百或数千张图像可能需要很长时间,因此最好先只处理少量图像。这样,如果函数工作不佳,您可以快速发现。

处理选定的图像后,将分割掩膜与原始图像并排显示以进行比较。目前,结果看起来很有希望。因此,让我们将该函数应用到所有图像上。

许多二值图像看起来不错。但请注意,这个分割函数并非在所有情况下都表现良好。通过在应用程序中可视化原始图像和处理后的图像,您可以快速判断处理步骤是否需要进一步优化。


迭代处理过程

处理多张图像是一个迭代的过程。

首先,在少量图像上开发您的图像处理步骤。

然后,使用图像批处理器应用程序来判断处理步骤是否需要调整。

一旦对结果满意,您就可以导出它们以进行进一步分析。


优化与重新测试

在本例中,图像165和166的分割效果不佳。我们已经使用这些图像来优化了分割函数。

因此,现在我们可以对所有图像重新测试新的函数。结果看起来好多了。

请记住,最终目标是对裂缝进行区域分析以确定其严重程度。为了自动化整个过程,您需要创建一个结合了分割和分析步骤的单一函数。


创建组合函数

在图像批处理器应用程序中,您可以创建一个新的模板函数,该函数以图像作为输入。

并返回一个单一的输出变量。

对于此示例,这个新函数的具体代码将在后续阅读材料中介绍,但现在,您将学习如何生成自己的函数的一般过程。

首先,删除示例代码。

接下来,您将输入图像传递给预处理图像并生成分割的函数。

然后,对分割后的图像应用分析函数。

二值图像和区域属性都可以使用结构数组数据类型保存为单一的输出变量。结构体是多个变量的容器,您可以使用点表示法添加变量。

以下是向名为 results 的输出结构体变量添加二值图像、区域数量、总裂缝面积和最大裂缝宽度的代码行:

results.binaryImage = BW;
results.numRegions = numRegions;
results.totalCrackArea = totalArea;
results.maxCrackWidth = maxWidth;

完成后,请务必保存函数文件。


验证函数并导出结果

现在,是时候通过将此函数应用于原始的未分割图像来检查其是否有效了。

并通过目视检查结果。看起来较大的裂缝确实具有较大的面积值。

通过将结果导出到工作区来保存它们以进行进一步分析。这允许您将区域属性保存为表格变量。

现在,您可以使用此表格执行进一步分析,例如研究裂缝面积的分布。


总结

在本节课中,我们一起学习了如何自动化图像处理和分析步骤。当处理您自己的数据集时,请记住图像处理是一个迭代的过程。您可能需要多次改进一个函数,才能有效地处理整个图像集合。

24:使用图像数据存储进行批处理

在本节课中,我们将学习如何使用代码,通过图像数据存储(Image Datastore)来批量处理大型图像数据集。我们将以手写数字数据集为例,创建一个包含文件名、标签和旋转角度的数据表。

概述

上一节我们介绍了使用批处理处理器应用程序处理数据。本节中,我们来看看如何通过编写代码实现相同的功能。标准方法是使用图像数据存储。数据存储不直接加载全部图像,而是存储图像的位置信息,以便在需要时导入和处理。

创建图像数据存储

首先,在MATLAB中打开一个新的实时脚本。通过调用 imageDatastore 函数并指定数据集的位置来创建一个数据存储变量。本例中,图像包含在MATLAB的神经网络工具箱根文件夹中。

由于每个数字的图像都存储在独立的子文件夹中,请确保将 ‘IncludeSubfolders’ 选项设置为 true

imds = imageDatastore('path_to_neural_network_toolbox_root_folder', 'IncludeSubfolders', true);

运行此代码后,新的数据存储变量 imds 将保存所有10000张图像的位置信息。

处理单张图像

为了生成最终的数据表,我们首先编写代码来收集单张图像的文件名、标签和旋转角度。然后,我们将这段代码封装在循环中,以处理数据存储中的每一张图像。

使用 readimage 函数从数据存储中导入单张图像。第一个输入是数据存储变量,第二个输入是要加载的图像的索引。然后使用 imshow 函数显示图像。

img = readimage(imds, 1);
imshow(img);

在处理自己的批量图像时,建议尝试不同的索引值,以观察处理和分析步骤在不同图像上的表现。

readimage 函数的功能不止于加载图像本身。如果请求第二个输出,它还会返回图像的元数据,包括其位置,这可以用来保存文件名。

[img, info] = readimage(imds, 1);
fileName = info.Filename;

接下来,请注意数据存储中的 Labels 字段最初是空的。为了将子文件夹名称提取为标签,在初始化数据存储时,可以使用 ‘LabelSource’ 选项并将其设置为使用文件夹名称。

imds = imageDatastore('path_to_neural_network_toolbox_root_folder', 'IncludeSubfolders', true, 'LabelSource', 'foldernames');

现在,每张图像都有一个标签。根据你的数据集和目标,可以从这里开始进行多种不同的图像处理和分析。

计算图像旋转角度

在本例中,我们的目标是计算每张图像的旋转角度。首先,使用 imbinarize 函数将图像转换为二值图像。然后,使用 regionprops 函数计算数字的方向。

bwImg = imbinarize(img);
stats = regionprops(bwImg, 'Orientation');
angle = stats.Orientation;

如果你想了解更多关于区域方向的定义,请查阅相关文档。

自动化批量处理

现在已经建立了处理单张图像的工作流程,但为所有10000张图像执行此代码将非常耗时。我们可以通过将处理步骤封装在 while 循环中来自动化此过程。

首先,将 readimage 函数改为 read 函数。read 函数总是读取数据存储中的下一张图像,无需提供索引。然后,使用 hasdata 函数作为循环条件,该函数将在所有图像读取完毕后退出循环。

在运行代码之前,还需要一种方法来存储每张图像的文件名、标签和方向角。初始化将保存这些信息的空数组。在循环结束时,将这些变量组合成一个表格。

在循环内部,使用垂直连接将数据追加到每个数组中。这样,数组在每次迭代中都会增加一行。为了连接图像名称数组,需要将字符向量转换为字符串。

以下是代码框架:

% 初始化空数组
fileNames = [];
labels = [];
angles = [];

![](https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/mathworks-engi-sci-imgproc/img/bea0adb640e1d9734b44baf3e28bd852_7.png)

% 重置数据存储到起始位置
reset(imds);

% 循环处理所有图像
while hasdata(imds)
    [img, info] = read(imds);
    fileName = string(info.Filename);
    label = imds.Labels(imds.UnderlyingDatastores{1}.CurrentFileIndex); % 获取当前图像的标签

    % 处理图像并计算角度
    bwImg = imbinarize(img);
    stats = regionprops(bwImg, 'Orientation');
    
    % 检查是否只检测到一个区域
    if length(stats) == 1
        angle = stats.Orientation;
        % 垂直连接数据
        fileNames = [fileNames; fileName];
        labels = [labels; label];
        angles = [angles; angle];
    end
end

% 创建最终数据表
dataTable = table(fileNames, labels, angles, 'VariableNames', {'FileName', 'Label', 'Angle'});

处理异常情况

运行上述代码时,可能会遇到错误,提示“所有表变量必须具有相同的行数”。这是因为 regionprops 函数分析某些图像时,检测到了两个或更多个不连续的区域,导致角度数组的行数多于图像数量。

由于我们对10000张图像使用相同的分割函数,这种不完美是可以预料的。知道具有多个区域的图像分割不正确,你可以返回去尝试完善分割算法。

然而,鉴于数据集中图像数量庞大,丢弃少数几个是可以接受的。只需将追加数组的代码包装在一个 if 语句中,检查图像中是否只检测到一个区域即可,如上例所示。

总结

本节课中,我们一起学习了如何使用图像数据存储通过代码批量处理图像。我们创建了一个数据存储,编写了处理单张图像的代码,并将其封装在循环中以处理整个数据集,同时还处理了分割过程中可能出现的异常情况。

你现在知道了如何使用应用程序或代码工作流处理图像。你应该对两者都进行实验,以更好地理解它们的工作原理。如果你寻求更多的控制和灵活性,可以尝试编写自己的图像数据存储代码。但如果你想快速测试一系列分析步骤,那么应用程序可能更适合。

25:处理视频文件 🎬

在本节课中,我们将学习如何将图像分割技能应用于视频处理。视频本质上是一系列连续的图像帧,因此我们可以通过逐帧处理来分析视频内容。我们将以分析一个容器内液体填充速率的任务为例,介绍从视频中提取帧、开发分割函数、批量处理所有帧以及验证结果的全过程。


熟悉视频文件 📹

视频文件广泛应用于各种场景。由于视频由一系列图像组成,你可以运用已学到的分割技能来处理视频。

例如,考虑分析容器被液体填充速率的目标。你可以通过访问视频的单个帧,并对每一帧进行分割来计算真实像素的百分比来实现此目标。

当接触一个新视频时,一个好的第一步是使用视频查看器应用程序来了解它。


使用视频查看器应用程序

首先导入视频。选择课程文件中包含的液体视频。

打开视频后,应用程序会显示视频分辨率、帧率和总帧数。应用程序包含播放视频或跳转到特定感兴趣帧的控制功能。

为了确定容器中有多少液体,你需要一个分割函数来将液体与背景分离。


开发分割函数

从视频中选择几个有代表性的帧,用于开发此函数,并将它们导出为图像。这里我们保存了第92、185和224帧。

使用你的代表性帧,运用分割技能创建一个用于隔离液体的掩膜。我们使用颜色阈值应用程序来创建我们的掩膜。你需要创建自己的函数来完成此分析。

一旦你对样本帧上的处理步骤感到满意,就是时候将它们应用到整个视频了。


批量处理所有视频帧

使用 VideoReader 函数在 MATLAB 中创建一个用于读取单个帧的对象。

v = VideoReader('liquid_video.mp4');

请注意,此对象包含大量关于视频的信息。你可以通过键入变量名,后跟一个点和属性名来访问这些属性。

totalFrames = v.NumFrames;
frameRate = v.FrameRate;

要处理所有帧,请使用一个 for 循环,从1开始,到视频的总帧数结束。

for k = 1:totalFrames
    % 处理每一帧
end

在循环内部,使用 readFrame 函数将下一个可用帧作为图像导入。

frame = readFrame(v);

现在,你可以应用你的处理和分析步骤。作为练习,你需要在此循环内创建几个变量,以计算视频中每个时间点容器的填充程度。使用 CurrentTime 属性获取每一帧的时间信息。

currentTime = v.CurrentTime;

最好重置 CurrentTime 属性,以防你再次运行 for 循环。

v.CurrentTime = 0; % 重置到视频开头

处理结束后,你应该得到一个与此类似的图表。


验证与调试处理结果

但是,如果你得到一些意外的结果怎么办?如何判断处理步骤中哪里出错了?

一种方法是为视觉检查创建一个处理后的图像视频。

使用 VideoWriter 函数创建一个用于将图像写入视频文件的对象。将文件名和视频格式指定为输入参数。

outputVideo = VideoWriter('processed_video.avi');

VideoReader 对象一样,你可以查看输出视频将具有的属性。请注意,默认帧率是30,这与我们的原始视频不匹配。让我们更改它,使它们相同。

outputVideo.FrameRate = v.FrameRate;

要创建新视频,你需要在 for 循环之前打开 VideoWriter 对象,并在循环之后关闭它。

open(outputVideo);
for k = 1:totalFrames
    % ... 处理帧 ...
    writeVideo(outputVideo, processedImage);
end
close(outputVideo);

for 循环内部,将一个图像传递给 writeVideo 函数。在这个例子中,我们使用 imfuse 函数和 montage 方法来创建原始图像和处理后图像并排显示的图像。

运行代码后,你将在当前文件夹中拥有一个新视频。你可以在视频查看器应用程序中查看此视频。使用此视频来验证你的处理步骤,并识别需要进一步调查的帧。


总结 📝

本节课中我们一起学习了处理视频文件的完整工作流程。

总结一下,处理视频文件时,一个好的做法是:

  1. 首先使用视频查看器应用程序熟悉视频并导出一些帧。
  2. 使用这些帧开发你的处理和分析流程。
  3. 然后,使用 VideoReader 对象配合循环来处理所有帧。
  4. 使用 VideoWriter 对象对图像处理结果进行视觉检查。
  5. 如有必要,导出新的测试帧以优化你的方法。

通过遵循这些步骤,你可以系统地将图像处理技术应用于视频分析任务。

26:检测运动物体 🦃

在本节课中,我们将学习如何从复杂的静态背景中检测一个或多个运动物体。我们将以一段火鸡在Mathworks园区内行走的视频为例,介绍背景减除法的核心原理和实现步骤。

概述

假设你需要在一个复杂背景上检测一个或多个运动物体,例如这只在Mathworks园区内行走的火鸡。

在每一帧图像中,火鸡本身没有简单的形状、易于区分的颜色或清晰的图像簇可供直接分割。

然而,在每一帧中,如果能够找到背景图像与当前整体图像之间的差异,问题就能迎刃而解。

这是因为背景区域相减后会变为零,而包含火鸡的区域则会保持非零值。

获取背景图像

那么,如何获得用于计算差异的背景图像呢?如果你有一帧或多帧只包含背景的图像,可以直接使用。但在本例中,火鸡出现在每一帧里,因此我们需要自己创建背景图像。

由于火鸡一直在移动,在视频的大部分时间里,每个像素位置都曾是背景。这意味着对所有帧取平均值可以近似得到背景图像。

以下是计算所有帧平均值的步骤:

  1. 初始化一个求和变量,将第一帧转换为双精度类型。
  2. 使用循环,迭代地将剩余的每一帧图像加到求和变量上,得到总合。
  3. 确保所有算术运算都在双精度数据类型下进行。
  4. 最后,用总合除以帧数,得到平均值。

请注意,视频处理代码可能需要一些时间来执行。

可以看到,得到的背景图像有些模糊。

这在平均视频帧时很常见,通常是由于相机抖动或背景中的微小移动造成的。

分割运动物体

上一节我们介绍了如何获取平均背景图像,本节我们来看看如何通过从其他帧中减去这个平均背景并对结果进行分割,来隔离出火鸡。


第一步是从视频中选择一些有代表性的帧。

这里我们使用 read 函数从视频对象中提取一帧,并将其转换为双精度类型以便计算。我们刚刚计算的平均帧已经是双精度类型,但如果你将来保存并重新加载它,也需要确保进行转换。

为了隔离火鸡像素,需要计算当前帧与背景图像差值的绝对值。

diff_frame = abs(current_frame - background_frame);

可以看到,在结果图像中火鸡非常突出。

由于平均背景的模糊,图像中存在一些伪影。然而,相对于火鸡来说它们很小,因此分割应该不会太困难。

请记住,在处理视频时,你的函数需要处理大量帧。因此,很可能需要在视频的多个代表性帧上重复“计算与平均背景的差异”这一过程,并开发一个在所有帧上都表现良好的分割函数。

为了节省时间,我们已经通过该过程开发了一个 segment_turkey 函数。

可以看到,它能很好地勾勒出火鸡的轮廓。

标记检测到的物体

突出显示检测到的物体的一种常见方法是在其周围放置一个边界框。

让我们在这个掩膜上使用 regionprops 函数来获取火鸡边界框的坐标,并使用 insertShape 函数将其添加到原始帧中。在进行此操作时,通常指定颜色并调整线宽以提高边界框的可见性会很有帮助。

效果很好。😊

整合算法处理整个视频

现在,让我们将以上所有步骤整合起来,以检测视频中移动的火鸡。

以下是处理流程:

  1. 创建并打开一个新的视频写入对象。
  2. 将我们刚刚开发的算法包装在一个循环中。
  3. 调整 read 函数以使用循环索引。
  4. 在循环末尾添加代码,将每一帧写入新视频。
  5. 最后,添加代码关闭视频写入对象,即可开始处理视频。

再次提醒,视频处理代码可能需要一些时间来执行。

检查结果时,你可能会注意到某些帧的分割似乎不正确。

这时你可能需要返回并优化你的步骤。然而,完美通常难以实现。

总结

本节课中,我们一起学习了如何从相对静态但复杂的背景中隔离运动物体。

总而言之,如果你需要从相对静态但复杂的背景中隔离运动物体,使用背景减除法通常是一个好方法。


一般步骤如下:

  1. 为了计算,需要在双精度数据类型下工作。
  2. 选择一个只显示背景的帧,或者通过对多帧取平均来创建一个背景图像。
  3. 然后计算每一帧与背景的绝对差值。
  4. 最后对差值结果进行分割。

27:在MATLAB中分析数据 📊

在本节课中,我们将学习如何对图像处理后的数据进行深入分析。我们将以混凝土裂缝图像为例,通过比较区域属性来识别数据集中最宽的裂缝,并学习如何对图像进行分类和识别异常值。

上一节我们介绍了图像分割与特征提取,本节中我们来看看如何利用提取出的数据进行后续分析。

导入数据表

首先,我们需要导入之前通过裂缝分割与分析函数生成的数据表。该变量存储在一个.mat文件中,已随课程资料提供。

load('crack_data.mat');

导入表格后,可以注意到表格的每一行对应一张混凝土裂缝图像。表格包含三列,分别代表每个裂缝的属性,以及第四列的文件名。

分析裂缝面积分布

为了解数据概况,我们使用直方图来查看裂缝面积的分布情况。

histogram(dataTable.Area);

有趣的是,裂缝面积似乎呈现出两个不同的组别。histogram函数允许使用第二个输入参数来指定分组数量,这有助于进一步区分这两个组。

histogram(dataTable.Area, 20);

分界线大约在4000像素的裂缝面积处。

对图像进行分类标记

因此,为了标记图像,我们将使用discretize函数,将区域面积大于4000像素的裂缝定义为“严重”裂缝。

cutoffs = [0, 4000, Inf];
categories = {'mild', 'severe'};
dataTable.Risk = discretize(dataTable.Area, cutoffs, 'categorical', categories);

此命令根据提供的阈值将面积值划分到不同的区间,并用分类标签(此处为“轻微”和“严重”)进行标记。最后,将输出保存到名为Risk的新表格列中。

此时,使用montage函数查看结果会很有帮助。让我们仅选择风险为“严重”的文件名。

severeFiles = dataTable.FileName(dataTable.Risk == 'severe');
montage(severeFiles);

使用图像浏览器查看大量图像

使用montage将多张图像合并到一个图中,对于少量图像很有用。但如果你有数千张图像,这种方法可能不太方便。另一种方法是使用图像浏览器应用程序,它可以从工作区加载数据存储变量。

以下是创建仅包含严重裂缝图像的数据存储的步骤:

severeImageDS = imageDatastore(severeFiles);

然后,在图像浏览器应用程序中加载此数据存储变量。

检查分类结果与识别异常

现在,让我们查看结果。这些严重裂缝似乎都被正确分类了,除了这里的两个裂缝,它们看起来像是被错误标记为严重的轻微裂缝。

在自动化图像处理中,这类错误并不意外,很可能是不良分割的结果。我们通过肉眼发现了这些情况,但如果图像数量超过几百张,这种工作流程将非常耗时。

那么,如果有数千张图像,如何识别潜在的错误呢?不良分割的一个常见结果是区域数量异常。

因此,在“区域数量”变量中寻找异常值应有助于我们找到分类错误的图像。

使用实时任务识别异常值

实时任务可以插入到脚本中,以帮助完成平滑数据或组合变量等常见步骤。花时间探索它们是非常值得的。现在,我们使用“清理异常数据”实时任务。

首先,选择数据表作为输入数据。然后,选择“区域数量”作为指定变量,并将清理方法设置为“移除异常值”。提供了多种异常值检测算法,对于这种非正态分布的变量,“中位数”方法是一个不错的选择。

% 使用“清理异常数据”实时任务进行配置
% 输入: dataTable
% 指定变量: NumberOfRegions
% 方法: Median

文档是了解更多关于此实时任务中不同选项的好地方。

最后,为了更好地解释结果,将绘图样式更改为直方图。

从可视化结果中可以看到,大约一半的200张图像在最终分割中只产生了一个区域,而另一半则有多个区域。少数图像甚至有10个或更多区域,正如我们将要看到的,这是分割过程的问题。

实时任务还创建了变量outlierIndices,用于存储每个异常值的索引。

请注意,实时任务可以像应用程序一样转换为常规的MATLAB代码。

查看异常图像

现在,要一起查看所有异常图像,首先使用outlierIndices变量获取每个异常图像的名称,然后将其作为输入传递给montage函数。

outlierFiles = dataTable.FileName(outlierIndices);
montage(outlierFiles);

可以注意到,我们之前提到的两个被错误标记为严重的图像也被标记为异常值。但还有其他几个被识别出来以供进一步调查。

并排比较图像与其掩膜

为了并排比较图像与其分割掩膜,使用以下代码,该代码应用了课程前面描述的分割函数。imageName变量是你从可用异常值列表中选择的位置。

% 假设有一个分割函数 segmentCrack
[originalImg, maskImg] = segmentCrack(imageName);
subplot(1,2,1); imshow(originalImg); title('原始图像');
subplot(1,2,2); imshow(maskImg); title('分割掩膜');

在此脚本中,我们还将此变量转换为下拉菜单控件。此功能允许你手动快速在异常图像之间切换。

可以注意到,大量的区域几乎总是不良分割的结果。但幸运的是,我们通过检测异常值找到了它们,而不是用肉眼去寻找。

自主练习与探索

本脚本中的工作流程只是一个示例。请抓住这个机会自行练习数据分析。例如,你是否在“最大宽度”变量中注意到任何有趣的趋势?

如果你有任何发现,请在论坛中发布你的结果。


本节课中我们一起学习了如何在MATLAB中分析图像处理后的数据。我们通过直方图分析数据分布,使用discretize函数对图像进行分类,并利用实时任务和异常值检测来识别分割错误或分类有问题的图像。掌握这些数据分析技能,能帮助你更高效地验证和优化自动化图像处理流程的结果。

28:实用图像处理指南 🛠️

在本节课中,我们将学习如何将之前介绍的各种图像处理技术应用于实际项目。我们将探讨如何根据具体目标选择和组合技术,并理解在现实场景中做出权衡的重要性。

从工具箱到实际应用

上一节我们介绍了多种图像处理技术。本节中我们来看看如何在实际项目中应用它们。

你现在已经掌握了一个包含各种图像处理技术的工具箱。但在开始一个项目时,可能很难确定应该使用哪些技术。

例如,在处理混凝土图像时,我们开发并比较了数十种分割算法。

尝试过的技术方法

以下是我们在实验中尝试过的一些主要方法:

  • 阈值分割:使用了基于灰度颜色的方法。
  • 模糊处理:应用了高斯滤波器进行平滑。
  • 形态学操作:使用了不同形状大小的结构元素进行各种操作。

在大多数情况下,你很难找到一种对每张图像都完美适用的方法。

明确项目目标

因此,预先明确你的目标至关重要。这样,你才能知道可以容忍哪些误差,以及结果达到何种精度就可以继续进行。

例如,考虑一个用于检测混凝土裂缝的算法,其目的是估算修复人行道所需的材料量。

在这种情况下,目标是准确测量裂缝的面积

因此,你需要一个能精确捕捉裂缝边缘排除其内部任何碎屑的掩膜。

根据目标调整方法

反之,如果该算法用于评估大坝的状况,目标则是识别需要修复的严重裂缝

因此,在执行分割之前,你可能会考虑对原始图像进行模糊处理,以平滑混凝土中的任何粗糙纹理。

这个模糊步骤可能导致分割忽略狭窄的裂缝,但这没关系,因为目标不是创建精确的掩膜,而是识别严重裂缝。很难预先知道哪种方法最好。

实践建议与数据考量

在尝试不同方法时,请始终专注于你的最终目标,这样你才知道哪些误差是可以接受的。务必使用图像批处理器来查看算法对你所有图像的泛化效果。

你也可以在MATLAB中进行数据分析,以帮助识别需要进一步调查的异常值。

你的图像处理方法取决于你的图像,但无法将糟糕的数据变成好的结果

如果条件允许,改进实验设置并重新捕获所有图像可能是值得的。

应对现实世界的约束

然而,这并非总是可行,例如对于自动驾驶汽车,它们必须实时安全运行,无论遇到何种光照或天气条件。

在这些情况下,一个常见的解决方案是将视频与来自其他类型传感器(如雷达或激光雷达)的数据相结合。这是需要算法快速处理的实时图像捕获的一个例子。

不同领域的权衡

另一方面,在医学成像等领域,为了获得最准确的结果,等待一个耗时的处理算法可能是值得的。

在实践中,图像处理很少能达到完美。因此,务必预先定义你的目标,并用它们来确定处理项目的最佳方法。

总结

本节课中,我们一起学习了如何将图像处理技术应用于实际项目。关键在于根据具体目标(如精确测量或快速识别)选择技术,明确可接受的误差,并利用工具评估算法性能。记住,良好的数据是基础,在实际约束下需要在速度、精度和资源之间做出明智的权衡。

29:最终项目介绍 🚗

在本节课中,我们将介绍一个综合性的最终项目。该项目旨在让你实践运用本课程中学到的多种图像处理工具与技术。我们将通过一个具体的场景——分析办公楼外的交通流量——来巩固所学知识。

项目概述

一家公司正为其办公楼外的严重交通拥堵问题所困扰。他们雇佣你来测量全天的交通流量。为了帮助你开始,他们提供了一段由楼顶摄像头拍摄的短视频片段。你的目标是统计视频每一帧中驶过的汽车数量。

这个项目被划分为三个主要步骤。

项目步骤详解

上一节我们介绍了项目的背景与目标,本节中我们来看看具体的实施步骤。

第一步:视频预处理

首先,你需要对视频进行预处理。这包括去除视频中的噪声,并将其转换为灰度图像。预处理是许多图像处理流程的基础,它能帮助我们简化后续的分析步骤。

以下是预处理可能涉及的核心操作:

  • 降噪:使用滤波器(如中值滤波器)来减少图像中的随机噪声。
  • 灰度转换:将彩色视频帧转换为灰度图像,公式为:I_gray = 0.2989 * R + 0.5870 * G + 0.1140 * B

第二步:分割与提取汽车

接下来,你需要在每一帧中隔离出汽车。这通过创建一个二值掩码来实现,该掩码能将汽车从背景中分割出来。

在此步骤中,你可能会遇到一些有趣的挑战。例如,在某一帧中,电线杆将一辆汽车分割成了两个独立的区域。

因此,你需要找到一种方法将它们重新连接在一起。以下是可能用到的技术列表:

  • 图像形态学操作:例如使用imclose函数进行闭运算,以连接相邻的物体区域。
  • 调整阈值:优化二值化过程,使属于同一辆车的像素更可能被归类为同一区域。

然后,你将利用每一帧中分割出的区域来计算各种属性(如面积、质心),并为整个视频分析这些结果。

第三步:可视化与跟踪

最后,你将修改原始视频,通过叠加边界框来跟踪驶过的汽车。这能直观地展示你的分析结果。

项目评估与须知

评估将在每一步之后用于指导你的进度。它们充当检查点,可以重复进行多次。请确保在进入下一步之前,你的评估通过率达到100%。

与大多数图像处理问题一样,本项目没有唯一正确的解决方案,因此每个人的结果可能会略有不同。

在开始项目之前,会有一个小测验来帮助你熟悉所提供的视频素材。

总结

本节课中我们一起学习了最终项目的整体框架。我们明确了项目的目标——统计视频中的汽车数量,并了解了实现该目标需要经历的三个核心阶段:视频预处理汽车分割与提取以及结果可视化与跟踪。准备好迎接挑战,祝你成功!😊

30:课程回顾与展望 🎉

在本节课中,我们将回顾整个《工程与科学图像处理》课程的核心内容,总结所学技能,并展望这些技能在更广阔领域的应用前景。

概述

恭喜你完成了《工程与科学图像处理》课程。通过本课程的学习,你已经掌握了一系列使用MATLAB处理图像的技术。随着技术的进步,图像处理在日益广泛的应用领域中扮演着关键角色。无论你从事医学研究、微生物学、机器人技术还是气候研究,你现在都具备了将图像处理技能应用于实际工作所需的能力。

课程核心内容回顾

要从图像中获取有用信息,首先需要识别出你感兴趣的区域。第一门课程教会了你如何操作图像并进行阈值处理,以便聚焦于重要的对象。

然而,很多时候,基本的阈值处理不足以充分分离对象。

因此,在第二门课程中,你学习了如何使用空间滤波器来减少噪声,并利用形态学操作来改进你的掩膜。

一旦获得了合适的结果,你便可以计算区域的各项属性,例如面积、方向或周长。

在第三门课程中,你整合了各种图像处理方法,并将它们应用于批量的图像和视频文件。随后,你使用统计分析来找出结果中需要额外关注的异常值。

最后,在最终项目中,你通过检测视频中的移动车辆来实践所学知识。

技能应用与拓展

在本系列课程中,你处理了多种图像,所学的工具适用于广泛的应用场景。边缘检测、分割和区域分析通常是解决更复杂问题的第一步。

在计算机视觉和深度学习领域,这些技能尤为重要。

如果你有兴趣深入了解这些主题,我们推荐你报名参加我们的《工程与科学计算机视觉》系列课程。

总结

本节课中,我们一起回顾了整个《工程与科学图像处理》课程的学习历程。你掌握了从图像预处理、分割到自动化分析的一系列核心技能。我们希望你喜欢这个系列课程,并请花时间回顾课程内容,向我们提供你的反馈。

现在,去将你的新技能应用到自己的工作中吧。

祝你好运。

posted @ 2026-03-26 08:57  布客飞龙I  阅读(3)  评论(0)    收藏  举报