
1. 从零搭建图像处理直觉先搞清楚OpenCV到底在算什么很多朋友一开始学OpenCV就扎进各种函数的海洋里今天调一个cv2.cvtColor明天试一下cv2.Canny函数都跑通了但遇到真实项目还是懵。我最初也是这么过来的后来才意识到问题出在哪OpenCV不是一堆孤立函数的集合而是一套从像素出发解决问题的方法论。每个算法本质上都在做同一件事——把一张由成百上千万个数字组成的像素矩阵逐步变成对我们有意义的信息。比如灰度化是把三维颜色压缩成一维亮度滤波是在像素和邻居之间做加权平均边缘检测是在找亮度变化最剧烈的那条分界线。想通这一点之后再看算法就非常顺了它不是在处理图片而是在用数学手段回答这张图里哪部分值得关注。这篇总结并不是把OpenCV官方文档的算法目录抄一遍而是按我在实际项目里最常用的路线来梳理。你可能是刚入门不久的学生可能是在做毕业设计或竞赛也可能是工作中突然要接手图像相关模块的开发者。无论哪种情况我的建议都是不要按官方文档的顺序学按预处理 → 特征提取 → 结构分析 → 实际调优这条主线学效率会高很多。下面每一章都会先讲明白算法在解决什么问题再给关键参数和使用场景最后补充一些文档里不会写的实战经验。2. 图像预处理三件套灰度化、滤波、直方图均衡化2.1 灰度化为什么是第一步而不是可选项先说一个几乎所有OpenCV教程都默认你懂、但没人真正讲透的问题为什么大多数算法都要从彩色图转成灰度图原因不光是计算量小了三倍更本质的是很多算法在数学定义上就是建立在单通道上的。比如边缘检测的核心是计算亮度梯度——彩色图有三个通道每个通道都有各自的梯度它们之间怎么加权合成反而会引入干扰。拿一个实际例子来说。我之前处理一批从外部设备抓取的工业零件图像光源偏黄整个画面白平衡不对。直接跑彩色图的边缘检测黄光在金属表面的反射会造成大量伪边缘。转成灰度后虽然白平衡问题依然存在但至少梯度计算只针对亮度单一维度干扰源少了一个。再用下面的滤波和均衡化把亮度分布拉平问题基本就解决了。import cv2 img cv2.imread(industrial_part.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)2.2 滤波选型均值、高斯、中值到底差在哪滤波是所有预处理里最考验经验感的一步因为它的本质是模糊而模糊多少、怎么模糊直接影响后面所有算法的输入质量。均值滤波cv2.blur窗口内所有像素取平均值。速度快但对噪声敏感因为噪声点也被平均进去了。高斯滤波cv2.GaussianBlur按距离给窗口内像素赋权重离中心越近权重越大。这是用得最广的因为它更符合自然图像中相邻像素相关性随距离衰减的规律。绝大多数场景先试它准没错。中值滤波cv2.medianBlur取窗口内像素的中位数。这是去椒盐噪声黑白孤点噪声的利器但对边缘细节的保留也最差。我自己在项目里的选型经验是不知道噪声类型时优先高斯滤波。它能在去噪和保留边缘之间取得一个比较稳妥的平衡。如果图像里明显有黑白颗粒状的噪声就换中值滤波如果对速度要求极高且图像质量本身不错才考虑均值滤波。这里有一个我踩过的坑——高斯滤波的核大小ksize必须是正奇数而且核越大图像越模糊但不是越大越好。对一张1080p的图像我用(5, 5)的核做预处理边缘检测效果最好用(11, 11)之后细小的边缘直接被抹平了后面怎么调阈值都救不回来。核大小要跟目标特征尺寸匹配这是个需要反复试的经验值。2.3 直方图均衡化把对比度拉满的数学原理直方图均衡化是我早期最容易忽略、后来觉得真香的一个算法。它的原理一句话能讲清楚把像素值的分布从集中在某个区间拉伸到均匀分布在整个0-255范围。但为什么这样能增强对比度因为人眼和算法对细节的感知都依赖于亮度差异——如果整张图的灰度值都堆在100到120之间那这20个级别的差异肉眼几乎看不出区别拉开到0到255之后原本微小的差异就变成明显可辨的了。# 全局直方图均衡化 equ cv2.equalizeHist(gray) # CLAHE限制对比度自适应直方图均衡化 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) clahe_img clahe.apply(gray)在实际项目中我几乎不用cv2.equalizeHist处理照片因为全局均衡化对光照不均匀的图效果很差——它会把整体亮度的变化也当成细节去增强导致画面发灰、过曝感严重。更实用的是CLAHE自适应直方图均衡化它把图像分成若干个小块tile每个块内单独做均衡化再用插值把块之间的边界做平滑。这样能保留局部的细节增强效果又不会让全局亮度失真。举个真实案例我处理过一批室外拍摄的交通指示牌图像白天强光下牌子亮部过曝、暗部欠曝。用全局均衡化后整张图变得灰蒙蒙的换成CLAHE之后牌面上的文字和图案细节都清晰了不少。clipLimit这个参数控制对比度增强的强度我一般设2.0到3.0之间太大会出现水彩画效果颜色过度饱和太小则跟没处理一样。3. 边缘检测算法Sobel、Laplacian、Canny的共性与差异3.1 边缘的本质是变化率不是边界线很多新手对边缘检测有个误区以为它是像人的眼睛一样看到物体的轮廓。实际上边缘检测算法全是在做同一件事——计算像素亮度值的导数变化率。平坦区域的像素变化率接近0不是边缘变化剧烈的地方导数大就是候选边缘。理解这一点之后再看各种算子就不乱了Sobel算子计算一阶导数区分水平方向和垂直方向分别用cv2.Sobel的dx和dy参数控制。因为只算了一阶它对噪声比较敏感但实现简单、速度快。Laplacian算子计算二阶导数。二阶导数为0的点对应一阶导数的极值也就是边缘的位置。它对孤立噪声点特别敏感噪声点的二阶导数极大所以一般要先做高斯模糊再用。Canny算子不是单一算子而是一个多阶段流程——先用高斯滤波去噪再算梯度幅值和方向然后做非极大值抑制把梯度方向上不是局部最大值的点去掉最后用双阈值连接边缘。3.2 为什么实战中Canny是默认首选我自己在项目里90%的场景直接选Canny不是因为它高级而是因为它把边缘检测变成了一个可调参的工程问题。Sobel的阈值和核大小对结果的影响不太好控制Canny的两个阈值threshold1和threshold2给了你一个明确的调参维度小于threshold1的梯度不算边缘大于threshold2的必算边缘两者之间的按连通性决定是否保留。# Canny边缘检测 edges cv2.Canny(blurred, threshold150, threshold2150)这段代码里50和150的比例经验值是1:2到1:3之间。threshold1设得低边缘会多但噪声也多设得高边缘干净但一些小细节可能丢失。调参的起点我一般是先跑一遍看结果然后盯着图像里想保留的最弱边缘来反推threshold1。比如希望保留一条细缝的边缘而它的梯度幅值在60左右那threshold1就设在50上下留一点余量。3.3 边缘检测前要不要先滤波这是一个先有鸡还是先有蛋的问题Canny内部自带高斯滤波所以很多人会忽略提前滤波这一步。但我在实践中发现处理真实噪声较多的图像时先对原图做一次中值滤波或高斯滤波再进Canny效果远比只用Canny内建滤波好。原因在于Canny内置的高斯滤波核大小是固定的而不同图像的噪声尺度差异很大提前滤波可以针对性地消除特定尺度的噪声。需要强调的是这篇博文里的所有示例、参数和调优思路都来自我在模拟项目和公开数据集上的复现经验不是某个具体商业产品的参数。图像处理本身就是三分算法、七分调参的活儿参数要跟着你的数据和目标走。4. 形态学操作腐蚀、膨胀、开运算、闭运算以及它们如何修理二值图4.1 形态学不是在处理图像是在处理形状形态学操作处理的输入一般是经过阈值分割或边缘检测后得到的二值图只有0和255两种值。它的核心操作只有两个腐蚀和膨胀。其他所有操作都是这两个的组合。用最直观的话解释腐蚀用一个结构元素核扫过图像只有当核完全落在白色区域内时中心点才保留白色。效果是白色区域缩小一圈能去掉小块的白色噪点。膨胀只要核与白色区域有交集中心点就变白。效果是白色区域扩大一圈能填补空洞、连接断开的区域。4.2 开运算和闭运算解决去噪与补洞这对矛盾开运算 先腐蚀后膨胀。它能消除白色区域周围的小凸起和孤立噪点但整体形状大小基本不变。我第一次用开运算处理二值图时惊讶于它能把被打碎的字符边缘修得干干净净而字符的粗细几乎没变化。闭运算 先膨胀后腐蚀。它能填补白色区域内部的小孔洞、连接断裂的笔画。比如检测印刷体文字时如果字体内部有白色小点干扰连通域分析用闭运算就能解决。import cv2 import numpy as np kernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 开运算消除白色小噪点 opening cv2.morphologyEx(binary_img, cv2.MORPH_OPEN, kernel) # 闭运算填补白色区域内部空洞 closing cv2.morphologyEx(binary_img, cv2.MORPH_CLOSE, kernel)核大小的选择直接影响效果。我用(5, 5)的核做开运算把一些细小的印刷噪点去掉了但当我换成(9, 9)的核发现字符笔画的主体也开始被吞噬。结构元素尺寸应略小于你想保留的最小特征尺寸——这是我在多次调整后总结的一句话放这里最合适。4.3 形态学梯度被很多人忽略的边缘增强神器形态学梯度 膨胀结果 减 腐蚀结果。它提取的是二值图的前景边缘效果类似于边缘检测但因为只基于形态学操作速度非常快而且对粗细变化不敏感。在需要快速提取形状外轮廓的场景比如实时视频目标跟踪的预处理形态学梯度比Canny更省计算资源。gradient cv2.morphologyEx(binary_img, cv2.MORPH_GRADIENT, kernel)这段代码在很多教科书里只是被提了一句但实际用起来效果出奇地稳定。我把形态学梯度作为检查二值图质量的辅助手段——如果梯度结果里出现了大量杂散的孤立边缘说明二值图噪声偏多需要回头调整阈值分割的参数。5. 轮廓检测与几何特征计算从一堆点组成有意义的对象5.1findContours的返回值陷阱OpenCV版本差异必须搞清轮廓检测是图像处理中最常用的结构化工具它能把二值图中连通的白色区域提取成一组点的集合轮廓从这以后图像处理就从像素操作升级为几何分析。但这里有一个非常经典的坑。OpenCV的cv2.findContours在OpenCV 3.x和4.x中返回值的数量不同老版本返回3个值image, contours, hierarchy新版本返回2个值contours, hierarchy。# 适用于OpenCV 4.x的写法 contours, hierarchy cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)如果从网上复制了老版本的代码直接跑多半会报ValueError: not enough values to unpack。这类语法层面的差异最烦人因为报错和实际算法无关纯粹是版本适配问题。另外两个关键参数也值得展开。cv2.RETR_EXTERNAL只提取最外层的轮廓忽略内层洞比如一个圆环只取外圆不取内圆。适合目标彼此分离的场景。cv2.RETR_CCOMP和cv2.RETR_TREE提取所有层级的轮廓适合处理有嵌套结构的图像比如文字内还有小洞的情况。cv2.CHAIN_APPROX_SIMPLE压缩水平、垂直和对角方向的轮廓点只保留端点能大幅减少点数。如果不压缩cv2.CHAIN_APPROX_NONE轮廓点数巨大后续计算复杂度也高。5.2 轮廓筛选的实用套路面积、长宽比、凸包一个都不能少提取到轮廓之后下一步通常不是直接用而是筛选。我整理了最常用的三个筛选维度面积cv2.contourArea过滤掉太小或太大的噪声区域。比如检测工件上的螺丝孔时面积小于20像素的轮廓基本可以断定是灰尘或噪点。宽高比cv2.boundingRect用外接矩形宽高比排除不符合长宽约束的轮廓。比如检测车牌区域字符轮廓的宽高比一般在一个固定范围内超出范围的直接丢弃。凸包cv2.convexHull判断轮廓是否近似凸形。对检测圆形、矩形等规则目标时很有用——偏离凸形程度大的轮廓大概率不是目标。areas [cv2.contourArea(c) for c in contours] # 按面积从大到小排序取前N个 sorted_contours sorted(contours, keycv2.contourArea, reverseTrue)[:N]5.3 形状匹配与轮廓近似当你要判断它是不是一个圆时轮廓近似的本质是用更少的点去逼近原始轮廓关键是cv2.approxPolyDP的epsilon参数。这个参数表示近似结果与原始轮廓之间的最大距离值越大近似后的点多边形越粗糙但也越精简。epsilon 0.01 * cv2.arcLength(contour, True) approx cv2.approxPolyDP(contour, epsilon, True) if len(approx) 3: shape triangle elif len(approx) 4: shape rectangle elif len(approx) 5: shape pentagon else: # 一般用圆度来判断 area cv2.contourArea(contour) perimeter cv2.arcLength(contour, True) circularity 4 * np.pi * area / (perimeter * perimeter) if circularity 0.85: shape circle这段代码展示了一个很常见的业务逻辑检测物体是三角形、矩形、五边形还是圆形。circularity圆度是一个非常经典的几何度量值在0到1之间越接近1说明形状越接近圆。这个方法我在做形状识别的小工具时反复用过它的优势是不依赖机器学习模型纯几何计算快且直观。5.4 坐标排序的细节做OCR项目前必须掌握的定位顺序轮廓检测在OCR项目里经常用来定位单个字符但有个问题检测到的轮廓顺序完全取决于图像扫描的顺序不一定是按阅读顺序从左到右、从上到下排列的。我早期做OCR demo时就栽在这里——轮廓顺序一乱最后拼出来的文本顺序完全不对。解决办法是用外接矩形框的中心点坐标先按y坐标排序从上到下再把同一行内按x坐标排序从左到右。rectangles [cv2.boundingRect(c) for c in contours] # 按 y 排序然后按 x 排序 sorted_rects sorted(rectangles, keylambda r: (r[1] // h, r[0]))这里对y除以行高h再取整是为了把同一行内的字符归为一组避免因为轻微的倾斜导致同一行的字符y坐标有差异而被拆成多行。这个技巧对倾斜校正后的图像尤其有效。6. 特征点检测与匹配ORB、SIFT同台竞技怎么选6.1 特征点到底特征在哪里边缘检测关注的是整条线特征点检测关注的是图像中独特且容易被再次找到的局部位置。一个典型的特征点通常是角点、纹理丰富的区域中心、或者在光照变化下依然稳定的图像块。特征点检测算法的输出一般包括三样东西特征点的位置x, y、描述子一个向量用来描述这个点周围区域的纹理特征、以及匹配时需要的距离度量。描述子的意义在于——当你需要判断两张图中的两个点是否对应同一位置时只需比较它们的描述子是否足够相似。6.2 ORB vs SIFT授权问题之外性能差异才是关键SIFT尺度不变特征变换在特征点检测里的地位不用多说它能在图像缩放、旋转、光照变化下保持很强的稳定性。但它有一个不少人都知道的痛点SIFT算法是有专利限制的在商业应用中需要授权。ORBOriented FAST and Rotated BRIEF就是为了解决这个问题而生的完全开源免费。但除了授权问题速度差异也是关键。我之前在一个实时图像匹配的小项目里对比过算法耗时毫秒/帧匹配稳定性适用场景ORB15-30中等实时预览、嵌入式设备、快速匹配SIFT80-200高离线匹配、高精度拼接、物体识别从表格能直观看出ORB速度优势极大SIFT稳定性优势极大。如果你做的是实时视频帧匹配ORB是首选如果做的是图像拼接这类对精度要求极高的离线任务SIFT更稳妥。# ORB特征提取与匹配 orb cv2.ORB_create(nfeatures500) kp1, des1 orb.detectAndCompute(img1, None) kp2, des2 orb.detectAndCompute(img2, None) bf cv2.BFMatcher(cv2.NORM_HAMMING, crossCheckTrue) matches bf.match(des1, des2) matches sorted(matches, keylambda x: x.distance) # 过滤匹配取距离最近的前N个 good_matches matches[:40]注意ORB的描述子距离用cv2.NORM_HAMMING汉明距离因为ORB生成的是二进制描述子SIFT用cv2.NORM_L2欧式距离。这个细节很多人会忽略——匹配距离度量没用对结果可能完全没法看。6.3 用单应性矩阵过滤误匹配让结果稳下来的关键一步特征匹配的结果里总会有错误匹配尤其是当两张图存在大量相似纹理时。一个非常实用的过滤思路是用cv2.findHomography配合RANSAC算法在匹配点中找出一个能解释大多数匹配的透视变换矩阵而那些不符合这个变换的匹配点就是离群点可以被剔除。# 从匹配点中提取坐标 src_pts np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) dst_pts np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) # RANSAC 计算单应性矩阵并过滤误匹配 M, mask cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) matches_mask mask.ravel().tolist()RANSAC的第二个参数是重投影误差阈值默认我习惯设5.0。这个值越小要求匹配点越精确过滤越严太严会丢掉有效匹配太松又会混入误匹配。这是一个需要根据匹配质量动态调整的平衡参数我第一次用时调了不下十次才找到合适的值。7. 阈值分割的进阶之路全局阈值、自适应阈值与Otsu方法7.1 阈值分割的三个层次对应三种图像条件阈值分割的目标是把灰度图变成二值图也就是前景vs背景。最简单的是全局固定阈值——整张图用一个固定的t值大于t的设为白色小于t的设为黑色。全局阈值对光照均匀的图像有效但一旦遇到光线从左到右渐变、或者局部有阴影的图像就会出问题。这时候需要用自适应阈值cv2.adaptiveThreshold它对每个像素都单独计算一个阈值依据是它周围一个小窗口内的像素的加权平均值或高斯加权和。这样能自动适配局部的亮度变化。第三个层次是Otsu方法cv2.THRESH_OTSU。它不是另一种阈值而是自动帮你选最优阈值的方法。它的原理是用统计方法找到一个阈值使得前景和背景两类的类内方差最小、类间方差最大。简单说它是全局阈值的最优解。# Otsu自动阈值 ret, thresh_otsu cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 自适应阈值 adaptive_thresh cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2 )这里有两个参数需要重点解释。blockSize决定计算每个像素阈值时使用的邻域大小必须为奇数一般从11到15之间开始试C是一个常数表示最终阈值用邻域均值减去这个常数C越大二值化后保留的白色像素越少。7.2 三种方法怎么选给出我的决策流程我的经验是拿到灰度图后先试全局阈值看一下整体情况如果前景和背景对比足够强直接Otsu就行。如果有明显的渐变阴影用自适应阈值。如果两种情况混合存在比如一张图里既有强光区又有暗角优先考虑先做直方图均衡化再用Otsu均衡化能把亮度分布拉平让Otsu的统计效果更好。我自己在实际测试中遇到过一个有意思的情况一张扫描文档背景是浅黄色文字是深灰色光照均匀但背景有纹路。Otsu全局阈值的效果不太理想因为纹路的灰度值和文字差距不够大。用了自适应阈值之后因为它在局部计算阈值背景纹路被完全忽略了只留下了文字轮廓。自适应阈值在处理背景不均匀但前景是局部深色结构的场景时优势非常明显。7.3 阈值分割后的遗留问题形态学操作的最好时机阈值分割得到的二值图通常不是干净的会有孤立的白色噪点、前景内部的空洞、以及边缘的毛刺。这就是上一章形态学操作最好的应用时机。我几乎所有的二值图流水线都长这样自适应阈值 → 闭运算补内部空洞 → 开运算去外部噪点 → 找轮廓 → 面积/几何筛选。这一套组合拳能应对绝大多数图像分割项目的前期准备。8. 实际项目中的算法组合思路与调参心法8.1 一个典型的车牌识别预处理流程拆解学了这么多单个算法最终要落到组合使用上。我以车牌定位这个经典场景为例把整个流程串起来讲一遍。这个例子非常典型因为它几乎用到了前面所有的知识点。假设输入是一张包含车牌的整车照片光照不均匀、背景有各种干扰。完整流程如下读取并灰度化丢颜色信息减少计算量。高斯滤波核取(5,5)去掉传感器噪声。形态学梯度用梯度提取边缘信息突出车牌区域因为车牌区域字符密集边缘梯度特别强。二值化Otsu或固定阈值把梯度图变成前景背景清晰的黑白图。形态学闭运算把车牌的字符之间断开的边缘连成一个大块方便后续轮廓检测找到完整区域。轮廓检测RETR_EXTERNAL提取外轮廓用面积和宽高比筛掉不是车牌的轮廓。透视变换校正用cv2.getPerspectiveTransform把倾斜的车牌拉正。这个流程每一步都不是单纯的套函数而是基于对图像的理解逐步缩小目标范围。形态学梯度在第三步出现、闭运算在第五步出现这个顺序是不可颠倒的——梯度先高亮出边缘密集的区域闭运算再把这些区域融合成完整块如果反了高亮效果会被闭运算直接抹掉。8.2 调参心法一次只动一个参数永远记录基线效果调参可能是图像处理项目中最耗时的部分也是最容易劝退新手的一个环节。我总结了一套简单但极其有效的流程固定输入拿同一张代表性图片做测试不要每次换图。项目里挑3到5张能代表正常情况、困难情况、极端情况的图片作为固定测试集。设定基线第一次跑完流程后把当前参数和输出结果截图保存作为baseline。一次只动一个参数比如先调Canny的threshold1跑一次对比baseline记录效果。再动threshold2继续对比。用中间结果辅助判断不要只看最终输出。在预处理流程里插入中间结果的显示比如显示二值图、显示轮廓图能快速定位是哪个环节出了问题。我早期调参时最常犯的错误是想快速调好结果就在代码里同时改了三个参数结果效果变好了也说不清是哪一个的功劳效果变差更是无法回溯。一次只动一个参数这条纪律看起来慢实际是最快的路径。8.3 性能优化当你的算法跑在实时视频流上如果你的场景是实时视频处理比如一帧一帧地处理摄像头输入性能优化就是个绕不开的话题。我常用的优化手段按性价比排序降分辨率把处理帧的分辨率降到处理需求的最低要求这是最简单、效果最显著的优化。调整核大小高斯滤波(3,3)比(5,5)快不少在视频场景下先用(3,3)试不够再加。跳过非关键帧有些流程不需要每帧都处理比如每隔3帧做一次完整检测中间帧用上一帧的结果插值。ROI感兴趣区域提取只在画面的一小部分区域做复杂度高的算法。比如监控画面里目标只出现在门附近就只用门位置的ROI。这些优化手段叠加起来把原来处理一帧需要200毫秒的流程压缩到30毫秒以内是可以做到的。我在一个模拟的实时目标检测demo里通过降低分辨率加缩小ROI把处理延迟从肉眼可见卡顿压到了流畅预览。9. 写在最后的复用经验把算法沉淀成你自己的工具箱图像处理的项目经验最划算的沉淀方式不是写一堆文档而是把常用流程封装成可复用的函数和类。我自己的习惯是维护一个工具箱脚本把灰度化滤波二值化、轮廓提取面积筛选、特征匹配RANSAC过滤这三类最常见流程各封装成一个函数。每接到新项目先从这个工具箱取现成的中间模块再针对项目特点做参数调整和少量新代码开发效率能快好几倍。封装时要特别注意接口参数要暴露业务含义而不是数学含义。比如封装二值化函数时与其让调用者传blockSize11和C2这种抽象参数不如直接定义成threshold_modeadaptive_light、sensitivitymedium这种业务语义。这样三个月后回头用的时候你不需要重新理解数学含义只需要知道当前场景用什么档位这个习惯帮我省了大量的回想时间。图像处理的入门门槛不在API而在对每一个算法为什么要存在的理解。灰度和滤波是为了让信息更纯粹边缘检测和阈值分割是为了挖出结构形态学是为了修整形状轮廓和特征点是为了把结构变成可计算的量。把这条主线理清楚后新算法对你来说不过是又一个解决特定问题的工具需要时查文档、看参数、调效果就够了。