日常碎碎念 2021.07.20

前言

时间过的飞快,转眼间7月份就过去一半了,实习第一周也就这么过去了。不知不觉间,透过《日常碎碎念》这个形式的周志,来填充博客的内容看来是非常明智的选择。毕竟技术文章的产出是需要时间的沉淀的,中间的空窗期不写点东西上来的话,很容易让自己失去继续写博客的热情🔥。通过这段时间的记录📝,除了感受到生活的苦,也能更好的发现生活中的甜。希望这个栏目能够继续延续下去,继续记录生活(即使没人来看😂)。

上周总结

  1. 完成实习中的第一个需求的开发
  2. 宜家餐厅游记
  3. 去了MUJI Diner吃了个晚饭
  4. 好物分享:《米家充电台灯》
  5. 亿本新书:《岩田先生》

1. 完成实习中的第一个需求的开发

大公司就是不一样,所有的开发流程都是十分规范的。大概流程就是: 产品经理从内部系统发需求单,部门leader分配任务,开发者接单,评估时间,完成开发,等待测试验收,体验环境测试,外网上线。

本周的主要任务是为内部的一个广告平台增加了一个小功能。任务的大致内容就是给一个表单加一个按钮而已。但万万没想到的是这个表单的生成为了解耦和方便配置,做成了JSON生成的表单系统。这些其实都是小问题,不就是编写配置文件嘛,so easy啦!如果你也是这么想的,那就掉坑里了,我也是。这东西的配置项完全没有整理文档,表单生成器一行注释都没有。不幸中的万幸就是开发者还在助理,遇到技术难题还能请教一下,要是离职了就更痛苦了。

作为实习生,总不能一上来就说做不了吧,这也太尴尬了😅。没办法,只能硬着头皮上了,慢慢翻源码打断点,慢慢理解前开发者的idea。最终还是在周五完成了✅开发任务,大概也就比原来的计划慢了一天。(其实也没这么痛苦😖,只是要费点时间而已,做是肯定能做出来的。另外腾大晚上的免费晚饭着实是成为了我奋斗💪坚实的后盾,每天解决完一个小问题后,跑去吃顿爽的,再回来开工,着实是让人放松愉悦呀😄。)

2. 宜家餐厅游记

长这么大个人,去宜家就去了两次,这周末是第二次。第一次去甚至都没机会品尝一下宜家餐厅,吃了1元的冰淇淋🍦就走了。趁这次周末有空余时间,又来到了深圳,出去逛一下。因此选定了宜家餐厅作为目的地之一。

我去的深圳宜家,位于欧洲城。从白石洲站出来,转M487公共汽车,一直搭到总站就行了,全程1.5元,可以直接刷羊城通

大致方向

别人去宜家,主要目的应该是为了购买家具🪑。我这次去宜家,主要的目的就是吃吃吃。废话不多说,直接上图。

1.香草三文鱼

整顿饭中最令人欣喜的部分,真的是好好吃啊😋。鱼肉本身就很有油脂的香味,搭配芥末酱吃,芥末的辣味更能激发出鱼本身的鲜味。再撒上旁边配的柠檬汁,酸酸的,非常的清新开胃。作为前菜而言,非常棒👍。(一楼门口的食品超市有的卖,但是考虑到晚上还要去MUJI Diner,拿着不太方便;另外秉持着”少食多滋味”的教训,还是没买下来。待到下次再来的时候,再细细品尝吧😄)

2.瑞典🇸🇪肉丸

听说这是宜家的招牌菜,于是乎就拿了一份。但说实话,就是普普通通的猪肉丸的味道,可能比较新奇的是,配了一点蓝莓🫐酱,可以蘸着吃,解解腻。凭良心讲,我是不推荐大家点这个的,不会特别惊艳。

3.肉酱意面🍝

点这个纯粹是我怕不够吃拿的。大概就是普通的番茄肉酱意面吧,没啥可圈可点的。

4.提拉米苏

这个提拉米苏貌似也是招牌点心,因此拿了一份尝一下。确实挺好吃的,有点甜,跟朗姆酒的酒味一中和,感觉非常协调。用这份甜点🍮收尾确实不错。

全餐总花费83.2元,如果不吃意大利面的话还能更便宜。另外取餐的时候看到牛扒貌似也好好吃的样子,下次可以来试试看。感觉宜家以后会成为我的一个周末消遣的好去处。

另外这次最大的一个收获,就是买了3.5元的宜家环保袋♻️,真的是好好看啊。便宜耐操值了!

3. 去了MUJI Diner吃了个晚饭

全球第一家MUJI Hotel,全球第二家MUJI Diner就位于深圳的深业上城2楼处。去到旗舰店除了吃饭,还买到了限定的小零食和杯子,好开心啊😄。

溜达了一圈,又出去Shopping Mall逛了一圈,直到下午6点才走回去吃了一顿饭。但是真的是好贵啊,想要吃日式定食,还是中午来吃吧🤷‍♂️。

首先,最近推出的58元夏日新品套餐仅限中午,晚上就只有牛扒汉堡等大菜,而且巨贵,一份和牛沙拉88元,一份植物肉汉堡78元。(虽然处于好奇心,我还是点了植物肉汉堡,但真的好痛心,毕竟这个价钱,中午已经可以吃上沙拉+主菜+甜品了)

汉堡是用黄油🧈煎过的,因此非常的酥脆。搭配植物肉的”肉香味”,非常的好吃,薯条也是新鲜脆口的,很赞。但是植物肉的口感就不敢恭维了,就跟小卖部,售价1元一包的素肉一样,没啥区别,与真正的肉饼还是有一段距离的。如果财力雄厚确实可以来试试看。

4. 好物分享:《米家充电台灯》

最近搬了新的房子,可恶的是灯只有一个开关,设置在入口处。这个设计主要是为了回到家的时候,在门口就能开灯。但最反人类的地方在于,上床前,你得走到门口,关了灯,摸黑走回床边。太弱智了吧!

因此,最近上小米商城,购入了价值99元的米家充电台灯。

4.1 优点

  1. 纯白MUJI简约风
  2. 金属材质
  3. 价格便宜
  4. 无频闪

4.2 缺点

  1. Micro USB充电头
  2. 只有三种模式,暖光亮,暖光暗,白光亮
  3. 充电提示灯不明显(如果开着灯,很难察觉到闪烁,提醒自己要充电)

4.3 使用感受

简洁大气,MUJI简约风+价格便宜,实在是一把好台灯。质感也非常棒,金属材质摸起来非常稳重。作为床头灯或者办公台灯都是非常好的选择。

5. 亿本新书:《岩田先生》

最近在稽核看到了这本新书,出于好奇买了下来。

这本书主要是写了两个部分。

首先是岩田聪的传奇一生,从札幌的天才高中程序员,到HAL研究所社长,再到任天堂社长,他是如何一步步走过来的,他的个人经历与经验中,对创意的思考。

其次是岩田聪在公司治理理念等方面的语录。

这本书我也是刚刚到货,刚刚看。以后有时间也会跟着《自制编程语言》之后,将读后感或者读书笔记分享出来。目前暂定这个读书栏目为”亿本新书”,希望能够在这个栏目上有所产出呀。

下周计划

  1. 上班摸🐟
  2. 需求转测试 做下一个开发任务
  3. 背单词
  4. 每天中午做一部分的雅思题目(当日做错题积累)

总结

周末确实是玩爽了,这周又要开始做打工人啦。加油LOL!

最近博客会迁移到腾讯云,到时候就会有HTTP2.0啦,搭配对象存储,国内的访问速度应该能更快了。

mapStateToProps 和 mapDispatchToProps的区别

背景

最近开始实习,部门主要使用的是一个内部的跨端框架,这东西本质上还是React Native,只不过是做了一层套娃重命名了标签名称(像极了ReactRouter和ReactRouter-Dom的关系),并且封装📦了自己的一堆私有的API。由于导师就是负责开发这个框架,因此很无奈只能转React,做React开发了(印证了之前的猜想)。

今天这篇文章主要就分享一下我在使用Redux过程中的一些笔记📒或者使用经验。

(本文章不会详细阐述其具体的使用方法,只是会记录一些实际应用的效果)

区别

  1. 相同点
  • 两者都是函数
  • 用于将Redux的Store映射到组件props(入口参数)上
  1. 异同点
函数名称官方描述个人理解
mapStateToProps建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系将Store上的State挂载到props上 (实现了单向数据流)
mapDispatchToProps建立 UI 组件的参数到store.dispatch方法的映射将Store上的Dispatch函数挂载到(调用的时候,相当于是在触发Store上的函数)

实例

(此处引用阮一峰老师提供的“计数器”代码,作为本文章的例子,感谢阮老师的分享🙏)

初始化环境

 import React, { Component } from 'react'
 import ReactDOM from 'react-dom'
 import { createStore } from 'redux'
 import { Provider, connect } from 'react-redux'

创建计数器组件

 class Counter extends Component {
   // 创建渲染器
   render() {
     // 从入口参数上获取mapState和mapDispatch挂载的属性和方法
     const { value, onIncreaseClick } = this.props
     // 返回JSX
     return (
       <div>
         <span>{value}</span>
         <button onClick={onIncreaseClick}>Increase</button>
       </div>
    )
  }
 }

创建Action

Action说明了Redux中可以做什么动作。

 const increaseAction = { type: 'increase' }

有点类似于Java中的抽象类的概念

创建Reducer

Reducer说明了Action做什么动作。

 function counter(state = { count: 0 }, action) {
   // 读取Redux中管理的count变量
   const count = state.count
   // 检测dispatch触发了哪一个动作
   switch (action.type) {
       // 触发"增加"动作
     case 'increase':
       // 将值+1并返回一个state到Redux的Store中
       return { count: count + 1 }
     default:
       return state
  }
 }

有点类似于Java中的实现类

PS:

Action和Reducer的关系,在我看来有点类似于抽象实现的关系,不知道🤷‍♂️对不对,以后再继续深入探究一下。

创建Store

 const store = createStore(counter)

实现挂载函数

 // Redux的角度: 将Redux管理的全局State(Store)上的值挂载到了组件上
 // 组件的角度: 获取(订阅)了Redux管理的全局State(Store)上的值
 function mapStateToProps(state) {
   return {
     value: state.count
  }
 }
 ​
 // Redux的角度: 将操作全局State(Store)上的方法挂载到了组件上
 // 组件的角度: 获取了管理的全局State(Store)的方法
 function mapDispatchToProps(dispatch) {
   return {
     onIncreaseClick: () => dispatch(increaseAction)
  }
 }

挂载到组件上

 // Connected Component
 const App = connect(
   mapStateToProps,
   mapDispatchToProps
 )(Counter)

页面渲染

 ReactDOM.render(
   <Provider store={store}>
     <App />
   </Provider>,
   document.getElementById('root')
 )

流程图例

Redux与组件之间是如何传递reducer和state以及组件如何dispatch reducer的
  • 通过mapStateToProps将全局State挂载到了组件的Props上,组件因此可以直接读取到Store中管理的state值。
  • 通过mapDispatchToProps将管理全局State的方法挂载到了组件的Props上,组件就可以从入参中获取操作方法,进而在组件中dispatch(触发)State中的Reducer进而修改State中的值。

结尾

这篇文章主要是梳理了一下本周解决的一个知识点,加深自私点在脑海中的印象。文章中阐述不清楚的点,欢迎透过邮箱📮与发起讨论,希望透过讨论也能让你明白这个过程发生了什么。

延伸资料

  1. React-Redux 的用法(中文)
  2. 使用mapStateToProps导出数据(官方英文)
  3. 使用mapDispatchToProps触发Actions(官方英文)
  4. 计算器代码(Github)PS:建议可以先不看🙈这两个函数入参的描述,因为很容易把人搞晕😵,可以着重看看[1]中,阮一峰老师提供的实例,搭配[2] [3]思考🤔一下,相信你很快也能get到这个函数在干什么,有什么用🤷‍♂️。

日常碎碎念 2021.07.13

前言

期末考试完成,终于可以启程去🐧厂搬砖了。

上周总结

  1. 深圳租房
  2. 入职鹅厂
  3. 周末去深圳湾公园拍了照

1. 深圳租房

说实话,虽然来了深圳很多次了,但来深圳租房子还是第一次,也是在外租房子的第一次☝️。着实是花了不少的时间做准备,不过幸好有家人👪的帮助,最终还是顺利租下了一间房子。(虽然对比合租贵了几倍,但是优越的交通和商业便利性,绝对是物有所值的)

另外,u1s1,深圳租房是真TM贵,要不是来大厂了,真的还得倒贴钱,或者住更远的郊区去。但深圳的公共交通我认为是真的稀烂,便利程度跟妖都相比简直就是辣鸡🚮。(也有可能是因为鹅厂的写字楼太偏僻了,给我造成公共交通不方便的错觉。)

腾讯的写字楼专门建在立交桥边,根本没有什么很好的公共交通通勤,要么是搭班车,要么就只能选择骑电驴或者自行车去,再或者是走路,上下班痛苦的一批,太傻逼了。

2. 入职鹅厂

普天同庆同庆,我终于也能成为一名鹅厂人咯LOL。6月底的时候,深圳还有疫情,搞得我心神不宁的,差点就要改期去阿里了。(其实阿里也很香,家-公司通勤时间只要30min)。不过万幸的是,在政府有效率的处理+大家的努力配合,遏制住了疫情,最终我也得以来到深圳加入鹅厂,开始实习工作,希望在这几个月的实习中能够有所收获吧。

我的牛牛鹅宝

3. 深圳湾公园游记

周末搬完家之后,就出去逛一下,去了深圳湾公园。(主要是网上说有一个地铁口出来就能看到大海,所以慕名前来,虽然还是走错了😂)吹着微微的海风真的很舒服,还看到了新石器的无人车在肯德基,深圳政府真的好有钱啊,能够吸引这么多的高新企业来落地,慕了慕了。

对象就是香港元朗了🇭🇰

本周计划

  1. 上班摸🐟
  2. 学习开发规范 & 开发流程 (Hippy好难用啊🤯)
  3. 背单词 + 雅思口语准备
  4. 准备雅思考试

总结

来到鹅厂的这几天,真的是感觉非常充实,好吃的饭堂,965的工作时间,完善的人事流程&福利,都让我非常感恩,虽然不考虑转正,但希望日后从海外学成归来时,还能做企鹅人呀LOL

PS:昨天出成绩了,绩点3.75,人生巅峰啊😄,希望能够再创佳绩吧🤷‍♂️

日常碎碎念 2021.07.02

前言

u1s1,学校真的是NT,7月2日最后一门考试开卷+手机考试+线下考试。干嘛不早点考试嘛?真的是脑瘫,之前那么多空余时间,👴就是玩,就是不考试,就吊着你,不让你离校,真的是吐了🤮。白白浪费了起码一周的时间,搞得实习+学习的计划都要往后延迟,麻烦的一批。美名其曰对你好,佛了🙏。

本周总结

  1. 完成GuBlog前端页面的开发
  2. 完成Instagram Downloader快捷指令的开发
  3. 技术分享《PPG式心率测量小程序的技术分享》和《React脚手架V1.0》
  4. 期末考试结束

1.完成GuBlog前端页面的开发

下周就要去实习了,之前Leader说可以提前了解一下React Native的最佳实践。但是很可惜的是,我大学期间学习的是Vue,没办法👐,只能从头开始学习React咯🤷‍♂️。经过6月下半个月的学习,大致掌握了React和React Router的使用,因此开了这个小项目来实践一下。预估完成✅时间需要一个月,目前只是抱着做着玩的心态在编程,能做多少就做多少,不期望能够完整实现,目标只是为了熟悉一下React的脚手架搭建以及React的开发流程。

2.完成Instagram Downloader快捷指令的开发

3.技术分享《PPG式心率测量小程序的技术分享》和《React脚手架V1.0》

最近一周有空,将之前的一些项目和当前的一些新的进度做了总结分享以及技术储备,在博客上记录📝下来。希望留待以后能够用上。

4.期末考试结束

终于考完期末考试啦🥳!!LOL

终于可以在家里开始摸🐟生活了,被困在学校一个半月了,学校距离市区比较远,困了一个半月真的是无聊🥱死了。终于可以回到城区开始划水生活了,开心!

下周计划

  1. 鹅厂实习
  2. 准备雅思口语

总结

时光飞逝,转眼间2021年就过去一半了。虽然6月的突然袭来了疫情,但是靠着政府强有力的把控,还是成功的在一个月内控制住了疫情的蔓延,并最终清零,🐂🍺的不行!7月终于要迎来人生的第一次实习生活了(而且还是在从未想象过的鹅厂),希望能够顺顺利利吧。加油!

[快捷指令] Instagram下载器【已废弃】

背景

最近把Facebook的账号找回来了,上Instagram看了一下。发现有好多胶卷的照片真的好毒啊!好好看,好想下载下来做背景图。但是此时我才发现,Ins居然是不能长按照片下载的,包括网页端也是需要透过插件,解析出埋藏在DOM结构中的图片链接🔗才能下载的。这种防君子不防小人的防盗链方法真的是非常的鄙视🤬。但是能透过DOM中找到图片的链接,并且实验证明是可以直接下载的,已经属于不幸中的万幸了。(要是加上UA验证之类的就死掉☠️了)

最近我从菊花切换回了Apple,所以就可以借助快捷指令+Scriptable来帮助我自动化完成解析下载。

快捷指令链接:

https://www.icloud.com/shortcuts/ebce83c29b3b4c63b782adae6656bb80

前置要求:

  1. 安装Instagram
  2. 安装Scriptable

使用说明:

  1. 点击想要下载的图片的···
  2. 选择分享到..
  3. 选择Instagram Downloader
  4. 等待运行
  5. 返回到Instagram时则代表运行完成

注意事项⚠️:

  1. 作者如果设置了Private的话,快捷指令将无法下载图片
  2. 账号被Ban,快捷指令同样无法下载图片

React脚手架V1.0

背景

最近开始捯饬React,想着学会了React就能把React Native给学习了,然后就能写App。为了能够在React Native上少踩坑,我搭建这个脚手架之后,准备开发一个博文发布系统,作为DEMO练练手。

依赖构成

  1. React核心
  2. React-router-dom 路由
  3. Redux 状态管理
  4. ESlint 代码格式管理
  5. Vite 打包工具
  6. Axios 网络请求库
  7. Antd UI库

搭建流程

1. 初始化React

1.1 模板初始化

我们可以直接使用Vite自带的模板,初始化React,在本教程中我使用的是template指令来初始化项目。

运行以下指令初始化项目

 # npm 6.0
 npm init @vitejs/app gublog(项目名称) --template react(模板名称)
 ​
 # npm 7.0+ (需要额外的双横线)
 npm init @vitejs/app gublog(项目名称) --(额外的双横线) --template react(模板名称)

运行指令后,npm将创建一个以项目名称命名的文件夹📁,来初始化项目。

初始化完成后的文件树结构为:

 root
 └── gublog(项目名称)
    ├── dist
    ├── node_modules
    ├── src
    ├── index.html
    ├── package-lock.json
    ├── package.json
    └── vite.config.js

1.2 启动环境

  1. cd 项目文件夹
  2. npm install #下载依赖
  3. npm run dev #启动开发环境
环境启动成功

出现上图的网页时,我们就已经搭建出Vite + React的开发环境

2. 安装React-router-dom

为了能够实现无刷新切换页面,此处我的脚手架也会引入官方提供的Router来做前端路由管理。

2.1 安装依赖

运行指令安装路由组件

 npm install react-router-dom --save-dev

2.2 注意⚠️

React-Router-Dom就是React Router V4的浏览器版本,原来文档的Router要被替换成BrowserRouter才能使用。为了不使用方面,我们可以使用as关键字来修改别名。

 import 'BrowserRouter' as 'Router' from 'react-router-dom'

这里我真的很想吐槽,搞一个React-Router就好了嘛🤷‍♂️,干嘛还搞一个React-Router-Dom。这东西本质上就是套娃再封装📦。而且最近的V4版本还舍弃了旧的Router标签,改成了BrowserRouter。真的是NT。

解决方法来源:

3. 安装Redux

安装Redux主要是为了使得组件的耦合降低,使得更容易的管理状态,因此被打包到脚手架当中。

3.1 安装依赖

 npm install redux --save-dev

4. 安装ESlint

4.1 安装依赖

 npm install eslint --save-dev

5. 安装Axios网络请求库

5.1 安装依赖

 npm install axios --save-dev

6. 安装AntdUI库

AntdUI是阿里针对React设计的一个UI库,开箱即用,有丰富的文档。(而且还能改颜色)

6.1 安装依赖

 npm install redux --save-dev

6.2 引入环境

  1. main.css中添加import 'antd/dist/antd.css'导入CSS库
  2. Main.jsx的代码头部添加import {组件名称} from antd导入组件

结尾

这篇文章就此告一段落。Axios和ESlint的使用我还在摸索中,后面还会继续更新,敬请期待。目前在使用这个脚手架重写GuBlog日志程序当中,后面时机成熟后也会同新版本的脚手架文章一同推出。

npm 安装、删除依赖命令

npm安装依赖

  • npm install xxx 利用 npm 安装xxx依赖到当前命令行所在目录
  • npm install xxx -g 利用npm安装全局依赖xxx
  • npm install xxx –save 安装并写入package.json的”dependencies”中
  • npm install xxx –save-dev 安装并写入package.json的”devDependencies”中

npm删除依赖

  • npm uninstall xxx 删除xxx依赖
  • npm uninstall xxx -g 删除全局依赖xxx

PPG式心率测量小程序的技术分享

故事

背景

去年6月份的时候,参加了微信小程序官方举办的《高校微信小程序开发大赛》,做出了《活力健身房》这个小程序。按照初版设计,除了可以检测用户的跑步数据外,还可以测量用户的静止心率。但由于个人除了要准备比赛,还要准备痛苦的概率论补考,所以导致日程太过于紧凑,因此在该情况下,我就只完成了第一个目标而忽略了第二个心率测量的目标没有实现。

改进

在暑假期间,处于追求完美的想法,我花了一部分的时间继续学习并完成了心率检测算法的V1.0版本。而后再在这周,时隔一年的时间,重写了检测算法,推出了V1.1的版本。(至于为什么算是1.1版,在文中会说明的😂)。

技术分享

今天我写这篇文章的主要目的是跟大家分享以下的三点内容:

  1. PPG式心率测量的原理
  2. PPG式心率测量的实现
  3. 应用到小程序端的难点

1.技术原理

光体积变化描记图法 (Photoplethysmography,简称PPG) 是借光电手段在活体组织中检测血液容积变化的一种无创检测方法。当一定波长的光束照射到指端皮肤表面,每次心跳时,血管的收缩和扩张都会影响光的透射 (例如在透射PPG中,通过指尖的光线) 或是光的反射 (例如在反射PPG中,来自手腕表面附近的光线)。当光线透过皮肤组织然后再反射到光敏传感器时,光照会有一定的衰减。像肌肉、骨骼、静脉和其他连接组织对光的吸收是基本不变的 (前提是测量部位没有大幅度的运动),但是动脉会不同,由于动脉里有血液的脉动,那么对光的吸收自然也会有所变化。[1]

PPG检测的核心是通过心脏跳动影响血管中血液光线的吸收率变化,进而影响血液对光的反射率,估算受测者的心率数据。用人话说,心脏的跳动,会影响血液反射光的强度。反映到图表上,就如下图所示,心脏的舒张和收缩,会影响光强变化,形成下图的波动曲线。而心率的估算,则是通过计算60s内,有多少个波峰或者波谷。比如60s内,图表上产生了60个个波峰,则意味着1s中,心脏收缩和扩张了一次,即跳动了一次,进而可以得出60BPM的心率数据。

PPG传感器工作原理

2.技术实现

根据技术原理可以知道,PPG检测中我们需要做的事情是,计算一段时间内出现了多少个波峰,即可估算出受测者的心率。由此我们需要考虑实现以下的几个功能:

  1. 获取视频流中的某一帧
  2. 检测传感器是否被覆盖
  3. 获取当前帧的光强数据
  4. 计算波峰个数
  5. 计算BPM

1.获取视频中的某一帧

在当今社会中,人人都会有一台的智能手机。而手机上最好的颜色传感器就是摄像头了,因此在小程序端的实现中,对于数据的来源主要是摄像头。

为了实现方便,这次我依旧是使用小程序作为我的开发平台。之所以使用小程序的核心原因是官方提供了摄像头的API,使得我可以通过简单的$API$就可以获取到来自用户摄像头的视频流数据,并且不用考虑兼容性的问题。

在小程序中,我是用的是wx.createCameraContext()API来获取到视频流,同时借助onCameraFrame()来直接获取到每一帧的视频信息,极大的降低了开发的难度。通过前述的API,我们可以直接获取到当前帧Uint8数组,也就是每个像素的RGBA值。

2.检测传感器是否被覆盖

为了避免外部光照影响血管对光线的反射,因此需要受测者的手指完全覆盖摄像头。为了检测摄像头是否覆盖,我们可以通过经验主义设定一个阈值,当每个像素的实际光强小于阈值,则代表传感器完全被覆盖,此时就可以开始测试。

3.获取当前帧的光强数据

通过API,我们可以获得视频流中特定的一帧的Uint8数据。之所以使用Uint8是因为在该RGBA中,每一个通道的颜色只有256种,而我们仅仅是需要获取该帧中的光强。因此选用Uint8可以大幅度减少运算量,提高运行效率。

通过经验主义的测量以及结合论文[2] [3]的理论支持,我们可知血液在红光和绿光的照射下的吸收率是最大的,也就意味着强度值是最大的。因此可以更好的检测到强度变化,所以在后续的开发当中,我们都将使用单个像素中的红绿通道的强度值,来作为数据的来源。由于只使用了其中两个通道的强度值,我们的运算量就可以几何级数减少。

从原来的232个数据转变为了216个数据

获取光强的方法是:

该帧的光强值=单个通道的强度值像素的个数

4.计算波峰个数

在1.0版本中我的实现方法是通过经验值测算出一条固定的基线,来判断历史采集的光强值,有多少个跃出基线的数据,就计作多少个波峰。

v1 固定基线版

但这种设计是有问题的,比如最右侧的波峰,因为各种外部原因强度没有达到基线,导致丢失。当通过人眼的辨识,这个波峰不属于噪声,应该是要被记录的。

另外通过测试我们可以很容易的发现下图的假波峰和假波谷的噪声信号,这种变化幅度过大的数据也应该是被丢弃的。

噪声曲线

因此在1.1版本中,基线的计算方法做了改进。通过动态基线可以适应复杂环境光线的变化,提高测量的效率和准确性。

v2 动态基线版

通过动态基线,判断有多少多少次实际的跃出,进而换算出有多少个波峰。

5.计算BPM

仅仅记录波峰个数是远远不够的,我们还要计算一次跃出所消耗的时间(从波谷到跃出基线到波峰所消耗的时间)。因为受测者的光线环境如果不稳定,含有频繁的光线变化,则可能导致光强变化幅度过大,造成数据的计算误差。因此我们还需要将跃出时间作为计算的一个因素,计算平均的跃出时间。最后计算出每分钟的心率次数:

测量最大时间平均跃出时间

小程序端的技术难点

到目前为止,小程序端最大的难点不在于测量算法的开发上。反而是在优雅的展示上画了大量的时间。

  1. FrameData大小
  2. Canvas绘图效率低

1.FrameData大小。通过Camera组件,我们可以直接获取到手机的视频流。由于我们这个算法中仅仅需要计算帧的光强,所以仅仅需要最低质量的FrameData即可。即降低了资源的消耗也提升了算法运行的效率。通过frame-data属性,修改为small即可启用最低质量的FrameData。

2.Canvas绘图效率低。在去年小程序退出了Canvas 2D的绘图API,可以实现同层渲染。为了更高的渲染效率,我自然就是选择并使用了最新的API进行曲线的绘制。在电脑模拟器上的表现确实印证了效率更好的这一说法。但诡异的是在Android端上,Canvas2D的运行效率非常低,导致经常性假死,不知道怎么修复。因此只能暂时被搁置,待到日后有能力后再做修复。

收获

这次的开发通过学习更多前人的研究结论来帮助我将算法优化出来,收获满满。感觉到非常的振奋人心。期待后面能继续搞点好玩的DEMO。

未来的方向

最近看到Tensorflow的BlazeFace。可以用很小的体积的模型来识别人脸,感觉可以借此,结合PPG算法,实现rPPG式的检测算法,日后有时间可以继续搞一下,感觉会非常的好玩。同时Canvas的问题也需要被修复,或许能够通过WebGL来修复?但是我还不肯定,需要后续的实验才能证明,待到后面再修吧。

结尾

由于本人不是电子工程专业,所以上文中可能会涉及到部分不规范的说明和解释。如果您能够在评论中帮助我指出来并帮助我改正,我将十分感激您,谢谢。

参考

[1] ECG/PPG量测解决方案

[2] 不同波长近红外光下手掌静脉图像质量分析

[3] 血液成分对光吸收规律的实验研究

日常碎碎念 2021.6.20

上周总结

  1. 疫苗接种
  2. 期末考试
  3. 重写PPG式心率测量算法
  4. 学习React

1.疫苗接种。学校终于通知我们打疫苗了,下个月要去深圳实习,打了疫苗才能安心一点呀。打疫苗过程中真的是热死🥵人了,被热蒸气熏蒸,感觉人都快晕掉了,好在以后不用再接种了。(希望)

2.期末考试。上周四的时候,终于将最重要也是最难的两科考试考完了,普天同庆。虽然NT学校安排了一门开卷考试在7月2日。但是可以放松下来,随心所欲的学习也是不错的感觉。希望最后一门考试也能取得好成绩吧。

3.重写PPG式心率测量算法。之前在《活力健身房》中做了一个心率测量的功能。但是随着我的电脑更换,我那部分的代码被迫丢失了。因此趁着最近有空余时间了,整理了一下之前的思路,重写了整个心率测量的算法。并且由于最近也在学习React+React Native。所以希望将这套算法重写验证后,移植到React Native上,做出一个App上架到App Store中国呢赚一点零花钱。(到时候我也会单独写一篇文章来介绍算法的原理以及具体的实现方式)

3.学习React。下个月就要开始鹅厂的实习生活了,问了leader有什么内容需要我提前学习的。React就是入门的门槛了,后面还要转成React Native。看来跨端开发真的是未来5-6年的发展方向啊。UniAPP,Vue多端编译,React Native。另外React的JSX语法其实还挺有趣的,感觉小程序与React的实现非常相似,后面也打算出一些关于小程序底层的分析文章,到时候可以相互比较一下思路。

本周计划

  1. 学习React
  2. 完成《创新创业课程实训》
  3. 完成《PPG式心率测量小程序的技术分析》撰写
  4. 摸鱼

结尾

这一周的主旋律还是摸🐟为主,毕竟没有很难得考试了。可以花点时间摸摸鱼休息一下,如果能出门去逛一下就更好了