资讯详情 OpenCV色彩空间详解:从BGR到HSV、LAB、YUV及颜色分割实战
📅 2026/10/14 18:18:18
1. 色彩空间为什么值得花一整篇去啃不管是刚接触OpenCV的小白还是已经写过一阵子图像处理代码的开发者色彩空间都是绕不过去的基础命题。很多人在刚开始学的时候对着文档里那一堆cvtColor转换代码觉得无非就是从一个字符串换成另一个字符串BGR转GRAY、BGR转HSV、BGR转LAB参数照着抄就行。但真正上手做项目——比如跟踪一个红色小球、检测路面的黄色标线、分析皮肤区域、做白平衡校正——就会发现颜色这玩意儿远没有想象中那么简单明明肉眼看起来非常明显的红色用RGB阈值却死活分割不出来光照稍微变一下原本调好的参数就全线崩溃。这时候回头补色彩空间的知识才意识到当初偷的懒全都要还回来。我的建议是色彩空间属于那种“早晚都要系统学一遍”的知识点。你可以在入门阶段先照着demo跑通流程但如果你想真正自己做视觉应用、调参调得动、遇到问题能定位到底出在哪个环节那色彩空间的原理、各个空间的特性、OpenCV里具体每个参数的含义必须扎扎实实过一遍。这篇博文我尽量把这块内容讲透既包括概念层面的理解也包括OpenCV代码层面的实操还有我在实际项目里踩过的一些坑。适合正在学OpenCV的初学者系统阅读也适合有一定基础但一直靠“试参数”混日子的开发者查漏补缺。这篇文章会围绕几个核心问题展开为什么OpenCV默认用BGR而不是RGB为什么调色的时候大家更爱用HSV灰度、LAB、YUV这些空间分别在什么场景下不可替代cvtColor里那一百多种转换代码到底怎么选以及我强烈建议你掌握的HSV调参法和物体跟踪实战套路。2. 先弄清楚色彩空间到底在描述什么2.1 颜色不是“客观存在”而是“编码方式”色彩空间通俗点说就是用一组数字去描述颜色的规则。同样的一个颜色在RGB体系下是一组(R, G, B)值在HSV体系下是(H, S, V)值在LAB体系下是(L, a, b)值。它们描述的是同一个物理颜色但编码方式完全不同用途也因此天差地别。打个比方同一个地址用经纬度坐标可以精确标定用“某某路某某小区几栋几号”也可以定位用“从火车站出来往东走五百米再右拐”也能找到地方。三种方式描述的是同一个位置但适用的场景不一样经纬度适合导航系统门牌号适合快递配送指路式说法适合问路的人。色彩空间也是同样的道理RGB适合显示器显示HSV适合人来调色和做颜色分割LAB适合衡量颜色差异YUV适合视频压缩传输。RGB的思路是把颜色拆成红绿蓝三个分量的混合这在显示设备上非常直观因为屏幕的像素就是由红绿蓝三个子像素点组成的。但RGB有一个致命问题三个分量之间的相关性太强而且对光照变化极其敏感。同一个物体在阳光直射下和阴影里的RGB值差距巨大但人眼却觉得“这还是同一个颜色”。这导致直接用RGB做阈值分割时你很难找到一个稳定的范围来框住目标颜色。2.2 OpenCV为什么反直觉地使用BGR刚接触OpenCV的人几乎都会困惑一个问题为什么OpenCV的默认通道顺序是BGR而不是RGB按理说图片读进来通道顺序是B、G、R如果你用cv2.imread读入一张图片然后用cv2.split拆通道通道0是蓝色通道1是绿色通道2是红色。很多人在用matplotlib显示图片时发现颜色不对正是因为matplotlib默认按RGB解释而OpenCV的数组里存的是BGR直接显示自然就是红蓝颠倒的。这件事的根源在于OpenCV的历史沿革。早期OpenCV诞生的时候很多底层库和硬件接口采用BGR顺序比如当时的Windows BMP格式、某些摄像头驱动和图像采集设备都是BGR输出。OpenCV为了和这些底层组件无缝对接就把BGR定为了默认通道顺序一直延续到今天。虽然现在这个历史包袱让很多新手困扰但它已经成了OpenCV生态的一个基本约定你没法改变它只能去适应它。实际编码中要注意的点是用cv2.imread读图后用OpenCV的imshow显示一切正常因为读写都遵循BGR约定但一旦换成matplotlib、PIL或者某些深度学习框架的预处理流程就得记得显式做一次cv2.cvtColor(img, cv2.COLOR_BGR2RGB)。这种“BGR与RGB混用”的错误是我见过初学者栽跟头最多的地方之一。3. 核心色彩空间逐个拆解灰度、HSV、LAB、YUV3.1 灰度空间丢掉色彩留下亮度结构灰度空间是最简化的一种色彩空间每个像素只有一个值代表亮度。从BGR转到灰度OpenCV用的公式是Y 0.299 * R 0.587 * G 0.114 * B这套权重系数不是拍脑袋定的而是根据人眼对不同颜色敏感度的实测统计得到的。人眼对绿色最敏感所以绿色通道权重最高0.587蓝色最不敏感权重最低0.114。你可以自己验证一下在纯黑背景上画一个纯绿方块和纯蓝方块绿色明显显得更亮。这也是为什么视频编码和图像压缩中亮度信息会被优先保留、色彩信息可以适当压缩的原因。灰度化在OpenCV里属于最常用的预处理操作。很多算法根本不关心颜色比如边缘检测、轮廓提取、特征点匹配、OCR识别颜色信息反而会引入噪声和计算量。转到灰度既能保留关键的亮度结构信息又能把三通道的数据量降为三分之一处理速度显著提升。但我也要提醒一点灰度化之后你就永久丢失了颜色信息无法从灰度图恢复出原图。所以如果你的判断逻辑依赖颜色比如红绿灯检测、肤色分割千万不能在灰度图上做必须在彩色图上把目标颜色区域先提取出来再做灰度化或形态学处理。3.2 HSV空间人眼友好分割利器HSV是我在OpenCV项目里用得最多的色彩空间没有之一。它的三个分量分别是H色相Hue描述“这是什么颜色”用角度表示范围0到180OpenCV里为了能装进8位整数把原来的0到360度完整色相环折半压缩了S饱和度Saturation描述颜色鲜艳程度0表示灰色255表示纯色V明度Value描述亮度0表示黑色255表示最亮这一套描述方式非常接近人眼感知颜色的逻辑也接近你平时在图像处理软件里用取色器时的思维方式。比如“一个鲜艳的大红色”用HSV描述就是“H在0附近S很高V适中或偏高”三个分量各司其职、互不干扰。而用RGB描述同一个红色要同时调整三个分量而且光照一变三个数都变非常难搞。HSV的典型应用就是做颜色分割。举个例子你想在画面中找红色的圆形区域思路是先转到HSV然后用cv2.inRange设定红色的H、S、V范围生成掩膜再在掩膜上做轮廓检测。这个流程我在第5章会完整演示一遍。相比之下用RGB做同样的事情光是红色在不同亮度下的取值范围就能让人崩溃。H分量有一个初学者特别容易踩的坑红色在色相环上位于0度附近这意味着红色既在H0附近又在H180即原来的360度附近。如果只给一个区间比如H ∈ [0, 10]那H 175到180的大红色就漏掉了。正确的做法是要么把区间写成分段的两段要么把H做一个180 - H的翻转处理或者直接用cv2.inRange取两个区间再做或运算。这个细节在实际项目中几乎一定会遇到。S和V的取值也要根据场景灵活调整。户外强光下物体被照得很亮V很高阴影下V很低。要想增强鲁棒性可以在调参时适当放宽V的范围把S的范围设得松一些但也不能太松否则会把很多灰色、白色区域误判进来。这个度需要通过实验来标定没有固定的万能参数。3.3 LAB空间衡量颜色差异的标尺LAB全称是CIELAB由国际照明委员会制定设计目标就是模拟人眼对颜色的感知并且让颜色之间的“距离”能够用欧氏距离近似衡量。L是亮度A是绿到红的轴B是蓝到黄的轴。因为它的设计目标就是感知均匀性所以LAB特别适合用在颜色匹配、色彩校正、图像分割中需要计算颜色相似度的场景。在OpenCV里BGR转LAB也是一行代码的事lab cv2.cvtColor(img, cv2.COLOR_BGR2LAB)你可以把L通道单独拿出来做亮度相关的处理而A、B通道保留颜色信息。很多美白、磨皮、色调调整算法就是在LAB空间里操作的——只调整L通道不碰AB通道就能比较自然地改变明暗而不影响色相。LAB还有一个实用场景就是计算两幅图的颜色差异。你可以分别把两张图转到LAB然后计算对应区域的欧氏距离距离越小说明颜色越接近这往往比直接在RGB空间算距离更符合人眼的主观感受。RGB空间里两个颜色看起来差不多数值距离可能很大LAB空间里数值距离和视觉差异的对应关系就好得多。3.4 YUV与YCbCr视频与图像压缩的功臣YUV这种色彩空间的思路是把亮度Y和色度U、V分开。人眼对亮度细节敏感、对色彩细节不敏感所以视频编码在压缩时可以对色度通道做大幅度的降采样而亮度通道保留更多细节这样在画质影响很小的情况下省下大量码率。JPEG压缩、H.264、H.265这些主流编码体系里都有类似的设计思想。OpenCV里常见的对应操作是cv2.cvtColor(img, cv2.COLOR_BGR2YUV)或cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)。YCrCb和YUV在OpenCV里不完全一样主要差别在色度分量的表示范围和偏移量。实际应用中YCrCb最经典的用途之一是人脸肤色检测因为肤色在YCrCb空间里聚集在一个相对稳定的椭圆区域内比RGB空间的分布要集中得多。如果你只是做一般的图像处理YUV空间用得不一定多但理解它有助于你明白为什么视频传输带宽能省下来以及为什么有些摄像头输出的原始格式是YUV而不是RGB。相机传感器采集到的原始数据经过ISP处理后输出YUV格式给显示或编码链路是最常见的流水线架构。4. cvtColor函数使用要点与转换组合4.1 函数签名与一百多种转换码OpenCV的cvtColor函数签名很简单dst cv2.cvtColor(src, code, dstCn0)第一个参数是输入图像第二个参数是转换代码第三个参数是输出通道数默认传0表示让OpenCV根据转换代码自动判断。需要注意的是输入图像的通道数和数据类型必须和转换代码匹配否则会直接报错。最常见的输入是8位无符号三通道BGR图也就是imread默认读出来的格式以及8位单通道灰度图。OpenCV支持的转换代码算下来有一百五十多种但绝大多数你根本用不到。经常用的就这么几类转换代码作用cv2.COLOR_BGR2GRAYBGR转灰度cv2.COLOR_GRAY2BGR灰度转BGR复制到三通道cv2.COLOR_BGR2HSVBGR转HSVcv2.COLOR_HSV2BGRHSV转BGRcv2.COLOR_BGR2LABBGR转LABcv2.COLOR_BGR2YCrCbBGR转YCrCbcv2.COLOR_BGR2RGBBGR转RGB主要用于显示和对接其他库还有一个很容易被忽略的细节灰度图转回BGR时输出的三个通道内容完全一样。也就是说一个像素值比如128转成三通道后是(128, 128, 128)看起来是灰色但它并不会“还原”出原来的颜色因为原始彩色信息在灰度化时已经丢了。4.2 数据类型对转换结果的巨大影响cvtColor对不同数据类型的输入输出的数值范围是不同。8位无符号整数np.uint8是最常见的范围0到255。但如果输入是32位浮点类型np.float32那么OpenCV假设像素值范围是0.0到1.0HSV里的H取值范围也会变成0到360而不是0到179。很多人从网上复制一段代码换了图片类型运行后调色完全不对往往就是float和uint8这层差异导致的。举一个很典型的错误img_float img.astype(np.float32) # 没有归一化像素值还是0-255 hsv cv2.cvtColor(img_float, cv2.COLOR_BGR2HSV) # 结果完全乱套因为输入是float32OpenCV按0到1的范围来解释像素值而你的像素值却是0到255的等于把255当成了1.0来算所有值都被错误地映射了。正确的做法是除以255.0归一化img_float img.astype(np.float32) / 255.0或者在转完之后手动乘以255再转回uint8。这种类型问题调试起来非常隐蔽因为程序不报错只是颜色全不对。4.3 转换顺序与通道拆分合并不容马虎做色彩空间转换时一个常见需求是拆分通道查看各分量情况。比如你想看HSV的H通道长什么样代码是hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) h, s, v cv2.split(hsv) cv2.imshow(H channel, h)很多人不知道的是H通道的数值范围是0到179而不是0到255所以用imshow显示时会整体偏暗看起来像一张很黑的图。这属于正常现象不是代码写错了。如果你想把H通道可视化得直观一点可以先用cv2.normalize把范围拉伸到0到255再用伪彩色映射显示效果会好很多。合并通道的时候也有坑。比如你把单通道的S通道复制成三通道再合并回BGR顺序一定要对。OpenCV的merge是按列表顺序排通道的cv2.merge([b, g, r])输出BGRcv2.merge([r, g, b])输出的是RGB直接传给cv2.imshow就会得到颜色错乱的画面。我建议在代码里明确写清楚通道顺序不要靠记忆。4.4 反向转换从HSV回到BGR再到显示处理完HSV之后如果需要显示或保存结果常常需要转回BGR。比如你做了颜色范围的掩膜要把它可视化出来一般是把掩膜在原图上叠加显示这一步不需要转回BGR但如果你是修改了HSV的某些通道比如调高S值增强饱和度然后希望看到处理后的图像就必须cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)再显示。这里有一个极易出错的点HSV图像本身看起来非常奇怪尤其当H在低范围时图像整体会呈现一种不太自然的色调新手往往会怀疑自己转换错了。其实只要记得“HSV只是中间表达形式显示前一定要转回BGR”就不会再被这种视觉现象迷惑。我自己的习惯是处理中间过程全部在HSV或LAB空间做只有最终输出和保存时统一转回BGR这样代码的可读性和逻辑清晰度都能保证。5. 实战案例用HSV做颜色物体跟踪5.1 为什么不能用BGR直接做分割先说一个我经常拿来举例的反面教材。假设你要在实时视频中跟踪一个红色的球用BGR阈值的方式你会试着设定BGR三个通道的范围比如B在0到50之间、G在0到50之间、R在180到255之间。看起来好像挺合理但实际跑起来你就会发现问题球在运动过程中光线变化极大有时候球转到背光面R值掉到100以下整个目标就从掩膜里消失了有时候背景里有红色的海报或衣服又会被误识别进来。原因在于RGB三个分量高度耦合。亮度的变化同时影响三个通道你很难用一组固定的数值框住一个颜色在多种光照条件下的所有表现。而HSV把亮度V和颜色属性H、S分离你可以固定H的范围色相基本不随光照变化适当放宽S和V的范围就能在较大的光照变化下维持稳定的分割效果。这就是HSV做颜色分割的核心优势。5.2 标定颜色范围的实用工具滑动条调参法调HSV参数最直观的方法是用拖动条实时试验。不要靠猜不要靠打印一串数值之后硬调效率太低。我自己写过一个小的标定程序用cv2.createTrackbar在窗口里创建6个滑动条分别对应H_min, H_max, S_min, S_max, V_min, V_max然后实时显示cv2.inRange(hsv, lower, upper)的结果拖到理想效果后把参数记录下来。基本框架如下import cv2 import numpy as np def nothing(x): pass img cv2.imread(target.jpg) img_hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) cv2.namedWindow(mask) cv2.createTrackbar(H_min, mask, 0, 179, nothing) cv2.createTrackbar(H_max, mask, 179, 179, nothing) cv2.createTrackbar(S_min, mask, 0, 255, nothing) cv2.createTrackbar(S_max, mask, 255, 255, nothing) cv2.createTrackbar(V_min, mask, 0, 255, nothing) cv2.createTrackbar(V_max, mask, 255, 255, nothing) while True: h_min cv2.getTrackbarPos(H_min, mask) h_max cv2.getTrackbarPos(H_max, mask) s_min cv2.getTrackbarPos(S_min, mask) s_max cv2.getTrackbarPos(S_max, mask) v_min cv2.getTrackbarPos(V_min, mask) v_max cv2.getTrackbarPos(V_max, mask) lower np.array([h_min, s_min, v_min]) upper np.array([h_max, s_max, v_max]) mask cv2.inRange(img_hsv, lower, upper) cv2.imshow(mask, mask) if cv2.waitKey(1) 0xFF ord(q): break cv2.destroyAllWindows()这个程序的调试效率比改代码重跑高一个量级。调参的时候有一个小技巧先把S_min和V_min临时设成0只看H通道能不能把目标区分出来确认H的范围之后再慢慢加上S和V的约束。这样做的好处是你可以快速判断“目标在H通道上是否可分”如果不能根本不用浪费时间调其他的。5.3 完整流程掩膜提取、形态学清理、轮廓定位拿到mask之后不能直接拿去用因为原始的mask往往有很多噪声。比如地面反光、镜头噪点、外部环境干扰都会在mask里产生零零散散的白色小区域。我的习惯是先做一次形态学开运算先腐蚀后膨胀去掉小的噪声块再做一次闭运算先膨胀后腐蚀把目标内部的小黑洞补上核大小一般取5×5或7×7的椭圆核。接着用cv2.findContours找轮廓按面积排序取最大的那个或者过滤掉面积小于一定阈值的轮廓然后在这个轮廓上画矩形框或者圆形拟合mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area cv2.contourArea(cnt) if area 500: continue x, y, w, h cv2.boundingRect(cnt) cv2.rectangle(img, (x, y), (x w, y h), (0, 255, 0), 2)实时视频流里的处理就是加上cv2.VideoCapture(0)读帧每一帧都做“转HSV - inRange - 形态学 - 轮廓提取 - 画出结果”这个流水线。经过这样的流程红色小球在绝大多数场景下都能稳定被框出来就算球在移动、光照变化只要H范围标定得合理跟踪依然稳定。5.4 光照变化太大了怎么办HSV虽然比RGB抗光照变化但也不是万能的。如果在室外大太阳和阴影交替的场景里追踪目标你会发现V通道的变化范围特别大S也可能因为反光而波动。这时有几个可用的改进手法一是把图像先做一个伽马校正或直方图均衡化比如cv2.equalizeHist在YCrCb空间的Y通道上做能能缓解一部分光照不均的影响。二是用自适应阈值代替固定阈值但HSV的H通道不太适合自适应阈值因为H是环形量数值在0和179之间跳变。三是采集多组光照下的样本点统计H、S、V的分布范围而不是只在一个时刻标定一次参数。我比较推荐第三种方案它最直接、最能反映真实场景的多样性。如果场景的复杂性已经超出了HSV能搞定的范围那就要考虑引入机器学习方法了比如采集正负样本训练一个颜色分类器或者用深度学习语义分割模型这些是后话但思路要提前知道避免在传统视觉方案上死磕太久。至少我遇到的大多数工业级视觉需求HSV加形态学加合理标定已经能撑起很多实用的效果了。6. 几个高频踩坑场景与排查思路6.1 显示图片颜色发蓝发红如果你用matplotlib直接显示cv2.imread的图片颜色会明显偏蓝偏红。原因前面提过OpenCV默认BGR而matplotlib用RGB解释。遇到这个问题的第一反应不是怀疑图像数据坏了而是确认有没有做通道顺序转换。解决方案有两种一是cv2.cvtColor(img, cv2.COLOR_BGR2RGB)之后传给matplotlib二是用OpenCV自己的imshow显示它内部知道通道顺序不会有这个问题。但有一点要注意cv2.imshow的窗口在Jupyter Notebook里不生效只能在本地Python环境运行所以很多人在Notebook里绕一圈最后还是用matplotlib那就老老实实转换。6.2 为什么我用inRange选红色总是不成功前面提到过红色在色相环上的特殊性H0和H180其实是同一个位置色相环0度和360度重合所以红色的H区间实际上横跨了两端。如果只写一组lower和upper比如[0, 100, 100]到[10, 255, 255]你会漏掉H在170到180之间的深红、紫红。正确做法是用两组范围分别取再合并mask1 cv2.inRange(hsv, (0, 100, 100), (10, 255, 255)) mask2 cv2.inRange(hsv, (170, 100, 100), (180, 255, 255)) mask cv2.bitwise_or(mask1, mask2)如果你遇到标定颜色调了又调还是不理想先拿print把目标像素的HSV值打出来看看px_hsv hsv[y, x] # (y, x)是目标点坐标 print(px_hsv)这一步能直接告诉你当前目标的H、S、V大致落在哪个区间比盲目调参快得多。6.3 cvtColor报错通道数不匹配、数据类型不支持cvtColor最常见的报错是CV_8UC3或CV_32FC3之类通道数或类型不匹配。比如你给一个单通道灰度图调用BGR2HSVOpenCV直接抛异常因为灰度图没有三个通道不能转。遇到这种错误先看输入图的shapeprint(img.shape)。如果是(h, w)说明只有一个通道如果是(h, w, 3)是三通道如果是(h, w, 4)还有Alpha通道要先cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)才能再转HSV。还有一种特殊情况是cv2.imread读进来的图片偶尔会是0通道返回None也就是图片路径写错或者文件损坏导致整个流程从源头就空了。可以用下面的方式加一个防御性检查img cv2.imread(path) if img is None: raise ValueError(f无法读取图片: {path})这行检查能给你后续排错省下大量时间因为找不到图片的报错信息非常容易混淆到后续的cvtColor步骤里。6.4 处理视频时颜色转换的帧率问题cvtColor本身不算贵但如果你在实时视频每一帧里做多次转换、多重分割、大核形态学操作帧率会明显下降。我的经验是能复用中间结果的绝不复算比如同一帧要同时分割红色和黄色就只转一次HSV然后两次inRange不要每分一次转一次。形态学核也不要动不动用大尺寸5×5不够用再往上加盲目上大核CPU开销成倍上涨。如果你发现CPU占用高、画面卡顿优先看是不是核尺寸太大、或者整张图做了不必要的缩放。对于大分辨率视频另一个优化思路是先缩小再处理frame_small cv2.resize(frame, (0, 0), fx0.5, fy0.5)先在小图上做分割拿到目标位置之后映射回原图坐标精度虽然略降但对于很多跟踪场景完全够用而且速度提升非常明显。要把映射坐标的缩放关系写对不要直接拿小图上的坐标去原图标注。7. 我在实际项目中摸索出的一点心得做视觉项目这几年色彩空间这块我最大的体会是别把cvtColor当黑盒用。你不需要背住每一组转换代码但一定要清楚每个空间的通道含义和数值范围以及每个空间适合解决什么问题。调参的时候数据驱动比肉眼感觉靠谱得多——多打印几个像素的HSV值多采集几种光照下的样本你的阈值才有说服力。不少初学者喜欢在网上下载一组现成的HSV阈值就到处套用这种习惯在实验室干净环境下也许没问题一到真实场景立刻原形毕露。我也越来越意识到色彩空间虽然是基础得不能再基础的知识但它决定了一个视觉系统鲁棒性的天花板。RGB、HSV、LAB、YCrCb这些空间各有各的设计初衷用对了场景效果立竿见影用错了场景怎么调都是事倍功半。比如肤色检测我用YCrCb明显比HSV稳得多颜色相似度比较LAB的欧氏距离比RGB直观得多颜色分割HSV比BGR好用太多。没有哪个空间是全能的关键是你手上有多张牌知道什么时候该出哪张。以后如果你继续深入OpenCV你会发现很多高级算法——直方图反向投影、均值漂移跟踪、SLIC超像素分割、色彩校正常数估算——底层都依赖色彩空间的选取和通道操作的正确使用。把这一篇啃下来后面遇到这些概念你会觉得顺理成章而不是每一步都像在翻字典。建议你自己动手跑几个小实验找一张颜色丰富的照片分别转到HSV和LAB把每个通道单独显示出来感受一遍再回头看我上面写的这些坑印象一定会深刻很多。最后分享一个小技巧。当你处理好一张图准备调试HSV阈值又不想反复改代码重跑的时候可以在窗口里同时显示原图、HSV三通道分量图、当前mask和最终结果图四联图铺在一屏上动一个滑动条就能看到所有环节的实时变化。这个调试布局我一直在用比单看一个mask窗口效率高得多。具体实现就是用np.hstack把四张图横向拼在一起用imshow显示注意它们的尺寸和通道数要一致不一致时先resize和cvtColor统一下。调试工具越顺手你调参的时间就越短剩下的时间就能花在真正值得花的地方。