当前位置: 首页 > news >正文

从基础到人脸识别与目标检测

前言

从本文开始,我们将开始学习ROS机器视觉处理,刚开始先学习一部分外围的知识,为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本,系统采用Ubuntu20.04,ROS采用noetic。

颜色编码格式,图像格式和视频压缩格式

(1)RGB和BGR:这是两种常见的颜色编码格式,分别代表了红、绿、蓝三原色。不同之处在于,RGB按照红、绿、蓝的顺序存储颜色信息,而BGR按照蓝、绿、红的顺序存储。

rgb8图像格式:常用于显示系统,如电视和计算机屏幕。RGB值以8 bits表示每种颜色,总共可以表示256×256×256=16777216种颜色。例如: (255,0,0) 表示红色,(0,255,0) 表示绿色,(0,0,255) 表示蓝色。
bgr8图像格式:由一些特定的硬件制造商采用,软件方面最著名的就是opencv,其默认使用BGR的颜色格式来处理图像。与RGB不同, (0,0,255) 在BGR中表示红色,(0,255,0) 仍然表示绿色,(255,0,0) 表示蓝色。

在自动驾驶里,使用rgb8图像格式的图像,一般称为原图,是数据量最大的格式,没有任何压缩。(2)(2)YUV:这是另一种颜色编码方法,与RGB模型不同的是,它将图像信息分解为亮度(Y)和色度(U和V)两部分。这种方式更接近于人类对颜色的感知方式。

Y:代表亮度信息,也就是灰阶值。
U:从色度信号中减去Y得到的蓝色信号的差异值。
V:从色度信号中减去Y得到的红色信号的差异值。

YUV颜色编码主要用在电视系统以及视频编解码标准中,在这些系统中,Y通道信息可以单独使用,这样黑白电视机也能接收和显示信号。而彩色信息则通过U和V两个通道传输,只有彩色电视机才能处理。这样设计兼容了黑白电视和彩色电视。YUV色彩空间相比RGB色彩空间,更加符合人眼对亮度和色彩的敏感度,在视频压缩时,可以按照人眼的敏感度对YUV数据进行压缩,以达到更高的压缩比。由于历史和技术的原因,YUV的标准存在多种,例如YUV 4:4:4、YUV 4:2:2和YUV 4:2:0等,这些主要是针对U和V通道的采样方式不同定义的。采样不同,对应的压缩比也不同。

(3)图像压缩格式

jpeg:Joint Photographic Experts Group,是一种常见的用于静态图像的损失性压缩格式,它特别适合于全彩色和灰度图片,被广泛使用。通常情况下,JPEG可以提供10:1到20:1的有损压缩比,根据图像质量自由调整。
png: Portable Network Graphics,PNG是一种无损压缩格式,主要使用了DEFLATE算法。由于这是无损压缩,所以解压缩图像可以完全恢复原始数据。被广泛应用于需要高质量图像的场景,如网页设计、艺术作品等。
bmp:Bitmap,BMP是Windows系统中常用的一种无压缩的位图图像格式,通常会创造出较大的文件。

位图(Bitmap)是一种常见的计算机图形,最小单位是像素,每个像素都包含一定量的信息,如颜色和亮度等。位图图像的一个主要特点就是,在放大查看时,可以看到图像的像素化现象,也就是我们常说的"马赛克"。BMP、JPEG、GIF、PNG等都是常见的位图格式。

(4)H264和H265:这是两个视频压缩格式,也是两种视频编解码标准。以1280*720的摄像头为例,如果是rgb8格式的原图,一帧图像的大小是:

3*1280*720=27648000字节,即2.7648MB

如果是一小时的视频,那将是非常大的数据量,对网络传输,数据存储,都是很大的压力。而H264通过种种帧间操作,可以达到10:1到50:1的压缩比,甚至更高。H265更进一步,压缩比更高,用来解决4K或8K视频的传输。

更具体的原理见:图像编码与 H264 基础知识在自动驾驶领域,图像数据也使用h264格式,主要用于数采和回放,控制数据量。

usb_cam

(1)linux针对摄像头硬件有一套Video for Linux内核驱动框架,对应提供的有命令行工具 v4l2-ctl (Video for Linux 2),可以查看摄像头硬件信息:

ls /dev/video0  //一般video0是笔记本自带摄像头设备文件
v4l2-ctl -d /dev/video0 --all

这里截取了部分关键信息,下面的usb_cam的launch文件将用到:

(2)usb_cam是ros里usb camera的软件包,一般称为ros摄像头驱动,但这是一个应用程序,其调用v4l2并通过ros topic发出图像数据。搞机器视觉,第一步就是要有图。安装并启动usb_cam,查看图像:

sudo apt-get install ros-noetic-usb-cam 
roslaunch usb_cam usb_cam-test.launch
rqt_image_view

usb_cam-test.launch:

<launch><node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" >//指定设备文件名,默认是/dev/video0<param name="video_device" value="/dev/video0" />// 宽和高分辨率	<param name="image_width" value="640" /><param name="image_height" value="480" />// 像素编码,可选值:mjpeg,yuyv,uyvy<param name="pixel_format" value="yuyv" /><param name="color_format" value="yuv422p" />// camera坐标系名<param name="camera_frame_id" value="usb_cam" />// IO通道,可选值:mmap,read,userptr,大数据量信息一般用mmap<param name="io_method" value="mmap"/></node><node name="image_view" pkg="image_view" type="image_view" respawn="false" output="screen">// 指定发出的topic名:/usb_cam/image_raw<remap from="image" to="/usb_cam/image_raw"/><param name="autosize" value="true" /></node>
</launch>

(3)/usb_cam/image_raw的数据结构体:

rostopic info /usb_cam/image_raw
rosmsg show  sensor_msgs/Image

//消息头,每个topic都有
std_msgs/Header header	uint32 seqtime stamp// 坐标系名string frame_id
// 高和宽分辨率
uint32 height
uint32 width
// 无压缩的图像编码格式,包括rgb8,YUV444
string encoding
// 图像数据的大小端存储模式
uint8 is_bigendian
// 一行图像数据的字节数量,作为步长参数
uint32 step
// 存储图像数据的柔性数组,大小是step*height
uint8[] data

/usb_cam/image_raw内容展示:

(4)/usb_cam/image_raw/compressed的数据结构体:

rostopic info /usb_cam/image_raw/compressed
rosmsg show sensor_msgs/CompressedImage

std_msgs/Header headeruint32 seqtime stampstring frame_id
// 压缩的图像编码格式,jpeg,png
string format
uint8[] data

/usb_cam/image_raw/compressed内容展示:

摄像头标定

标定引入

(1)Calibration:翻译过来就是校准和标定。(2)摄像头标定:Camera Calibration是计算机视觉中的一种关键技术,其目的是确定摄像头的内部参数(Intrinsic Parameters)和外部参数(Extrinsic Parameters)。

内部参数:包括焦距、主点坐标以及镜头畸变等因素。这些参数与相机本身的硬件有关,如镜头和图像传感器等,一般由厂家提供。
外部参数:摄像头相对于环境的位置和方向。例如,它可能描述了一个固定摄像头相对于周围环境的姿态或者安装位置。以汽车为例,外参包括各个摄像头之间的关系,摄像头与radar,摄像头与lidar的关系。

(3)汽车各种传感器的之间的相对位置和朝向,用3自由度的旋转矩阵和3自由度的平移向量表示,这些外参由整车厂自己标。一般整车下线之后,进入特定的房间,使用静态标靶、定位器的等高精度设备,完成Camera、radar、Lidar等传感器的标定,称之为产线标定,也叫做下线标定。

笔记本摄像头内参标定

这里我们使用标定常用的标靶图形,完成笔记本摄像头的内参标定。usb_cam可以使用内参标定,避免图像畸变。(1)安装标定功能包(ubuntu20.04+noetic)

sudo apt-get install ros-noetic-camera-calibration

(2)创建 robot_vision 软件包,并标定相关文件

cd ~/catkin_ws/src
catkin_create_pkg robot_vision cv_bridge image_transport sensor_msgs std_msgs geometry_msgs message_generation roscpp rospycd robot_vision 
mkdir doc launch
touch launch/cameta_calibration.launch

标定靶图片:

cameta_calibration.launch:

<launch>// 使用usb_cam包,发出/usb_cam/image_raw图像数据<node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" ><param name="video_device" value="/dev/video0" /><param name="image_width" value="640" /><param name="image_height" value="480" /><param name="pixel_format" value="yuyv" /><param name="camera_frame_id" value="usb_cam" /><param name="io_method" value="mmap"/></node>// 使用标定功能包,完成标定。// 参数中,8x6表示横向8个内部角点,竖向有6个// square 是每个棋盘格的边长// /usb_cam/image_raw是监听的图像topic<nodepkg="camera_calibration"type="cameracalibrator.py"name="camera_calibration"output="screen"args="--size 8x6 --square 0.024 image:=/usb_cam/image_raw camera:=/usb_cam"/>
</launch>

(3)编译并运行

cd ~/catkin_ws/
catkin_make --source src/robot_vision 
source devel/setup.bash
roslaunch robot_vision cameta_calibration.launch

不断晃动,直到COMMIT按键亮起,然后点击,即可生成标定文件,本人的路径为:/home/mm/.ros/camera_info/head_camera.yaml。

opencv和cv_bridge引入

(1)opencv和cv_bridge

安装opencv(ubuntu20.04+noetic):

sudo apt-get install ros-noetic-vision-opencv libopencv-dev python3-opencv

(2)opencv和cv_bridge的简单架构图如下:

根据这个图,在ros里,处理图像的流程一般是:

    # 第一步:使用cv_bridge将ROS的图像数据转换成OpenCV的图像格式cv_image = cv_bridge.imgmsg_to_cv2(msg, "bgr8")# 第二步:使用opencv进行图像处理。。。# 第三步,再将opencv格式额数据转换成ros image格式的数据ros_image = cv_bridge.cv2_to_imgmsg(cv_image, "bgr8")

(3)在 上节的robot_vision包里,我们新增一个cv_bridge的小样例,主要功能是在捕捉到的图像上打个蓝色的圆标。

本文不深入讲解opencv,推荐一个资料:W3Cschool - OpenCV教程

cv_bridge_test.py:

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import rospy
import cv2
from functools import partial
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Imagedef image_cb(msg, cv_bridge, image_pub):# 使用cv_bridge将ROS的图像数据转换成OpenCV的图像格式try:cv_image = cv_bridge.imgmsg_to_cv2(msg, "bgr8")except CvBridgeError as e:print(e)# 在opencv的显示窗口中绘制一个圆,作为标记# cv_image.shape返回一个元组,包含图像的行数(高度),列数(宽度)和通道数(通常是3个通道:BGR)(rows, cols, channels) = cv_image.shape# 当图像的宽度和高度都大于60时,才执行画圆标动作if cols > 60 and rows > 60:# 在计算机图像处理中,图像的原点(0,0)通常定义为图像的左上角。(60,60)是圆心的坐标。# 30是圆的半径。# (255,0,0)定义了圆的颜色。在OpenCV中,默认的颜色空间是BGR,所以这其实是绘制了一个蓝色的圆。# -1表示填充圆。如果这个值是正数,则代表绘制的圆的线宽;如果是负数,则代表填充该圆。cv2.circle(cv_image, (60,60), 30, (255,0,0), -1)# 使用Opencv的接口,显示Opencv格式的图像cv2.imshow("ycao: opencv image window", cv_image)cv2.waitKey(3)# 再将opencv格式额数据转换成ros image格式的数据发布try:image_pub.publish(cv_bridge.cv2_to_imgmsg(cv_image, "bgr8"))except CvBridgeError as e:print(e)def main():rospy.init_node("cv_bridge_test")rospy.loginfo("starting cv_bridge_test node")bridge = CvBridge()image_pub = rospy.Publisher("/cv_bridge_image", Image, queue_size=1)bind_image_cb = partial(image_cb, cv_bridge=bridge, image_pub=image_pub)// 订阅/usb_cam/image_raw,然后再回调函数里处理图像,并发布出来rospy.Subscriber("/usb_cam/image_raw", Image, bind_image_cb)rospy.spin()cv2.destroyAllWindows()
if __name__ == "__main__":main()

cv_bridge_test.launch

<launch><node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" ><param name="video_device" value="/dev/video0" /><param name="image_width" value="640" /><param name="image_height" value="480" /><param name="pixel_format" value="yuyv" /><param name="camera_frame_id" value="usb_cam" /><param name="io_method" value="mmap"/></node><nodepkg="robot_vision"type="cv_bridge_test.py"name="cv_bridge_test"output="screen"/><nodepkg="rqt_image_view"type="rqt_image_view"name="rqt_image_view"output="screen"/>
</launch>

(4)编译并运行

cd ~/catkin_ws/
catkin_make --source src/robot_vision 
source devel/setup.bash
roslaunch robot_vision cv_bridge_test.launch

总结

本文主要系统介绍了机器视觉处理的外围知识,引入了opencv和cv_bridge,后面几篇文章,我们将用它们继续丰富 robot_vision 软件包。

相关文章:

从基础到人脸识别与目标检测

前言 从本文开始&#xff0c;我们将开始学习ROS机器视觉处理&#xff0c;刚开始先学习一部分外围的知识&#xff0c;为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本&#xff0c;系统采用Ubuntu20.04&#xff0c;ROS采用noetic。 颜…...

Elasticsearch:在 Elastic 中玩转 DeepSeek R1 来实现 RAG 应用

在这个春节&#xff0c;如一声春雷&#xff0c;DeepSeek R1 横空出世。现在人人都在谈论 DeepSeek R1。这个大语言模型无疑在中国及世界的人工智能发展史上留下了重要的里程碑。那么我们改如何结合 DeepSeek R1 及 Elasticsearch 来实现 RAG 呢&#xff1f;在之前的文章 “使用…...

寒假2.6--SQL注入之布尔盲注

知识点 原理&#xff1a;通过发送不同的SQL查询来观察应用程序的响应&#xff0c;进而判断查询的真假&#xff0c;并逐步推断出有用的信息 适用情况&#xff1a;一个界面存在注入&#xff0c;但是没有显示位&#xff0c;没有SQL语句执行错误信息&#xff0c;通常用于在无法直接…...

CTF中特别小的EXE是怎么生成的

我们在打CTF时候&#xff0c;出题的爷爷们给出的exe都很小 就10k左右&#xff0c;有的甚至就5k&#xff0c;那时候我很郁闷啊。现在我也能了啊哈哈 不多bb按如下操作&#xff1a; 我们来看看正常的release生成的代码# Copy #include "windows.h" int main(){ Messa…...

git rebase 和 git merge的区别

Rebase 可使提交树变得很干净, 所有的提交都在一条线上。 Merge 则是包含所有的调试记录&#xff0c;合并之后&#xff0c;父级的所有信息都会合并在一起 Rebase 修改了提交树的历史 比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的&#xf…...

Gitlab中如何进行仓库迁移

需求&#xff1a;之前有一个自己维护的新仓库A&#xff0c;现在需要将这个仓库提交并覆盖另一个旧的仓库B&#xff0c;需要保留A中所有的commit信息。 1.方法一&#xff1a;将原有仓库A导出后再导入到新的仓库B中 适用场景&#xff1a;新的仓库B是一个待建仓库&#xff0c;相当…...

LabVIEW 开发航天项目软件

在航天项目软件开发中&#xff0c;LabVIEW 凭借其图形化编程优势被广泛应用。然而&#xff0c;航天项目的高可靠性、高精度及复杂环境适应性要求&#xff0c;使得在使用 LabVIEW 开发时&#xff0c;有诸多关键要点需要特别关注。本文将详细分析在开发航天项目软件时需要重点注意…...

深度整理总结MySQL——MySQL加锁工作原理

MySQL加锁工作原理 前言前置知识- 锁为什么加在索引上锁的粒度优化提高并发性避免全表扫描优化死锁处理解决幻读问题 什么SQL语句会加行级锁MySQL是如何加行级锁场景模拟代码唯一索引等值查询退化为记录锁为什么会退化为记录锁分析加了什么锁为什么会退化为间隙锁为什么我可以插…...

kafka专栏解读

kafka专栏文章的编写将根据kafka架构进行编写&#xff0c;即先编辑kafka生产者相关的内容&#xff0c;再编写kafka服务端的内容&#xff08;这部分是核心&#xff0c;内容较多&#xff0c;包含kafka分区管理、日志存储、延时操作、控制器、可靠性等&#xff09;&#xff0c;最后…...

1-portal认证功能

很多时候公共网络需要提供安全认证功能&#xff0c;比如我们去星巴克或者商场、酒店&#xff0c;我们连接wifi上网的时候&#xff0c; 需要认证后才可以上网。 用户可以主动访问已知的Portal认证网站&#xff0c;输入用户名和密码进行认证&#xff0c;这种开始Portal认证的方式…...

MySQL面试题合集

1.MySQL中的数据排序是怎么实现的? 回答重点 排序过程中,如果排序字段命中索引,则利用 索引排序。 反之,使用文件排序。 文件排序中,如果数据量少则在内存中排序, 具体是使用单路排序或者双路排序。 如果数据大则利用磁盘文件进行外部排序,一 般使用归并排序。 知识…...

spring学习(druid、c3p0的数据源对象管理)(案例学习)

目录 一、博客引言。 二、阿里云-druid案例准备(依赖坐标、配置文件、测试类)。 &#xff08;1&#xff09;初始依赖坐标、配置文件与测试类。 &#xff08;2&#xff09;导入阿里云-druid依赖坐标。 &#xff08;3&#xff09;DruidDataSource。 &#xff08;4&#xff09;set…...

WordPress博客在fnOS环境下的极简搭建与公网地址配置指南

文章目录 前言1. Docker下载源设置2. Docker下载WordPress3. Docker部署Mysql数据库4. WordPress 参数设置5. 飞牛云安装Cpolar工具6. 固定Cpolar公网地址7. 修改WordPress配置文件8. 公网域名访问WordPress 前言 你是否曾经为搭建自己的网站而头疼不已&#xff1f;是不是觉得…...

【PG】DROP TABLE ... CASCADE

问题 ERROR: cannot drop table wx_user_tag because other objects depend on it DETAIL: default value for column id of table wx_user_tag depends on sequence wx_user_tag_id_seq HINT: Use DROP … CASCADE to drop the dependent objects too. 解决 这个错误消息表…...

绕组电感 - Ansys Maxwell 磁通链与电流

在本博客中&#xff0c;我将演示如何使用 Ansys Maxwell 中磁瞬态求解器的磁通链和电流结果来计算绕组电感。Ansys Maxwell 磁瞬态求解器在场计算中考虑了涡流效应&#xff0c;我将展示一种使用磁通链和电流结果来计算绕组电感的简单方法。 实际上&#xff0c;电感是非线性的…...

物联网软件开发与应用方向应该怎样学习,学习哪些内容,就业方向是怎样?(文末领取整套学习视频,课件)物联网硬件开发与嵌入式系统

随着物联网技术的飞速发展&#xff0c;物联网软件开发与应用方向成为了众多开发者关注的焦点。那么&#xff0c;如何在这个领域中脱颖而出呢&#xff1f;本文将为你提供一份详细的学习指南&#xff0c;帮助你从零开始&#xff0c;逐步掌握物联网软件开发与应用的核心技能。 一…...

《LeetCode Hot100》 Day01

Day01 轮转数组 思路&#xff1a; &#xff08;1&#xff09; 使用O(1) 空间复杂度解决&#xff0c;就需要原地解决&#xff0c;不能创建新的数组。 &#xff08;2&#xff09; 先整体反转数组&#xff0c;再反转前k个数&#xff0c;再反转剩下的数。即可完整本题。 &…...

vue动态table 动态表头数据+动态列表数据

效果图: <template><div style"padding: 20px"><el-scrollbar><div class"scrollbar-flex-content"><div class"opt-search"><div style"width: 100px"> </div><div class"opt-b…...

1.3 GPT vs BERT 终极选择指南:从架构差异到企业级落地策略

GPT vs BERT 终极选择指南&#xff1a;从架构差异到企业级落地策略 引言&#xff1a;两大巨头的分道扬镳 2018年&#xff0c;BERT和GPT系列同时引爆NLP领域&#xff0c;却在架构选择上走向截然不同的道路&#xff1a; BERT采用双向Transformer Encoder&#xff0c;在11项NLP…...

python-leetcode 23.回文链表

题目&#xff1a; 给定单链表的头节点head,判断该链表是否为回文链表&#xff0c;如果是&#xff0c;返回True,否则&#xff0c;返回False 输入&#xff1a;head[1,2,2,1] 输出&#xff1a;true 方法一&#xff1a;将值复制到数组中后用双指针法 有两种常用的列表实现&#…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...