当前位置: 首页 > 编程日记 > 正文

android专题-蓝牙扫描、连接、读写

android专题-蓝牙扫描、连接、读写

概念

外围设备

可以被其他蓝牙设备连接的外部蓝牙设备,不断广播自身的蓝牙名及其数据,如小米手环、共享单车、蓝牙体重秤

中央设备

可以搜索并连接周边的外围设备,并与之进行数据读写通讯,如手机

日常生活中常见的场景是手机app通过蓝牙开启共享单车,手机app通过蓝牙获取蓝牙体重秤的体重结果,这时候共享单车、蓝牙体重秤就称为外围设备,而手机就称为中央设备

经典蓝牙BT

泛指支持蓝牙协议在4.0以下的模块,一般用于数据量比较大的传输,如:语音、音乐等较高数据量的传输。经典蓝牙模块又可细分为:传统蓝牙和高速蓝牙模块。传统蓝牙模块在2004年推出,主要代表是支持蓝牙2.1协议的模块,在智能手机爆发的时期得到了广泛的使用。高速蓝牙模块在2009年推出,速率提高到约24Mbps,传输速率是经典蓝牙的八倍,可以轻松的应用于录像机到电视、PC到PMP、UMPC到打印机之间的资料传输。

低功耗蓝牙BLE

是指支持蓝牙协议4.0或者以上的模块,也被称为BLE模块,最大的特点就是成本和功耗的降低,可以应用于实时性要求较高的产品当中,比如:智能家居类(蓝牙锁、蓝牙灯)、传感设备的数据发送(血压计、温度传感器)、消费类电子(电子烟、遥控玩具)等。

目前市面上大部分的蓝牙都是4.0以上的低功耗蓝牙,经典蓝牙已经很少见到了。

通讯过程

一个外围设备可以发布多个服务service,每个服务可以包含多个特征值characteristic,每个特征值都有他的属性,例如长度(size),权限(permission),值(value),描述(descriptor),读写通讯都是通过Characteristic进行的。
每个service、characteristic都含有一个对应的UUID,通过和外围设备蓝牙约定UUID来进行读写通讯。

整个通讯流程为:

创建蓝牙实例
搜索扫描外围设备
连接外围设备
获取外围设备的服务service
获取服务的特征characteristic
从外围设备读取数据,即读数据
给外围设备发送数据写数据,即写数据
断开连接

常用业务API

1.判断当前蓝牙是否已经开启,如果没有开启提示用户开启
2.实时扫描周边蓝牙,获取蓝牙名给用户选择
3.解析蓝牙广播数据处理业务
4.监听外围设备发送给app的数据,处理对应业务
5.app发送数据给外围设备以处理业务

Android常用的第三方蓝牙框架:okble、Android-BluetoothKit

以okble为例:

package com.wrs.project.module.app.common.bluetooth;
import android.content.Context;
import android.util.Log;import com.a1anwang.okble.client.core.OKBLEDevice;
import com.a1anwang.okble.client.core.OKBLEDeviceImp;
import com.a1anwang.okble.client.core.OKBLEDeviceListener;
import com.a1anwang.okble.client.core.OKBLEOperation;
import com.a1anwang.okble.client.scan.BLEScanResult;
import com.a1anwang.okble.client.scan.DeviceScanCallBack;
import com.a1anwang.okble.client.scan.OKBLEScanManager;
import com.a1anwang.okble.common.OKBLECharacteristicModel;
import com.a1anwang.okble.common.OKBLEServiceModel;
import com.wrs.project.module.app.common.AppMgr;import java.util.List;public class Bluetooth implements DeviceScanCallBack, OKBLEDeviceListener{private OKBLEScanManager scanManager;private OKBLEDevice okbleDevice;// 当前连接的蓝牙设备private OKBLECharacteristicModel writeCharacteristicModel; // 蓝牙可写的Characteristicprivate Context context = AppMgr.context;private String tag = "Bluetooth";public Bluetooth() {scanManager = new OKBLEScanManager(context);scanManager.setScanCallBack(this);}/*** 扫描到蓝牙设备* @param device* @param rssi*/@Overridepublic void onBLEDeviceScan(BLEScanResult device, int rssi) {Log.e(tag, "扫描到蓝牙设备:" + device.toString());String localName = device.getCompleteLocalName();if (null != localName && localName.startsWith("ABC-")) {stopScanBluetooth();connectBluetoothDevice(device);}}/*** 扫描失败* @param code*/@Overridepublic void onFailed(int code) {}@Overridepublic void onStartSuccess() {}/*** 蓝牙是否已经开启* @return*/public boolean bluetoothIsEnable() {if (null != scanManager) {scanManager.bluetoothIsEnable();}return false;}/*** 关闭手机蓝牙*/public void disableBluetooth() {if (null != scanManager) {scanManager.disableBluetooth();}}/*** 打开手机蓝牙*/public void enableBluetooth() {if (null != scanManager) {scanManager.enableBluetooth();}}/*** 开始扫描蓝牙*/public void startScanBluetooth() {if (null != scanManager) {if (!scanManager.isScanning()) {scanManager.startScan();}}}/*** 停止扫描蓝牙*/public void stopScanBluetooth() {if (null != scanManager) {if (scanManager.isScanning()) {scanManager.stopScan();}}}/*** 断开蓝牙连接*/public void disConnect() {if (null != okbleDevice) { // 如果当前已经连接其他设备,先断开连接okbleDevice.removeDeviceListener(this);okbleDevice.disConnect(false);okbleDevice = null;}}/*** 发送数据给蓝牙设备* @param data*/public void writeData(byte[] data) {if (null != okbleDevice && null != writeCharacteristicModel && null != data && data.length > 0) {okbleDevice.addWriteOperation(writeCharacteristicModel.getUuid(), data, new OKBLEOperation.WriteOperationListener() {@Overridepublic void onWriteValue(byte[] value) {Log.e(tag, "蓝牙写数据成功");}@Overridepublic void onFail(int code, String errMsg) {Log.e(tag, "蓝牙写数据失败:" + code + " " + errMsg);}@Overridepublic void onExecuteSuccess(OKBLEOperation.OperationType type) {}});} else {Log.e(tag, "蓝牙写数据失败: 蓝牙没有连接或没发现可写Characteristic");}}public void connectBluetoothDevice(BLEScanResult device) {// 先断开当前连接disableBluetooth();okbleDevice = new OKBLEDeviceImp(context, device);okbleDevice.addDeviceListener(this);okbleDevice.connect(true);//true表示连接断开后OKBLE的会自动重连}/*** 蓝牙设备连接成功* @param deviceTAG*/@Overridepublic void onConnected(String deviceTAG) {Log.e(tag, "设备连接成功 " + deviceTAG);// 连上蓝牙后,获取蓝牙的WriteCharacteristic用后面给蓝牙设备发送数据,获取蓝牙的ReadCharacteristic用来监听蓝牙设备发送过来的数据List<OKBLEServiceModel> serviceModels = okbleDevice.getServiceModels();if (null != serviceModels && serviceModels.size() > 0) {for (int i = 0; i < serviceModels.size(); i++) {OKBLEServiceModel serviceModel = serviceModels.get(i);String serviceUUID = serviceModel.getUuid();if (serviceUUID.startsWith("aaaaaaa-")) { // 匹配找到读写的服务List<OKBLECharacteristicModel> characteristicModels = serviceModel.getCharacteristicModels();if (null != characteristicModels && characteristicModels.size() > 0) {for (int j = 0; j < characteristicModels.size(); j++) {OKBLECharacteristicModel characteristicModel = characteristicModels.get(j);String characteristicUUID = characteristicModel.getUuid();if (characteristicUUID.startsWith("bbbbbbbbb") && characteristicModel.isCanWrite() && characteristicModel.isCanWriteNoResponse()) { // 匹配找到写的CharacteristicfindWriteCharacteristic(characteristicModel);} else if (characteristicUUID.startsWith("8653000b-") && characteristicModel.isCanNotify()) { // 匹配找到读的CharacteristicfindReadCharacteristic(characteristicModel);}}}break;}}}}@Overridepublic void onDisconnected(String deviceTAG) {}@Overridepublic void onReadBattery(String deviceTAG, int battery) {}/*** 接收到蓝牙发送的数据* @param deviceTAG* @param uuid* @param value*/@Overridepublic void onReceivedValue(String deviceTAG, String uuid, byte[] value) {}@Overridepublic void onWriteValue(String deviceTAG, String uuid, byte[] value, boolean success) {}@Overridepublic void onReadValue(String deviceTAG, String uuid, byte[] value, boolean success) {}@Overridepublic void onNotifyOrIndicateComplete(String deviceTAG, String uuid, boolean enable, boolean success) {}private void findWriteCharacteristic(OKBLECharacteristicModel characteristic) {if (null != okbleDevice && null != characteristic) {writeCharacteristicModel = characteristic;}}private void findReadCharacteristic(OKBLECharacteristicModel characteristic) {if (null != okbleDevice && null != characteristic) {String uuid = characteristic.getUuid();boolean enableNotifyEnable = okbleDevice.isNotifyEnabled(uuid);if (enableNotifyEnable) {Log.e(tag, "打开读属性成功");} else {okbleDevice.addNotifyOrIndicateOperation(uuid, true, new OKBLEOperation.NotifyOrIndicateOperationListener() {@Overridepublic void onFail(int code, String errMsg) {Log.e(tag, "打开读属性失败");}@Overridepublic void onExecuteSuccess(OKBLEOperation.OperationType type) {Log.e(tag, "打开读属性成功");}@Overridepublic void onNotifyOrIndicateComplete() {Log.e(tag, "打开读属性成功");}});}}}
}

蓝牙广播数据包解析

广播包有两种: 广播包 (Advertising Data)和 响应包 (Scan Response),其中广播包是每个设备必须广播的,而响应包是可选的。
每个包都是 31 字节,分为有效数据和无效数据两部分。

有效数据部分 :包含若干个广播数据单元,称为 AD Structure 。

AD Structure 的组成是:
第一个字节是长度值 Len ,表示接下来的 Len 个字节是数据部分。
数据部分的第一个字节表示数据的类型 AD Type ,剩下的 Len - 1 个字节是真正的数据 AD data 。其中 AD type 非常关键,决定了 AD Data 的数据代表的是什么和怎么解析。
无效数据部分 :因为广播包的长度必须是 31 个 byte,如果有效数据部 分不到 31 自己,剩下的就用 0 补全。这部分的数据是无效的,解释的时候,忽略即可。在 Android 中,系统会把这两个数据拼接在一起,返回一个 62 字节的数组。
在这里插入图片描述
例如:
在这里插入图片描述

第一个 字节代表广播数据单元的长度 ,02 转为10进制就是 2代表其数据长度为2 , 而数据单元的第一个字节代表类型 。
01 代表 代表物理连接功能为普通发现模式 06代表其数据类容
紧接着下一个数据单元:
0B代表数据长度为11 ,数据类型为 02 即Serviceuuid代表是非完整的16bit uuid, 所以紧接着的后10位就是其uuid。
接下来就是下一个数据单元
首位是13转为二进制就是19,其长度就是19,类型就是09 ,代表设备名称,30-》字符0,65代表字符e,61代表字符a,73代表字符s,79代表字符y,4E代表N,65代表e,57代表W,44代表D,43代表C,53代表S ,00 代表字符null,01代表字符soh(SOH是序始字符(Start Of Header),它表示标题的开始),56代表V ,31代表字符1,2E代表字符.,30代表字符0,44代表D所有其设备名称就是0easyNewDCS V1.0D。
接下来的一个数据单元长度是5,广播类型12 连接间隔范围,有四个字节,接下来数据长度是02,类型是0A代表信号强度 剩余都是00000都是补位的无效数据。

广播数据类型:
(1)Flags: TYPE = 0x01。这个数据用来标识设备 LE 物理连接的功能。DATA 是 0 到多个字节的 Flag 值,每个 bit 上用 0 或者 1 来表示是否为 True。如果有任何一个 bit 不为 0,并且广播包是可连接的,就必须包含此数据。各 bit 的定义如下: bit 0: LE 有限发现模式 bit 1: LE 普通发现模式 bit 2: 不支持 BR/EDR bit 3: 对 Same Device Capable(Controller) 同时支持 BLE 和 BR/EDR bit 4: 对 Same Device Capable(Host) 同时支持 BLE 和 BR/EDR bit 5…7: 预留
(2)Service UUID: 广播数据中一般都会把设备支持的 GATT Service 广播出来,用来告诉外面本设备所支持的 Service。有三种类型的 UUID:16 bit, 32bit, 128 bit。广播中,每种类型类型有有两个类别:完整和非完整的。这样就共有 6 种 AD Type。
非完整的 16 bit UUID 列表: TYPE = 0x02;
完整的 16 bit UUID 列表: TYPE = 0x03;
非完整的 32 bit UUID 列表: TYPE = 0x04;
完整的 32 bit UUID 列表: TYPE = 0x05;
非完整的 128 bit UUID 列表: TYPE = 0x06;
完整的 128 bit UUID 列表: TYPE = 0x07;
(3) Local Name: 设备名字,DATA 是名字的字符串。 Local Name 可以是设备的全名,也可以是设备名字的缩写,其中缩写必须是全名的前面的若干字符。 设备全名: TYPE = 0x08 设备简称: TYPE = 0x09
(4)TX Power Level: TYPE = 0x0A,表示设备发送广播包的信号强度。DATA 部分是一个字节,表示 -127 到 + 127 dBm。
(5) 带外安全管理(Security Manager Out of Band):TYPE = 0x11。DATA 也是 Flag,每个 bit 表示一个功能: bit 0: OOB Flag,0 表示没有 OOB 数据,1 表示有 bit 1: 支持 LE bit 2: 对 Same Device Capable(Host) 同时支持 BLE 和 BR/EDR bit 3: 地址类型,0 表示公开地址,1 表示随机地址 。
(6)外设(Slave)连接间隔范围:TYPE = 0x12。数据中定义了 Slave 最大和最小连接间隔,数据包含 4 个字节:
前 2 字节:定义最小连接间隔,取值范围:0x0006 ~ 0x0C80,而 0xFFFF 表示未定义; 后 2 字节:定义最大连接间隔,同上,不过需要保证最大连接间隔大于或者等于最小连接间隔。
(7) 服务搜寻:外围设备可以要请中心设备提供相应的 Service。其数据定义和前面的 Service UUID 类似:
16 bit UUID 列表: TYPE = 0x14
32 bit UUID 列表: TYPE = 0x??
128 bit UUID 列表: TYPE = 0x15
(8) Service Data: Service 对应的数据。
16 bit UUID Service: TYPE = 0x16, 前 2 字节是 UUID,后面是 Service 的数据;
32 bit UUID Service: TYPE = 0x??, 前 4 字节是 UUID,后面是 Service 的数据;
128 bit UUID Service: TYPE = 0x??, 前 16 字节是 UUID,后面是 Service 的数据;
(9) 公开目标地址:TYPE = 0x17,表示希望这个广播包被指定的目标设备处理,此设备绑定了公开地址,DATA 是目标地址列表,每个地址 6 字节。
(10) 随机目标地址:TYPE = 0x18,定义和前一个类似,表示希望这个广播包被指定的目标设备处理,此设备绑定了随机地址,DATA 是目标地址列表,每个地址 6 字节。
(11) Appearance:TYPE = 0x19,DATA 是表示了设备的外观。
(12) 厂商自定义数据: TYPE = 0xFF,厂商自定义的数据中,前两个字节表示厂商 ID,剩下的是厂商自己按照需求添加,里面的数据内容自己定义。

项目源码:https://codechina.csdn.net/android1/projectbasic
上篇:android专题-数据库Room 目录 下篇: Android专题-常用第三方框架

如果觉得可以就点个👍吧,欢迎粉丝收藏,土豪打赏,您的关注就是我们创作的动力!

读者有什么想看的相关技术篇章,欢迎评论留言!

QQ交流群:908058499

相关文章:

2 并发编程--开启进程的两种方式

multiprocessing 英 /mʌltɪprəʊsesɪŋ/ n. [计][通信] 多重处理 1、multiprocessing 模块介绍 python中的多线程无法利用多核优势&#xff0c;如果想要充分地使用多核CPU的资源&#xff08;os.cpu\_count\(\)查看&#xff09;&#xff0c;在python中大部分情况需要使用多…

POJ 2112 Optimal Milking(二分+最大流)

POJ 2112 Optimal Milking 题目链接 题意&#xff1a;给定一些机器和奶牛&#xff0c;在给定距离矩阵&#xff0c;&#xff08;不在对角线上为0的值代表不可达&#xff09;&#xff0c;每一个机器能容纳m个奶牛。问全部奶牛都能挤上奶&#xff0c;那么走的距离最大的奶牛的最小…

ajax的loading方法,Ajax加载中显示loading的方法

使用ajaxStart方法定义一个全局的“加载中。。。”提示$(function(){$("#loading").ajaxStart(function(){$(this).html.("");});$("#loading").ajaxSuccess(function(){$(this).html.("");// $(this).empty(); // 或者直接清除});});…

机器学习02-分类、逻辑回归

目录 一、分类问题 Classification 二、分类问题的估值 Hypothesis Representation 三、分类问题的决策边界 Decision Boundary 四、分类问题的代价函数 Cost Function 五、简化的代价函数与梯度下降Simplified Cost Function & Gradient Descent 5.1 简化代价函数 …

python绘制盖尔圆并做特征值的隔离

本程序并非智能到直接运行隔离出所有特征值&#xff0c;而是需要高抬贵手&#xff0c;手动调节变换矩阵D的参数&#xff0c;以实现特征值的隔离。若期待直接找到能特征值隔离的D矩阵参数变化范围&#xff0c;怕足下要失望了&#xff0c;鄙人暂没有做到那一步&#xff0c;一是因…

mysql 电商项目(一)

mysql 电商项目 - MySQL数据库开发规范 1、数据库基本设计规范  2、索引设计规范 3、数据库字段设计规范 4、数据库SQL开发规范 5、数据库操作行为规范 转载于:https://www.cnblogs.com/Eric15/articles/9719814.html

Android专题-常用第三方框架

Android专题-常用第三方框架 HTTP网络请求 带*号的是个人推荐比较好用的 HTTP网络请求 okhttp * :https://github.com/square/okhttp retrofit:https://github.com/square/retrofit Volley:https://github.com/google/volley Android Async HTTP:https://github.com/andr…

WPF显示经常使用的几个显示文字控件TextBox, TextBlock, Lable

WPF显示经常使用的几个显示文字控件TextBox&#xff0c; TextBlock&#xff0c; Lable TextBox&#xff0c; TextBlock。 Lable 当中TextBox 和Lable均继承了Control类 能够对其进行模板编辑。而TextBlock没有继承Control所以不能对其进行模板编辑 我的程序中须要做一个二级菜单…

机器学习03-神经网络

目录 一、非线性估值Non-Linear Hypothesis 二、神经网络建模 Neural Network 三、复习逻辑回归问题矩阵式 3.1 没有进行正则化 3.2 进行正则化 四、神经网络的代价函数 4.1 符号约定Notation 4.2 代价函数 五、反向传播算法 Backpropagation Alg 5.1 任务 5.2 一个…

python 打包

一、下载 pip install Pyinstaller 二、使用Pyinstaller 1、使用下载安装的方式安装的Pyinstaller打包方式 将需要打包的文件放在解压得到的Pyinstaller文件夹中&#xff0c;打开cmd窗口&#xff0c;把路径切换到当前路径打开命令提示行&#xff0c;输入以下内容&#xff08;最…

iOS架构篇-3 网络接口封装

iOS架构篇-3 网络接口封装 关键字:iOS,网络接口封装,Alamofire,swift 网络接口API通常都需要自己封装一套管理,这里以swift版的Alamofire为例. 实现功能: 1.暴露参数请求地址url、请求方法method、请求参数params、请求头header、请求响应response(响应数据、响应头resp…

coursera 《现代操作系统》 -- 第十一周 IO系统

本周要求 错题 下列I/O控制方式中&#xff0c;哪一个不需要硬件支持&#xff1f; 中断方式 轮询方式 DMA方式 I/O处理机方式 中断方式&#xff1a;中断控制器 轮询方式&#xff1a;CPU不断查询设备以了解其是否就绪 DMA:使用到了 DMA 控制器 4。 在设备管理中&#xff0c;缓冲…

matlab图形绘制基础(东北大学MOOC笔记)

%% 二维图形绘制 % 多纵轴曲线绘制 figure(1); t 0:0.01:2*pi; y1 sin(t); y2 10*cos(t); % plotyy(t, y1, t, y2); yyaxis left plot(t, y1); ylim([min(y1), max(y1)]); yyaxis right plot(t, y2); ylim([min(y2), max(y2)]);% 绘制极坐标图 figure(2); theta 0 : 0.01 :…

【转载】pycharm远程调试配置

pycharm远程调试配置https://www.cnblogs.com/liangjiongyao/p/8794324.html

Tornado 类与类组合降低耦合

转载于:https://www.cnblogs.com/shiluoliming/p/6760548.html

matlab生成多组多维高斯分布数据

matlab生成多组多维高斯分布数据 之所以写这么一个函数&#xff0c;是因为在练习用matlab实现聚类分析&#xff0c;用matlab生成的高斯分布数据可以作为很好的数据。当然&#xff0c;直接load进鸢尾花数据集也可以拿来练手&#xff0c;到后边再对鸢尾花数据集进行分析。 代码…

Android架构篇-5 CI/CD(持续集成、持续交付、持续部署)

Android架构篇-5 CI/CD(持续集成、持续交付、持续部署) CI CI是指持续集成,代码的更新会定期自动构建、测试并合并到公共仓库中,方便多分支时解决冲突问题 CD CD是指持续交付和/或持续部署,开发人员改动代码会自动测试提交到仓库,运维实施人员将其部署到生产环境中,方…

OpenCV中图像以Mat类型保存时各通道数据在内存中的组织形式及python代码访问各通道数据的简要方式...

OpenCV中图像以Mat类型保存时各通道数据在内存中的组织形式及python代码访问各通道数据的简要方式 以最简单的4 x 5三通道图像为例&#xff0c;其在内存中Mat类型的数据组织形式如下&#xff1a; 每一行的每一列像素的三个通道数据组成一个一维数组&#xff0c;一行像素组成一个…

CV01-语义分割笔记和两个模型VGG ResNet的笔记

目录 一、语义分割 二、VGG模型 2.1 VGG特征提取部分 2.2 VGG图像分类部分 三、ResNet模型 3.1 为什么是ResNet 3.2 11卷积调整channel维度大小 3.3 ResNet里的BottleNeck 3.4 Global Average Pooling 全局平均池化 3.5 Batch Normalization 学习语义分割理论&#x…

matlab编程实现k_means聚类(k均值聚类)

1. 聚类的定义 以下内容摘抄自周志华《机器学习》 根据训练数据是否拥有标记信息&#xff0c;机器学习任务可以大致分为两大类&#xff1a;“监督学习”&#xff08;supervised learning&#xff09;和“无监督学习”&#xff08;unsupervised learning&#xff09;。分类和回…

一目了然了解JAVA集合体系

在编程中&#xff0c;常常需要集中存放多个数据。从传统意义上讲&#xff0c;数组是我们的一个很好的选择&#xff0c;前提是我们事先已经明确知道我们将要保存的对象的数量。一旦在数组初始化时指定了这个数组长度&#xff0c;这个数组长度就是不可变的&#xff0c;如果我们需…

杭电1175简单搜索 连连看

连连看 Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 34807 Accepted Submission(s): 8657 Problem Description “连连看”相信很多人都玩过。没玩过也没关系&#xff0c;下面我给大家介绍一下游戏规则&#…

IOS专栏目录

IOS 专栏目录 iOS基础篇 iOS高级篇 ios架构篇-1 项目组织架构 ios架构篇-2 国际化多语言 iOS架构篇-3 网络接口封装 iOS架构篇-4 架构模式MVVM iOS架构篇-5 CI/CD(持续集成、持续交付、持续部署) iOS专题1-蓝牙扫描、连接、读写 iOS 直播专题1-直播流程原理 iOS 直播专题2-…

CV03-双线性差值pytorch实现

一、双线性差值 1.1 公式 在理解双线性差值&#xff08;Bilinear Interpolation&#xff09;的含义基础上&#xff0c;参考pytorch差值的官方实现注释&#xff0c;自己实现了一遍。 差值就是利用已知点来估计未知点的值。一维上&#xff0c;可以用两点求出斜率&#xff0c;再…

matlab编程实现基于密度的聚类(DBSCAN)

1. DBSCAN聚类的基本原理 详细原理可以参考链接&#xff1a; https://www.cnblogs.com/pinard/p/6208966.html 这是找到的相对很详细的介绍了&#xff0c;此链接基本仍是周志华《机器学习》中的内容&#xff0c;不过这个链接更通俗一点&#xff0c;且算法流程感觉比《机器学习…

EAST 自然场景文本检测

自然场景文本检测是图像处理的核心模块&#xff0c;也是一直想要接触的一个方面。刚好看到国内的旷视今年在CVPR2017的一篇文章&#xff1a;EAST: An Efficient and Accurate Scene Text Detector。而且有开放的代码&#xff0c;学习和测试了下。 题目说的是比较高效&#xff0…

通过httpmodule获取webapi返回的信息

我写了一个webapi&#xff0c;想在module中获取请求的信息和返回的信息&#xff0c;写进log里&#xff0c;以方便以后查询。request信息很容易能拿到&#xff0c;但是返回信息得费一番周折。不多说&#xff0c;上代码 public class ResponseLoggerModule : IHttpModule {privat…

iOS SwiftUI篇-2 UI控件 Text Button Image List

iOS SwiftUI篇-2 UI控件 Text Button Image List Text 显示文本,相当于UILabel import SwiftUIstruct TextContentView: View {var body: some View {//VStack(垂直排列视图)可以将其内部的多个视图,在垂直方向进行等距排列,VStack最多可以容纳十个子视图,VStack(spacin…

numpy和torch数据操作对比

对numpy和torch数据操作进行对比&#xff0c;避免遗忘。 ndarray和tensor import torch import numpy as npnp_data np.arange(6).reshape((2, 3)) torch_data torch.arange(6) # 张量 tensor2array torch_data.numpy()print(\nnumpy array:\n, np_data,\ntorch tensor\n,…

ZooKeeper学习

一、ZooKeeper 的实现 1.1 ZooKeeper处理单点故障 我们知道可以通过ZooKeeper对分布式系统进行Master选举&#xff0c;来解决分布式系统的单点故障&#xff0c;如图所示。 那么我们继续分析一下&#xff0c;ZooKeeper通过Master选举来帮助分布式系统解决单点故障&#xff0c; 保…