opencv学习 特征提取
内容来源于《opencv4应用开发入门、进阶与工程化实践》
图像金字塔
略
拉普拉斯金字塔
对输入图像进行reduce操作会生成不同分辨率的图像,对这些图像进行expand操作,然后使用reduce减去expand之后的结果,就会得到拉普拉斯金字塔图像。
详情可查看https://zhuanlan.zhihu.com/p/80362140

图像金字塔融合
拉普拉斯金字塔通过源图像减去先缩小再放大的图像构成,保留的是残差,为图像还原做准备。
根据拉普拉斯金字塔的定义可以知道,拉普拉斯金字塔的每一层都是一个高斯差分图像。:
原图 = 拉普拉斯金字塔图L0层 + expand(高斯金字塔G1层),也就是说,可以基于低分辨率的图像与它的高斯差分图像,重建生成一个高分辨率的图像。
详情参考https://zhuanlan.zhihu.com/p/454085730的图像融合部分,讲的很好。
步骤:
- 生成苹果、橘子的高斯金字塔
和
- 求苹果、橘子的的拉普拉斯金字塔
和
- 求mask的高斯金字塔
- 在每个尺度(分辨率)下,用
拼接
和
,最终得到拼接的拉普拉斯金字塔
- 生成最低分辨率的起始图(都选取最低分辨率下的
和
根据同分辨率下
进行拼接,得到最低分辨率下的拼接结果
- 从
开始,利用
得到最高分辨率的拼接结果
示例代码:
int level = 3;
Mat smallestLevel;
Mat blend(Mat &a, Mat &b, Mat &m) {int width = a.cols;int height = a.rows;Mat dst = Mat::zeros(a.size(), a.type());Vec3b rgb1;Vec3b rgb2;int r1 = 0, g1 = 0, b1 = 0;int r2 = 0, g2 = 0, b2 = 0;int red = 0, green = 0, blue = 0;int w = 0;float w1 = 0, w2 = 0;for (int row = 0; row<height; row++) {for (int col = 0; col<width; col++) {rgb1 = a.at<Vec3b>(row, col);rgb2 = b.at<Vec3b>(row, col);w = m.at<uchar>(row, col);w2 = w / 255.0f;w1 = 1.0f - w2;b1 = rgb1[0] & 0xff;g1 = rgb1[1] & 0xff;r1 = rgb1[2] & 0xff;b2 = rgb2[0] & 0xff;g2 = rgb2[1] & 0xff;r2 = rgb2[2] & 0xff;red = (int)(r1*w1 + r2*w2);green = (int)(g1*w1 + g2*w2);blue = (int)(b1*w1 + b2*w2);// outputdst.at<Vec3b>(row, col)[0] = blue;dst.at<Vec3b>(row, col)[1] = green;dst.at<Vec3b>(row, col)[2] = red;}}return dst;
}vector<Mat> buildGaussianPyramid(Mat &image) {vector<Mat> pyramid;Mat copy = image.clone();pyramid.push_back(image.clone());Mat dst;for (int i = 0; i<level; i++) {pyrDown(copy, dst, Size(copy.cols / 2, copy.rows / 2));dst.copyTo(copy);pyramid.push_back(dst.clone());}smallestLevel = dst;return pyramid;
}vector<Mat> buildLapacianPyramid(Mat &image) {vector<Mat> lp;Mat temp;Mat copy = image.clone();Mat dst;for (int i = 0; i<level; i++) {pyrDown(copy, dst, Size(copy.cols / 2, copy.rows / 2));pyrUp(dst, temp, copy.size());Mat lapaian;subtract(copy, temp, lapaian);lp.push_back(lapaian);copy = dst.clone();}smallestLevel = dst;return lp;
}
void FeatureVectorOps::pyramid_blend_demo(Mat &apple, Mat &orange) {Mat mc = imread("D:/images/mask.png");if (apple.empty() || orange.empty()) {return;}imshow("苹果图像", apple);imshow("橘子图像", orange);vector<Mat> la = buildLapacianPyramid(apple);Mat leftsmallestLevel;smallestLevel.copyTo(leftsmallestLevel);vector<Mat> lb = buildLapacianPyramid(orange);Mat rightsmallestLevel;smallestLevel.copyTo(rightsmallestLevel);Mat mask;cvtColor(mc, mask, COLOR_BGR2GRAY);vector<Mat> maskPyramid = buildGaussianPyramid(mask);Mat samllmask;smallestLevel.copyTo(samllmask);Mat currentImage = blend(leftsmallestLevel, rightsmallestLevel, samllmask);imwrite("D:/samll.png", currentImage);// 重建拉普拉斯金字塔vector<Mat> ls;for (int i = 0; i<level; i++) {Mat a = la[i];Mat b = lb[i];Mat m = maskPyramid[i];ls.push_back(blend(a, b, m));}// 重建原图Mat temp;for (int i = level - 1; i >= 0; i--) {pyrUp(currentImage, temp, ls[i].size());add(temp, ls[i], currentImage);}imshow("高斯金子图像融合重建-图像", currentImage);
}
Harris角点检测
角点是图像中亮度变化最强的地方,反映了图像的本质特征。
图像的角点在各个方向上都有很强的梯度变化。
亚像素级别的角点检测
详细请参考https://www.cnblogs.com/qq21497936/p/13096048.html
大概理解是角点一般在边缘上,边缘的梯度与沿边缘方向的的向量正交,也就是内积为0,根据内积为零,角点周围能列出一个方程组,方程组的解就是角点坐标。
opencv亚像素级别定位函数API:
void cv::cornerSubPix(InputArray imageInputOutputArray corners //输入整数角点坐标,输出浮点数角点坐标Size winSize //搜索窗口Size zeroZone TermCriteria criteria //停止条件
)
示例代码
void FeatureVectorOps::corners_sub_pixels_demo(Mat &image) {Mat gray;cvtColor(image, gray, COLOR_BGR2GRAY);int maxCorners = 400;double qualityLevel = 0.01;std::vector<Point2f> corners;goodFeaturesToTrack(gray, corners, maxCorners, qualityLevel, 5, Mat(), 3, false, 0.04);Size winSize = Size(5, 5);Size zeroZone = Size(-1, -1);//opencv迭代终止条件类TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.001);cornerSubPix(gray, corners, winSize, zeroZone, criteria);for (size_t t = 0; t < corners.size(); t++) {printf("refined Corner: %d, x:%.2f, y:%.2f\n", t, corners[t].x, corners[t].y);}
}
HOG特征描述子
详细请参考:https://baijiahao.baidu.com/s?id=1646997581304332534&wfr=spider&for=pc&searchword=HOG%E7%89%B9%E5%BE%81%E6%8F%8F%E8%BF%B0%E5%AD%90
讲的很好。
大概就是以一种特殊的直方图来表示图像特征,直方图存储的是梯度的方向和幅值(x轴是方向,y轴是幅值且加权)。
示例代码:
virtual void cv::HOGDescriptor::compute(InputArray imgstd::vector<float> & descriptorsSize winStride=Size()Size padding=Size()const std::vector<Point> &locations = std::vector<Point>()
)void FeatureVectorOps::hog_feature_demo(Mat &image) {Mat gray;cvtColor(image, gray, COLOR_BGR2GRAY);HOGDescriptor hogDetector;std::vector<float> hog_descriptors;hogDetector.compute(gray, hog_descriptors, Size(8, 8), Size(0, 0));std::cout << hog_descriptors.size() << std::endl;for (size_t t = 0; t < hog_descriptors.size(); t++) {std::cout << hog_descriptors[t] << std::endl;}
}
HOG特征行人检测
opencv基于HOG行人特征描述子的检测函数:
void HOGDescriptor::detectMultiScale(InputArray img,vector<Rect>& foundLocations, double hitThreshold=0, Size winStride=Size(), Size padding=Size(),double scale=1.05,double finalThreshold=2.0,bool useMeanshiftGrouping=false
)
//示例代码
void FeatureVectorOps::hog_detect_demo(Mat &image) {HOGDescriptor *hog = new HOGDescriptor();hog->setSVMDetector(hog->getDefaultPeopleDetector());vector<Rect> objects;hog->detectMultiScale(image, objects, 0.0, Size(4, 4), Size(8, 8), 1.25);for (int i = 0; i < objects.size(); i++) {rectangle(image, objects[i], Scalar(0, 0, 255), 2, 8, 0);}imshow("HOG行人检测", image);
}
ORB特征描述子
没看懂。
描述子匹配
暴力匹配:
再使用暴力匹配之前先创建暴力匹配器:
static Ptr<BFMatcher> cv::BFMatcher::create(int normType=NORM_L2 //计算描述子暴力匹配时采用的计算方法bool crossCheck=false //是否使用交叉验证
)
调用暴力匹配的匹配方法,有两种,最佳匹配和KNN匹配
void cv::DescriptorMatch::match(InputArray queryDescriptorsInputArray trainDescriptorsstd::vector<DMatch> & matchesInputArray mask=noArray
)void cv::DescriptorMatch::knnMatch(InputArray queryDescriptorsInputArray trainDescriptorsstd::vector<DMatch> & matchesint kInputArray mask=noArraybool compactResult =false
)
FLANN匹配:
cv::FlannBasedMatcher::FlannBasedMatcher(const Ptr<flann::IndexParams> & indexParams=makePtr<flann::KDTreeIndexParams>()const Ptr<flann::SearchParams> & searchParams=makePtr<flann::SearchParams>()
)
示例代码:
void FeatureVectorOps::orb_match_demo(Mat &box, Mat &box_in_scene) {// ORB特征提取auto orb_detector = ORB::create();std::vector<KeyPoint> box_kpts;std::vector<KeyPoint> scene_kpts;Mat box_descriptors, scene_descriptors;orb_detector->detectAndCompute(box, Mat(), box_kpts, box_descriptors);orb_detector->detectAndCompute(box_in_scene, Mat(), scene_kpts, scene_descriptors);// 暴力匹配auto bfMatcher = BFMatcher::create(NORM_HAMMING, false);std::vector<DMatch> matches;bfMatcher->match(box_descriptors, scene_descriptors, matches);Mat img_orb_matches;drawMatches(box, box_kpts, box_in_scene, scene_kpts, matches, img_orb_matches);imshow("ORB暴力匹配演示", img_orb_matches);// FLANN匹配auto flannMatcher = FlannBasedMatcher(new flann::LshIndexParams(6, 12, 2));flannMatcher.match(box_descriptors, scene_descriptors, matches);Mat img_flann_matches;drawMatches(box, box_kpts, box_in_scene, scene_kpts, matches, img_flann_matches);namedWindow("FLANN匹配演示", WINDOW_FREERATIO);cv::namedWindow("FLANN匹配演示", cv::WINDOW_NORMAL);imshow("FLANN匹配演示", img_flann_matches);
}
基于特征的对象检测
特征描述子匹配之后,可以根据返回的各个DMatch中的索引得到关键点对,然后拟合生成从对象到场景的变换矩阵H。根据矩阵H可以求得对象在场景中的位置,从而完成基于特征的对象检测。
opencv中求得单应性矩阵的API:
Mat cv::findHomograph(InputArray srcPointsOutputArray dstPointsint method=0double ransacReprojThreshold=3OutputArray mask=noArray()const int maxIters=2000;const double confidence=0.995
)
有了变换矩阵H ,可以运用透视变换函数求得场景中对象的四个点坐标并绘制出来。
透视变换函数:
void cv::perspectiveTransform(InputArray srcOutputArray dstInputArray m
)
示例代码:
void FeatureVectorOps::find_known_object(Mat &book, Mat &book_on_desk) {// ORB特征提取auto orb_detector = ORB::create();std::vector<KeyPoint> box_kpts;std::vector<KeyPoint> scene_kpts;Mat box_descriptors, scene_descriptors;orb_detector->detectAndCompute(book, Mat(), box_kpts, box_descriptors);orb_detector->detectAndCompute(book_on_desk, Mat(), scene_kpts, scene_descriptors);// 暴力匹配auto bfMatcher = BFMatcher::create(NORM_HAMMING, false);std::vector<DMatch> matches;bfMatcher->match(box_descriptors, scene_descriptors, matches);// 好的匹配std::sort(matches.begin(), matches.end());const int numGoodMatches = matches.size() * 0.15;matches.erase(matches.begin() + numGoodMatches, matches.end());Mat img_bf_matches;drawMatches(book, box_kpts, book_on_desk, scene_kpts, matches, img_bf_matches);imshow("ORB暴力匹配演示", img_bf_matches);// 单应性求Hstd::vector<Point2f> obj_pts;std::vector<Point2f> scene_pts;for (size_t i = 0; i < matches.size(); i++){//-- Get the keypoints from the good matchesobj_pts.push_back(box_kpts[matches[i].queryIdx].pt);scene_pts.push_back(scene_kpts[matches[i].trainIdx].pt);}Mat H = findHomography(obj_pts, scene_pts, RANSAC);std::cout << "RANSAC estimation parameters: \n" << H << std::endl;std::cout << std::endl;H = findHomography(obj_pts, scene_pts, RHO);std::cout << "RHO estimation parameters: \n" << H << std::endl;std::cout << std::endl;H = findHomography(obj_pts, scene_pts, LMEDS);std::cout << "LMEDS estimation parameters: \n" << H << std::endl;// 变换矩阵得到目标点std::vector<Point2f> obj_corners(4);obj_corners[0] = Point(0, 0); obj_corners[1] = Point(book.cols, 0);obj_corners[2] = Point(book.cols, book.rows); obj_corners[3] = Point(0, book.rows);std::vector<Point2f> scene_corners(4);perspectiveTransform(obj_corners, scene_corners, H);// 绘制结果Mat dst;line(img_bf_matches, scene_corners[0] + Point2f(book.cols, 0), scene_corners[1] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);line(img_bf_matches, scene_corners[1] + Point2f(book.cols, 0), scene_corners[2] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);line(img_bf_matches, scene_corners[2] + Point2f(book.cols, 0), scene_corners[3] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);line(img_bf_matches, scene_corners[3] + Point2f(book.cols, 0), scene_corners[0] + Point2f(book.cols, 0), Scalar(0, 255, 0), 4);//-- Show detected matchesnamedWindow("基于特征的对象检测", cv::WINDOW_NORMAL);imshow("基于特征的对象检测", img_bf_matches);
}
相关文章:
opencv学习 特征提取
内容来源于《opencv4应用开发入门、进阶与工程化实践》 图像金字塔 略 拉普拉斯金字塔 对输入图像进行reduce操作会生成不同分辨率的图像,对这些图像进行expand操作,然后使用reduce减去expand之后的结果,就会得到拉普拉斯金字塔图像。 …...
关于maven项目构建的解释
在Idea中使用模块化构建项目 项目介绍: sky-take-out sky-common pom.xml sky-pojo pom.xml sky-server pom.xml pom.xml 说明 sky-server依赖sky-pojo和sky-common,继承sky-take-outsky-pojo继承sky-take-outsky-common继承sky-take-out 由于Idea编…...
IMU/捷联惯导常见的术语,以及性能评价标准(附Python解析代码)
0. 简介 现在的机器人领域在普遍使用IMU(惯性导航单元)。该系统有三个加速度传感器与三个角速度传感器(陀螺)组成,加速度计用来感受飞机相对于地垂线的加速度分量,陀螺仪用来感知飞机的角速率变化…...
Debezium发布历史98
原文地址: https://debezium.io/blog/2020/11/12/debezium-1-3-1-final-released/ 欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯. Debezium 1.3.1.Final 发布 十一月 12, 2020 作者: 克里…...
APUE学习之进程间通信(IPC)(下篇)
目录 一、进程间通信(IPC) 二、信号量(Semaphore) 1、基本概念 2、同步关系与互斥关系 3、临界区与临界资源 4、信号量的工作原理 5、信号量编程 6、实战演练 三、共享内存(Shared Memory) 1、…...
【Java 设计模式】行为型之中介者模式
文章目录 1. 定义2. 应用场景3. 代码实现结语 中介者模式(Mediator Pattern)是一种行为型设计模式,用于通过一个中介对象来集中管理多个对象之间的交互关系,从而降低对象之间的耦合度。中介者模式通过将对象之间的通信委托给中介者…...
MySql 慢SQL配置,查询,处理
一.慢SQL配置相关 1.查看慢SQL是否开启 执行下面命令查看是否开启慢SQL show variables like %slow_query_log; 复制代码 OFF: 未开启ON: 2.打开慢SQL配置 执行下面的命令开启慢查询日志 set global slow_query_logON; 复制代码 3.修改慢查询阈值 前面介绍了SQL执行到达了…...
算法:分界线
一、算法描述 电视剧《分界线》里面有一个片段,男主为了向警察透露案件细节,且不暴露自己,于是将报刊上的字 剪切下来,剪拼成匿名信。 现在有一名举报人,希望借鉴这种手段,使用英文报刊完成举报操作。 但为…...
STM32单片机基本原理与应用(四)
直流电机驱动控制原理 1、电机正反转控制 在STM32中,直流电机的正反转控制主要通过改变电机输入电源的极性来实现。当电机的电压极性发生变化时,电机的旋转方向也会相应改变。在硬件电路中,可以通过继电器或晶体管等电子开关来切换电机的电源…...
elk之安装和简单配置
写在前面 本文看下elk的安装和简单配置,安装我们会尝试通过不同的方式来完成,也会介绍如何使用docker,docker-compose安装。 1:安装es 1.1:安装单实例 下载es安装包 在这里 下载,下载后解压到某个目录…...
springboot(ssm环保网站 绿色环保宣传系统Java系统
springboot(ssm环保网站 绿色环保宣传系统Java系统 开发语言:Java 框架:springboot(可改ssm) vue JDK版本:JDK1.8(或11) 服务器:tomcat 数据库:mysql 5.7࿰…...
【MBtiles数据索引和服务发布】GeoServer改造Springboot番外系列二
xyz地图服务访问示例:http://192.168.1.240:8081/gmserver/raster/xyz/firstWP:Imagery-raster/{z}/{x}/{y}.jpg 访问示例如下: mbtiles目录结构 根据z,x,y获取对应mbtiles文件路径的工具方法 说明:重点是使用getMb…...
Redis抓取数据到Logstash再推到Elasticsearch集群
一、安装Logstash 前面安装过Logstash了,不做解释直接跳过 参考:上一篇文章 二、配置Logstash 在logstash目录下,编辑我们之前的配置文件logstash.conf vim logstash.confinput、output字面意思,从redis去拿取数据,输出到Elasticsearch data_type:数据类型为list k…...
【代码随想录-链表】反转链表
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…...
32GPIO输入LED闪烁蜂鸣器
一.GPIO简介 所有的GPIO都挂载到APB2上,每个GPIO有16个引脚 内核可以通过APB2对寄存器进行读写,寄存器都是32位的,但每个引脚端口只有16位 驱动器用于增加信号的驱动能力 二.具体…...
Qt|QPushButton控件讲解
前提 按钮分为了四种状态:常态、聚焦、按下、禁用 前一段时间更新了MFC框架下CButton的自绘。因为MFC框架下的按钮限制性很高,所以只能由自绘实现各种风格,但是QT框架完美的解决了这个问题,我们只需要了解如何调用,就…...
再学webpack
1 优化 webpack 打包体积的思路 优化 webpack 打包体积的思路包括: 提取第三方库或通过引用外部文件的方式引入第三方库:将第三方库单独打包,并通过 CDN 引入,减少打包体积。使用代码压缩插件:例如 UglifyJsPlugin&a…...
systemd:service与target使用及相关命令
文章目录 一、 unit1.1 unit常用命令 二、 service系统服务2.1 unit service配置文件2.1.1 [Unit]区块2.1.2 [Service]区块2.1.3 [Install]区块2.1.4 示例介绍 2.2 service常用命令 三、target3.1 tartget有关命令 四、其他系统命令4.1 systemctl 相关系统管理命令4.2 journalc…...
FairGuard游戏加固入选《CCSIP 2023中国网络安全行业全景册(第六版)》
2024年1月24日, FreeBuf咨询正式发布《CCSIP 2023中国网络安全行业全景册(第六版)》。本次发布的全景图,共计展示20个一级分类、108个细分安全领域,旨在为广大企业提供网络安全产品选型参考,帮助企业了解中国网络安全技术与市场的…...
文心一言 VS ChatGPT :谁是更好的选择?
前言 目前各种大模型、人工智能相关内容覆盖了朋友圈已经各种媒体平台,对于Ai目前来看只能说各有千秋。GPT的算法迭代是最先进的,但是它毕竟属于国外产品,有着网络限制、注册限制、会员费高昂等弊端,难以让国内用户享受。文心一言…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
