作者 | Jose Garcia

译者 | 吴振东

校对 | 张一豪、林亦霖,编辑 | 于腾凯

来源 | 数据派(ID:datapi)

导读:本文将利用OpenCV,Python和Ubidots来编写一个行人计数器程序,并对代码进行了较为详细的讲解。

数字图像处理(DIP)技术目前发展非常迅速,这在很大程度上要归功于开发人员可以访问云来运用机器学习技术。通过云处理数字图像可以绕过任何专用硬件的要求,这使得使用DIP成为了大家的首选。作为处理图像最经济和最通用的方法,DIP已经被广泛应用。这种其中最常见的当属行人检测和计数 - 这对于机场、火车站、零售店体育馆、公共活动和博物馆来说都是一项非常有用的指标。
现有的传统行人计数技术不仅价格昂贵,而且它们所生成的数据通常与专有系统相关联,这些系统限制了数据提取和KPI的优化选择。相反,使用你的个人相机和SBC的嵌入式DIP不仅可以节省时间和金钱,还可以根据你所关注的KPI来自由定制应用程序,并从云中获取独特的领悟。
使用云来启用DIP IoT(物联网)应用程序可以增强整体的功能性。随着可视化、报告、警报和交叉引用外部数据源(如天气、实时供应商定价或业务管理系统)等功能的增强,DIP为开发人员提供了他们所需的自由空间。
想象一下一家摆着冰淇淋冰柜的杂货店:他们想要追踪统计经过店门口的人数,顾客所选择的产品,以及门被打开的次数和冰柜的内部温度。 从这几个数据点中,零售商可以运行相关性分析,以更好地了解和优化他们的产品定价和冰箱的整体能耗。
为了开启你的数字图像处理应用程序开发工作,Ubidots运用OpenCV和Python来创建了一套人员计数系统教程,用于分析统计给定区域中的人数。其实不仅是统计人数这样简单,添加Ubidots IoT开发平台的资源还可以扩展你的应用程序。在这里,你可以看到如何实现利用Ubidots来构建的实时人数统计仪表板。
在本文中,我们将介绍如何使用OpenCV和Ubidots来实现简单的DIP叠加并创建行人计数器。此示例最适用于任何基于Linux的发行版系统,也适用于Raspberry Pi,Orange Pi或类似的嵌入式系统。
有关其他集成的查询,请与Ubidots支持中心取得联系,以便来了解你的企业如何使用这项增值技术。
目录:
  1. 应用需求
  2. 编码 – 8个小节
  3. 测试
  4. 创造你自己的仪表板
  5. 结果展示
1、应用需求

  • 任何带有Ubuntu衍生版本的嵌入式Linux
  • 操作系统中安装了Python 3或更高版本
  • OS中安装了OpenCV 3.0或更高版本。如果使用Ubuntu或其衍生产品,请按照官方安装教程或运行以下命令:
pip install opencv-contrib-python

当你成功安装Python 3和OpenCV时,你可以通过这段简单的代码来进行检验(首先在你的terminal里输入‘python’)
import cv2 cv2.__version__

你应该在屏幕上看到你所安装的OpenCV版本:
按照官方操作指南来安装Numpy,或者运行下面的命令
pip install numpy

安装imutils
pip install imutils

安装requests
pip install requests

2、编码
可以在这一章节找到检测和发送数据的整个例程。为了更好地解释这段代码,我们将其分为八个部分,以便更好地解释代码的各个方面,让你更容易理解。
第1节
from imutils.object_detection
import non_max_suppression
import numpy as np
import imutils
import cv2
import requests
import time
import argparse URL_EDUCATIONAL = "http://things.ubidots.com"
URL_INDUSTRIAL = "http://industrial.api.ubidots.com"
INDUSTRIAL_USER = True  # Set this to False if you are an educational user
TOKEN = "...."  # Put here your Ubidots TOKEN
DEVICE = "detector"  # Device where will be stored the result
VARIABLE = "people"  # Variable where will be stored the result # Opencv pre-trained SVM with HOG people features
HOGCV = cv2.HOGDescriptor()
HOGCV.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

在第1节中,我们导入必要的库来实现我们的探测器,imutils是一个有用的DIP库工具,让我们从结果中执行不同的转换,cv2是我们的OpenCV Python包装器,requests 可以通过HTTP发送数据/结果到Ubidots,argparse让我们从脚本中的命令终端来读取命令。
重要提示:不要忘记使用您的Ubidots帐户TOKEN更改这段代码,如果是学生用户,请务必将INDUSTRIAL_USER设置为FALSE。
导入库后,我们将对方向梯度直方图(Histogram of Oriented Gradient)进行初始化。方向梯度直方图的简称是HOG,它是最受欢迎的对象检测技术之一,已经在多个应用程序中实现并取得成功。OpenCV已经以高效的方式将HOG算法与支持向量机这种用于预测目的的经典机器学习技术(SVM)相结合,成为了一笔我们可以利用的财富。
这项声明:
cv2.HOGDescriptor_getDefaultPeopleDetector()调用了预先训练的模型,用于OpenCV的行人检测,并提供支持向量机特征的评估功能。
第2节
def detector(image):    ''' @image is a numpy array ''' image = imutils.resize(image, width=min(400, image.shape[1]))   clone = image.copy()    (rects, weights) = HOGCV.detectMultiScale(image, winStride=(8, 8),  padding=(32, 32), scale=1.05)   # Applies non-max supression from imutils package to kick-off overlapped    # boxes rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects])  result = non_max_suppression(rects, probs=None, overlapThresh=0.65) return result

detector()函数是“神奇”诞生的地方,它可以接收分成三个颜色通道的RGB图像。为了避免出现性能问题,我们用imutils来调整图像大小,再从HOG对象调用detectMultiScale()方法。然后,检测多尺度方法可以让我们使用SVM的分类结果去分析图像并知晓人是否存在。关于此方法的参数介绍超出了本教程的范围,但如果你想了解更多信息,请参阅官方OpenCV文档或查看Adrian Rosebrock的精彩解释。
HOG分析将会生成一些捕获框(针对检测到的对象),但有时这些框的重叠会导致误报或检测错误。为了避免这种混淆,我们将使用imutils库中的非最大值抑制实用程序来删除重叠的框 - 如下所示:
图片转载自
https://www.pyimagesearch.com
第3节
def localDetect(image_path):   result = [] image = cv2.imread(image_path)  if len(image) <= 0:  print("[ERROR] could not read your local image")    return result   print("[INFO] Detecting people")    result = detector(image)    # shows the result  for (xA, yA, xB, yB) in result: cv2.rectangle(image, (xA, yA), (xB, yB), (0, 255, 0), 2)    cv2.imshow("result", image) cv2.waitKey(0)  cv2.destroyAllWindows() return (result, image)

现在,在这一部分代码中,我们必须定义一个函数来从本地文件中读取图像并检测其中是否有人存在。为了实现这一点,我只是简单地调用了detector()函数并添加了一个简单的循环来绘制探测器的圆框。它会返回检测到的框的数量和带有绘制检测的图像。然后,只需在新的OS窗口中重新创建结果即可。
第4节
def cameraDetect(token, device, variable, sample_time=5):  cap = cv2.VideoCapture(0)   init = time.time()  # Allowed sample time for Ubidots is 1 dot/second   if sample_time < 1:  sample_time = 1 while(True):    # Capture frame-by-frame    ret, frame = cap.read() frame = imutils.resize(frame, width=min(400, frame.shape[1]))   result = detector(frame.copy()) # shows the result  for (xA, yA, xB, yB) in result: cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2)    cv2.imshow('frame', frame)  # Sends results if time.time() - init >= sample_time:    print("[INFO] Sending actual frame results")    # Converts the image to base 64 and adds it to the context  b64 = convert_to_base64(frame)  context = {"image": b64}    sendToUbidots(token, device, variable,  len(result), context=context)   init = time.time()  if cv2.waitKey(1) & 0xFF == ord('q'):   break   # When everything done, release the capture cap.release()   cv2.destroyAllWindows() def convert_to_base64(image):   image = imutils.resize(image, width=400)    img_str = cv2.imencode('.png', image)[1].tostring() b64 = base64.b64encode(img_str) return b64.decode('utf-8')

与第3节的函数类似,第4节的函数将调用detector()方法和绘图框,并使用OpenCV中的VideoCapture()方法直接从网络摄像头检索图像。我们还稍微修改了officialOpenCV,从而在相机中获取图像,并且每隔“n”秒将结果发送到一个Ubidots帐户(sendToUbidots()函数将在本教程的后面部分进行回顾)。 函数convert_to_base64()会将图像转换为基本的64位字符串,这个字符串对于在HTML Canvas widget中使用JavaScript代码查看Ubidots中的结果非常重要。
第5节
def detectPeople(args): image_path = args["image"]  camera = True if str(args["camera"]) == 'true' else False   # Routine to read local image   if image_path != None and not camera:   print("[INFO] Image path provided, attempting to read image")   (result, image) = localDetect(image_path)   print("[INFO] sending results") # Converts the image to base 64 and adds it to the context  b64 = convert_to_base64(image)  context = {"image": b64}    # Sends the result  req = sendToUbidots(TOKEN, DEVICE, VARIABLE,    len(result), context=context)   if req.status_code >= 400:   print("[ERROR] Could not send data to Ubidots") return req  # Routine to read images from webcam    if camera:  print("[INFO] reading camera images")   cameraDetect(TOKEN, DEVICE, VARIABLE)

这个方法旨在通过终端插入参数并触发例程,对本地存储的图像文件或通过网络摄像来搜索行人。
第6节
def buildPayload(variable, value, context):   return {variable: {"value": value, "context": context}} def sendToUbidots(token, device, variable, value, context={}, industrial=True): # Builds the endpoint   url = URL_INDUSTRIAL if industrial else URL_EDUCATIONAL url = "{}/api/v1.6/devices/{}".format(url, device)  payload = buildPayload(variable, value, context)    headers = {"X-Auth-Token": token, "Content-Type": "application/json"}   attempts = 0    status = 400    while status >= 400 and attempts <= 5:    req = requests.post(url=url, headers=headers, json=payload) status = req.status_code    attempts += 1   time.sleep(1)   return req

第6节的这两个函数是把结果发送给Ubidots从而理解和对数据进行可视化的两条主干道。第一个函数 def buildPayload是在请求中构建有效的负载,而第二个函数 def sendToUbidots则接收你的Ubidots参数(TOKEN,变量和设备标签)用于存储结果。在这种情况下,OpenCV可以检测到圆盒的长度。作为备选,也可以发送上下文来将结果存储为base64图像,以便稍后进行检索。

第7节
def argsParser():    ap = argparse.ArgumentParser()  ap.add_argument("-i", "--image", default=None,  help="path to image test file directory")   ap.add_argument("-c", "--camera", default=False,    help="Set as true if you wish to use the camera")   args = vars(ap.parse_args())    return args

对于第7节,我们即将完成对代码的分析。函数 argsParser()简单地解析并通过终端将脚本的参数以字典的形式返回。在解析器中有两个参数:
· image:在你的系统中图片文件的路径
· camera:这个变量如果设置为‘true’,那么就会调用cameraDetect()方法
第8节
def main():  args = argsParser() detectPeople(args)  if __name__ == '__main__':  main()

第8节是我们主函数代码中的最终部分,只是用来获取console中的参数,然后发起指定的程序。
别忘了,全部的代码都可以从Github上下载。

3、测试

打开你最喜爱的代码编辑器(sublime-text,notepad,nano等),然后复制并粘贴此处所提供的完整代码。使用你特定的Ubidots TOKEN更新代码并将文件另存为“peopleCounter.py”。
正确保存代码后,让我们从Caltech Dataset和Pexels公共数据集中随机选择的下面四个图像来进行测试:
为了对这些图像进行分析,首先你必须将图像存储在笔记本电脑或PC中,并记录好要分析图像的存放路径。
python peopleCounter.py PATH_TO_IMAGE_FILE

在我的例子中,我将图像存储在标记为“dataset”的路径中。要执行一项有效的命令,请运行以下命令,但请更换为你个人的文件存放路径。
python peopleCounter.py -i dataset/image_1.png

如果你希望是从相机而不是本地文件中获取图像,只需运行以下命令:
python peopleCounter.py -c true

测试结果:
除了这种查看测试结果的方式之外,你还可以实时查看存储在Ubidots帐户中的测试的结果:

4、创造你自己的仪表板

我们将使用HTML Canvas来实时观察所取得的结果,本教程不对HTML canvas widget做讲解,如果你不了解如何使用它们,请参阅以下文章:
  • Canvas Widget Examples
  • Canvas Widget Introductory Demo
  • Canvas Creating a Real Time Widget
我们将使用基本的实时示例加上微小的修改,便于观看我们的图像。 你可以在下面看到关于widget的代码。
HTML
<img id="img" width="400px" height="auto"/>
JS
var socket;
var srv = "industrial.ubidots.com:443";
// var srv = "app.ubidots.com:443"  // Uncomment this line if you are an educational user
var VAR_ID = "5ab402dabbddbd3476d85967"; // Put here your var Id
var TOKEN = ""  // Put here your token
$( document ).ready(function() {    function renderImage(imageBase64){  if (!imageBase64) return;   $('#img').attr('src', 'data:image/png;base64, ' + imageBase64);
}   // Function to retrieve the last value, it runs only once
function getDataFromVariable(variable, token, callback) {   var url = 'https://things.ubidots.com/api/v1.6/variables/' + variable + '/values';  var headers = { 'X-Auth-Token': token,  'Content-Type': 'application/json'  };  $.ajax({    url: url,   method: 'GET',  headers: headers,   data : {    page_size: 1    },  success: function (res) {   if (res.results.length > 0){ renderImage(res.results[0].context.image);  }   callback(); }   });
}   // Implements the connection to the server
socket = io.connect("https://"+ srv, {path: '/notifications'});
var subscribedVars = [];    // Function to publish the variable ID
var subscribeVariable = function (variable, callback) { // Publishes the variable ID that wishes to listen  socket.emit('rt/variables/id/last_value', { variable: variable  }); // Listens for changes  socket.on('rt/variables/' + variable + '/last_value', callback);    subscribedVars.push(variable);
};  // Function to unsubscribed for listening
var unSubscribeVariable = function (variable) { socket.emit('unsub/rt/variables/id/last_value', {   variable: variable  }); var pst = subscribedVars.indexOf(variable); if (pst !== -1){    subscribedVars.splice(pst, 1);  }
};  var connectSocket = function (){    // Implements the socket connection socket.on('connect', function(){    console.log('connect'); socket.emit('authentication', {token: TOKEN});  }); window.addEventListener('online', function () { console.log('online');  socket.emit('authentication', {token: TOKEN});  }); socket.on('authenticated', function () {    console.log('authenticated');   subscribedVars.forEach(function (variable_id) { socket.emit('rt/variables/id/last_value', { variable: variable_id });   }); });
}   /* Main Routine */
getDataFromVariable(VAR_ID, TOKEN, function(){  connectSocket();
}); connectSocket();    //connectSocket();
// Subscribe Variable with your own code.
subscribeVariable(VAR_ID, function(value){  var parsedValue = JSON.parse(value);    console.log(parsedValue);   //$('#img').attr('src', 'data:image/png;base64, ' + parsedValue.context.image); renderImage(parsedValue.context.image); })
});

不要忘记把你的帐户TOKEN和变量ID放在代码段的开头。
第三部分的LIBRARIES
增加下面第三部分的libraries:
  • https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
  • https://iot.cdnedge.bluemix.net/ind/static/js/libs/socket.io/socket.io.min.js
  • 当你保存你的widget,你可以获得类似于下面的结果:

5、结果展示

在此链接中你可以看到带有结果的仪表板。
在本文中,我们探讨了如何使用DIP(图像处理),OpenCV和Ubidots来创建物联网人员计数器。通过这些服务,在对人物、场景或事物的检测与识别问题上,你的DIP应用程序会比PIR或其他光学传感器更加准确 – 这套程序提供了高效的行人计数器,而且不需要对早期数据的静态进行任何操作。
做一个快乐的码农!

关于作者Jose García:

UIS电子工程师,Ubuntu用户,布卡拉曼加人,程序员,有时很无聊,想要环游世界但却没太有希望完成这一梦想。 硬件和软件开发人员@Ubidots
译者介绍:
法国洛林大学计算机与决策专业硕士。现从事人工智能和大数据相关工作,以成为数据科学家为终生奋斗目标。来自山东济南,不会开挖掘机,但写得了Java、Python和PPT。
原文标题: 
People Counting with OpenCV, Python & Ubidots 
原文链接:
https://ubidots.com/blog/people-counting-with-opencv-python-and-ubidots/

(*本文为AI科技大本营转载文章,转载联系作者)

精彩推荐

倒计时!由易观携手CSDN联合主办的第三届易观算法大赛还剩 5 天,冠军团队将获得3万元!

本次比赛主要预测访问平台的相关事件的PV,UV流量(包括Web端,移动端等),大赛将会提供相应事件的流量数据,以及对应时间段内的所有事件明细表和用户属性表等数据,进行模型训练,并用训练好的模型预测规定日期范围内的事件流量。

推荐阅读

  • 知乎算法团队负责人孙付伟:Graph Embedding在知乎的应用实践

  • 大数据工程师手册:全面系统的掌握必备知识与工具

  • 经典再读 | NASNet:神经架构搜索网络在图像分类中的表现

激光雷达,马斯克看不上,却又无可替代?

  • 卷积神经网络中十大拍案叫绝的操作

  • Docker是啥?容器变革的火花?

  • 5大必知的图算法,附Python代码实现

  • 阿里云弹性计算负责人蒋林泉:亿级场景驱动的技术自研之路

你点的每个“在看”,我都认真当成了喜欢

利用OpenCV、Python和Ubidots构建行人计数器程序(附完整代码)相关推荐

  1. OpenCV输出版本和构建配置的实例(附完整代码)

    OpenCV输出版本和构建配置的实例 OpenCV输出版本和构建配置的实例 OpenCV输出版本和构建配置的实例 #include <opencv2/core/utility.hpp> # ...

  2. Py之pygame:有趣好玩—利用pygame库实现鱼儿自动实时目标跟踪(附完整代码)

    Py之pygame:有趣好玩-利用pygame库实现鱼儿自动实时目标跟踪(附完整代码) 目录 输出结果 实现代码 输出结果 实现代码 #Py之pygame:利用pygame库实现鱼儿自动实时目标跟踪i ...

  3. c++代码好玩_Py之pygame:有趣好玩—利用pygame库实现鱼儿自动实时目标跟踪(附完整代码)...

    Py之pygame:有趣好玩-利用pygame库实现鱼儿自动实时目标跟踪(附完整代码) 目录 输出结果 实现代码 输出结果 ​ 实现代码 #Py之pygame:利用pygame库实现鱼儿自动实时目标跟 ...

  4. python中import cv2遇到的错误及安装方法_独家利用OpenCV,Python和Ubidots来构建行人计数器程序(附代码amp;解析)...

    作者:Jose Garcia 翻译:吴振东 校对:张一豪 本文约4000字,建议阅读14分钟. 本文将利用OpenCV,Python和Ubidots来编写一个行人计数器程序,并对代码进行了较为详细的讲 ...

  5. 干货 | 利用OpenCV,Python和Ubidots来构建行人计数器程序(附代码解析)

    作者 | Jose Garcia 翻译 | 吴振东 校对 | 张一豪 来源 | 数据派THU(DatapiTHU ) 数字图像处理(DIP)技术目前发展非常迅速,这在很大程度上要归功于开发人员可以访问 ...

  6. opengl实现三维动画简单代码_使用Python简单实现马赛克拼图!内附完整代码

    今天小编带大家使用python简单实现马赛克拼图,内容比以往会稍长一些,各位看官老爷可以慢慢细读,若有不足之处还望请斧正,闲话不多说,请看文章. 先看原图: 效果图: 思路: 拼图的原理其实很简单,就 ...

  7. 基于python nlp PyTorch智能对联生成系统 附完整代码 毕业设计

    软件标题:智能对联生成系统 b 系统概述 使用项目:智能对联生成系统 软件用途:通过网页端可以获取到根据已有上联只能生成的下联. 开发历史:本项目未曾有前置版本.但在服务器搭建,Tensorflow ...

  8. python firmata协议利用arduino操控麦克纳姆轮小车(附完整代码)

    python firmata协议利用arduino操控麦克纳姆轮小车 前言 一.python中pyfirmata的环境 材料 二.使用步骤 1.小车的安装方式 我在这采用的是X型安装方式 总结 前言 ...

  9. 利用pyecharts绘制新浪微博传播图(文末附完整代码地址)

    文章目录 任务 来龙去脉 过程 绘图代码 绘图参数分析 结点参数格式 联系参数格式 类别参数格式 总览 获取转发关系 构造数据结构 集成json文件 代码地址 任务 延续之前的爬虫任务,最初同学提出的 ...

最新文章

  1. nodejs 转换long到unsigned long(NXT)
  2. 【Android】RxJava的使用(一)基本用法
  3. 加强数据中心安全的六条措施
  4. 水利水电工程管理与实务电子版习题_水利水电工程管理与实务复习题集
  5. (六)docker-compose使用教程
  6. webpack简单修改版本号(单页面)
  7. 详解一个自己原创的正则匹配IP的表达式
  8. 史上最全Java多线程面试60题,含答案大赠送!
  9. java查询数据库的方式_Java连接各种数据库方式速查表
  10. webmin账户重置密码
  11. C++调用编译好的darknet来进行物体监测
  12. Delphi基础教程图文版之数组
  13. 关于微PE的那些事和重装系统
  14. Shiro面试题总结
  15. 那些年 用过的经典App
  16. 码云推送代码后不显示贡献度
  17. webstorm 使用技巧
  18. 【饭谈】为什么总有人劝你用mac来办公?
  19. Baxer双臂机器人Ubuntu20.04+ROS noetic开发环境配置
  20. 【MySQL·水滴计划】第三话- SQL的基本概念

热门文章

  1. tomcat中server.xml文件详解
  2. ARM 位置无关代码(PIC)的分析理解
  3. JavaBean规范
  4. ESX上ORACLE 10.2RAC(4.在REHAT4.7中安装ORACLE RAC)
  5. SpringDataJpa根据多个id物品清单id查询房源编号
  6. 待解决--LaTex为什么其他符号可以用,插入大于等于,或者小于等于就报错?
  7. spring中实现自己的初始化逻辑
  8. codeforce843B Interactive LowerBound
  9. Python之路-python(Queue队列、进程、Gevent协程、Select\Poll\Epoll异步IO与事件驱动)
  10. java中文乱码解决之道(二)—–字符编码详解:基础知识 + ASCII + GB**