windows 基于 MediaPipe 实现 Holistic

news2024/10/5 13:51:41

在这里插入图片描述
主页: https://google.github.io/mediapipe/solutions/holistic.html

MediaPipe Holistic pipelines 集成了姿势、面部和手部组件的独立模型,每个组件都针对其特定领域进行了优化,每个组件的推断输入图不同。

MediaPipe Holistic 首先通过 BlazePose 的姿势检测器和后续的关键点模型来估计人的姿势。然后,利用推断出的姿势关键点,为每只手和脸部推导出三个感兴趣区域(ROI)裁剪,并采用 re-crop 模型来改进 ROI

然后,pipelines 将全分辨率输入帧上裁剪这些 ROI,并应用特定任务的模型来估计它们对应的关键点。最后,将所有关键点与姿势模型的关键点合并,得出全部 540 多个关键点。


实现过程 🚀​

编译demo

  1. bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 --action_env PYTHON_BIN_PATH="C:xxx//python.exe" mediapipe/examples/desktop/holistic_tracking:holistic_tracking_cpu --verbose_failures
    
    bazel-bin\mediapipe\examples\desktop\holistic_tracking\holistic_tracking_cpu --calculator_graph_config_file=mediapipe/graphs/holistic_tracking/holistic_tracking_cpu.pbtxt
    
  2. 如果运行成功,则默认会调用摄像头

编写测试demo

  1. 环境配置参考:

    • https://blog.csdn.net/sunnyblogs/article/details/118891249
    • https://blog.csdn.net/haiyangyunbao813/article/details/119192951
  2. 修改 mediapipe\examples\desktop\holistic_trackingBUILD

  3. 增加个测试程序节点

    cc_binary(
        name = "holistic_tracking_sample",
        srcs = ["main.cpp","holistic_detect.h","holistic_detect.cpp"],
        # linkshared=True,
        deps = [
            "//mediapipe/graphs/holistic_tracking:holistic_tracking_cpu_graph_deps",
        ],
    )
    
  4. holistic_detect.h

    #ifndef _HOLISTIC_DETECT_H_
    #define _HOLISTIC_DETECT_H_
    
    #include <cstdlib>
    #include "absl/flags/flag.h"
    #include "absl/flags/parse.h"
    #include "mediapipe/framework/calculator_framework.h"
    #include "mediapipe/framework/formats/image_frame.h"
    #include "mediapipe/framework/formats/image_frame_opencv.h"
    #include "mediapipe/framework/port/file_helpers.h"
    #include "mediapipe/framework/port/opencv_highgui_inc.h"
    #include "mediapipe/framework/port/opencv_imgproc_inc.h"
    #include "mediapipe/framework/port/opencv_video_inc.h"
    #include "mediapipe/framework/port/parse_text_proto.h"
    #include "mediapipe/framework/port/status.h"
    #include "mediapipe/framework/formats/detection.pb.h"
    #include "mediapipe/framework/formats/landmark.pb.h"
    #include "mediapipe/framework/formats/rect.pb.h"
    #include "mediapipe/framework/formats/classification.pb.h"
    #include "mediapipe/framework/port/ret_check.h"
    
    namespace mp {
    
    	using holistic_callback_t = std::function<void()>;
    
    	class HolisticDetect
    	{
    	public:
    		int initModel(const char* model_path) noexcept;
    		int detectVideoCamera(holistic_callback_t call);
    		int detectVideo(const char* video_path, int show_image, holistic_callback_t call);
    		int detectVideo();
    		int release();
    	private:
    		absl::Status initGraph(const char* model_path);
    		absl::Status runMPPGraphVideo(const char* video_path, int show_image, holistic_callback_t call);
    		absl::Status releaseGraph();
    
    		void showFaceLandmarks(cv::Mat& image);
    		void showPoseLandmarks(cv::Mat& image);
    		void showLHandLandmarks(cv::Mat& image);
    		void showRHandLandmarks(cv::Mat& image);
    
    		const char* kInputStream = "input_video";
    		const char* kOutputStream = "output_video";
    		const char* kWindowName = "MediaPipe";
    		const char* kOutputLandmarks = "face_landmarks";
    		const char* kOutputPoseLandmarks = "pose_landmarks";
    		const char* kOutputLHandLandmarks = "left_hand_landmarks";
    		const char* kOutputRHandLandmarks = "right_hand_landmarks";
    
    		bool init_ = false;
    		mediapipe::CalculatorGraph graph_;
    
    		const float kMinFloat = std::numeric_limits<float>::lowest();
    		const float kMaxFloat = std::numeric_limits<float>::max();
    
    		std::unique_ptr<mediapipe::OutputStreamPoller> pPoller_;
    		std::unique_ptr<mediapipe::OutputStreamPoller> pPollerFaceLandmarks_;
    		std::unique_ptr<mediapipe::OutputStreamPoller> pPollerPoseLandmarks_;
    		std::unique_ptr<mediapipe::OutputStreamPoller> pPollerLHandLandmarks_;
    		std::unique_ptr<mediapipe::OutputStreamPoller> pPollerRHandLandmarks_;
    	};
    }
    
    #endif
    
    
  5. holistic_detect.cpp

    #include "holistic_detect.h"
    using namespace mp;
    
    int HolisticDetect::initModel(const char* model_path) noexcept {
    	absl::Status run_status = initGraph(model_path);
    	if (!run_status.ok())
    		return -1;
    	init_ = true;
    	return  1;
    }
    
    int HolisticDetect::detectVideoCamera(holistic_callback_t call) {
    	if (!init_)
    		return -1;
    
    	absl::Status run_status = runMPPGraphVideo("", true, call);
    	return run_status.ok() ? 1 : -1;
    }
    
    int HolisticDetect::detectVideo(const char* video_path, int show_image, holistic_callback_t call) {
    	if (!init_)
    		return -1;
    
    	absl::Status run_status = runMPPGraphVideo(video_path, show_image, call);
    	return run_status.ok() ? 1 : -1;
    }
    
    int HolisticDetect::detectVideo() {
    	if (!init_)
    		return -1;
    	absl::Status run_status = runMPPGraphVideo("", 1, nullptr);
    	return run_status.ok() ? 1 : -1;
    }
    
    int HolisticDetect::release() {
    	absl::Status run_status = releaseGraph();
    	return run_status.ok() ? 1 : -1;
    }
    
    absl::Status HolisticDetect::initGraph(const char* model_path) {
    
    	std::string calculator_graph_config_contents;
    	MP_RETURN_IF_ERROR(mediapipe::file::GetContents(model_path, &calculator_graph_config_contents));
    	mediapipe::CalculatorGraphConfig config =
    		mediapipe::ParseTextProtoOrDie<mediapipe::CalculatorGraphConfig>(
    			calculator_graph_config_contents);
    	MP_RETURN_IF_ERROR(graph_.Initialize(config));
    	auto sop = graph_.AddOutputStreamPoller(kOutputStream);
    	assert(sop.ok());
    	pPoller_ = std::make_unique<mediapipe::OutputStreamPoller>(std::move(sop.value()));
    
    	// 脸部特征
    	mediapipe::StatusOrPoller faceLandmark = graph_.AddOutputStreamPoller(kOutputLandmarks);
    	assert(faceLandmark.ok());
    	pPollerFaceLandmarks_ = std::make_unique<mediapipe::OutputStreamPoller>(std::move(faceLandmark.value()));
    
    	// 姿态特征
    	mediapipe::StatusOrPoller poseLandmark = graph_.AddOutputStreamPoller(kOutputPoseLandmarks);
    	assert(poseLandmark.ok());
    	pPollerPoseLandmarks_ = std::make_unique<mediapipe::OutputStreamPoller>(std::move(poseLandmark.value()));
    
    	// 左手特征点
    	mediapipe::StatusOrPoller leftHandLandmark = graph_.AddOutputStreamPoller(kOutputLHandLandmarks);
    	assert(leftHandLandmark.ok());
    	pPollerLHandLandmarks_ = std::make_unique<mediapipe::OutputStreamPoller>(std::move(leftHandLandmark.value()));
    
    	// 右手特征点
    	mediapipe::StatusOrPoller rightHandLandmark = graph_.AddOutputStreamPoller(kOutputRHandLandmarks);
    	assert(rightHandLandmark.ok());
    	pPollerRHandLandmarks_ = std::make_unique<mediapipe::OutputStreamPoller>(std::move(rightHandLandmark.value()));
    
    	MP_RETURN_IF_ERROR(graph_.StartRun({}));
    	std::cout << "======= graph_ StartRun success ============" << std::endl;
    	return absl::OkStatus();
    }
    
    void HolisticDetect::showFaceLandmarks(cv::Mat& image) {
    	mediapipe::Packet packet_landmarks;
    	if (pPollerFaceLandmarks_->QueueSize() == 0 || !pPollerFaceLandmarks_->Next(&packet_landmarks))
    		return;
    
    	//  468 face landmark
    	auto& output_landmarks = packet_landmarks.Get<mediapipe::NormalizedLandmarkList>();
    	for (int i = 0; i < output_landmarks.landmark_size(); ++i)
    	{
    		const mediapipe::NormalizedLandmark landmark = output_landmarks.landmark(i);
    		float x = landmark.x() * image.cols;
    		float y = landmark.y() * image.rows;
    		//cv::circle(image, cv::Point(x, y), 2, cv::Scalar(0, 255, 0));
    		// todo
    		// ...
    	}
    }
    
    void HolisticDetect::showPoseLandmarks(cv::Mat& image) {
    	mediapipe::Packet packet_landmarks;
    	if (pPollerPoseLandmarks_->QueueSize() == 0 || !pPollerPoseLandmarks_->Next(&packet_landmarks))
    		return;
    
    	//  33 pose landmark
    	auto& output_landmarks = packet_landmarks.Get<mediapipe::NormalizedLandmarkList>();
    	for (int i = 0; i < output_landmarks.landmark_size(); ++i)
    	{
    		const mediapipe::NormalizedLandmark landmark = output_landmarks.landmark(i);
    		float x = landmark.x() * image.cols;
    		float y = landmark.y() * image.rows;
    		//cv::circle(image, cv::Point(x, y), 2, cv::Scalar(0, 255, 0));
    		// todo
    		// ...
    	}
    }
    
    void HolisticDetect::showLHandLandmarks(cv::Mat& image) {
    	mediapipe::Packet packet_landmarks;
    	if (pPollerLHandLandmarks_->QueueSize() == 0 || !pPollerLHandLandmarks_->Next(&packet_landmarks))
    		return;
    	//  21 left hand landmark
    	auto& output_landmarks = packet_landmarks.Get<mediapipe::NormalizedLandmarkList>();
    	for (int i = 0; i < output_landmarks.landmark_size(); ++i)
    	{
    		const mediapipe::NormalizedLandmark landmark = output_landmarks.landmark(i);
    		float x = landmark.x() * image.cols;
    		float y = landmark.y() * image.rows;
    		//cv::circle(image, cv::Point(x, y), 2, cv::Scalar(0, 255, 0));
    		// todo
    		// ...
    	}
    }
    
    void HolisticDetect::showRHandLandmarks(cv::Mat& image) {
    	mediapipe::Packet packet_landmarks;
    	if (pPollerRHandLandmarks_->QueueSize() == 0 || !pPollerRHandLandmarks_->Next(&packet_landmarks))
    		return;
    	//  21 right hand landmark
    	auto& output_landmarks = packet_landmarks.Get<mediapipe::NormalizedLandmarkList>();
    	for (int i = 0; i < output_landmarks.landmark_size(); ++i)
    	{
    		const mediapipe::NormalizedLandmark landmark = output_landmarks.landmark(i);
    		float x = landmark.x() * image.cols;
    		float y = landmark.y() * image.rows;
    		//cv::circle(image, cv::Point(x, y), 2, cv::Scalar(0, 255, 0));
    		// todo
    		// ...
    	}
    }
    
    absl::Status HolisticDetect::runMPPGraphVideo(const char* video_path, int show_image, holistic_callback_t call) {
    	cv::VideoCapture capture(video_path);
    	RET_CHECK(capture.isOpened());
    #if (CV_MAJOR_VERSION >= 3) && (CV_MINOR_VERSION >= 2)
    	capture.set(cv::CAP_PROP_FRAME_WIDTH, 640);
    	capture.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
    	capture.set(cv::CAP_PROP_FPS, 30);
    #endif
    	int tmp = 0;
    	bool grab_frames = true;
    	while (grab_frames) {
    		// Capture opencv camera or video frame.
    		cv::Mat camera_frame_raw;
    		capture >> camera_frame_raw;
    		if (camera_frame_raw.empty())
    			break;
    
    		cv::Mat camera_frame;
    		cv::cvtColor(camera_frame_raw, camera_frame, cv::COLOR_BGR2RGB);
    		cv::flip(camera_frame, camera_frame, /*flipcode=HORIZONTAL*/ 1);
    
    		// Wrap Mat into an ImageFrame.
    		auto input_frame = absl::make_unique<mediapipe::ImageFrame>(
    			mediapipe::ImageFormat::SRGB, camera_frame.cols, camera_frame.rows,
    			mediapipe::ImageFrame::kDefaultAlignmentBoundary);
    		cv::Mat input_frame_mat = mediapipe::formats::MatView(input_frame.get());
    		camera_frame.copyTo(input_frame_mat);
    
    		// Send image packet into the graph.
    		size_t frame_timestamp_us =
    			(double)cv::getTickCount() / (double)cv::getTickFrequency() * 1e6;
    
    		MP_RETURN_IF_ERROR(graph_.AddPacketToInputStream(
    			kInputStream, mediapipe::Adopt(input_frame.release())
    			.At(mediapipe::Timestamp(frame_timestamp_us))));
    
    		// Get the graph result packet, or stop if that fails.
    		mediapipe::Packet packet;
    		if (!pPoller_->Next(&packet)) break;
    
    		showFaceLandmarks(camera_frame);
    		showPoseLandmarks(camera_frame);
    		showLHandLandmarks(camera_frame);
    		showRHandLandmarks(camera_frame);
    
    		if (show_image) {
    			auto& output_frame = packet.Get<mediapipe::ImageFrame>();
    			// Convert back to opencv for display or saving.
    			cv::Mat output_frame_mat = mediapipe::formats::MatView(&output_frame);
    			cv::cvtColor(output_frame_mat, output_frame_mat, cv::COLOR_RGB2BGR);
    
    			cv::imshow(kWindowName, output_frame_mat);
    			cv::waitKey(1);
    			/*	cv::Mat output_frame_mat;
    				cv::cvtColor(camera_frame, output_frame_mat, cv::COLOR_RGB2BGR);
    				cv::imwrite(cv::format("out_image/%d.jpg", tmp++), output_frame_mat);*/
    		}
    	}
    	if (show_image)
    		cv::destroyWindow(kWindowName);
    	return absl::OkStatus();
    }
    
    absl::Status HolisticDetect::releaseGraph() {
    	MP_RETURN_IF_ERROR(graph_.CloseInputStream(kInputStream));
    	MP_RETURN_IF_ERROR(graph_.CloseInputStream(kOutputLandmarks));
    	return graph_.WaitUntilDone();
    }
    
  6. main.cpp

    #include "holistic_detect.h"
    using namespace mp;
    HolisticDetect holisticDetect_;
    void call() {
    }
    int main(int argc, char* argv[]) {
    	std::cout << "======= holistic ============" << std::endl;
    	const char* model = argv[1];
    	const char* video_path = argv[2];
    	int isShow = 1;
    	int res = holisticDetect_.initModel(model);
    	if (res < 0) {
    		std::cout << "======= initModel error ============" << std::endl;
    		return -1;
    	}
    	res = holisticDetect_.detectVideo(video_path, isShow , call);
    	if (res < 0)
    		return -1;
    	holisticDetect_.release();
    	return 0;
    }
    
  7. 编译

    bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 --action_env PYTHON_BIN_PATH="C://xx//python.exe" mediapipe/examples/desktop/holistic_tracking:holistic_tracking_sample --verbose_failures
    
  8. 运行

    bazel-bin\mediapipe\examples\desktop\holistic_tracking\holistic_tracking_sample "mediapipe/graphs/holistic_tracking/holistic_tracking_cpu.pbtxt" "video/test.mp4"
    
  9. 运行成功, 则可获取人脸面部 468 个特征点 ,左手 21 个特征点 , 右手 21 个特征点 ,姿态 33 个特征点。

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述


其他模块 ☀️

  1. IRIS: https://blog.csdn.net/haiyangyunbao813/article/details/122225445?spm=1001.2014.3001.5502
  2. Pose: https://blog.csdn.net/haiyangyunbao813/article/details/119192951?spm=1001.2014.3001.5502
  3. Hand: https://blog.csdn.net/haiyangyunbao813/article/details/122464972?spm=1001.2014.3001.5502
  4. 其他待续…

青春不过几届世界杯

输赢并不是足球的全部 , 真正的热爱或许是笑着庆祝, 哭着鼓掌 。加油马儿,愿 2026 再战一届。 🌟​🌟​🌟​

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/81321.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于极限学习机进行股市预测(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

MySQL-InnoDB的事务隔离级别

MySQL 是一个服务器&#xff0f;客户端架构的软件&#xff0c;对于同一个服务器来说&#xff0c;可以有若干个客户端与之连接&#xff0c;每个客户端与服务器连接上之后&#xff0c;就可以称之为一个会话&#xff08; Session &#xff09;。我们可以同时在不同的会话里输入各种…

【图像处理】opencv | 图像的载入,显示,保存 | 视频流的载入,显示,保存

文章目录前言一、cv2读取图片并展示1.1、cv2.imread读取图片1.2、cv2.imshow展示图片1.3、完整代码1.4、封装函数调用1.5、cv2读取为灰度图像1.6、cv2.imwrite保存图像二、cv2读取视频并且展示2.1 展示彩色视频2.2 展示灰度视频2.3 保存视频前言 本文参考视频&#xff1a;唐宇…

二进制搭建k8s——部署etcd集群和单master

二进制搭建k8s——部署etcd集群和单master二进制搭建k8s——部署etcd集群和单master环境1、操作系统初始化配置&#xff08;全部节点&#xff09;2、部署 docker 引擎&#xff08;所有节点&#xff09;3、部署 etcd 集群准备签发证书环境在 master01 节点上操作在 node01 和 no…

端口隔离实现同一vlan下,二层和三层的互不通

如图&#xff1a;我们要实现下图中&#xff0c;PC1和PC2不通&#xff0c;但都和PC3互通&#xff1a; 配置如下&#xff1a; vlan batch 10 port-isolate mode all # interface GigabitEthernet0/0/1 port link-type access port default vlan 10 port-isolate enable grou…

【golang】 demo 之王realworld,使用golang+gin做后端技术,使用vue做前端项目的开源博客项目

目录前言1&#xff0c;关于realworld项目2&#xff0c;前端项目使用vue3开发的3&#xff0c;后端使用golanggin进行接口开发4&#xff0c;总结前言 本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/108971807 未经博主允许不得转载。 博主CSDN地址是&…

idea中打包docker镜像

idea中打包docker镜像 说明 ​ 构建和推送镜像都是需要docker环境的&#xff0c;这个大家可以使用同一个远程的环境&#xff0c;这里说的就是idea使用服务器上的docker进行镜像的构建和发布&#xff0c; ​ 默认高版本的idea中默认集成了docker插件&#xff0c;这个插件的官…

高并发高可用

一、高并发 1、异步并发 同步阻塞 异步Future 异步CallBack 异步编排CompletableFuture 请求缓存 请求合并 2、扩容 单体应用垂直扩容 单体应用水平扩容 应用拆分 数据库拆分水平/垂直拆分 使用Sharding-jdbc分库分表/读写分离 数据异构 任务系统扩容 3、队列 …

[LeetCode周赛复盘] 第 93 场双周赛20221015-补

[LeetCode周赛复盘] 第 93 场双周赛20221015-补 一、本周周赛总结二、 [Easy] 6261. 数组中字符串的最大值1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6262. 图中最大星和1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6263. 青蛙过河 II1. 题目描述2. 思路分析3. 代码实…

【OpenCV学习】第13课:基本阈值操作

仅自学做笔记用,后续有错误会更改 参考文章:https://blog.csdn.net/qq_37835727/article/details/123373339 理论 图像阈值&#xff1a;什么是图像阈值&#xff1f;简单点来说就是把图像分割的标尺&#xff0c; 举个栗子&#xff0c; 现在想象一个场景&#xff0c; 一个桌子上…

BA_重投影误差e对于相机的位姿ξ和对空间点的坐标P的雅可比矩阵的推导

1. 基本思路 重投影误差表示为e, 相机的位姿表示为ξ (或者表示为T(R,t))&#xff0c; 空间点表示为P, 则空间点投影到相机坐标系下的空间坐标点的相机坐标表示为P[X, Y, Z], 则 重投影误差e对于相机的位姿ξ的雅克比矩阵表示为 分别求等式右侧的两半部分&#xff08;误差对空…

DevExpress WinForms 22.2

DevExpress WinForms 22.2 添加了对Microsoft.NET 7的完全支持。 此版本现在需要.NET 6和Microsoft Visual Studio 2022(v17.0)或更高版本。 这不会影响.NET Framework客户&#xff0c;产品程序集在此发布周期中将继续以.NET Framework 4.5.2为目标。 蒙皮和矢量图标 WXI皮肤的…

每天五分钟机器学习:经典的降维算法——主成分分析法PCA

本文重点 前面我们学习了降维算法的两大应用场景,本节课程我们将学习具体的降维算法PCA,它是主成分分析法。 PCA要做什么? 将二维数据降维到一维,关键就是找到一个方向向量,然后把所有的数据都投射到该向量上,那么什么样的方向向量最好呢? 我们希望投射平均均方误差…

UIPickerView,UIDatePicker,UITextView

文章目录UIPickerView概念应用创建选中数据时的回调代理UIDatePicker概念创建四种模式UITextView概念创建常用属性与方法常用协议方法UIPickerView 概念 UIPickerView是一个列表控件。它可以提供给用户有限个数的可供选择的选项。 他可以设置列数和每一列的行数&#xff0c;然…

Fiddler抓包和Fiddler过滤器

目录 一、Fiddler与其他抓包工具的区别 二、Fiddler的工作原理 三、使用fiddler实现手机抓包 四、Filters过滤器 一、Fiddler与其他抓包工具的区别 1、Firebug虽然可以抓包&#xff0c;但是对于分析http请求的详细信息&#xff0c;不够强大。模拟http请求的功能也不够&…

简述基于JavaEE企业级开发技术

一、绪论 1、学习内容 javaEE企业开发技术概述javaEE容器——SpringORM数据层——MyBatis/JPAWeb层——Spring MVC展现层——JSP/Thymeleaf整合框架——SSM/SSH用户模块分析用户模块功能模块设计 前端框架&#xff1a;Bootstrap&#xff0c;NodeJS&#xff0c;Vue/React/Ang…

[附源码]计算机毕业设计个性化名片网站Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Appium基础 — Appium测试环境搭建总结

1、Appium测试环境搭建整体思路 &#xff08;1&#xff09;Android测试环境搭建 Android测试环境需要搭建三个环境&#xff0c;Java&#xff0c;AndroidSDK&#xff0c;Android模拟器。 为什么要安装这三个环境&#xff1f; Java&#xff1a;Android的应用程序层使用的语言是…

理解Linux设备树(DTS)

DTS Dts&#xff1a;DTS即Device Tree Source&#xff0c;是一个文本形式的文件&#xff0c;用于描述硬件信息。一般都是固定信息&#xff0c;无法变更&#xff0c;无法overlay。 Dtsi&#xff1a;可以理解为dts的公共部分&#xff0c;添加、变更非常灵活。Dtsi包含在dts中。 …

基于 EasyOCR + HanLp 实现图片文字实体(中文姓名、机构名、地域名)识别

一、EasyOCR HanLp EasyOCR 是一个python版的文字识别工具。目前支持80中语言的识别。并且支持&#xff1a;图像预处理&#xff08;去噪、色彩饱和度、尖锐处理)、CRAFT文字检测、中间处理&#xff08;倾斜处理等&#xff09;、文字识别、后续处理、输出结果。框架如下&#…