颜色协调模型Color Harmoniztion

前言

最近做换脸,在肤色调整的那一块,看到一个有意思的文章,复现一波玩玩。不过最后一步掉链子了,有兴趣的可以一起讨论把链子补上。

主要是github上大佬的那个复现代码和原文有点差异,而且代码复杂度过高,阅读费劲,这里为了清晰理解理论知识,就一步一步按照论文的每个章节走,不过有很大一部分代码都借鉴大佬了。

国际惯例,参考文献:

论文《Color Harmonization》

opencv超像素分割

大佬的实现

简介

这篇论文主要干啥呢,看下图

在这里插入图片描述

注意小姑凉的衣服的颜色从左边的原图变成了右边的变化了的图片。

那么论文的工作就是:在尽量保持原色的基础上,调整图片颜色,使得图片整体颜色看起来更加协调。

基本原理就是将RGB图像转成HSV,然后调整色相(hue)直方图。下图中左边的彩色圆环代表原始图片的色相直方图,右边为论文调整后的色相直方图。

在这里插入图片描述

感觉这个论文用来去除杂色应该挺有用,不过更好玩的是能交互式自动调色。

【注】要知道色度hue的范围是0°∼360°0°\sim360°0°360° ,opencv里面提取出来的是0°∼180°0°\sim 180°0°180°

流程复现

环境python3opencv-python==3.4.2.16opencv-contrib-python==3.4.2.16

注意如果找不到ximgproc,手动卸载opencv-contrib-python再重新安装,自动卸载安装无法解决问题。

颜色模板

首先,文章介绍了8种比较适合用于调色的模板,注意灰色区域的大小固定,但是位置不是固定的,可以绕着圆心旋转。

在这里插入图片描述

当图像的色相都落在灰色区域,就是颜色处理的比较好的图像。

文末介绍了每个模板的灰色区域面积比:

  • 模板中大的扇形区域:V,Y,X占26%的面积,角度是93.6°93.6°93.6°

  • 模板中小的扇形区域:i,L,I,Y占5%的面积,角度是18°18°18°

  • 模板L占22%面积,角度是79.0°79.0°79.0°

  • 模板T占50%面积,角度是180°180°180°

  • 模板I,X,Y的两个扇形区的夹角是180°180°180°

  • 模板L两个扇形区的夹角是90°90°90°

不过大佬已经把这部分列出来了,两个数,第一个是扇形区域的中心,第二个是扇形区域的宽度:

#定义模板,分别定义的中心与边界偏转角度
HueTemplates = {"i"       : [( 0.00, 0.05)],"V"       : [( 0.00, 0.26)],"L"       : [( 0.00, 0.05), ( 0.25, 0.22)],"mirror_L": [( 0.00, 0.05), (-0.25, 0.22)],"I"       : [( 0.00, 0.05), ( 0.50, 0.05)],"T"       : [( 0.25, 0.50)],"Y"       : [( 0.00, 0.26), ( 0.50, 0.05)],"X"       : [( 0.00, 0.26), ( 0.50, 0.26)],
}

随便显示一个瞅瞅

#预览模板
def show_temp(template_name,template_alpha):canvas = np.zeros((canvas_h, canvas_w, 3))  #画布cv2.circle(canvas, (yc, xc), circle_r, (255,255,255), -1) #画圆for t in HueTemplates[template_name]:center = t[0]*360 + template_alphawidth = t[1]*360start = center - width/2end = center + width/2cv2.ellipse(canvas,(yc,xc),(circle_r,circle_r),0,start,end,(0,0,0),-1,cv2.LINE_AA)cv2.circle(canvas, (yc, xc), 10, (0,0,0), -1,cv2.LINE_AA) #画中心点canvas = np.array(canvas,np.uint8)    canvas = cv2.cvtColor(canvas,cv2.COLOR_BGR2RGB)return canvas
plt.imsave('./temp/show_temp.png',show_temp('X',90),cmap='gray')

在这里插入图片描述

色相直方图

就是单纯的将图像的H通道提取出来,看看像素个数。

def count_hue_histogram(X):N = 360H = X[:, :, 0].astype(np.int32) * 2    H_flat = H.flatten()histo = np.zeros(N)for i in range(N):histo[i] = np.sum(H_flat==i);return histo

然后把它画到一个环上

canvas_h = 600 #画布高度
canvas_w = 600 #画布宽度
yc = int(canvas_h/2) #圆心位置y
xc = int(canvas_w/2) #圆心位置x
circle_r = 250 #半径
def draw_polar_histogram(histo):N = 360histo = histo.astype(float)histo /= np.max(histo)histo *= circle_rcanvas = np.zeros((canvas_h, canvas_w, 3))  #画布cv2.circle(canvas, (yc, xc), circle_r, (255,255,255), -1) #画圆for i in range(N):theta = -i * np.pi / 180 #各个hue的弧度count = histo[i] #各个hue的数目#当前hue的柱子y1 = yc - int(circle_r * np.sin(theta))x1 = xc + int(circle_r * np.cos(theta))y2 = yc - int((circle_r-histo[i]) * np.sin(theta))x2 = xc + int((circle_r-histo[i]) * np.cos(theta))color_HSV = np.zeros((1,1,3), dtype=np.uint8)color_HSV[0,0,:] = [int(i/2),255,255] #每个角度的Hcolor_BGR = cv2.cvtColor(color_HSV, cv2.COLOR_HSV2BGR) #将HSV转换为BGRB = int(color_BGR[0,0,0])G = int(color_BGR[0,0,1])R = int(color_BGR[0,0,2])cv2.line(canvas, (x1,y1), (x2,y2), (B,G,R), 3,cv2.LINE_AA) #画柱子canvas = cv2.circle(canvas, (yc, xc), 5, (0,0,0), -1) #圆心canvas = np.array(canvas,np.uint8)canvas = cv2.cvtColor(canvas,cv2.COLOR_BGR2RGB)return canvas

测试代码

img_rgb = cv2.imread('peacock.png')
img_hsv = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2HSV)
h_hist = count_hue_histogram(img_hsv)
img_hue = img_hsv[...,0].copy()*2.0
img_sat = img_hsv[...,1].copy()/2.55
hist_img = draw_polar_histogram(h_hist)
plt.imsave('./temp/hist_img.png',hist_img)

在这里插入图片描述

图像评分

正式进入论文第3章节,根据图像的色相与每个模板扇形区域的边界的最小距离,以及每个像素的饱和度,来计算得分,公式为
F(x,(m,α))=∑p∈X∣∣H(p)−ETm(α)(p)∣∣⋅S(p)F(x,(m,\alpha))=\sum_{p\in X}||H(p)-E_{T_m(\alpha)}(p)||\cdot S(p) F(x,(m,α))=pXH(p)ETm(α)(p)S(p)
X代表图像,p代表图像每个像素位置的色度值(Hue),ETm(α)(p)E_{T_m(\alpha)}(p)ETm(α)(p)代表色度值p与扇形区域最接近的边界,S(p)S(p)S(p)代表HSV中的饱和度S

接下来先看某个模板中,0°∼360°0°\sim360°0°360°每个色相与边界的最短距离(角度值差),如果色相在模板的扇形区域内,距离为000

#圆弧距:在hue圆上的角度差
def deg_distance(a, b):d1 = np.abs(a - b)d2 = np.abs(360-d1)d = np.minimum(d1, d2)return d
#是否在区域内
def in_border(h,center,width):return deg_distance(h,center)<width/2
#区域外的h,计算最近边界的圆弧距
def dist_to_border(h,border):H1=deg_distance(h,border[0])   H2=deg_distance(h,border[1])   H_dist2bdr = np.minimum(H1,H2)return H_dist2bdr
#计算当前模板的每个hue值的加权
def hue_weight(temp_name,alpha):hweight = []h = np.arange(360)for t in HueTemplates[temp_name]:center = t[0]*360 + alpha #中心位置width = t[1]*360 #宽度border = [center - width/2,center + width/2]  #起止位置temp_dist = dist_to_border(h,border) #色相与当前边界的距离temp_dist[in_border(h,center,width)]=0        hweight.append(temp_dist)hweight = np.array(hweight)hweight = hweight.min(axis=0)return hweight

接下来计算第一项:
∣∣H(p)−ETm(α)(p)∣∣||H(p)-E_{T_m(\alpha)}(p)|| H(p)ETm(α)(p)
直接将图像H值换成对应的最近边界距离即可:

# 利用template的hue权重对图像的hue直方图进行加权
def h_dist(img_h,temp_name,alpha):score = np.zeros((img_h.shape[0],img_h.shape[1]),dtype=np.float32)hw = hue_weight(temp_name,alpha)for i in range(360):score[img_h==i]= hw[i]return score

再跟饱和度一起,把图像与每个模板对应的得分都计算出来

#计算最小的模板得分
def cal_scores(img_hue,img_s):scores = np.zeros((len(HueTemplates.keys()),360),dtype=np.float32)temp_keys = list(HueTemplates.keys())for i in range(len(temp_keys)):#遍历模板print('temp_keys',temp_keys[i])for alpha in range(360):#每个旋转角度scores[i,alpha]=np.sum(np.multiply(h_dist(img_hue,temp_keys[i],alpha),img_s))return scores

提取最好的那个模板和角度,使得当前的图像得分最大

# 计算每个模板的得分
hue_scores = cal_scores(img_hue,img_sat)
#得到最好的template和alpha
[best_m,betst_alpha] = np.unravel_index(np.argmin(hue_scores),hue_scores.shape)
'''
temp_keys i
temp_keys V
temp_keys L
temp_keys mirror_L
temp_keys I
temp_keys T
temp_keys Y
temp_keys X
'''

把最好的模板、旋转角度与对应图像色相直方图放一起显示一波瞅瞅

#画图
hist_img = draw_polar_histogram(h_hist)
temp_keys = list(HueTemplates.keys())
print(temp_keys[best_m],betst_alpha)
temp_img = show_temp(temp_keys[best_m],betst_alpha)
overlay_img = cv2.addWeighted(temp_img,0.2,hist_img,1-0.2,0)
plt.figure(figsize=(8,8))
plt.imshow(overlay_img)
plt.imsave('./temp/overlay_img.png',overlay_img)
'''
T 46
'''

在这里插入图片描述

图像分割

上面有些模板有两个对称扇形,这两个扇形代表互补色,也就是颜色差距很大,但是当我们把扇形外面的像素压到扇形里面去的时候,中间的一些颜色到两个扇形的距离差不多,这时候就会出现相似的颜色被压到了完全不同的颜色。如下图所示

在这里插入图片描述

看(d)图,两个箭头从同一个位置出发,但是这个位置的色相到两个扇形的距离差不多,这时候如果随机分配颜色,就会出现孔雀脖子上相似的颜色被压到完全不同的两个颜色里面去了。

这时候,我们就需要对每个像素指定他所压缩的扇形,并且要保证图像比较连续的区域的颜色不要被压的不连续了,那么就可以先对图像每个像素进行二分类,当有两个扇形的时候,像素标签为0或10或101 ,分别表示应该被划分到哪个区域。

文章的算法貌似比较耗时,大佬使用了opencv里面的一个超像素块划分方法,参考文档看前言。这个超像素块划分有点类似于目标分割,达到的效果如下:

在这里插入图片描述

原图戳这里

可以看出来,连续区域基本被划分到一块了。每一块都被标记为一个单独的类别。

我们现在要把这一对类别合并起来,变成两个类别。

分割流程大致可以分为这样

先不看颜色的连续性,单纯计算每个像素的色相到哪个扇形区域最近,先为每个像素单独打标签

#先计算每个像素到哪个扇形区域距离最短
def class_pix(h,temp_name,alpha):# 先计算当前模板下,每个hue对应哪个扇区img_label=np.zeros((h.shape[0],h.shape[1]),dtype=np.int32)hlabels = []hue_stand=np.arange(360)#扇区里外一起处理,某个扇区内的距离自己的扇区边界更近for t in HueTemplates[temp_name]:center = t[0]*360 + alpha #中心位置width = t[1]*360 #宽度border = [center - width/2,center + width/2]  #起止位置temp_dist = dist_to_border(hue_stand,border) #hue与当前扇区边界的距离,不要排除扇区内的hue的计算hlabels.append(temp_dist)hlabels = np.array(hlabels)hlabel = hlabels.argmin(axis=0)#再把图像的hue转换为对应扇区标签for i in range(360):img_label[h==i]=hlabel[i]return img_label

然后再统计opencv划分的每个超像素块里面的像素标签哪个多,就把当前超像素的标签置为谁:

#制作每个像素的标签,确定每个像素往哪个扇区shift
def split_img(img_hsv,img_h,temp_name,alpha):num_superpixels=200h_cls = class_pix(img_hue,temp_name,alpha)SEEDS = cv2.ximgproc.createSuperpixelSEEDS(img_hsv.shape[1],img_hsv.shape[0],img_hsv.shape[2],num_superpixels,10)SEEDS.iterate(img_hsv,4)V=np.zeros((img_hue.shape))N=V.shape[0]grid_num = SEEDS.getNumberOfSuperpixels() #超像素块个数labels = SEEDS.getLabels() #原图每个像素的属于哪个超像素块(标签)for i in range(grid_num):P=[[],[]]s=np.average(h_cls[labels==i]) #当前超像素块的所有hue的标签的均值if(s>0.5):#像素块大部分朝哪个扇区,就确定是哪个扇区s=1else:s=0h_cls[labels==i]=sreturn h_cls

这两个函数就保证了,颜色又连续,每个颜色被压缩的距离又最小,色差变化不会太大。

压色相

每个像素往哪个扇形块压,我们都知道了,那就执行最后一步,把像素按照下式压到对应扇形区域去
H′(p)=C(p)+w2(1−Gσ(∣∣H(p)−C(p)∣∣))H'(p)=C(p)+\frac{w}{2}(1-G_\sigma(||H(p)-C(p)||)) H(p)=C(p)+2w(1Gσ(H(p)C(p)))
上式中C(p)代表扇形区域的中心,www代表扇形区域的宽度,GσG_\sigmaGσ代表标准高斯函数,均值为0,方差为σ\sigmaσ,作者建议这个方差σ\sigmaσ 等于w2\frac{w}{2}2w

那么先把高斯函数写出来呗:
1(2π)σe−12(x−μσ)2\frac{1}{\sqrt{(2\pi)}\sigma}e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2} (2π)σ1e21(σxμ)2

#标准正态分布采样
def normalized_gaussian(X,mu,sigma):X=np.asarray(X).astype(np.float64)sigma=np.asarray(sigma).astype(np.float64)mu=X-muM2=np.multiply(mu,mu)S2=np.multiply(sigma,sigma)return (1/(sigma*np.sqrt(2*np.pi)))*np.exp(-M2/(2*S2))

最后一步,根据H′(p)H'(p)H(p)的公式,压色相值

# 对每个标签块分别shift到对应扇区
def hue_shift(img_hsv,temp_name,alpha):img_hue = img_hsv[...,0].copy()*2.0new_img_hue = np.zeros_like(img_hue)h_cls = split_img(img_hsv,img_hue,temp_name,alpha) #确定每个像素对应的扇区i=0for t in HueTemplates[temp_name]: #每个扇区分别处理center = t[0]*360 + alphawidth = t[1]*360mask = (h_cls==i) #当前扇区所对应的所有标签temp_hue = img_hue*mask #排除其他扇区超像素块的干扰center_dist = deg_distance(temp_hue,center)G = normalized_gaussian(center_dist,0,width/2)new_h = center + (1-G)*width/2new_img_hue=new_img_hue+new_h*maski=i+1new_img_hsv = img_hsv.copy()new_img_hsv[...,0]=new_img_hueresult_img = cv2.cvtColor(new_img_hsv,cv2.COLOR_HSV2RGB)return result_img

搞定!!

可视化看看结果

temp_name=temp_keys[best_m]
alpha=betst_alpha
plt.imshow(hue_shift(img_hsv,temp_name,alpha))

在这里插入图片描述

嗯?真心绿。

其实还有更好玩的,自己随便指定一个templatealpha,能玩一天,比如

temp_name='X'#temp_keys[best_m]
alpha=betst_alpha
plt.imshow(hue_shift(img_hsv,temp_name,alpha))
plt.imsave('./temp/result1.png',hue_shift(img_hsv,temp_name,alpha))

在这里插入图片描述

问题

最后一步的实现应该有问题,和大佬的代码有点出入,可能我对论文的最后一步理解有遗漏,后面再来查缺补漏吧。有大神看论文以后,也可以在这个基础上完善一下。

博客代码下载:

链接:https://pan.baidu.com/s/1DavXVylYx3Sd0K-LygnBoQ
提取码:ub0n

本文已经同步到微信公众号中,公众号与本博客将持续同步更新运动捕捉、机器学习、深度学习、计算机视觉算法,敬请关注

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Openpose推断阶段原理

前言 之前出过一个关于openpose配置的博客&#xff0c;不过那个代码虽然写的很好&#xff0c;而且是官方的&#xff0c;但是分析起来很困难&#xff0c;然后再opencv相关博客中找到了比较清晰的实现&#xff0c;这里分析一波openpose的推断过程。 国际惯例&#xff0c;参考博…

换脸系列——眼鼻口替换

前言 想着整理一下换脸相关的技术方法&#xff0c;免得以后忘记了&#xff0c;最近脑袋越来越不好使了。应该会包含三个系列&#xff1a; 仅换眼口鼻&#xff1b;换整个面部&#xff1b;3D换脸 先看看2D换脸吧&#xff0c;网上已经有现成的教程了&#xff0c;这里拿过来整理一…

换脸系列——整脸替换

前言 前面介绍了仅替换五官的方法&#xff0c;这里介绍整张脸的方法。 国际惯例&#xff0c;参考博客&#xff1a; [图形算法]Delaunay三角剖分算法 维诺图&#xff08;Voronoi Diagram&#xff09;分析与实现 Delaunay Triangulation and Voronoi Diagram using OpenCV (…

3D人脸重建——PRNet网络输出的理解

前言 之前有款换脸软件不是叫ZAO么&#xff0c;分析了一下&#xff0c;它的实现原理绝对是3D人脸重建&#xff0c;而非deepfake方法&#xff0c;找了一篇3D重建的论文和源码看看。这里对源码中的部分函数做了自己的理解和改写。 国际惯例&#xff0c;参考博客&#xff1a; 什…

tensorflow官方posenet模型解析

前言 tensorflow官方有个姿态估计项目&#xff0c;这个输入和openpose还有点不一样&#xff0c;这里写个单人情况下的模型输出解析方案。 国际惯例&#xff0c;参考博客&#xff1a; 博客: 使用 TensorFlow.js 在浏览器端上实现实时人体姿势检测 tensorflow中posnet的IOS代…

tensorflow2安装时候的一个dll找不到的错误

电脑环境&#xff1a; vs2015python3.7.6&#xff0c;使用anaconda安装的CUDA 10.1cuDnn 7.6.5tensorflow2.1.0 错误内容 File "C:\Users\zb116\anaconda3\lib\imp.py", line 242, in load_modulereturn load_dynamic(name, filename, file)File "C:\Users\z…

PCA、SVD、ZCA白化理论与实现

简介 在UFLDL中介绍了主成分分析这一块的知识&#xff0c;而且当时学机器学习的时候&#xff0c;老师是将PCA和SVD联系起来将的&#xff0c;同时UFLDL也讲到了使用PCA做数据白化whitening处理&#xff0c;这个词经常在论文里面看到。 国际惯例&#xff0c;参考博客&#xff1…

OpenCV使用Tensorflow2-Keras模型

前言 最近工作上需要在C上快速集成Tensorflow/Keras训练好的模型&#xff0c;做算法验证。首先想到的就是opencv里面的dnn模块了&#xff0c;但是它需要的格式文件比较郁闷&#xff0c;是pb格式的模型&#xff0c;但是keras通常保存的是h5文件&#xff0c;查阅了很多资料&…

3D人脸表情驱动——基于eos库

前言 之前出过三篇换脸的博文&#xff0c;遇到一个问题是表情那一块不好处理&#xff0c;可行方法是直接基于2D人脸关键点做网格变形&#xff0c;强行将表情矫正到目标人脸&#xff0c;还有就是使用PRNet的思想&#xff0c;使用目标人脸的顶点模型配合源人脸的纹理&#xff0c…

3D姿态估计——ThreeDPose项目简单易用的模型解析

前言 之前写过tensorflow官方的posenet模型解析&#xff0c;用起来比较简单&#xff0c;但是缺点是只有2D关键点&#xff0c;本着易用性的原则&#xff0c;当然要再来个简单易用的3D姿态估计。偶然看见了ThreeDPose的项目&#xff0c;感觉很强大的&#xff0c;所以把模型扒下来…

简易的素描图片转换流程与实现

前言 之前经常在网上看到用PS实现真实图片到素描图片的转换&#xff0c;但是流程都大同小异&#xff0c;身为一只程序猿&#xff0c;必须来个一键转化额。 国际惯例&#xff0c;参考博客&#xff1a; Photoshop基础教程&#xff1a;混合模式原理篇 颜色减淡的原理讲解以及应…

一个简单好用的磨皮祛斑算法理论和python实现

前言 最近看了一个磨皮算法祛斑感觉效果不错&#xff0c;效果图看文末就行&#xff0c;个人觉得效果非常不错滴。 国际惯例&#xff0c;参考博客&#xff1a; 磨皮算法的源码&#xff1a;YUCIHighPassSkinSmoothing How To Smooth And Soften Skin With Photoshop 图像算法…

OpenVINO——配置与道路分割案例

前言 最近看到了一个深度学习库OpenVINO&#xff0c;专门用于Intel硬件上部署深度学习模型&#xff0c;其内置了非常非常多使用的预训练模型&#xff0c;比如道路分割、人脸提取、3D姿态估计等等。但是配置和调用有点小恶心&#xff0c;这里以道路分割为例&#xff0c;展示如何…

图像颜色迁移《color transfer between images》

前言 前段时间&#xff0c;在深度学习领域不是有个比较火的方向叫风格迁移的嘛&#xff0c;对于我这种不喜欢深度学习那种不稳定结果的人来说&#xff0c;还是想看看传统图像处理领域有什么类似的技术&#xff0c;发现了一个颜色迁移的算法&#xff0c;很久前的论文了。 国际…

ColorSpace颜色空间简介

前言 如果看过之前的介绍的图像颜色迁移《color transfer between images》和颜色协调模型Color Harmoniztion就会发现&#xff0c;大部分图像处理算法虽然输入输出是RGB像素值&#xff0c;但是中间进行算法处理时很少直接更改RGB值&#xff0c;而是转换到其它空间&#xff0c…

Ogre共享骨骼与两种骨骼驱动方法

前言 最近业务中用到Ogre做基于3D关键点虚拟角色骨骼驱动&#xff0c;但是遇到两个问题&#xff1a; 身体、头、眼睛、衣服等mesh的骨骼是分开的&#xff0c;但是骨骼结构都是一样的&#xff0c;需要设置共享骨骼驱动的时候可以直接修改骨骼旋转量&#xff0c;或者将旋转量存…

仿射变换和透视变换

前言 在前面做换脸的博客中提到了使用仿射变换和透视变换将两张不同的人脸基于关键点进行对齐&#xff0c;保证一张人脸贴到另一张人脸时&#xff0c;大小完全一致&#xff1b;所以有必要理解一下这两个概念的区别&#xff0c;由于以实用性为目的&#xff0c;所以所有的图像算…

obj格式解析

前言 最近处理一些网格渲染的时候&#xff0c;需要解析Obj文件&#xff0c;从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例&#xff0c;参考博客&#xff1a; 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中OBJ文…

Flask服务部署与简单内网穿透

前言 最近学习部署的时候&#xff0c;想到深度学习里面通常用的部署方法是flask做服务端&#xff0c;然后使用nginx做负载均衡&#xff0c;貌似也能做内网穿透。不过我不太懂负载均衡&#xff0c;只想利用本地电脑搭建一个简单的服务器&#xff0c;实现外部调用API服务的功能。…

OpenCV学习——轮廓检测

前言 轮廓检测是传统视觉中非常常用的功能&#xff0c;这里简单记录一下opencv中的轮廓检测算法使用方法&#xff0c;至于理论&#xff0c;后续有机会再去细品。 国际惯例&#xff1a; OpenCV官方的轮廓检测教程python版 OpenCV中的二值化方法教程 OpenCV轮廓层级官方文档…