ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

opencv-python学习笔记(十)—— 图像处理之轮廓

2021-07-07 16:57:31  阅读:1079  来源: 互联网

标签:cnt 层次 python contour opencv 图像处理 图像 轮廓 cv


往期文章目录

文章目录

轮廓

理解什么是轮廓线。学习寻找轮廓线,绘制轮廓线等

函数: cv.findContours(), cv.drawContours()

1. 寻找轮廓

什么是轮廓

轮廓可以简单地解释为(沿边界)连接所有连续点的曲线,具有相同的颜色或强度。轮廓是形状分析和目标检测与识别的有效工具。

为了更好的精度,应当使用二值图像。因此,在寻找轮廓之前,应用阈值或canny边缘检测。

从OpenCV 3.2开始, findContours()不再修改源图像。

在OpenCV中,寻找轮廓就像从黑色背景中寻找白色物体。记住,要找到的对象应该是白色的,背景应该是黑色的。

让我们看看如何找到二值图像的轮廓:

import numpy as np
import cv2 as cv
im = cv.imread('test.jpg')
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

可见,cv.findContours()函数中有三个参数,第一个是源图像,第二个是轮廓检索方式,第三个是轮廓近似方法。并输出轮廓和层次结构。轮廓是图像中所有轮廓的Python列表。每个单独的轮廓是一个Numpy数组的边界点(x,y)坐标的对象。

注意:我们将在后面详细讨论第二个和第三个参数和层次结构。在此之前,代码示例中给它们的值将适用于所有图像。

怎么绘制轮廓

绘制轮廓使用cv.drawContour函数。它也可以用来绘制任何形状,只要你有它的边界点。它的第一个参数是源图像,第二个参数是应该作为Python列表传递的轮廓,第三个参数是轮廓的索引(在绘制单独的轮廓时很有用。绘制所有的轮廓,通过-1)和其他参数是颜色,厚度等。

把所有的轮廓画在一个图像里:

cv.drawContours(img, contours, -1, (0,255,0), 3)

要画一条单独的轮廓,比如第4条轮廓:

cv.drawContours(img, contours, 3, (0,255,0), 3)

但大多数情况下,下面的方法是有用的:

cnt = contours[4]
cv.drawContours(img, [cnt], 0, (0,255,0), 3)

注意:
最后两个方法是相同的,但是当你继续使用时,你会发现最后一个更有用。

轮廓近似方法

这是cv.findContours函数中的第三个参数。它实际上表示什么?

上面,我们说了轮廓是具有相同强度的形状的边界。它存储一个形状边界的(x,y)坐标。但是它存储了所有的坐标吗?这是由这种等值线近似方法指定的。

如果你通过了cv.CHAIN_APPROX_NONE,存储所有边界点。但实际上我们需要所有的点吗?例如,你发现了一条直线的轮廓线。需要这条直线上的所有点来表示这条直线吗?不,我们只需要这条线的两个端点。这就是cv.CHAIN_APPROX_SIMPLE。它去除所有冗余点,压缩轮廓,从而节省内存。

下面的矩形图像演示了这种技术。只要在轮廓数组中的所有坐标上画一个圆(用蓝色绘制)。第一张图片显示了我从简历cv.CHAIN_APPROX_NONE中得到的要点(734点)。第二个图像显示了使用cv.CHAIN_APPROX_SIMPLE(只有4点)。看,它节省了多少内存啊!!
在这里插入图片描述

2. 轮廓特征

寻找轮廓的不同特征,如面积,周长,质心,边界框等

你将看到许多与轮廓相关的函数。

矩特征

图像矩可以帮助你计算物体的质心、物体的面积等特征。查看维基百科页面 Image Moments

函数 **cv.moments()**给出一个字典的所有矩值计算。见下文:

import numpy as np
import cv2 as cv
img = cv.imread('star.jpg',0)
ret,thresh = cv.threshold(img,127,255,0)
contours,hierarchy = cv.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv.moments(cnt)
print( M )

从矩中,可以提取有用的数据,如面积,质心等。质心由Cx=M10/M00和Cy=M01/M00关系式给出。

cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

轮廓面积

轮廓面积由函数cv.contourArea()或矩M[‘m00’]给出。

area = cv.contourArea(cnt)

轮廓周长

也叫弧长。可以使用 **cv.arcLength()**函数来查找。第二个参数指定shape是一个封闭轮廓(如果传递为True),还是只是一个曲线。

perimeter = cv.arcLength(cnt,True)

轮廓拟合

根据我们指定的精度,它将一个轮廓形状近似为另一个顶点数较少的形状。它是 Douglas-Peucker algorithm算法的一个实现。查看维基百科页面的算法和演示。

为了理解这一点,假设你试图在图像中找到一个正方形,但由于图像中的一些问题,你没有得到一个完美的正方形,而是一个“糟糕的形状”(如下面的第一张图像所示)。现在你可以用这个函数来近似这个形状。在这里,第二个参数被称为,它是从等值线到近似等值线的最大距离。是一个精度参数。为了得到正确的输出,需要明智地选择。

epsilon = 0.1*cv.arcLength(cnt,True)
approx = cv.approxPolyDP(cnt,epsilon,True)

下图中,绿线显示了= 10%弧长的近似曲线。第三幅图显示= 1%的弧长也是一样的。第三个参数指定曲线是否闭合。

在这里插入图片描述

Hull凸包

凸包看起来与轮廓近似类似,但实际上并非如此(两者在某些情况下可能会提供相同的结果)。这里,cv.convexhull()函数检查曲线的凹凸缺陷并纠正它。一般来说,凸曲线是指总是凸出的曲线,或者至少是平坦的曲线。如果它是鼓的内部,它被称为凸缺陷。例如,检查下面的手的图像。红线表示手的凸包。双面箭头标记显示出凸性缺陷,即凸包距离轮廓的局部最大偏离。

在这里插入图片描述

关于它还有一点需要讨论它的语法:

hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]

参数说明:

points 是我们经过的轮廓。

Hull 是输出,通常我们避开它。

clockwise:方向flag。如果为True,则输出凸包为顺时针方向。否则,是逆时针方向。

returnPoints 默认为True。然后返回Hull点的坐标。如果为False,则返回hull点对应的轮廓点坐标的索引。

因此,要得到如上图所示的凸包,下面就足够了:

hull = cv.convexHull(cnt)

但是如果你想找到凸性缺陷,你需要传递returnPoints = False。为了理解它,我们取上面的矩形图像。首先找到它的轮廓cnt。然后设参数returnPoints = True寻找它的凸包,将得到以下值:[[[234 202]],[[51 202]],[[51 79]],[[234 79]]],这是矩形的四个角点。

现在设参数returnPoints = False做同样的操作,将得到以下结果:[[129],[67],[0],[142]]。这些是轮廓上对应点的索引值。例如,检查第一个值:cnt[129] =[[234,202]],这与第一个结果相同(其他结果以此类推)。

当我们讨论凸性缺陷时,你会再次看到它。

凸度检查

有一个函数可以检查曲线是否为凸曲线,即 cv.isContourConvex()。它只返回True或False。

k = cv.isContourConvex(cnt)

外接矩形

有两种类型的边界矩形。

  • 直边界矩形

它是一个直线矩形,它不考虑物体的旋转。所以边界矩形的面积不会是最小的。使用函数**cv.boundingRect()**.

设(x,y)为矩形的左上角坐标,(w,h)为矩形的宽和高。

x,y,w,h = cv.boundingRect(cnt)
cv.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
  • 旋转矩形

这里绘制的边界矩形面积最小,因此也考虑了旋转。使用的函数是cv.minAreaRect()。它返回一个Box2D结构,该结构包含以下细节(中心(x,y),(宽度,高度),旋转角度)。但要画出这个矩形,我们需要矩形的4个角。它是由函数 **cv.boxPoints()**获得的。

rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(img,[box],0,(0,0,255),2)

两个矩形都显示在一个图像中。绿色矩形表示正常的边界矩形,红色矩形是旋转后的矩形。

在这里插入图片描述

最小封闭圆

cv.minEnclosingCircle().函数查找对象的外圆。它是一个以最小面积完全覆盖物体的圆。

(x,y),radius = cv.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv.circle(img,center,radius,(0,255,0),2)

在这里插入图片描述

拟合椭圆

拟合一个椭圆。它返回椭圆内接的旋转矩形。

ellipse = cv.fitEllipse(cnt)
cv.ellipse(img,ellipse,(0,255,0),2)

在这里插入图片描述

拟合直线

同样地,我们可以用直线来拟合一组点。

rows,cols = img.shape[:2]
[vx,vy,x,y] = cv.fitLine(cnt, cv.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)

在这里插入图片描述

3. 轮廓属性

在这里我们将学习提取一些经常使用的属性,如立体度(Solidity),等效直径,掩模图像,平均强度等。更多功能可以在网站上找到 Matlab regionprops documentation.

注意:质心,面积,周长等也属于这个范畴,但我们已经在上面看到了

长宽比(Aspect Ratio)

它是物体的边界矩形的宽高之比。

在这里插入图片描述

x,y,w,h = cv.boundingRect(cnt)
aspect_ratio = float(w)/h

延伸度(Extent)

延伸度是轮廓面积与外接矩形面积的比值。

在这里插入图片描述

area = cv.contourArea(cnt)
x,y,w,h = cv.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area

实心度(Solidity)

实心度是轮廓面积与其凸包面积的比值。

在这里插入图片描述

area = cv.contourArea(cnt)
hull = cv.convexHull(cnt)
hull_area = cv.contourArea(hull)
solidity = float(area)/hull_area

等效直径(Equivalent Diameter)

等效直径是与轮廓面积相等的圆的直径。

在这里插入图片描述

area = cv.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)

方向(Orientation)

方向是物体指向的角度。下面的方法也给出了长轴和短轴的长度。

(x,y),(MA,ma),angle = cv.fitEllipse(cnt)

掩模和像素点

在某些情况下,我们可能需要构成这个对象的所有要点。可以这样做:

mask = np.zeros(imgray.shape,np.uint8)
cv.drawContours(mask,[cnt],0,255,-1)
pixelpoints = np.transpose(np.nonzero(mask))
#pixelpoints = cv.findNonZero(mask)

这里给出了两个方法,一个使用Numpy函数,另一个使用OpenCV函数(最后一行注释)来做同样的事情。结果也一样,但略有不同。==Numpy给出的坐标是**(行,列)格式,而OpenCV给出的坐标是(x,y)**格式。==所以得到了记过x,y会互换。注意,行= y,列= x。。

最大值,最小值和它们的位置

我们可以用掩模图像找到这些参数.

min_val, max_val, min_loc, max_loc = cv.minMaxLoc(imgray,mask = mask)

平均颜色或平均强度

在这里,我们可以找到一个物体的平均颜色。或者它可以是物体在灰度模式下的平均强度。我们还是用相同的掩膜来做。

mean_val = cv.mean(im,mask = mask)

极值点

leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])
rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])
topmost = tuple(cnt[cnt[:,:,1].argmin()][0])
bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])

例如,如果我将其应用于印度地图,我将得到以下结果:

在这里插入图片描述

其他特征

离心率(Eccentricity)、欧拉数(EulerNumber)、FilledArea、MajorAxisLength、MinorAxisLength等

4. 更多函数

  • 凸性缺陷及其查找方法。

  • 求从点到多边形的最短距离

  • 匹配不同的形状

凸缺陷

上面已经介绍了什么是凸包。物体与凸包的任何偏差都可视为凸缺陷。

OpenCV提供了一个现成的函数来找到它: cv.convexityDefects()。一个基本函数调用如下所示:

hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)

注意:记住,在寻找凸包时,我们必须通过returnPoints = False来寻找凸缺陷。

它返回一个数组,其中每行包含这些值-[起点,终点,最远点,到最远点的近似距离]。我们可以用图像把它形象化。我们画一条线连接起点和终点,然后在最远的点画一个圆。记住,返回的前三个值是cnt的索引。所以我们要从cnt中得到这些值。

import cv2 as cv
import numpy as np
img = cv.imread('star.jpg')
img_gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,thresh = cv.threshold(img_gray, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt = contours[0]
hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)
for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv.line(img,start,end,[0,255,0],2)
    cv.circle(img,far,5,[0,0,255],-1)
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

多边形点测试

这个函数找到图像中点和轮廓之间的最短距离。它返回的距离,当点在轮廓外时为负,当点在轮廓内时为正,如果点在轮廓上则为零。

例如,我们可以对点(50,50)进行如下检查:

dist = cv.pointPolygonTest(cnt,(50,50),True)

在函数中,第三个参数是measureDist。如果为True,则找到带符号的距离。如果为False,它将发现该点是在轮廓内部、外部还是在轮廓上(分别返回+1、-1、0)。

注意:如果你不想找到距离,请确保第三个参数为False,因为这是一个耗时的过程。因此,将其设置为False将提供2-3倍的加速。

形状匹配

OpenCV提供了一个函数cv.matchShapes(),它使我们能够比较两个形状或两个轮廓,并返回显示相似性的指标。==结果越低,匹配越好。==它是根据hu-moment值计算的。不同的测量方法在文档中有解释。

import cv2 as cv
import numpy as np
img1 = cv.imread('star.jpg',0)
img2 = cv.imread('star2.jpg',0)
ret, thresh = cv.threshold(img1, 127, 255,0)
ret, thresh2 = cv.threshold(img2, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt1 = contours[0]
contours,hierarchy = cv.findContours(thresh2,2,1)
cnt2 = contours[0]
ret = cv.matchShapes(cnt1,cnt2,1,0.0)
print( ret )

我尝试将形状与下面给出的不同形状进行匹配:

在这里插入图片描述

我得到了以下结果:

匹配图像A与自身匹配结果= 0.0

图像A与图像B匹配结果 = 0.001946

图像A与图像C匹配结果 = 0.326911

你看,即使图像旋转也不会对这个比较产生很大影响。

注意:Hu-Moments 是七个矩不变的平移,旋转和缩放。第七个是偏不变的。这些值可以使用**cv.HuMoments()** 函数

作业:

查看**cv.pointPolygonTest()**的文档,你可以找到一个漂亮的红蓝色图像。它表示从每一个像素到白色曲线的距离。曲线内的像素是红色的,且颜色取决于距离。类似地,外面的点是蓝色的。轮廓边缘用白色标记。问题很简单。写一个代码来创建这样的距离表示。

在这里插入图片描述

使用cv.matchShapes()比较数字或字母的图像。(这是迈向OCR的简单一步)

5. 轮廓层次

轮廓的层次结构,即轮廓中的父子关系

理论

在前几篇关于轮廓的文章中,我们使用了OpenCV提供的几个与轮廓相关的函数。但是当我们使用cv.findContours()函数在图像中找到轮廓时,我们传递了一个参数,Contour Retrieval Mode。我们通常使用cv.RETR_LIST或cv.RETR_TREE,它很有。但这到底是什么意思呢?

同样,在输出中,我们得到了三个数组,第一个是图像,第二个是我们的轮廓,还有一个我们称为层次结构的输出(请复习前面文章中的代码)。但是我们从来没有用过层次结构。那么这个层次结构是什么,它的目的是什么?它与前面提到的函数参数Contour Retrieval Mode的关系是什么?

这就是我们将在本节中讨论的内容。

  • 什么是层次机构(Hierarchy)?

通常我们使用cv.findContours()函数来检测图像中的物体轮廓,有时物体在不同的位置。但在某些情况下,有些轮廓在其他轮廓的内部。就像嵌套图形一样。在这种情况下,我们将外部轮廓称为parent,将内部轮廓称为child。这样,图像中的轮廓彼此之间就有了某种关系。我们可以指定一个轮廓是如何相互连接的,比如,它是其他轮廓的子轮廓,还是父轮廓等等。这种关系的表示称为层次结构。

考虑下图的一个例子:

在这里插入图片描述
在这张图中,有一些形状,我从0-5开始编号。2和2a表示box的内外轮廓。这里,轮廓(0,1,2)位于外部的或最靠外的。我们可以说,它们在层次结构-0中,简单地说,它们在相同的层次结构中。
其次是contour-2a。它可以被认为是轮廓2的child(或者相反,轮廓2是contour-2a的parent)。让它在层次-1中。类似地,轮廓3是轮廓2的child,它低一个层次。最后,轮廓4,5是轮廓3a的child,它们在最后一层。根据我给box编号的方式,我可以说轮廓-4是等轮廓-3a的第一个child(轮廓-5也是)。

我提到这些是为了理解相同的层次,外部轮廓,子轮廓,父轮廓,第一个child等术语。现在让我们看一下OpenCV中的函数。

层次表示(opencv)

每个轮廓都有自己的信息关于它是什么层次,谁是它的子轮廓,谁是它的父轮廓等等。OpenCV将其表示为一个包含四个值的数组:

[Next, Previous, First_Child, Parent]

“Next”表示同一层次上的下一个轮廓。

例如,在我们的图片中取contour-0。同一层它的下一个轮廓是contour-1。所以简单地把Next = 1。轮廓-1也是一样,接下来是轮廓-2。所以Next = 2。

contour-2在同层没有下一个轮廓,所以其Next = -1。contour-4与contour-5轮廓在同一层上。下一条轮廓是contour-5,所以next = 5。

*“Previous是指同一层次上前一个轮廓。”

同上。contour-1之前的轮廓是同一级的contour-0。同样,对于contour-2,它是contour-1。对于contour-0,没有前一个轮廓,设为-1。

*“First_Child表示它的第一个子轮廓。”*

contour-2的子轮廓是contour-2a,所以contour-2的First_Child为contour-2a的索引值。contour-3a它有两个子轮廓。但我们只取第一个子轮廓,即contour-4。因此,对于contour-3a, First_Child = 4。

*“Parent表示其父轮廓的索引。”*

它与First_Child相反。对于contour-4和contour-5,父轮廓都是contour-3a。对于contour-3a,其父轮廓为contour-3,以此类推。

注意:

如果没有子轮廓或父轮廓,则该字段取为-1

所以现在我们知道了OpenCV中使用的层次样式,我们可以在上面相同的图像的帮助下检查OpenCV中的轮廓检索模式,模式标志有 cv.RETR_LIST, cv.RETR_TREE, cv.RETR_CCOMP, cv.RETR_EXTERNAL等,它们都有什么含义呢?

轮廓索引模式

  1. RETR_LIST

这是四种模式中最简单的一种(从解释的角度来看)。它只是检索所有轮廓,但不创建任何父子关系。在这个规则下,父轮廓和子轮廓是平等的,他们只是轮廓。即它们都属于同一个层次结构。

在这里,层次数组的第3和第4项总是-1。但是很明显,Next和Previous会有它们对应的值。

下面是我得到的结果,每一行都是相应轮廓的层次细节。例如,第一行对应contour-0。下一个轮廓是contour-1,所以Next = 1,其前面没有轮廓,所以previous = -1。剩下的两个,就像上面所述为-1。

>>> hierarchy
array([[[ 1, -1, -1, -1],
        [ 2,  0, -1, -1],
        [ 3,  1, -1, -1],
        [ 4,  2, -1, -1],
        [ 5,  3, -1, -1],
        [ 6,  4, -1, -1],
        [ 7,  5, -1, -1],
        [-1,  6, -1, -1]]])

如果你不使用任何层次结构特性,这是在代码中使用的最优选择。

  1. RETR_EXTERNAL

如果使用此模式,它只返回最外围轮廓的层次。所有子轮廓都不被考虑。(我们可以说,根据这项法律,每个家庭中只有最年长的人得到照顾。它不关心其他家庭成员:)。

在我们的图像中,有多少个最外围轮廓?即处于0级?只有3个,也就是轮廓0,1,2,对吧?现在试着用此模式找出轮廓线。在这里,给每个元素的值也与上面相同。将其与上述结果进行比较。下面是我得到的结果:

>>> hierarchy
array([[[ 1, -1, -1, -1],
        [ 2,  0, -1, -1],
        [-1,  1, -1, -1]]])

如果你想只提取外部轮廓,你可以使用此模式。在某些情况下可能有用。

  1. RETR_CCOMP

此模式检索所有的轮廓,并将它们排列为一个2级的层次结构。即物体的外部轮廓(即其边界)置于层次-1。物体内部的洞(如果有)的轮廓被放置在hierarchy-2。继续,如果其里面还有东西,它的轮廓将再次置为在hierarchy-1中。内部的洞被置为hierarchy-2。

想象一个黑色背景上写一个白色的0。0的外圆属于第一级,0的内圆属于第二级。

我们可以用一个简单的图像来解释它。我在这里用红色标注了轮廓的顺序和它们所属的层次,用绿色标注(1或2)。这个顺序和OpenCV检测轮廓的顺序相同。

在这里插入图片描述

首先考虑轮廓,contour-0的层次为hierarchy-1。它有两个孔:contour-1和contour-2,属于第2层。对于contour-0,其下一个轮廓是contour-3。而且前面没有轮廓。它的第一个child是contour-1,其层次为hierarchy-2。contour-0也没有父轮廓,因为它在层次结构-1中。它的层次数组是[3,-1,1,-1]

接着看一下contour-1。它层次为hierarchy-2。在同一层次中的下一个轮廓(父轮廓都为contour-1)是contour-2。前面没有轮廓。没有子轮廓,但是父轮廓是contour-0。所以其层次数组是[2,-1,-1,0]

同样的,contour-2:在hierarchy-2中。在contour-0下,同一层次中没有下一个轮廓。所以next=-1。前一个轮廓为contour-1。没有子轮廓,父轮廓是contour-0。其层次数组是[-1,1,-1,0]

contour-3:下一个同为hierarchy-1的是contour-5。前一个是contour-0。子轮廓只有contour-4,没有父轮廓。其层次数组是[5, 0, 4, -1]

contour-4:在contour-3下面且同为hierarchy-2的,只有contour-4本身,所以统一层次下它没有上一个轮廓,也下一个没有轮廓,没有子轮廓(因为0虽然在4内部,但是0层次为1),父轮廓是contour-3。其层次数组是[-1,-1,-1,3]

剩下的可以类推。这是我得到的最终答案:

>>> hierarchy
array([[[ 3, -1,  1, -1],
        [ 2, -1, -1,  0],
        [-1,  1, -1,  0],
        [ 5,  0,  4, -1],
        [-1, -1, -1,  3],
        [ 7,  3,  6, -1],
        [-1, -1, -1,  5],
        [ 8,  5, -1, -1],
        [-1,  7, -1, -1]]])
  1. RETR_TREE

这是最后一种检索模式,完美先生。它检索所有的轮廓并创建一个完整的家族层次结构列表。它甚至告诉我们,谁是爷爷、父亲、儿子、孙子,甚至更遥远的……

标签:cnt,层次,python,contour,opencv,图像处理,图像,轮廓,cv
来源: https://blog.csdn.net/weixin_44456692/article/details/118549525

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有