Python+OpenCV实战:基于HSV颜色空间的图像分割入门指南
1. 项目概述:从颜色入手,理解图像分割的直观起点
在机器视觉和图像分析领域,图像分割是一个基础且核心的任务,它的目标是将图像划分成若干个具有独特属性(如颜色、纹理、亮度)的区域,或者说是将我们感兴趣的物体从背景中“抠”出来。对于很多刚接触这个领域的朋友来说,面对复杂的算法如U-Net、Mask R-CNN可能会感到无从下手。其实,有一个非常直观且强大的切入点,那就是基于颜色的图像分割。它不依赖复杂的深度学习模型,仅凭对图像颜色空间的理解和简单的阈值操作,就能解决一大类实际问题,比如从果园图片中分割出成熟的红色苹果,或者从监控画面中提取出特定颜色的安全帽。
这个项目,我们就来深入聊聊如何用Python,特别是借助OpenCV这个强大的库,来实现基于颜色的图像分割。我会带你从最基础的原理讲起,手把手走过完整的流程,并分享几个我工作中遇到的经典案例和踩过的坑。无论你是想快速上手解决一个具体问题,还是希望为后续学习更高级的分割算法打下坚实基础,这篇内容都会给你带来实实在在的帮助。我们将重点关注HSV颜色空间,因为它比常见的RGB空间更适合做颜色分割。
2. 核心原理与工具准备:为什么是HSV?
在动手写代码之前,我们必须搞清楚一个关键问题:为什么基于颜色的分割,大家普遍首选HSV颜色空间,而不是我们更熟悉的RGB?
2.1 RGB颜色空间的局限性
RGB(红、绿、蓝)颜色模型是基于加色原理,与我们显示设备的发光方式直接对应,非常直观。但是,它有一个致命缺点:三个通道(R, G, B)都与亮度信息高度耦合。想象一下一个红色的苹果,在阳光下和阴影中,它的RGB值会相差巨大。阴影中的红色像素点,其R、G、B值都会同比降低,这导致我们很难用一个固定的RGB范围(例如R>150 and G<50 and B<50)来稳定地识别出“红色”。光照的轻微变化就会让分割结果天差地别。
2.2 HSV颜色空间的优势
HSV颜色模型将颜色信息解耦为三个更符合人类直观感知的维度:
- H(色相/Hue):表示颜色的种类,如红、黄、绿、蓝。它用一个角度(0-360度)来表示,形成一个色环。这是颜色本身的属性,受光照影响相对较小。
- S(饱和度/Saturation):表示颜色的纯度或鲜艳程度。从0(灰色)到100%(完全饱和的纯色)。饱和度低,颜色就接近灰色。
- V(明度/Value):表示颜色的明亮程度。从0(黑色)到100%(白色)。这个通道直接受光照强度影响。
基于颜色分割时,我们主要依据**色相(H)来选定颜色范围,辅以饱和度(S)来过滤掉灰暗、不纯的颜色,有时也会用明度(V)**来排除过暗或过亮的区域。这种分离使得我们的分割算法对光照变化有了更强的鲁棒性。
注意:OpenCV中HSV的取值范围略有不同。H通道被压缩到
[0, 179](为了用一个字节存储),S和V通道范围是[0, 255]。所以,当我们说“红色大约在0度或180度附近”时,在OpenCV里对应的H值大约是[0, 10]和[170, 180]。
2.3 环境搭建与工具选型
工欲善其事,必先利其器。这里我强烈推荐使用Python 3.8+和OpenCV的组合。
安装步骤:
- 安装Python:从官网下载安装包,安装时务必勾选“Add Python to PATH”。
- 安装OpenCV:打开命令行(CMD或终端),使用pip安装是最快的方式。
这个包包含了OpenCV的主要模块。如果你还需要一些额外的贡献模块(如pip install opencv-pythoncv2.ximgproc),可以安装opencv-contrib-python。pip install opencv-contrib-python - 验证安装:创建一个Python脚本,输入以下代码并运行。
如果能成功打印出版本号(如import cv2 print(cv2.__version__)4.8.1),说明安装成功。
为什么选择OpenCV?
- 成熟稳定:拥有超过20年的发展历史,是计算机视觉领域的事实标准。
- 功能全面:从基础的图像读写、颜色空间转换,到高级的特征检测、机器学习,一应俱全。
- 社区活跃:遇到问题几乎都能在网上找到解决方案或讨论。
- 性能优异:底层由C++实现,并提供了Python接口,在速度和易用性之间取得了很好的平衡。
对于集成开发环境,VSCode是一个绝佳的选择,轻量、插件丰富(如Python、Pylance插件),调试方便。当然,Jupyter Notebook也非常适合做交互式的图像处理实验。
3. 实战流程拆解:五步完成颜色分割
理论清楚了,工具也备好了,接下来我们进入实战环节。一个完整的基于HSV的颜色分割流程,可以清晰地分为以下五个步骤。我会用分割红色物体作为贯穿始终的例子。
3.1 第一步:图像读取与预处理
任何图像处理的第一步都是把图像读进来。OpenCV的imread函数默认以BGR格式读取图像,这是一个需要时刻牢记的坑。
import cv2 import numpy as np # 读取图像 image_bgr = cv2.imread('your_image.jpg') # 检查是否读取成功 if image_bgr is None: print("错误:无法读取图像文件,请检查路径。") exit() # 可选:调整图像大小,处理大图时提高速度 # image_bgr = cv2.resize(image_bgr, (0,0), fx=0.5, fy=0.5) # 可选:简单的去噪,有助于减少后续分割的噪声点 # image_bgr = cv2.medianBlur(image_bgr, 5)实操心得:
imread读取失败最常见的原因是文件路径错误。建议使用绝对路径,或者在脚本所在目录使用相对路径。另外,处理前先缩放图像可以极大提升后续步骤的交互和运算速度,尤其是在算法开发阶段。
3.2 第二步:颜色空间转换(BGR -> HSV)
这是核心操作,将图像从BGR空间转换到HSV空间。
# 将图像从BGR颜色空间转换到HSV颜色空间 image_hsv = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2HSV)现在,image_hsv是一个三维数组,每个像素点由[H, S, V]三个值组成。
3.3 第三步:定义目标颜色的HSV范围
这是整个流程中最关键、最需要技巧的一步。我们需要确定想要分割的颜色在HSV空间中的上下阈值。
如何确定阈值?
- 经验值:对于常见颜色,有大致范围可以参考。例如:
- 红色:由于红色在色环两端,通常需要两个范围。
- 范围1(低红色):H在
[0, 10], S在[100, 255], V在[50, 255] - 范围2(高红色):H在
[170, 180], S和V范围同上
- 范围1(低红色):H在
- 绿色:H在
[35, 85], S在[50, 255], V在[50, 255] - 蓝色:H在
[100, 130], S和V范围类似。
- 红色:由于红色在色环两端,通常需要两个范围。
- 动态调试:更可靠的方法是写一个简单的程序,用滑动条实时调整阈值并观察效果。下面是一个极其有用的调试脚本:
def nothing(x): pass # 创建一个窗口和滑动条 cv2.namedWindow('Trackbars') cv2.createTrackbar('H_min', 'Trackbars', 0, 179, nothing) cv2.createTrackbar('H_max', 'Trackbars', 179, 179, nothing) cv2.createTrackbar('S_min', 'Trackbars', 0, 255, nothing) cv2.createTrackbar('S_max', 'Trackbars', 255, 255, nothing) cv2.createTrackbar('V_min', 'Trackbars', 0, 255, nothing) cv2.createTrackbar('V_max', 'Trackbars', 255, 255, nothing) while True: # 获取滑动条的当前位置 h_min = cv2.getTrackbarPos('H_min', 'Trackbars') h_max = cv2.getTrackbarPos('H_max', 'Trackbars') s_min = cv2.getTrackbarPos('S_min', 'Trackbars') s_max = cv2.getTrackbarPos('S_max', 'Trackbars') v_min = cv2.getTrackbarPos('V_min', 'Trackbars') v_max = cv2.getTrackbarPos('V_max', 'Trackbars') # 定义阈值范围 lower = np.array([h_min, s_min, v_min]) upper = np.array([h_max, s_max, v_max]) # 创建掩膜 mask = cv2.inRange(image_hsv, lower, upper) # 显示原图和掩膜 cv2.imshow('Original', image_bgr) cv2.imshow('Mask', mask) # 按‘q’键退出循环 if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows()运行这个脚本,拖动滑动条,直到掩膜图中你想要的物体区域尽可能白(255),背景尽可能黑(0)。记下此时的[H_min, S_min, V_min]和[H_max, S_max, V_max]值。
3.4 第四步:应用阈值创建掩膜(Mask)
得到阈值后,使用cv2.inRange函数生成一个二值掩膜。掩膜中,在阈值范围内的像素点为白色(255),否则为黑色(0)。
# 定义红色的HSV阈值(根据调试结果调整) # 范围1:低红色(靠近0度) lower_red1 = np.array([0, 100, 50]) upper_red1 = np.array([10, 255, 255]) # 范围2:高红色(靠近180度) lower_red2 = np.array([170, 100, 50]) upper_red2 = np.array([180, 255, 255]) # 创建两个掩膜 mask1 = cv2.inRange(image_hsv, lower_red1, upper_red1) mask2 = cv2.inRange(image_hsv, lower_red2, upper_red2) # 合并两个掩膜(因为红色分布在色环两端) mask_red = cv2.bitwise_or(mask1, mask2)mask_red就是一个单通道的二值图像,白色区域就是我们初步分割出的“红色”区域。
3.5 第五步:后处理与结果提取
直接得到的掩膜往往包含噪声(小的白点)或空洞(物体内部的黑点),并且物体边缘可能不连续。我们需要进行后处理来优化结果。
# 1. 形态学操作:去除小噪声,连接相邻区域 kernel = np.ones((5,5), np.uint8) # 定义一个5x5的核 mask_cleaned = cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel) # 开运算:先腐蚀再膨胀,去小白点 mask_cleaned = cv2.morphologyEx(mask_cleaned, cv2.MORPH_CLOSE, kernel) # 闭运算:先膨胀再腐蚀,填小黑洞 # 2. 查找轮廓 contours, _ = cv2.findContours(mask_cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 3. 在原图上绘制轮廓和结果 result = image_bgr.copy() for cnt in contours: area = cv2.contourArea(cnt) if area > 500: # 过滤掉面积太小的轮廓,避免噪声 x, y, w, h = cv2.boundingRect(cnt) cv2.rectangle(result, (x, y), (x+w, y+h), (0, 255, 0), 2) # 画绿色矩形框 cv2.drawContours(result, [cnt], -1, (0, 0, 255), 2) # 画红色轮廓 # 4. 提取感兴趣区域(ROI) # 假设我们只处理最大的那个轮廓 if contours: largest_contour = max(contours, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(largest_contour) roi = image_bgr[y:y+h, x:x+w] # 从原图截取ROI # 可以保存ROI: cv2.imwrite('red_object_roi.jpg', roi) # 显示所有结果 cv2.imshow('Original', image_bgr) cv2.imshow('HSV Mask', mask_red) cv2.imshow('Cleaned Mask', mask_cleaned) cv2.imshow('Result with BBox', result) cv2.waitKey(0) cv2.destroyAllWindows()注意事项:形态学操作中核的大小
(5,5)需要根据图像分辨率和你想要处理的噪声大小来调整。图像大,核可以适当增大;图像小,核要减小,否则容易把目标物体也腐蚀掉。轮廓面积阈值500也是一个需要根据实际情况调整的经验值。
4. 经典案例深度剖析
掌握了基础流程,我们来看几个具体的应用案例。这些案例都来源于实际项目或常见的需求,能帮你更好地理解如何灵活运用颜色分割技术。
4.1 案例一:交通标志识别中的红色标志检测
在辅助驾驶或简单的交通标志识别中,检测红色标志(如停止标志、禁止通行标志)是一个典型任务。
挑战:红色标志在自然光、阴天、黄昏等不同光照下,颜色表现差异大。且图像中可能存在其他红色干扰物(如红色车辆、衣服)。
解决方案:
- 精细调整HSV阈值:使用前面提到的滑动条调试工具,在多种光照条件的样本图像上反复调试,找到一个相对稳健的H、S、V范围。通常S(饱和度)的下限可以设得高一些(如>120),以过滤掉灰暗的“红色”。
- 利用形状信息:颜色分割初步定位后,必须结合形状分析。停止标志是八边形,禁止标志通常是圆形。我们可以用
cv2.approxPolyDP来近似轮廓,计算边数;或者用cv2.minEnclosingCircle计算圆度。for cnt in contours: area = cv2.contourArea(cnt) if area > 1000: # 面积过滤 perimeter = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.04 * perimeter, True) # 轮廓多边形近似 vertices = len(approx) # 判断是否为八边形(停止标志)或圆形(禁止标志) if vertices == 8: # 可能是停止标志 cv2.putText(result, "STOP", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,255,0), 2) # 计算圆度 (center_x, center_y), radius = cv2.minEnclosingCircle(cnt) circle_area = 3.1416 * radius * radius if area / circle_area > 0.8: # 圆度大于0.8 # 可能是圆形禁止标志 cv2.putText(result, "FORBIDDEN", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,255,0), 2) - 多阶段过滤:先颜色,后形状,能有效排除大部分误检。
4.2 案例二:生产线上的彩色工件分拣
在工业视觉中,经常需要根据颜色对工件进行分拣。例如,分拣不同颜色的塑料块、识别药片的颜色种类。
挑战:背景可能复杂或与工件颜色相近;光照需要稳定;要求高实时性和高准确率。
解决方案:
- 营造可控环境:这是工业应用成功的前提。尽可能使用单一颜色的背景(如黑色或白色传送带),并配备均匀、稳定的光源(如环形LED灯)。这能极大简化分割任务。
- 建立颜色查找表:对于已知的几种颜色工件(红、蓝、绿、黄),提前在标准光照下,用调试工具精确测量其HSV范围,并保存为配置字典。
color_ranges = { 'red': ([0, 150, 50], [10, 255, 255], [170, 150, 50], [180, 255, 255]), 'blue': ([100, 150, 50], [130, 255, 255]), 'green': ([35, 50, 50], [85, 255, 255]), 'yellow': ([20, 100, 100], [30, 255, 255]) # 黄色H值在红色和绿色之间 } - 实时处理与决策:在视频流或连续图像中,对每一帧进行颜色分割。对于每个检测到的色块,计算其中心坐标和面积,通过串口或网络将
(颜色, X坐标, Y坐标)发送给机械臂或分拣装置。 - 加入形态学与轮廓分析:确保分割出的区域是完整的、连通的,并过滤掉由反光造成的小面积高光点。
4.3 案例三:从自然场景中提取特定颜色物体(如绿色植物)
这是一个更贴近日常的案例,比如计算图片中草地的覆盖率,或者从街景中分割出树木。
挑战:“绿色”在自然界中范围极广,从嫩绿到墨绿,饱和度变化也很大。树叶间有缝隙,阴影会造成颜色变深。
解决方案:
- 放宽HSV阈值:对于植物,H范围可以设得较宽,例如
[25, 90]。S(饱和度)的下限可以设低一些(如>30),以包含阴影中饱和度较低的绿色。V(明度)的下限也要设低,以包含深绿色区域。lower_green = np.array([25, 30, 20]) # H_min, S_min, V_min upper_green = np.array([90, 255, 200]) # H_max, S_max, V_max (V_max不宜太高,避免过曝的黄色) - 处理颜色溢出:宽阈值可能会把一些土黄色(接近绿色H值)的区域也包含进来。这时可以尝试在RGB或Lab颜色空间进行辅助判断,或者利用纹理特征(植物的纹理通常比土地复杂)进行二次过滤。
- 使用掩膜计算比例:分割得到二值掩膜后,可以通过计算白色像素占总像素的比例,来估算植物的覆盖率。
total_pixels = mask_cleaned.size plant_pixels = cv2.countNonZero(mask_cleaned) coverage_ratio = plant_pixels / total_pixels print(f"植物覆盖率: {coverage_ratio:.2%}")
5. 进阶技巧与性能优化
当你熟练掌握了基础操作后,下面这些技巧可以帮助你应对更复杂的情况,或提升处理效率。
5.1 处理光照不均与阴影
光照不均是基于颜色分割的最大敌人。除了尽量控制环境,还可以在算法层面做一些补偿。
- 直方图均衡化:对V(明度)通道进行均衡化,可以增强对比度,但可能会改变颜色关系,慎用。
- 同态滤波:这是一种在频率域同时压缩亮度范围和增强对比度的方法,能较好地处理光照不均,但实现较复杂。
- 自适应阈值分割的变体:虽然
cv2.inRange是全局阈值,但我们可以先将图像分块,对每个小块单独计算其HSV的统计特征(均值、标准差),然后动态调整阈值。这种方法计算量较大,但对于固定场景的工业应用,可以离线计算好参数。
更实用的技巧是背景建模:如果摄像头固定,可以先拍摄一张没有目标物体的背景图。对于后续每一帧,先计算当前帧与背景图在HSV空间(尤其是V通道)的差异,只对差异较大的区域(即可能包含运动物体的区域)进行精细的颜色分割。这能有效排除静止的背景干扰。
5.2 使用掩膜进行位运算实现复杂操作
掩膜是二值图像,可以通过位运算进行组合,实现复杂逻辑。
- 提取特定颜色物体:
mask = cv2.inRange(hsv, lower, upper)已经是最直接的提取。 - 提取非某种颜色的区域:
mask_not = cv2.bitwise_not(mask) - 同时提取两种颜色:
mask_combined = cv2.bitwise_or(mask1, mask2) - 提取颜色A但排除颜色B的区域:
mask_a_not_b = cv2.bitwise_and(mask_a, cv2.bitwise_not(mask_b)) - 将分割结果应用到原图:这是最常见的操作,只保留我们感兴趣的区域。
# 方法1:使用cv2.bitwise_and object_only = cv2.bitwise_and(image_bgr, image_bgr, mask=mask_cleaned) # 此时object_only中,mask为黑色的区域像素全为0(黑色) # 方法2:将背景替换为特定颜色(如白色) background = np.full_like(image_bgr, (255, 255, 255)) # 创建白色背景 object_only_white_bg = cv2.bitwise_and(image_bgr, image_bgr, mask=mask_cleaned) background = cv2.bitwise_and(background, background, mask=cv2.bitwise_not(mask_cleaned)) final_result = cv2.add(object_only_white_bg, background)
5.3 提升处理速度的技巧
对于实时视频处理或处理大量图片,效率至关重要。
- 降低分辨率:这是最立竿见影的方法。在颜色分割前,先将图像缩放到一个较小的尺寸(如原图的1/2或1/4)。处理完获得目标位置后,如果需要高精度坐标,可以映射回原图坐标。
scale_factor = 0.5 small_img = cv2.resize(image_bgr, (0,0), fx=scale_factor, fy=scale_factor) # ... 在small_img上进行分割和轮廓查找 ... for cnt in contours_on_small: # 将轮廓点坐标放大回原图尺度 cnt_full_scale = cnt / scale_factor # 后续操作... - 定义感兴趣区域:如果目标物体只可能出现在图像的某个固定区域(如监控画面的下半部分),可以先裁剪出那个区域进行处理,减少不必要的计算。
- 优化循环与向量化:尽量使用OpenCV或NumPy的向量化操作,避免在Python中使用显式的逐像素循环。
cv2.inRange,cv2.bitwise_and等函数都是高度优化的。 - 使用更快的颜色空间?转换到HSV需要计算。如果条件允许,有时在RGB空间直接设定阈值也可能满足需求,且省去了转换开销,但鲁棒性会下降。
6. 常见问题排查与调试心得
在实际操作中,你一定会遇到各种问题。这里我总结了一份“避坑指南”,希望能帮你快速定位问题。
6.1 问题速查表
| 问题现象 | 可能原因 | 排查与解决方法 |
|---|---|---|
| 掩膜全黑/全白 | 1. HSV阈值范围设置完全错误。 2. 图像颜色空间不是HSV(忘记转换)。 3. 图像路径错误,读入的是空图像。 | 1. 使用滑动条调试工具重新确定阈值。 2. 检查 cv2.cvtColor转换代码,确认是COLOR_BGR2HSV。3. 打印 image_bgr.shape确认图像已正确加载。 |
| 分割结果包含大量噪声 | 1. HSV阈值范围太宽,包含了背景中相似颜色。 2. 图像本身有噪声(如高ISO拍摄)。 3. 光照不均,部分背景反射光进入阈值范围。 | 1. 收紧阈值,特别是S和V的下限可以提高。 2. 预处理时加入高斯模糊或中值滤波 cv2.GaussianBlur或cv2.medianBlur。3. 尝试使用形态学开运算 cv2.MORPH_OPEN去除小斑点。 |
| 目标物体分割不完整(有空洞) | 1. HSV阈值范围太窄,物体内部颜色有变化(如反光、阴影)。 2. 物体本身颜色不均匀。 | 1. 适当放宽阈值,特别是V(明度)的范围。 2. 使用形态学闭运算 cv2.MORPH_CLOSE填充内部小空洞。3. 考虑使用 cv2.floodFill进行区域填充。 |
| 目标物体边缘粗糙、不连续 | 1. 阈值化本身就是硬分割,边缘处颜色渐变导致。 2. 噪声影响。 | 1. 对掩膜进行高斯模糊cv2.GaussianBlur,再进行阈值化(二次阈值),可以使边缘平滑。2. 使用更精细的形态学操作,或考虑用 cv2.findContours后对轮廓进行平滑处理(如cv2.approxPolyDP)。 |
| 处理速度太慢 | 1. 图像分辨率过高。 2. 进行了不必要的循环操作。 3. 形态学操作的核太大。 | 1. 先缩放图像再处理。 2. 检查代码,确保所有操作都使用OpenCV/NumPy的向量化函数。 3. 减小形态学核的尺寸,或用腐蚀膨胀替代开闭运算。 |
| 在视频流中分割结果闪烁不稳定 | 1. 每帧光照有微小变化,导致阈值匹配不稳定。 2. 物体快速移动,轮廓位置抖动。 | 1. 对HSV阈值加入微小容差,或对连续多帧的检测结果取平均(如卡尔曼滤波)。 2. 在视频流开头几帧动态校准阈值(计算背景或目标区域的HSV均值±标准差)。 |
6.2 调试心得与高级技巧
可视化是王道:在开发过程中,务必把每一步的中间结果(原图、HSV各通道图、掩膜、处理后的掩膜、最终结果)都用
cv2.imshow显示出来。这能帮你最直观地理解算法在哪一步出了问题。可以写一个函数来并排显示多张图片。def show_multiple(images, titles, rows, cols): # 创建一个画布,将多张图拼接显示 # ... 实现代码略 ... pass理解通道分离:使用
cv2.split(image_hsv)可以将HSV图像的H、S、V三个通道分离成单通道灰度图。分别显示这三个通道,你能清楚地看到哪个通道的信息对分割起主要作用,哪个通道引入了噪声。h, s, v = cv2.split(image_hsv) cv2.imshow('Hue', h) # 色相图,看起来可能很斑驳 cv2.imshow('Saturation', s) # 饱和度图,越白越鲜艳 cv2.imshow('Value', v) # 明度图,就是黑白亮度图利用颜色分布直方图:对于不确定的颜色范围,可以计算目标区域(你手动框选的一块)的H通道直方图,观察其集中分布的区域,从而科学地设定H的上下限。
# 假设你手动选择了目标区域 roi_hsv roi_hist = cv2.calcHist([roi_hsv], [0], None, [180], [0, 180]) # 计算H通道直方图 # 找到直方图峰值,在其左右扩展一定范围作为阈值超越简单阈值:自适应与聚类:当颜色非常复杂或光照变化剧烈时,简单的固定阈值可能不够用。可以考虑:
- 自适应阈值:虽然
cv2.inRange不支持自适应,但可以先在V通道或灰度图上使用cv2.adaptiveThreshold,再与H、S通道的条件结合。 - 聚类算法:使用K-Means等算法对图像颜色进行聚类,然后将属于“目标颜色”类别的像素提取出来。这种方法更智能,但计算量也更大。
- 自适应阈值:虽然
基于颜色的图像分割是打开计算机视觉大门的一把钥匙,它直观、高效,在条件可控的场景下威力巨大。但它也有其天花板,对于颜色重叠、复杂纹理、剧烈光照变化的场景,就需要请出深度学习等更强大的工具了。不过,理解并掌握好颜色分割,不仅能解决很多实际问题,其背后关于颜色空间、阈值处理、形态学操作的知识,也是后续学习更高级视觉算法的坚实基础。我个人的习惯是,遇到一个新的图像分割任务,总是先想想能不能用颜色来解决,因为它往往是成本最低、速度最快的方案。
