linux opencv人脸检测,OpenCV实现人脸检测例程

2023-10-27

前段时间看的OpenCV,其实有很多的例子程序,参考代码值得我们学习,对图像特征提取三大法宝:HOG特征,LBP特征,Haar特征有一定了解后。

对本文中的例子程序刚开始没有调通,今晚上调通了,试了试效果还可以,还需要深入理解。值得大家动手试试,还是很有成就感的,虽然是现成的例子.......

环境:OpenCV3.1+VS2013+WIN10

/*!

* \file Capture.cpp

*

* \author ranjiewen

* \date 十一月 2016

*http://www.linuxidc.com/Linux/2016-11/137099.htm

解析opencv自带人脸识别源码(……/opencv-3.1.0/samples/cpp/facedetect.cpp)

*/

#include "opencv2/objdetect.hpp"

#include "opencv2/highgui.hpp"

#include "opencv2/imgproc.hpp"

#include

using namespace std;

using namespace cv;

static void help()

{

cout << "\nThis program demonstrates the cascade recognizer. Now you can use Haar or LBP features.\n"

"This classifier can recognize many kinds of rigid objects, once the appropriate classifier is trained.\n"

"It's most known use is for faces.\n"

"Usage:\n"

"./facedetect [--cascade= this is the primary trained classifier such as frontal face]\n"

"  [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]\n"

"  [--scale=]\n"

"  [--try-flip]\n"

"  [filename|camera_index]\n\n"

"see facedetect.cmd for one call:\n"

"./facedetect --cascade=\"../../data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml\" --scale=1.3\n\n"

"During execution:\n\tHit any key to quit.\n"

"\tUsing OpenCV version " << CV_VERSION << "\n" << endl;

}

void detectAndDraw(Mat& img, CascadeClassifier& cascade,

CascadeClassifier& nestedCascade,

double scale, bool tryflip);

string cascadeName;

string nestedCascadeName;

int main(int argc, const char** argv)

{

VideoCapture capture;

Mat frame, image;

string inputName;

bool tryflip;

// CascadeClassifier是Opencv中做人脸检测的时候的一个级联分类器,现在有两种选择:一是使用老版本的CvHaarClassifierCascade函数,一是使用新版本的CascadeClassifier类。老版本的分类器只支持类Haar特征,而新版本的分类器既可以使用Haar,也可以使用LBP特征。

CascadeClassifier cascade, nestedCascade;

double scale;

cv::CommandLineParser parser(argc, argv,

"{help h||}"

"{cascade|D:/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml|}"  //默认路径实在安装路径下sample,修改了路径,以便加载load成功

"{nested-cascade|D:/opencv/sources/data/haarcascades/haarcascade_eye_tree_eyeglasses.xml|}"  //修改路径

"{scale|1|}{try-flip||}{@filename||}" //文件为空时,设置摄像头,实时检测人脸

);

if (parser.has("help"))

{

help();

return 0;

}

cascadeName = parser.get("cascade");

nestedCascadeName = parser.get("nested-cascade");

scale = parser.get("scale");

if (scale < 1)

scale = 1;

tryflip = parser.has("try-flip");

inputName = parser.get("@filename");

std::cout << inputName << std::endl;  // test

if (!parser.check())

{

parser.printErrors();

return 0;

}

// 加载模型

if (!nestedCascade.load(nestedCascadeName))

cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;

if (!cascade.load(cascadeName))

{

cerr << "ERROR: Could not load classifier cascade" << endl;

help();

return -1;

}

// 读取摄像头

// isdigit检测字符是否为阿拉伯数字

if (inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1))

{

int c = inputName.empty() ? 0 : inputName[0] - '0';

// 此处若系统在虚拟机上,需在虚拟机中设置接管摄像头:虚拟机(M)-> 可移动设备 -> 摄像头名称 -> 连接(断开与主机连接)

if (!capture.open(c))

cout << "Capture from camera #" << c << " didn't work" << endl;

else {

capture.set(CV_CAP_PROP_FRAME_WIDTH, 640);

capture.set(CV_CAP_PROP_FRAME_HEIGHT, 480);

}

}

else if (inputName.size())

{

image = imread(inputName, 1);

if (image.empty())

{

if (!capture.open(inputName))

cout << "Could not read " << inputName << endl;

}

}

else

{

image = imread("../data/lena.jpg", 1);

if (image.empty()) cout << "Couldn't read ../data/lena.jpg" << endl;

}

if (capture.isOpened())

{

cout << "Video capturing has been started ..." << endl;

for (;;)

{

std::cout << "capturing..." << std::endl;  // test

capture >> frame;

if (frame.empty())

break;

Mat frame1 = frame.clone();

std::cout << "Start to detect..." << std::endl;  // test

detectAndDraw(frame1, cascade, nestedCascade, scale, tryflip);

int c = waitKey(10);

if (c == 27 || c == 'q' || c == 'Q')

break;

}

}

else

{

cout << "Detecting face(s) in " << inputName << endl;

if (!image.empty())

{

detectAndDraw(image, cascade, nestedCascade, scale, tryflip);

waitKey(0);

}

else if (!inputName.empty())

{

/* assume it is a text file containing the

list of the image filenames to be processed - one per line */

FILE* f = fopen(inputName.c_str(), "rt");

if (f)

{

char buf[1000 + 1];

while (fgets(buf, 1000, f))

{

int len = (int)strlen(buf), c;

while (len > 0 && isspace(buf[len - 1]))

len--;

buf[len] = '\0';

cout << "file " << buf << endl;

image = imread(buf, 1);

if (!image.empty())

{

detectAndDraw(image, cascade, nestedCascade, scale, tryflip);

c = waitKey(0);

if (c == 27 || c == 'q' || c == 'Q')

break;

}

else

{

cerr << "Aw snap, couldn't read image " << buf << endl;

}

}

fclose(f);

}

}

}

return 0;

}

void detectAndDraw(Mat& img, CascadeClassifier& cascade,

CascadeClassifier& nestedCascade,

double scale, bool tryflip)

{

double t = 0;

vector faces, faces2;

const static Scalar colors[] =

{

Scalar(255, 0, 0),

Scalar(255, 128, 0),

Scalar(255, 255, 0),

Scalar(0, 255, 0),

Scalar(0, 128, 255),

Scalar(0, 255, 255),

Scalar(0, 0, 255),

Scalar(255, 0, 255)

};

Mat gray, smallImg;

cvtColor(img, gray, COLOR_BGR2GRAY);

double fx = 1 / scale;

resize(gray, smallImg, Size(), fx, fx, INTER_LINEAR);

equalizeHist(smallImg, smallImg);

t = (double)cvGetTickCount();

cascade.detectMultiScale(smallImg, faces,

1.1, 2, 0

//|CASCADE_FIND_BIGGEST_OBJECT

//|CASCADE_DO_ROUGH_SEARCH

| CASCADE_SCALE_IMAGE,

Size(30, 30));

if (tryflip)

{

flip(smallImg, smallImg, 1);

cascade.detectMultiScale(smallImg, faces2,

1.1, 2, 0

//|CASCADE_FIND_BIGGEST_OBJECT

//|CASCADE_DO_ROUGH_SEARCH

| CASCADE_SCALE_IMAGE,

Size(30, 30));

for (vector::const_iterator r = faces2.begin(); r != faces2.end(); r++)

{

faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));

}

}

t = (double)cvGetTickCount() - t;

printf("detection time = %g ms\n", t / ((double)cvGetTickFrequency()*1000.));

for (size_t i = 0; i < faces.size(); i++)

{

Rect r = faces[i];

Mat smallImgROI;

vector nestedObjects;

Point center;

Scalar color = colors[i % 8];

int radius;

double aspect_ratio = (double)r.width / r.height;

if (0.75 < aspect_ratio && aspect_ratio < 1.3)

{

center.x = cvRound((r.x + r.width*0.5)*scale);

center.y = cvRound((r.y + r.height*0.5)*scale);

radius = cvRound((r.width + r.height)*0.25*scale);

circle(img, center, radius, color, 3, 8, 0);

}

else

rectangle(img, cvPoint(cvRound(r.x*scale), cvRound(r.y*scale)),

cvPoint(cvRound((r.x + r.width - 1)*scale), cvRound((r.y + r.height - 1)*scale)),

color, 3, 8, 0);

if (nestedCascade.empty())

continue;

smallImgROI = smallImg(r);

nestedCascade.detectMultiScale(smallImgROI, nestedObjects,

1.1, 2, 0

//|CASCADE_FIND_BIGGEST_OBJECT

//|CASCADE_DO_ROUGH_SEARCH

//|CASCADE_DO_CANNY_PRUNING

| CASCADE_SCALE_IMAGE,

Size(30, 30));

for (size_t j = 0; j < nestedObjects.size(); j++)

{

Rect nr = nestedObjects[j];

center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);

center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);

radius = cvRound((nr.width + nr.height)*0.25*scale);

circle(img, center, radius, color, 3, 8, 0);

}

}

imshow("result", img);

}

/*****************************************************

* \file Capture.cpp

* \date 2016/11/10 0:22

* \author ranjiewen

* \contact: ranjiewen@outlook.com

* \问题描述:http://www.linuxidc.com/Linux/2016-11/137103.htm

* \问题分析:

可以存avi,但是不能打开,待改善

*****************************************************/

//#include

//#include

//using namespace cv;;

//using namespace std;

//int main()

//{

//    CvCapture* capture = cvCaptureFromCAM(-1);

//    CvVideoWriter* video = NULL;

//    IplImage* frame = NULL;

//    int n;

//    if (!capture) //如果不能打开摄像头给出警告

//    {

//        cout << "Can not open the camera." << endl;

//        return -1;

//    }

//    else

//    {

//        frame = cvQueryFrame(capture); //首先取得摄像头中的一帧

//        video = cvCreateVideoWriter("camera.avi", CV_FOURCC('X', 'V', 'I', 'D'), 25,

//            cvSize(frame->width, frame->height)); //创建CvVideoWriter对象并分配空间

//        //保存的文件名为camera.avi,编码要在运行程序时选择,大小就是摄像头视频的大小,帧频率是32

//        if (video) //如果能创建CvVideoWriter对象则表明成功

//        {

//            cout << "VideoWriter has created." << endl;

//        }

//

//        cvNamedWindow("Camera Video", 1); //新建一个窗口

//        int i = 0;

//        while (i <= 300) // 让它循环200次自动停止录取

//        {

//            frame = cvQueryFrame(capture); //从CvCapture中获得一帧

//            if (!frame)

//            {

//                cout << "Can not get frame from the capture." << endl;

//                break;

//            }

//            n = cvWriteFrame(video, frame); //判断是否写入成功,如果返回的是1,表示写入成功

//            cout << n << endl;

//            cvShowImage("Camera Video", frame); //显示视频内容的图片

//            i++;

//            if (cvWaitKey(2) > 0)

//                break; //有其他键盘响应,则退出

//        }

//

//        cvReleaseVideoWriter(&video);

//        cvReleaseCapture(&capture);

//        cvDestroyWindow("Camera Video");

//    }

//    return 0;

//}

这是调用摄像头动态检测人脸的程序,实验结果:

0deb0057d14a5a5685a199afa94b1ae5.png

f09064aa44a45da3e81de320ad57a97e.png

控制台输出:

***** VIDEOINPUT LIBRARY - 0.1995 - TFW07 *****

SETUP: Setting up device 0

SETUP: Integrated Camera

SETUP: Couldn't find preview pin using SmartTee

SETUP: Default Format is set to 640x480

SETUP: trying specified format RGB24 @ 640x480

SETUP: trying format RGB24 @ 640x480

SETUP: trying format RGB32 @ 640x480

SETUP: trying format RGB555 @ 640x480

SETUP: trying format RGB565 @ 640x480

SETUP: trying format YUY2 @ 640x480

SETUP: Capture callback set

SETUP: Device is setup and ready to capture.

Video capturing has been started ...

capturing...

Start to detect...

detection time = 486.754 ms

capturing...

Start to detect...

detection time = 444.236 ms

capturing...

Start to detect...

detection time = 441.649 ms

capturing...

Start to detect...

detection time = 447.361 ms

capturing...

Start to detect...

detection time = 427.589 ms

capturing...

Start to detect...

detection time = 453.187 ms

capturing...

Start to detect...

d

OpenCV的详细介绍:请点这里

OpenCV的下载地址:请点这里

0b1331709591d260c1c78e86d0c51c18.png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux opencv人脸检测,OpenCV实现人脸检测例程 的相关文章

  • curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to raw.githubusercontent.com:443

    MacOS系统使用 Homebrew 官方地址时 报错 Mac安装homebrew的时候报错 Mac bin bash c curl fsSL https raw githubusercontent com Homebrew install
  • 自定义openwrt的配置界面:luci进阶之路

    5 20 晴 今天的太阳挺大的 晒得我进园区直接37 3警告 于是我百度搜索微信朋友圈怎么关闭 才把温度稳定下来 算了算了 上班说事 由于公司部门调动 逐渐接触到新的知识 新的模块 还听说这玩意比较冷门 没办法 该搞还是得搞 又不是搞不了
  • 渗透测试技术----常见web漏洞--命令执行原理及防御

    一 命令执行漏洞介绍 1 命令执行漏洞简介 命令执行漏洞时指服务器没有对执行的命令进行过滤 用户可以随意执行系统命令 命令执行漏洞属于高危漏洞之一 也属于代码执行的范围内 2 命令执行漏洞的原理 应用程序有时需要调用一些执行系统命令的函数
  • 嵌入式AI助力当代商业的发展

    数字化转型的业务影响是广泛的 但购买者应寻求嵌入式AI在以下领域具有最大的影响力 1 业务流程和任务的自动化 当买家搜索购买包含AI的软件时 他们应该研究该解决方案为员工自动执行日常任务的方式 嵌入式AI应该节省员工的时间和精力 以便他们可
  • 华为文稿演示服务器操作异常修复,修复服务器

    修复服务器 内容精选 换一换 安装Agent插件后 修复插件配置为用户提供了一键配置AK SK RegionID ProjectId的功能 省去了繁琐的手动配置步骤 提升配置效率 目前大部分区域已上线一键式授予该区域插件权限功能 即自动修复
  • java编码 第一次

    这是java的快速入门 演示java的开发步骤 对代码的相关说明 1 public class Hello 表示Hello是一个类 是一个public公有的类 2 Hello 表示一个类的开始和结束 3 public static void
  • java循环while之等差数列均值_java基础_while 循环语句的定义及用法

    一 while 循环语句的定义 在 C 语言中 while 循环是除了 for 循环外最常用的循环语句 相对于 for 循环而言 while 循环更多地应用于循环次数未定的循环控制中 while 循环的一般表达形式为 while 表达式 循
  • 色温

    色温是表示光线中包含颜色成分的一个计量单位 从理论上说 黑体温度指绝对黑体从绝对零度 273 开始加温后所呈现的颜色 黑体在受热后 逐渐由黑变红 转黄 发白 最后发出蓝色光 当加热到一定的温度 黑体发出的光所含的光谱成分 就称为这一温度下的
  • 线程池OOM错误

    1 LinkedBlockingQueue报错 package com spring pro threadpool completableFuture youhua test import java util concurrent Exec
  • 【场景】大数据常考场景题 - Bitmap

    大数据开发面试通常会问场景题 主要考察大数据中常用的数据结构 比如 Bitmap Bloom Filter 等等 今天就说一个工作中碰到的 比如昨天说到的问题 用户要在自定义时间区间内查询 就需要快速响应 可能用到 ClickHouse 可
  • Ubuntu 下安装 apt-get install npm

    步骤一 sudo apt get remove nodejs npm 删除原来的 sudo apt get install curl curl sL https deb nodesource com setup sudo bash sudo
  • 第十一届蓝桥杯国赛 奇偶覆盖

    我的主要思路是找到每个矩形内的单位方格 再使用set集合将所有方格统计 最后在set集合里遍历每一个方格 统计其再矩形里出现的次数 即为面积 再蓝桥刷题系统上只能过30 有优化方案的欢迎私信讨论 矩形类 class The it def i
  • Angular4 使用GET向后端请求数据

    Angular4 使用GET向后端请求数据 在工程的src app app module ts的头部引入http jsonp import HttpModule JsonpModule from angular http 在工程的src a
  • 线程池实例

    public Executor asyncServiceExecutor ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor 配置核心线程数 executor setCore
  • 日常Linux操作命令使用手册

    OS 查看操作系统 cat etc issue GPU 查看GPU 利用率 nvidia smi 文件和空间 查看linux文件目录的大小和文件夹包含的文件数 查看磁盘使用情况 df h 统计总数大小 du sh xmldb du sm s
  • Java: 断言(assert)

    断言 assert 语句一般用于程序不准备通过捕获异常来处理的错误 例如 账号交易时 对于支出的金额为正数 收入 支出 纯收入 或者收入的金额为负数 程序必须立即停止执行 同时发现错误 当正式收益时 就可以避开错误 但仍保留断言语句在程序中
  • 6.28同花顺笔试

    笔试共22题 题型比较多 选择 简答 编程 开放逻辑题都有 笔试时长2h 对深度学习方面考察的比较多 包含性能评价指标 常见定义等 选择题与简答题 1 提高深层神经网络特征表达能力的方法 题目问的下列哪个方法无法用来提高 a 增加层深度 b
  • 微信小程序遇到does not have a method “xxxx“ to handle event “tap“

    刚开始学小程序的小白 学到事件绑定的时候 跟着老师的步骤一步一步把代码输入好 结果发现和老师的结果不大一样 我在网上找了很多方法 有说多打空格的 有说要写进method 里的 都试过 没用 结果意外之下 我随手取消勾选 将JS编译成ES5
  • 2022.08 VMware官网下载安装+配置Linux虚拟机,最新最全

    前言 文章概要叙述 步骤一 安装VMware VMware 安装包下载 VMware官方下载页面 1 1小节 步骤二 配置虚拟机 步骤三 安装操作系统 CentOS7 9 镜像文件下载 CentOS阿里云镜像下载页 2 2 1小节 一 VM

随机推荐

  • UE4 获取目录下所有的图片转换成Texture2D并通过UMG显示出来

    主要内容 本文主要讲解简单插件的编写 将类继承至Subsystem 子系统 框架的好处 鼠标射线检测以及鼠标拖拽功能的实现 实现步骤 一 编写插件 1 首先新建一个空的C 项目 然后新建插件 Author是作者名 Description是描
  • 《Linux入门与基础》课程教案

    Linux入门与基础 linux的目录结构 打开终端 输入ls查看linux根目录下的情况 ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sy
  • 常用电路设计之分频器的设计

    引言 分频器在实际数字电路设计中是最基础的 也是最重要的 常见的分频器主要有偶数倍分频器 奇数倍分频器 半整数倍分频器 任意小数倍分频器等 本文主要对最常用的偶数倍分频器和奇数倍分频器展开介绍 1 偶数倍分频器 偶数倍分频器通过计数器可以很
  • uniapp自定义tabbar,中间凸起(支持H5、微信小程序)

    最近公司需要做一款app 需要中间按钮凸起 在网上找了一些 参考文献 做了一个demo H5效果图如下 小程序效果图如下 目录结构如下 page json的配置如下 pages path pages index index style na
  • 面试:字符串: 提取IP地址

    题目 给定一个只含数字的字符串 返回所有合法的ip地址 算法 ip地址是4段 每段三个情况 1 只取一个数字 2 取两个数字 第一个数字不是0 3 取三个数字 第一个数字不是0 而且三个数组成的数小于256 递归 import java u
  • React前端渲染优化--父组件导致子组件重复渲染的问题

    1 适用场景 当父组件本身状态更新时 这个状态并没有作为props传给子组件 也会触发子组件更新机制 这样就造成了组件重复渲染的问题 2 解决 类组件 pureComponent 它是一个类 组件继承自它后 其作为子组件时 每次父组件更新后
  • 数据库视图、触发器

    1 delete和truncate区别 在有自动增长的前提上 用delete清空表后再插入数据 id会从被删除的最大的id 1开始 用 truncate清空表后再插入数据 id会从起始值开始 2 索引 唯一索引 已存在表 alter tab
  • 【C++简明教程】找数组或者Vector中最大最小值的索引

    导言 今天带来的程序是找出数组或者 Vector 中最大最小值的索引 在 Python 中 我们可以使用 numpy 库快速实现 那接下来就看看 C 是怎么实现的吧 主要使用到的函数是 max element 和 min element 基
  • 小程序自定义头部,返回按钮,指定返回某页面

    function navback backUrl if Taro getCurrentPages length lt 1 Taro switchTab url pages home home return if backUrl let Pa
  • 多线程的作用

    1 发挥多核CPU的优势 随着工业的进步 现在的笔记本 台式机乃至商用的应用服务器至少也都是双核的 4核 8核甚至16核的也都不少见 如果是单线程的程序 那么在双核CPU上就浪费了50 在4核CPU上就浪费了75 单核CPU上所谓的 多线程
  • 高性能Mysql——创建高性能的索引

    文章目录 索引类型 高性能的索引策略 独立的列 前缀索引和索引选择性 多列索引 覆盖索引 使用索引扫描来做排序 重复索引和冗余索引 索引优化 MRR ICP 索引类型 我们知道 索引的实现有很多种 在Mysql中 索引是在引擎中实现的 所以
  • Spring 框架基础(04):AOP切面编程概念,几种实现方式演示

    一 AOP基础简介 1 切面编程简介 AOP全称 Aspect Oriented Programming 面向切面编程 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术 核心作用 可以对业务逻辑的各个部分进行隔离 从而使得业务
  • win10远程桌面连接服务器接示内部错误。

    搜出来的解决方案大多是让重置远程设置 这也是微软官方的解决方 案 如下 搜索框中输入 CMD 右键点击命令提示符 选择以管理员身份运行 在管理员运行的命令提示符窗口中执行以下的命令 netsh winsoc reset 奇怪的是我的电脑 重
  • GPT:通用预训练语言模型

    论文标题 Improving Language Understanding by Generative Pre Training 论文链接 https www cs ubc ca amuham01 LING530 papers radfor
  • linux下使用qt ,出现Unable to create a debugging engine问题的解决方法

    在进行调试 qt程序的时 出现了问题 Unable to create a debugging engine 根据百度经验 查看Kits下面的调试工具是否存在 如下图 我的调试工具是存在的 那么不能成功体调试的原因是什么呢 一个stack
  • MySQL之count(1)和count(*)的区别

    闲扯 很久以前 有一次我写了一个SQL select count from test 然后这个代码被我的其中一家公司的MySQL专家看到了 叫我过去说 你难道不知道咱们不允许写count 吗 你不知道count 1 更快吗 说完二话没说把我
  • jupyter修改默认工作路径

    修改Anaconda自动安装的jupyter notebook的默认工作路径 Anaconda默认安装的jupyter的工作路径是C Users 用户名 想要修改其默认的工作路径具体步骤如下 首先打开 Anaconda Prompt 在 A
  • 【react】react18的学习(十二)– 底层原理(二)之 迭代器 iterator

    迭代器iterator 是一种 ES6 规范 具有这种机制的数据结构才可以使用for of循环 返回每一项的值 原型链具有Symbol iterator属性的数据结构都具备 如数组 部分类数组 字符串等 普通对象就不能用 for of循环原
  • 【Echarts】配置项归纳

    Echarts 配置项归纳 一 title 二 legend 三 grid 四 xAxis yAxis 五 polar 六 radiusAxis 七 angleAxis 八 radar 九 dataZoom 1 内置型数据区域缩放组件 2
  • linux opencv人脸检测,OpenCV实现人脸检测例程

    前段时间看的OpenCV 其实有很多的例子程序 参考代码值得我们学习 对图像特征提取三大法宝 HOG特征 LBP特征 Haar特征有一定了解后 对本文中的例子程序刚开始没有调通 今晚上调通了 试了试效果还可以 还需要深入理解 值得大家动手试