文章目录

  • OpenCV
    • 1 OpenCV的Python接口
    • 2 OpenCV基础知识
      • 2.1 读取和写入图像
      • 2.2 颜色空间
      • 2.3 显示图像及结果
    • 3 处理视频
      • 3.1 视频输入
      • 3.2 将视频读取到NumPy数组
    • 4 跟踪
      • 4.1 光流
      • 4.2 Lucas-Kanade算法
        • 4.2.1 使用跟踪器
        • 4.2.2 使用发生器
    • 5 应用实例
      • 5.1 图像修复
      • 5.2 利用分水岭变换进行分割
      • 5.3 利用霍夫变换检测直线

OpenCV

OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux、Windows、Android和Mac OS操作系统上。 它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。

1 OpenCV的Python接口

OpenCV是一个C++库,它包含了计算机视觉领域的很多模块。除了C++和C,Python作为一种简洁的脚本语言,在C++代码基础上的Pyhton接口得到了越来越广泛的支持。

可以通过以下方式导入CV2模块:

import cv

2 OpenCV基础知识

OpenCV自带读取、写入图像函数以及矩阵操作和数学库,在这部分介绍一些基本的组件及其使用方法。

2.1 读取和写入图像

下面的例子会载入一幅图像,打印出图像大小,对图像进行转换并保存为.png格式:

import cv2im = cv2.imread('empire.jpg')
h, w = im.shape[:2]
print(h, w)cv2.imwrite('result_empire.png', im)

函数imread()返回图像为一个标准的NumPy数组,并且该函数能够处理很多不同格式的图像。函数imwrite()会根据文件后缀自动转换图像。

得到的打印出的图像大小如图:

图1 使用OpenCV库读取图像大小

2.2 颜色空间

在OpenCV中,图像不是按照传统的RGB颜色通道,而是按照BGR顺序存储的。读取图像时默认的是BGR,但是还有一些可用的转换函数。颜色空间的转换使用函数cvColor()来实现。可以使用下面的代码将原图像转为灰度图像:

import cv2im = cv2.imread('empire.jpg')gray = cv2.cvColor(im, cv2.COLOR_BGR2GRAY)

在读取图像之后,紧接其后的是OpenCV颜色转换代码,其中最有用的一些转换代码如下:

  • cv2.COLOR_BGR2GRAY
  • cv2.COLOR_BGR2RGB
  • cv2.COLOR_GRAY2BGR

上面的每个转换代码中,转换后的图像颜色通道数与对应的转换代码相匹配。例如,对于灰度图像只有一个通道,对于RGB和BGR图像则有三个通道。最后的cv2.COLOR_GRAY2BGR将灰度图像转换为BGR彩色图像。

2.3 显示图像及结果

下面这个例子是从文件中读取一幅图像,并创建一个整数图像表示:

import cv2im = cv2.imread('fisherman.jpg')gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)intim = cv2.integral(gray)intim = (255.0 * intim) / intim.max()
cv2.imwrite('result_fisherman.jpg', intim)

读取图像后,将其转化为灰度图像,函数integral()创建一幅图像,该图像每个像素值是原图上方和左边强度值相加后的结果。在保存图像前,通过除以图像中的像素最大值将其归一化到0至255之间。得到的结果如下图所示:

图2 用OpenCV的integral()函数计算积分图像

下面这个例子是从一个种子像素进行泛洪填充:

import cv2
from numpy import *filename = 'fisherman.jpg'
im = cv2.imread(filename)
h, w = im.shape[:2]diff = (6, 6, 6)
mask = zeros((h + 2, w + 2), uint8)
cv2.floodFill(im, mask, (10, 10), (255, 255,0),diff, diff)cv2.imshow('flood fill', im)
cv2.waitKey()cv2.imwrite('result_fisherman_1.jpg', im)

在这个例子中,对图像应用泛洪填充并在OpenCV窗口显示。waitKey()函数一直处于暂停状态,直到有按键按下,此时窗口才会自动关闭。这里的floodFill()函数获取(灰色或彩色)图像、一个掩模、一个种子像素以及新的颜色代替下限和上限阈值差的泛洪像素。泛洪填充以种子像素为起始,只要能在阈值的差异范围内添加新的像素,泛洪填充就会持续扩展,结果如下图所示:

图3 彩色图像泛洪填充

3 处理视频

单纯使用Python处理视频有些困难,因为需要考虑速度、编码器、摄像机、操作系统和文件格式。使用OpenCV的Python接口是一个很好的选择。

3.1 视频输入

OpenCV可以很好地支持从摄像头读取视频。下面给出一个捕获视频帧并在OpenCV窗口中显示这些视频帧的完整例子:

import cv2cap = cv2.VideoCapture(0)
while True:ret,im = cap.read()cv2.imshow('video test',im)key = cv2.waitKey(10)if key == 27:breakif key == ord(' '):cv2.imwrite('video_result.jpg',im)

捕获对象VideoCapture从摄像头或文件捕获视频。通过一个整数进行初始化,该整数为视频设备的id。如果仅有一个摄像头和计算机相连,那么该摄像头的id为0。read()方法解码并返回下一视频帧,第一个变量ret是一个判断视频帧是否成功读入的标志,第二个变量则是实际读入的图像数组。函数waitKey()等待用户按键:若按下Esc则退出,若按下空格则保存该视频帧。

得到如下结果:

图4 视频截图

拓展上面的例子,将摄像头捕获的数据作为输入,并在 OpenCV 窗口中实时显示经模糊的(彩色)图像:

import cv2cap = cv2.VideoCapture(0)
while True:ret,im = cap.read()blur = cv2.GaussianBlur(im, (0, 0), 5)cv2.imshow('camera blur', blur)if cv2.waitKey(10) == 27:break

每一视频帧都会被传递给 GaussianBlur() 函数,该函数会用高斯滤波器对传入的该帧图像进行滤波。这里,我们传递的是彩色图像,所以 Gaussian Blur() 函数会录入对彩色图像的每一个通道单独进行模糊。该函数需要为高斯函数设定滤波器尺寸 (保存在元组中)及标准差;在本例中标准差设为 5。如果该滤波器尺寸设为 0,则它由标准差自动决定,显示出的结果与上图相似。

3.2 将视频读取到NumPy数组

使用 OpenCV 可以从一个文件读取视频帧,并将其转换成 NumPy 数组。下面是一个从摄像头捕获视频并将视频帧存储在一个 NumPy 数组中的例子:

import cv2
from numpy import *cap = cv2.VideoCapture(0)
frames = []while True:ret,im = cap.read()cv2.imshow('video',im)frames.append(im)if cv2.waitKey(10) == 27:break
frames = array(frames)print (im.shape)
print (frames.shape)

上述代码将每一视频帧数组添加到列表末,直到捕获结束。最终得到的数组会有帧数、帧高、帧宽及颜色通道数(3 个),打印出的结果如下:

4 跟踪

跟踪是在图像序列或视频里对其中的目标进行跟踪的过程。

4.1 光流

光流是目标、场景或摄像机在连续两帧图像间运动时造成的目标的运动。它是图像在平移过程中的二维矢量场。

光流法主要依赖于三个假设。

  • 亮度恒定 (图像中目标的像素强度在连续帧之间不会发生变化)。
  • 时间规律 (相邻帧之间的时间足够短,以至于在考虑运行变化时可以忽略它们之 间的差异。该假设用于导出下面的核心方程)。
  • 空间一致性 (相邻像素具有相似的运动)。

在很多情况下这些假设并不成立,但是对于相邻帧间的小运动以及短时间跳跃,它还是一个非常好的模型。

下面就是一个利用calcOpticalFlowFarneback()在视频中寻找运动矢量的例子:

import cv2
import numpy as np
from pylab import  *
from PIL import Imagedef draw_flow(im,flow,step=16):h,w = im.shape[:2]y,x = mgrid[step/2:h:step,step/2:w:step].reshape(2,-1)fx, fy = flow[y, x].Tlines = vstack([x,y,x+fx,y+fy]).T.reshape(-1,2,2)lines = int32(lines)vis = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)for (x1, y1), (x2, y2) in lines:cv2.line(vis, (x1, y1), (x2, y2), (0, 255, 0), 1)cv2.circle(vis, (x1, y1), 1, (0, 255, 0), -1)return viscap = cv2.VideoCapture(0)
ret,im = cap.read()
prev_gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)while True:ret,im = cap.read()gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)flow = cv2.calcOpticalFlowFarneback(prev_gray,gray,None,0.5,3,15,3,5,1.2,0)prev_gray = graycv2.imshow('Optical flow',draw_flow(gray,flow))if cv2.waitKey(10) == 27:break

得到的结果如下图所示:

图5 展示光流矢量

在上述例子中,利用摄像头捕获图像,并对每个连续图像进行光流估计。由calcOpticalFlowFarneback()返回运动光流矢量保存在双通道图像变量flow中。辅助函数draw_flow()会在图像均匀间隔的点处绘制光流矢量,利用到了OpenCV的绘图函数line()和cirle(),并用变量step控制流样本的间距。

4.2 Lucas-Kanade算法

跟踪最基本的形式是跟随感兴趣点,比如角点。对此,一个流行的算法是Lucas-Kanade算法,它利用了稀疏光流算法。

Lucas-Kanade算法可以应用于任何一种特征,不过通常使用一些角点,例如Harris角点。如果基于每一个像素考虑,该光流方程是欠定方程,即每个方程中含很多未知变量。利用相邻像素有相同运动这一假设,对于n个相邻像素,可以写成一个系统方程,并用最小二乘法求解。对于周围像素的贡献可以进行加权处理,使越远的像素影响越小。最后求解超定方程组得出运动矢量。

这些Lucas-Kanade包含在OpenCV中,接下来创建一个Python跟踪类:

from numpy import *
import cv2lk_params = dict(winSize=(15,15),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,10,0.03)) subpix_params = dict(zeroZone=(-1,-1),winSize=(10,10),criteria = (cv2.TERM_CRITERIA_COUNT | cv2.TERM_CRITERIA_EPS,20,0.03))feature_params = dict(maxCorners=500,qualityLevel=0.01,minDistance=10)class LKTracker(object):def __init__(self,imnames):self.imnames = imnamesself.features = []self.tracks = []self.current_frame = 0

用一个文件名列表对跟踪对象进行初始化,变量features和tracks分别保存角点和对这些角点进行跟踪的位置,同时利用一个变量对当前帧进行跟踪。

在开始检测角点时,需要载入实际图像并转换为灰度图像,提取“利用跟踪好的特征”点。OpenCV的函数goodFeaturesToTrack()方法可以完成这一主要工作:

def detect_points(self):self.image = cv2.imread(self.imnames[self.current_frame])self.gray = cv2.cvtColor(self.image,cv2.COLOR_BGR2GRAY)features = cv2.goodFeaturesToTrack(self.gray, **feature_params)cv2.cornerSubPix(self.gray,features, **subpix_params)self.features = featuresself.tracks = [[p] for p in features.reshape((-1,2))]self.prev_gray = self.gray

上述代码中的cornerSubPix()提炼角点位置,并保存在成员变量features和tracks中。需要注意的是,运行该函数会清楚跟踪历史。

检测角点之后,还要对其进行跟踪。首先要获取一幅图像,然后应用OpenCV中的calcOpticalFlowPyrLK()找出这些点运动到哪里,最后清除这些包含跟踪点的列表。下面的函数track_points()可以完成该过程:

def track_points(self):if self.features != []:self.step() self.image = cv2.imread(self.imnames[self.current_frame])self.gray = cv2.cvtColor(self.image,cv2.COLOR_BGR2GRAY)tmp = float32(self.features).reshape(-1, 1, 2)features,status,track_error = cv2.calcOpticalFlowPyrLK(self.prev_gray,self.gray,tmp,None,**lk_params)self.features = [p for (st,p) in zip(status,features) if st]features = array(features).reshape((-1,2))for i,f in enumerate(features):self.tracks[i].append(f)ndx = [i for (i,st) in enumerate(status) if not st]ndx.reverse() for i in ndx:self.tracks.pop(i)self.prev_gray = self.gray

下面定义一个辅助函数step()用于移动下一视频帧:

def step(self,framenbr=None):if framenbr is None:self.current_frame = (self.current_frame + 1) % len(self.imnames)else:self.current_frame = framenbr % len(self.imnames)

该方法会跳转到一个给定的视频帧,如果没有参数则直接跳转到下一帧。

最后,添加draw()方法绘出跟踪的结果:

def draw(self):for point in self.features:cv2.circle(self.image,(int(point[0][0]),int(point[0][1])),3,(0,255,0),-1)cv2.imshow('LKtrack',self.image)cv2.waitKey()

4.2.1 使用跟踪器

我们将该跟踪类应用于真是的场景中。下面的脚本初始化一个跟踪对象,对视频序列进行角点检测、跟踪,并画出跟踪结果:

import lktrack
imnames = ['bt.003.pgm', 'bt.002.pgm', 'bt.001.pgm', 'bt.000.pgm']lkt = lktrack.LKTracker(imnames)lkt.detect_points()
lkt.draw()
for i in range(len(imnames)-1):lkt.track_points()lkt.draw()

每次画出一帧,并显示当前跟踪到的点,按任意键会转移到序列的下一帧。

图6 通过LKTrack类显示跟踪点

4.2.2 使用发生器

将下面的方法添加到LKTracker类:

def track(self):for i in range(len(self.imnames)):if self.features == []:self.detect_points()else:self.track_points()f = array(self.features).reshape(-1,2)im = cv2.cvtColor(self.image,cv2.COLOR_BGR2RGB)yield im,f

上面的方法创建一个发生器,可以使遍历整个序列并将获得的跟踪点和这些图像以RGB数组保存,以便画出跟踪结果。

import lktrack
import cv2
import numpy as np
from pylab import  *
from PIL import Imageimnames = ['bt.003.pgm', 'bt.002.pgm', 'bt.001.pgm', 'bt.000.pgm']lkt = lktrack.LKTracker(imnames)
for im,ft in lkt.track():print ('tracking %d features' % len(ft))figure()
imshow(im)
for p in ft:plot(p[0],p[1],'bo')
for t in lkt.tracks:plot([p[0] for p in t],[p[1] for p in t])
axis('off')
show()

画出的跟踪点的轨迹如图所示:

图7 用Lucas-Kanade跟踪算法画出跟踪点的轨迹

5 应用实例

5.1 图像修复

对图像丢失或损坏的部分进行重建的过程叫做修复,既包括以复原为目的的对图像丢失数据或损坏部分进行恢复的算法。典型例子是,图像的一个区域标记为“损坏”,并需要利用余下的数据对该区域进行填补。

运行下边的命令:

import cv2
from matplotlib import pyplot as plt
from pylab import  *
from PIL import Image
import numpy as npdef inpaint(img, threshold=1):h, w = img.shape[:2]if len(img.shape) == 3:  # RGBmask = np.all(img == 0, axis=2).astype(np.uint8)img = cv2.inpaint(img, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA)else:  # depthmask = np.where(img > threshold)xx, yy = np.meshgrid(np.arange(w), np.arange(h))xym = np.vstack((np.ravel(xx[mask]), np.ravel(yy[mask]))).Timg = np.ravel(img[mask])interp = interpolate.NearestNDInterpolator(xym, img)img = interp(np.ravel(xx), np.ravel(yy)).reshape(xx.shape)return imgim = cv2.imread('empire.jpg')
img = inpaint(im)imshow(img)

最后显示出修复的情况:

图8 用OpenCV进行图像修复

5.2 利用分水岭变换进行分割

分水岭是一种可以用于分割的图像处理技术。图像可以看成是一幅有很多种子区域“淹没”后形成的拓扑地貌。由于梯度幅值图像在突出的边缘有脊,而且分割通常在这些边缘处停止,所以通常会用到梯度幅值图像。

运行下边的代码:

import numpy as np
import cv2
from matplotlib import pyplot as pltsrc = cv2.imread('empire_1.jpg')
img = src.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)sure_bg = cv2.dilate(opening, kernel, iterations=3)dist_transform = cv2.distanceTransform(opening, 1, 5)
ret, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)ret, markers1 = cv2.connectedComponents(sure_fg)markers = markers1 + 1markers[unknown == 255] = 0markers3 = cv2.watershed(img, markers)
img[markers3 == -1] = [0, 0, 255]plt.subplot(241), plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB)),
plt.title('Original'), plt.axis('off')
plt.subplot(242), plt.imshow(thresh, cmap='gray'),
plt.title('Threshold'), plt.axis('off')
plt.subplot(243), plt.imshow(sure_bg, cmap='gray'),
plt.title('Dilate'), plt.axis('off')
plt.subplot(244), plt.imshow(dist_transform, cmap='gray'),
plt.title('Dist Transform'), plt.axis('off')
plt.subplot(245), plt.imshow(sure_fg, cmap='gray'),
plt.title('Threshold'), plt.axis('off')
plt.subplot(246), plt.imshow(unknown, cmap='gray'),
plt.title('Unknow'), plt.axis('off')
plt.subplot(247), plt.imshow(np.abs(markers), cmap='jet'),
plt.title('Markers'), plt.axis('off')
plt.subplot(248), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)),
plt.title('Result'), plt.axis('off')plt.show()

得到的结果如下:

图9 用分水岭变换分割图像

5.3 利用霍夫变换检测直线

霍夫变换是一种用于在图像中寻找各种形状的方法,原理是在参数空间中使用投票机制。霍夫变换常用于在图像中寻找直线结构,在该情况下,可以在二维直线参数空间对相同的直线参数进行投票,将边缘和线段组合在一起。

运行下面的这段代码:

import cv2
import numpy as npimg = cv2.imread("empire.jpg", 0)img = cv2.GaussianBlur(img, (3, 3), 0)
edges = cv2.Canny(img, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi / 180, 118)
result = img.copy()
for line in lines[0]:rho = line[0]  theta = line[1]  print (rho)print (theta)if (theta < (np.pi / 4.)) or (theta > (3. * np.pi / 4.0)): pt1 = (int(rho / np.cos(theta)), 0)pt2 = (int((rho - result.shape[0] * np.sin(theta)) / np.cos(theta)), result.shape[0])cv2.line(result, pt1, pt2, (255))else:  pt1 = (0, int(rho / np.sin(theta)))pt2 = (result.shape[1], int((rho - result.shape[1] * np.cos(theta)) / np.sin(theta)))cv2.line(result, pt1, pt2, (255), 1)cv2.imshow('Canny', edges)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

得到的结果如下图所示:

图10 利用霍夫变换检测直线

计算机视觉编程——OpenCV相关推荐

  1. OpenCV计算机视觉编程攻略之行人检测

    OpenCV计算机视觉编程攻略之行人检测,OpenCV 提供了一个基于HOG 和SVM且经过训练的行人检测器,可以用这个SVM 分类器以不同尺度的窗口扫描图像,在完整的图像中检测特定物体. 原图如下: ...

  2. OpenCV计算机视觉编程攻略之提取图片轮廓-使用Canny函数

    OpenCV计算机视觉编程攻略之提取图片轮廓-使用Canny函数,很方便..代码如下: #include <vector> #include <iostream> #inclu ...

  3. OpenCV计算机视觉编程攻略之生成椒盐噪声实现

    OpenCV计算机视觉编程攻略(第3版)P21的访问像素值,生成椒盐噪声实现. 运行结果图片,截图如下: 看书留下记录,代码如下: #include <random> #include & ...

  4. opencv计算机视觉编程攻略 第2版,OpenCV计算机视觉编程攻略(第2版)pdf

    摘要 1. 50多个知识点的案例解读,全面掌握基础知识与进阶内容 2. 学习OpenCV重要的图像操作类和函数 3. 初学者和从业者即查即用的工具书 4. 掌握计算机视觉与图像处理的基础知识与概念 O ...

  5. Python计算机视觉编程第十章——OpenCV基础知识

    Python计算机视觉编程 (一)OpenCV 的 Python 接口 (二)OpenCV 基础知识 2.1 读取和写入图像 2.2 颜色空间 2.3 显示图像及结果 (三)处理视频 3.1 视频输入 ...

  6. OpenCV计算机视觉编程攻略第2版pdf

    下载地址:网盘下载 内容简介  · · · · · · 计算机视觉是机器准确识别.理解和表示信息,从而感知并与世界交互的媒介,在人脸识别.智能驾驶.手势游戏.图像搜索.自动定位等各领域都发挥着极为重要 ...

  7. OpenCV计算机视觉编程篇三《处理图像的颜色》

    前言 前期回顾: OpenCV计算机视觉编程篇二<操作像素> 上面这篇里面写了操作像素相关. 本章包括以下内容: 用策略设计模式比较颜色: 用 GrabCut 算法分割图像: 转换颜色表示 ...

  8. opencv3计算机视觉python语言实现pdf_对比《OpenCV计算机视觉编程攻略第3版》《OpenCV 3计算机视觉Python语言实现第2版》PDF代码......

    OpenCV 3是一种先进的计算机视觉库,可以用于各种图像和视频处理操作,通过OpenCV 3 能很容易地实现一些有前景且功能先进的应用(比如:人脸识别或目标跟踪等).从图像处理的基本操作出发,计算机 ...

  9. 《Arduino计算机视觉编程》一3.3 总结

    本节书摘来自华章出版社<Arduino计算机视觉编程>一书中的第3章,第3.3节,作者[土耳其] 欧森·奥兹卡亚(zen zkaya),吉拉伊·伊利茨(Giray Yilliki),更多章 ...

最新文章

  1. nginx FastCGI错误Primary script unknown解决办法
  2. CentOS7 升级 Git 版本
  3. 【自动驾驶】16.计算机视觉:相机成像原理:世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换
  4. k8s常用对象图示:Deployment、ReplicaSet、Pod它们的关系
  5. Arrays.asList(arr)使用注意事项
  6. 安装php时,make步骤报错make: *** [sapi/fpm/php-fpm] Error 1
  7. (转)随机数生成工具
  8. 2021年中国中级订单选择器(3至8+m)市场趋势报告、技术动态创新及2027年市场预测
  9. iOS网络编程实践--蓝牙对等网络通信实例讲解
  10. 一个基于Spring Boot+Vue+Redis的物联网智能家居系统,可二次开发接私活!
  11. html 视频在线播放,HTML 视频(Video)播放
  12. 三险一金包括什么?三险和五险有什么区别?
  13. 7.计算机系统包括,windows7分几个版本_windows7有哪些版本
  14. 安卓 ANR 原因,解决方法
  15. WIN32 API串口通信编程
  16. 30几个HTML5经典动画应用回顾 让你大饱眼福
  17. 【模电知识总结】三极管
  18. C++设计模式——建造者模式(高屋建瓴)
  19. Elasticsearch 6 Mapping设置
  20. 【Shader】色调映射、视差贴图与实时阴影

热门文章

  1. vs显式导入(代码注入)依赖库
  2. Oracle 11g 数据库启动和关闭
  3. Oracle的FIXED_DATE参数
  4. Http压测工具wrk使用指南
  5. Android opencv cvCvtColor()的转换
  6. [20161219]关于LANGUAGE_MISMATCH.txt
  7. PHP获取当前域名$_SERVER['HTTP_HOST']和$_SERVER['SERVER_NAME']的区别
  8. 键盘样式风格有关设置-iOS开发
  9. ​linux 系统出现Give root password for maintenance 问题
  10. Java学习从入门到精通-旧版