Python 使用摄像头监测心率!这么强吗?
实验效果
文章插图
实验思路
- 用 opencv 打开摄像头 , 读取指定窗口区域的RGB分量均值 , 本实验读取前额皮肤
- 用 matplotlib 绘制动态序列曲线
- 用 HP 滤波过滤RGB序列的趋势部分 , 保留波动信息 , 如第2列图所示
- 对 HP 滤波后的残差 , 即波动信息 , 做FFT变换 , 获得信号频谱
- 绿色分量频谱的尖峰反映了心跳的频率 , 正常人的心跳频率在 1~2 Hz 之间
- 线程一作为生产者 , 用于 opencv 读取图片中的RGB信号 , 并发送到一个公共队列 data_queue
- 线程二作为消费者 , 但实际不消费 , 只是读取公共队列上的信息并用 matplotlib 画图
- 当公共队列满了之后 , 线程一无法插入新数据 , 这时由线程一弹出队首的数据 , 即最早的信号值
class Producer(threading.Thread):def __init__(self,data_queue,*args,**kwargs):super(Producer, self).__init__(*args,**kwargs)self.data_queue = data_queuedef run(self):capture = cv2.VideoCapture(0)# 0是代表摄像头编号 , 只有一个的话默认为0capture.set(cv2.CAP_PROP_FPS, 10)try:t0 = time.time()while (True):ref, frame = capture.read()frame = frame[:,::-1,:].copy()H, W, _ = frame.shapew, h = 40, 40x, y = W//2 -w//2, H//4-h//2area = frame[y:y + h, x:x + w, :]cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 2)frame[:h,:w] = areat = time.time()-t0cv2.putText(frame, 't={:.3f}'.format(t), (10, H-10), cv2.FONT_HERSHEY_PLAIN, 1.2, (255, 255, 255), 2)cv2.imshow("face", frame)B = np.average(area[:,:,0])G = np.average(area[:,:,1])R = np.average(area[:,:,2])if self.data_queue.full():self.data_queue.queue.popleft()self.data_queue.put((t,B,G,R))c = cv2.waitKey(10)--tt-darkmode-color: #979797;">线程二从公共队列中读取原始的 RGB 信号 , 做 HP 滤波 , 做傅里叶变换 , 作图
class Consumer(threading.Thread):def __init__(self,data_queue,*args,**kwargs):super(Consumer, self).__init__(*args,**kwargs)self.data_queue = data_queuedef run(self):time.sleep(1)fig, axes = plt.subplots(3, 3)axes[0, 0].set_title('原始信号')axes[0, 1].set_title('HP滤波残差')axes[0, 2].set_title('FFT频谱')axes[0, 0].set_ylabel('Blue')axes[1, 0].set_ylabel('Green')axes[2, 0].set_ylabel('Red')axes[2, 0].set_xlabel('Time(s)')axes[2, 1].set_xlabel('Time(s)')axes[2, 2].set_xlabel('Frequency(Hz)')start = Nonelines = [None, None, None]glines = [None, None, None]rlines = [None, None, None]flines = [None, None, None]BGR = [None, None, None]g = [None, None, None]r = [None, None, None]f = [None, None, None]num_fft = 256while True:# time.sleep(0.2)if self.data_queue.qsize() > 2:if self.data_queue.queue[-1] == 'Bye':breakts, BGR[0], BGR[1], BGR[2] = zip(*self.data_queue.queue)t = ts[-1] if len(ts) > 0 else 0for i in range(3):g[i] = hp(BGR[i], 1000)r[i] = BGR[i] - g[i]# FFTfor i in range(3):rr = r[i][-num_fft:]f[i] = np.fft.fft(rr, num_fft)f[i] = np.abs(f[i])[:num_fft//2]fs =len(rr)/ (ts[-1] - ts[-len(rr)])if start is None:start = 1lines[0] = axes[0,0].plot(ts, BGR[0], '-b')[0]lines[1] = axes[1,0].plot(ts, BGR[1], '-g')[0]lines[2] = axes[2,0].plot(ts, BGR[2], '-r')[0]glines[0] = axes[0,0].plot(ts, g[0], '-k')[0]glines[1] = axes[1,0].plot(ts, g[1], '-k')[0]glines[2] = axes[2,0].plot(ts, g[2], '-k')[0]rlines[0] = axes[0, 1].plot(ts, r[0], '-b')[0]rlines[1] = axes[1, 1].plot(ts, r[1], '-g')[0]rlines[2] = axes[2, 1].plot(ts, r[2], '-r')[0]flines[0] = axes[0, 2].plot(np.arange(num_fft//2)*fs/num_fft, f[0], '-b', marker='*')[0]flines[1] = axes[1, 2].plot(np.arange(num_fft//2)*fs/num_fft, f[1], '-g', marker='*')[0]flines[2] = axes[2, 2].plot(np.arange(num_fft//2)*fs/num_fft, f[2], '-r', marker='*')[0]for i in range(3):lines[i].set_xdata(ts)lines[i].set_ydata(BGR[i])glines[i].set_xdata(ts)glines[i].set_ydata(g[i])rlines[i].set_xdata(ts)rlines[i].set_ydata(r[i])flines[i].set_xdata(np.arange(num_fft//2)*fs/num_fft)flines[i].set_ydata(f[i])for i in range(3):axes[i,0].set_xlim([t - 10, t + 1])axes[i,0].set_ylim([np.min(BGR[i][-num_fft:]), np.max(BGR[i][-num_fft:])])axes[i, 1].set_xlim([t - 10, t + 1])axes[i, 1].set_ylim([np.min(r[i][-num_fft:]), np.max(r[i][-num_fft:])])axes[i, 2].set_xlim([0, fs//2])axes[i, 2].set_ylim([np.min(f[i]), np.max(f[i])])plt.pause(0.1)print('Consumer quit')
- 程序员为教师妻子开发应用:将iPhone变成文档摄像头
- 想自学Python来开发爬虫,需要按照哪几个阶段制定学习计划
- 未来想进入AI领域,该学习Python还是Java大数据开发
- 或使用天玑1000+芯片?荣耀V40已全渠道开启预约
- 外媒赞赏戴尔笔记本的摄像头隐私挡板设计 希望其它厂商能够跟进
- 苹果将推出使用mini LED屏的iPad Pro
- 手机能用多久?如果出现这3种征兆,说明“默认使用时间”已到
- 三星再曝屏下摄像头新专利,Redmi Note 10 Pro获得认证
- 三星再曝屏下摄像头新专利 消灭打孔屏指日可待
- 三星又一屏下摄像头技术专利曝光:要和打孔说再见