OpenCV –将视频流传输到Web浏览器/ HTML页面

https://www.pyimagesearch.com/2019/09/02/opencv-stream-video-to-web-browser-html-page/

https://translate.google.com.hk/translate?hl=zh-TW&sl=en&tl=zh-CN&u=https%3A%2F%2Fwww.pyimagesearch.com%2F2019%2F09%2F02%2Fopencv-stream-video-to-web-browser-html-page%2F

OpenCV –将视频流传输到Web浏览器/ HTML页面
作者 Adrian Rosebrock 于 2019 年 9月2日 在 嵌入式 , 物联网 , Raspberry Pi , 教程中
Python档案图示 单击此处将源代码下载到这篇文章

在本教程中,您将学习如何使用OpenCV使用Flask和Python将视频从网络摄像头流传输到网络浏览器/ HTML页面。

你的车被偷过吗?

我的周末被偷了。 而且我告诉你, 我很生气。

由于这是一项积极的刑事调查,因此我无法透露太多细节,但是我可以告诉您的是:

大约六个月前,我和我的妻子从康涅狄格州诺沃克搬到宾夕法尼亚州的费城。 我有一辆汽车,虽然我不经常开车,但仍然保持紧急状态。

在我们附近很难找到停车场,所以我需要一个停车场。

我听说有一个车库,报了名,然后开始在那儿停放我的车。

快过去到这个星期日。

我和妻子到达停车场抢我的车。 我们正要去马里兰拜访我的父母,并有一些蓝蟹(马里兰州以其螃蟹而闻名)。

我走到我的车上,取下了封面。

我立刻感到困惑-这不是我的车。

我的车在#$&@哪里?

几分钟后,我意识到了现实-我的汽车被盗了。

在过去的一周中,我即将发布的《 Raspberry Pi for Computer Vision》一书的工作被中断了–我一直在与停车场的所有者,费城警察局以及汽车上的GPS跟踪服务商合作,以找出问题所在。发生了

在解决之前,我无法公开讨论任何细节,但让我告诉您,我正陷入困境,那里到处都是文书工作,警察报告,律师函和保险索赔。

我希望这个问题在下个月得到解决-我讨厌分心,特别是使我无法专注于分散注意力的分心- 教学计算机视觉和深度学习。

我成功地利用挫折感激发了新的与安全性相关的计算机视觉博客文章。

在本文中,我们将学习如何使用Flask和OpenCV将视频流式传输到Web浏览器。

您将能够在不到5分钟的时间内将系统部署在Raspberry Pi上:

只需安装所需的软件包/软件并启动脚本。
然后打开您的计算机/智能手机浏览器,导航到URL / IP地址以观看视频提要(并确保您的所有内容均未被盗)。 

没有什么能像视频一样抓住小偷的证据了。

在我继续与警察,保险公司等进行文书工作的同时,您可以开始使用Raspberry Pi相机武装自己,以在生活和工作中的任何地方捕捉坏人。

要了解如何使用OpenCV和Flask将视频流式传输到Web浏览器HTML页面, 请继续阅读!

寻找这篇文章的源代码?
跳到下载部分。
OpenCV –将视频流传输到Web浏览器/ HTML页面

在本教程中,我们将从讨论Flask(一种用于Python编程语言的微型Web框架)开始。

我们将学习运动检测的基础知识,以便将其应用于我们的项目。 我们将继续通过背景减法器实现运动检测。

从那里,我们将Flask与OpenCV相结合,使我们能够:

通过RPi相机模块或USB网络摄像头访问相框。
处理帧并应用任意算法(这里我们将使用背景减法/运动检测,但是您可以应用图像分类,对象检测等)。
将结果流式传输到网页/网络浏览器。 

此外,我们将介绍的代码将能够支持多个客户端 (即,一个以上的人/ Web浏览器/选项卡可同时访问流),您在网上可以找到的绝大多数示例都无法解决。

将所有这些部分放在一起,就可以构成一个家庭监视系统,该系统能够执行运动检测,然后将视频结果流式传输到Web浏览器。

让我们开始吧!
Flask Web框架

图1: Flask是适用于Python的微型Web框架( 图像来源 )。

在本节中,我们将简要讨论Flask Web框架以及如何在系统上安装它。

Flask是使用Python编程语言编写的流行的微型Web框架。

与Django一起 ,Flask是使用Python构建Web应用程序时最常见的Web框架之一。

但是,与Django不同,Flask 非常轻巧,因此构建基本的Web应用程序非常容易。

正如我们将在本节中看到的那样,我们只需要少量代码即可使用Flask进行实时视频流传输-其余代码要么涉及(1)OpenCV和访问我们的视频流,要么涉及(2)确保我们的代码是线程安全的,可以处理多个客户端。

如果您需要在计算机上安装Flask,则只需执行以下命令即可:
OpenCV - Stream video to web browser/HTML page

$   pip  install  flask 

在进行此操作时,请继续安装NumPy,OpenCV和imutils:
OpenCV - Stream video to web browser/HTML page

 $   pip  install  numpy 
 $   pip  install  opencv - contrib - python 
 $   pip  install  imutils 

注意:如果您希望完整安装OpenCV,包括“非免费”(专利)算法,请务必从source编译OpenCV 。
项目结构

在继续之前,让我们看一下该项目的目录结构:
OpenCV - Stream video to web browser/HTML page

 $   tree   -- dirsfirst 
 . 
├──  pyimagesearch 
│   ├──  motion _ detection
│   │   ├──  __init__ .py 
│   │   └──  singlemotiondetector .py 
│   └──  __init__ .py 
├──  templates 
│   └──  index .html 
└──  webstreaming .py 
 
 3   directories ,   5   files 

为了执行背景扣除和运动检测,我们将实现一个名为SingleMotionDetector的类-该类将存在于pyimagesearch的motion_detection子模块中的singlemotiondetector .py文件中。

webstreaming .py文件将使用OpenCV访问我们的网络摄像机,通过SingleMotionDetector执行运动检测,然后通过Flask网络框架将输出帧提供给我们的网络浏览器。

为了让我们的网络浏览器拥有的东西来展示,我们需要填充指数 的.html的内容与HTML用来服务于视频输入。 我们只需要插入一些基本的HTML标记-Flask会为我们处理实际将视频流发送到我们的浏览器的过程。
实施基本的运动探测器

图2:使用Raspberry Pi,OpenCV,Flask和Web流进行视频监视。 通过使用背景减法进行运动检测,我们已经检测到我在椅子上移动的位置的运动。

我们的运动检测器算法将通过背景减法检测运动。

大多数背景扣除算法的工作原理是:

累积前N帧的加权平均值
取当前帧并从帧的加权平均值中减去它
阈值相减的输出,以突出显示与在像素值的实质差异(“白色”用于前景和“黑色”为背景)的区域
施加基本的图像处理技术,例如腐蚀和膨胀,以除去噪声
利用轮廓检测​​提取包含运动的区域 

我们的运动检测实现将存在于SingleMotionDetector类中,该类可在singlemotiondetector中找到。 py 。

我们将其称为“单个运动检测器”,因为算法本身仅对找到单个最大的运动区域感兴趣。

我们可以轻松地将此方法扩展为处理多个运动区域。

让我们继续实施运动检测器。

打开singlemotiondetector .py文件,并插入以下代码:
OpenCV - Stream video to web browser/HTML page
Python

import the necessary packages

 # import the necessary packages 
 import   numpy  as   np 
 import   imutils 
 import   cv2 
 
 class   SingleMotionDetector : 
	 def   __init__ ( self ,   accumWeight = 0.5 ) : 
		 # store the accumulated weight factor 
		 self . accumWeight   =   accumWeight 
 
		 # initialize the background model 
		 self . bg   =   None 

2-4行处理了我们所需的进口。

所有这些都是相当标准,包括对NumPy的数值处理,imutils我们的方便功能,并且CV2我们OpenCV的绑定。

然后,我们定义的6代线我们SingleMotionDetector类。 该类接受一个可选参数accumWeight ,这是用于我们的累计加权平均值的因子。

accumWeight越大,累加加权平均值时背景( bg )的影响就越小 。

相反, accumWeight 越小 ,计算平均值时将考虑的背景bg 越多 。

设置accumWeight = 0.5均匀加权背景和前景-我经常建议将其作为起点值(然后您可以根据自己的实验对其进行调整)。

接下来,让我们定义更新方法,该方法将接受输入帧并计算加权平均值:
OpenCV - Stream video to web browser/HTML page

def update(self, image):
	# if the background model is None, initialize it
	if self.bg is None:
		self.bg = image.copy().astype("float")
		return

	# update the background model by accumulating the weighted
	# average
	cv2.accumulateWeighted(image, self.bg, self.accumWeight)

在我们的BG帧是无 (这意味着更新从未被调用)的情况下,我们简单地存储BG帧(15〜18行 )。

否则,我们将计算输入帧 ,现有背景bg和我们相应的accumWeight因子之间的加权平均值。

给定我们的背景bg,我们现在可以通过detect方法应用运动检测:
OpenCV - Stream video to web browser/HTML page

 def   detect ( self ,   image ,   tVal = 25 ) : 
	 # compute the absolute difference between the background model 
	 # and the image passed in, then threshold the delta image 
	 delta   =   cv2 . absdiff ( self . bg . astype ( "uint8" ) ,   image ) 
	 thresh   =   cv2 . threshold ( delta ,   tVal ,   255 ,   cv2 . THRESH_BINARY ) [ 1 ] 
 
	 # perform a series of erosions and dilations to remove small 
	 # blobs 
	 thresh   =   cv2 . erode ( thresh ,   None ,   iterations = 2 ) 
	 thresh   =   cv2 . dilate ( thresh ,   None ,   iterations = 2 ) 

detect方法需要一个参数以及一个可选参数:

图像 :输入帧/图像的是运动检测将被应用到。
tVal :用于将特定像素标记为“运动”或不标记为“运动”的阈值。 

给定我们的输入图像,我们计算图像和bg之间的绝对差( 第27行 )。

任何具有差异的像素位置> tVal设置为255 (白色;前景),否则将其设置为0 (黑色;背景)( 第28行 )。

进行了一系列腐蚀和膨胀处理,以消除噪声和小的局部运动区域,否则这些运动区域会被认为是假阳性(可能是由于反射或光线的快速变化)。

下一步是应用轮廓检测​​以提取任何运动区域:
OpenCV - Stream video to web browser/HTML page

 # find contours in the thresholded image and initialize the 
 # minimum and maximum bounding box regions for motion 
 cnts   =   cv2 . findContours ( thresh . copy ( ) ,   cv2 . RETR_EXTERNAL , 
	 cv2 . CHAIN_APPROX_SIMPLE ) 
 cnts   =   imutils . grab_contours ( cnts ) 
 ( minX ,   minY )   =   ( np . inf ,   np . inf ) 
 ( maxX ,   maxY )   =   ( - np . inf ,   - np . inf ) 

第37-39行对我们的脱粒图像执行轮廓检测。

然后,我们初始化两组簿记变量,以跟踪包含任何运动的位置( 第40和41行 )。 这些变量将形成“边界框”,该边界框将告诉我们运动发生的位置。

最后一步是填充以下变量(当然,框架中存在提供的运动):
OpenCV - Stream video to web browser/HTML page

 # if no contours were found, return None 
 if   len ( cnts )   ==   0 : 
	 return   None 
 
 # otherwise, loop over the contours 
 for   c   in   cnts : 
	 # compute the bounding box of the contour and use it to 
	 # update the minimum and maximum bounding box regions 
	 ( x ,   y ,   w ,   h )   =   cv2 . boundingRect ( c ) 
	 ( minX ,   minY )   =   ( min ( minX ,   x ) ,   min ( minY ,   y ) ) 
	 ( maxX ,   maxY )   =   ( max ( maxX ,   x   +   w ) ,   max ( maxY ,   y   +   h ) ) 
 
 # otherwise, return a tuple of the thresholded image along 
 # with bounding box 
 return   ( thresh ,   ( minX ,   minY ,   maxX ,   maxY ) ) 

在第43-45行,我们检查轮廓列表是否为空。

如果真是这样,那么在框架中没有发现运动,我们可以放心地忽略它。

否则,框架中确实存在运动,因此我们需要开始在轮廓上循环( 第48行 )。

对于每个轮廓,我们计算边界框,然后更新簿记变量( 第47-53行 ),找到所有运动发生的最小和最大(x,y)坐标。

最后,我们将边界框位置返回给调用函数。
将OpenCV与Flask结合

图3: OpenCV和Flask(Python微型Web框架)非常适合涉及Raspberry Pi和类似硬件的Web流和视频监视项目。

让我们继续前进,并结合OpenCV的与瓶从视频流(在树莓派运行)的web浏览器成为了框架。

在您的项目结构中打开webstreaming .py文件,并插入以下代码:
OpenCV - Stream video to web browser/HTML page

 # initialize the output frame and a lock used to ensure thread-safe 
 # exchanges of the output frames (useful when multiple browsers/tabs 
 # are viewing the stream) 
 outputFrame   =   None 
 lock   =   threading . Lock ( ) 
 
 # initialize a flask object 
 app   =   Flask ( __name__ ) 
 
 # initialize the video stream and allow the camera sensor to 
 # warmup 
 #vs = VideoStream(usePiCamera=1).start() 
 vs   =   VideoStream ( src = 0 ) . start ( ) 
 time . sleep ( 2.0 ) 

首先,我们初始化我们在第17行 outputFrame -这将是将被提供给客户端的框架(后运动检测)。

然后,我们在第18行上创建一个锁 ,该锁将用于确保更新ouputFrame时的线程安全行为(即,确保一个线程在更新帧时不尝试读取该帧)。

第25行至第27行访问视频流时, 第 21行会初始化Flask 应用本身:

如果您使用的是USB网络摄像头,则可以按原样保留代码。
但是,如果您正在使用RPi摄像机模块,则应取消注释第25行并注释掉第26行。 

下一个函数index将呈现我们的index 。 html模板并提供输出视频流:
OpenCV - Stream video to web browser/HTML page

 @ app . route ( "/" ) 
 def   index ( ) : 
	 # return the rendered template 
	 return   render_template ( "index.html" ) 

此函数非常简单-要做的只是在HTML文件上调用Flask render_template 。

我们将审查索引 。 下一节中的html文件,因此在此之前,我们将不再进一步讨论文件内容。

我们的下一个职能负责:

循环播放视频流中的帧
应用运动检测
在outputFrame上绘制任何结果 

而且,此功能必须以线程安全的方式执行所有这些操作,以确保支持并发。

现在让我们看一下这个函数:
OpenCV - Stream video to web browser/HTML page

	
 def   detect_motion ( frameCount ) : 
	 # grab global references to the video stream, output frame, and 
	 # lock variables 
	 global   vs ,   outputFrame ,   lock 
 
	 # initialize the motion detector and the total number of frames 
	 # read thus far 
	 md   =   SingleMotionDetector ( accumWeight = 0.1 ) 
	 total   =   0 

我们的detection_motion函数接受单个参数frameCount ,这是在SingleMotionDetector类中构建背景bg所需的最小帧数:

如果我们至少没有frameCount帧,我们将继续计算累积的加权平均值。
一旦达到,帧数 ,我们将开始进行背景减除。 

第37行获取对三个变量的全局引用:

vs :我们实例化的VideoStream对象
outputFrame :将提供给客户端的输出帧
锁 :线程锁,我们必须更新之前获得outputFrame 

第41行使用AccumWeight = 0.1的值初始化我们的SingleMotionDetector类,这意味着在计算加权平均值时bg值将被加权更高。

42号线然后初始化帧的总数读迄今-我们需要确保有足够多帧已被读出,以建立我们的背景模型。

从那里,我们将能够执行背景扣除。

完成这些初始化后,我们现在就可以开始循环遍历相机中的帧了:
OpenCV - Stream video to web browser/HTML page

 # loop over frames from the video stream 
 while   True : 
	 # read the next frame from the video stream, resize it, 
	 # convert the frame to grayscale, and blur it 
	 frame   =   vs . read ( ) 
	 frame   =   imutils . resize ( frame ,   width = 400 ) 
	 gray   =   cv2 . cvtColor ( frame ,   cv2 . COLOR_BGR2GRAY ) 
	 gray   =   cv2 . GaussianBlur ( gray ,   ( 7 ,   7 ) ,   0 ) 
 
	 # grab the current timestamp and draw it on the frame 
	 timestamp   =   datetime . datetime . now ( ) 
	 cv2 . putText ( frame ,   timestamp . strftime ( 
		 "%A %d %B %Y %I:%M:%S%p" ) ,   ( 10 ,   frame . shape [ 0 ]   -   10 ) , 
		 cv2 . FONT_HERSHEY_SIMPLEX ,   0.35 ,   ( 0 ,   0 ,   255 ) ,   1 ) 

第48 行从我们的相机读取下一帧 ,而第49-51行执行预处理,包括:

调整为具有400像素的宽度( 较小的我们的输入帧, 就越数据有,因此更快我们的算法将要运行)。
转换为灰度。
高斯模糊(以减少噪声)。 

然后,我们获取当前时间戳并将其绘制在框架上 ( 第54-57行 )。

经过最后的检查,我们可以执行运动检测:
OpenCV - Stream video to web browser/HTML page

	
 # if the total number of frames has reached a sufficient 
 # number to construct a reasonable background model, then 
 # continue to process the frame 
 if   total   >   frameCount : 
	 # detect motion in the image 
	 motion   =   md . detect ( gray ) 
 
	 # check to see if motion was found in the frame 
	 if   motion  is   not   None : 
		 # unpack the tuple and draw the box surrounding the 
		 # "motion area" on the output frame 
		 ( thresh ,   ( minX ,   minY ,   maxX ,   maxY ) )   =   motion 
		 cv2 . rectangle ( frame ,   ( minX ,   minY ) ,   ( maxX ,   maxY ) , 
			 ( 0 ,   0 ,   255 ) ,   2 ) 

 # update the background model and increment the total number 
 # of frames read thus far 
 md . update ( gray ) 
 total   +=   1 
 
 # acquire the lock, set the output frame, and release the 
 # lock 
 with   lock : 
	 outputFrame   =   frame . copy ( ) 

在第62行中,我们确保已读取至少frameCount个帧以构建背景扣除模型。

如果是这样,我们应用。 检测我们的运动检测器,它返回一个单可变, 运动的运动。

如果运动为“ 无” ,那么我们知道当前帧中没有运动发生。 否则,如果运动 不是 None ( 第67行 ),那么我们需要在frame上绘制运动区域的边界框坐标。

第76行更新了我们的运动检测背景模型,而第77行则增加了到目前为止从相机读取的总帧数。

最后, 第81行获取支持线程并发所需的锁 ,而第82行则设置outputFrame 。

我们需要获取锁,以确保客户端在尝试更新 outputFrame变量时不会意外读取它。

我们的下一个函数generate是一个Python生成器,用于将outputFrame编码为JPEG数据-现在让我们看一下:
OpenCV - Stream video to web browser/HTML page

	
 def   generate ( ) : 
	 # grab global references to the output frame and lock variables 
	 global   outputFrame ,   lock 
 
	 # loop over frames from the output stream 
	 while   True : 
		 # wait until the lock is acquired 
		 with   lock : 
			 # check if the output frame is available, otherwise skip 
			 # the iteration of the loop 
			 if   outputFrame  is   None : 
				 continue 
 
			 # encode the frame in JPEG format 
			 ( flag ,   encodedImage )   =   cv2 . imencode ( ".jpg" ,   outputFrame ) 
 
			 # ensure the frame was successfully encoded 
			 if   not   flag : 
				 continue 
 
		 # yield the output frame in the byte format 
		 yield ( b '--frame\r\n'   b 'Content-Type: image/jpeg\r\n\r\n'   +  
			 bytearray ( encodedImage )   +   b '\r\n' ) 

第86行获取对我们的outputFrame和lock的全局引用,类似于detect_motion函数。

然后, generate在第89行开始一个无限循环,该循环将一直持续到我们杀死脚本为止。

在循环内部,我们:

首先获取锁 ( 第91行 )。
确保outputFrame不为空( 第94行 ),如果从相机传感器掉落一帧可能会发生这种情况。
在第98行上将帧编码为JPEG图像-此处执行JPEG压缩以减少网络负载并确保更快地传输帧。
检查成功标志是否失败( 第101和102行 ),这意味着JPEG压缩失败,我们应该忽略该帧。
最后,将编码后的JPEG帧作为字节数组提供给网络浏览器使用。 

这是相当多的工作在简短的代码量,所以一定要确保您查看此功能几次,以确保你了解它是如何工作的。

下一个函数video_feed调用我们的generate函数:
OpenCV - Stream video to web browser/HTML page

	
 @ app . route ( "/video_feed" ) 
 def   video_feed ( ) : 
	 # return the response generated along with the specific media 
	 # type (mime type) 
	 return   Response ( generate ( ) , 
		 mimetype   =   "multipart/x-mixed-replace; boundary=frame" ) 

注意此功能如何作为应用程序 。 路由签名,就像上面的index函数一样。

该应用程序 。 路线签名告诉瓶,这个函数是一个URL端点和数据从HTTP服务:/ / your_ip_address / video_feed。

video_feed的输出是实时运动检测输出,通过generate函数编码为字节数组。 您的Web浏览器足够聪明,可以获取此字节数组并将其作为实时供稿显示在浏览器中。

我们的最终代码块处理解析命令行参数并启动Flask应用程序:
OpenCV - Stream video to web browser/HTML page

	
 # check to see if this is the main thread of execution 
 if   __name__   ==   '__main__' : 
	 # construct the argument parser and parse command line arguments 
	 ap   =   argparse . ArgumentParser ( ) 
	 ap . add_argument ( "-i" ,   "--ip" ,   type = str ,   required = True , 
		 help = "ip address of the device" ) 
	 ap . add_argument ( "-o" ,   "--port" ,   type = int ,   required = True , 
		 help = "ephemeral port number of the server (1024 to 65535)" ) 
	 ap . add_argument ( "-f" ,   "--frame-count" ,   type = int ,   default = 32 , 
		 help = "# of frames used to construct the background model" ) 
	 args   =   vars ( ap . parse_args ( ) ) 
 
	 # start a thread that will perform motion detection 
	 t   =   threading . Thread ( target = detect_motion ,   args = ( 
		 args [ "frame_count" ] , ) ) 
	 t . daemon   =   True 
	 t . start ( ) 
 
	 # start the flask app 
	 app . run ( host = args [ "ip" ] ,   port = args [ "port" ] ,   debug = True , 
		 threaded = True ,   use_reloader = False ) 
 
 # release the video stream pointer 
 vs . stop ( ) 

第118-125行处理解析我们的命令行参数。

这里我们需要三个参数,包括:

-ip :您要从中启动webstream .py文件的系统的IP地址。
-port :Flask应用程序将在其上运行的端口号(您通常会为该参数提供8000的值)。
-frame - count :执行运动检测之前,用于累积和构建背景模型的帧数。 默认情况下,我们使用32帧来构建背景模型。 

线128-131发射将被用于执行运动检测的线程。

使用线程可确保detect_motion函数可以在后台安全运行-它将不断运行并更新我们的outputFrame,以便我们可以将任何运动检测结果提供给客户端。

最后, 第134和135行会启动Flask应用本身。
HTML页面结构

正如我们在webstreaming .py中所看到的,我们正在渲染一个名为index的HTML模板。 html 。

模板本身由Flask Web框架填充,然后提供给Web浏览器。

然后,您的Web浏览器需要生成的HTML并将其呈现到你的屏幕。

让我们检查一下索引的内容。 html文件:
OpenCV - Stream video to web browser/HTML page

 <html> 
   <head> 
     <title> Pi Video Surveillance </title> 
   </head> 
   <body> 
     <h1> Pi Video Surveillance </h1> 
     <img  src = "{{ url_for('video_feed') }}" > 
   </body> 
 </html> 

如我们所见,这是超级基本的网页; 但是,请密切注意第7行 -注意我们如何指示Flask动态呈现video_feed路线的URL。

由于video_feed功能是负责从我们的摄像头煮好帧,图像的src将自动与我们的输出帧填充。

这样,我们的网络浏览器就足够聪明,可以正确呈现网页并提供实时视频流。
拼凑在一起

现在我们已经对项目进行了编码,让我们对其进行测试。

打开一个终端并执行以下命令:
OpenCV - Stream video to web browser/HTML page

 $   python  webstreaming .py   -- ip   0.0.0.0   -- port   8000 
  *   Serving  Flask  app   "webstreaming"   ( lazy  loading ) 
  *   Environment :   production 
    WARNING :   This   is   a   development  server .   Do   not   use   it  in   a   production  deployment . 
    Use   a   production  WSGI  server  instead . 
  *   Debug  mode :   on 
  *   Running  on  http : / / 0.0.0.0 : 8000 /   ( Press  CTRL + C   to   quit ) 
 127.0.0.1   -   -   [ 26 / Aug / 2019   14 : 43 : 23 ]   "GET / HTTP/1.1"   200   - 
 127.0.0.1   -   -   [ 26 / Aug / 2019   14 : 43 : 23 ]   "GET /video_feed HTTP/1.1"   200   - 
 127.0.0.1   -   -   [ 26 / Aug / 2019   14 : 43 : 24 ]   "GET /favicon.ico HTTP/1.1"   404   - 

如您在视频中看到的,我从多个浏览器打开了Flask / OpenCV服务器的连接,每个浏览器都有多个选项卡。 我什至拔出了iPhone并从那里打开了一些连接。 服务器没有跳动,而是继续通过Flask和OpenCV可靠地提供帧。
加入嵌入式计算机视觉和深度学习革命!

我刚开始弹吉他二十年前,当我在中学。 我不是很擅长,几年后就放弃了。 回顾过去,我坚信我不坚持的原因是因为我没有以实际的,动手的方式学习。

相反,我的音乐老师一直在努力将理论付诸实践-但是作为一个十一岁的孩子,我只是想弄清楚我是否甚至喜欢弹吉他,更不用说我想学习音乐背后的理论了。

大约一年半以前,我决定再次开始上吉他课。 这次,我很小心地找到了一位可以将理论和实践融为一体的老师,向我展示了如何演奏歌曲或即兴演奏,同时又学习了一种理论技巧。

结果? 现在,我的手指速度比以往任何时候都快,我的节奏快到了,我可以惹恼我的妻子,让我的Les Paul摇动我的《我的甜蜜孩子》 。

我的意思是,无论您是在学习一项新技能,无论是计算机视觉,使用Raspberry Pi进行黑客攻击,甚至是弹吉他, 都可以采用(小型)真实的方法来最快,最简单地掌握该技术。 -world项目围绕该技能进行尝试,并尝试解决该问题。

对于吉他来说,这意味着学习简短的即兴演奏,不仅可以教给我一些实际的歌曲,而且还给了我一种有价值的技术(例如,掌握特定的五音阶)。

在计算机视觉和图像处理中,您的目标应该是集思广益小型项目 ,然后尝试解决它们。 不要太复杂,不要太快,这是失败的秘诀。

取而代之的是, 获取我的《 Raspberry Pi for Computer Vision》一书的副本 ,阅读并用作个人项目的启动板。

当您完成阅读后,请返回最能激发您灵感的章节,并了解如何以某种方式扩展它们(即使只是将相同的技术应用于不同的场景 )。

解决您脑力激荡的微型项目,不仅会使您对该主题保持兴趣(因为您亲自想到了这些主题),而且它们还将同时教会您动手实践的技能。

今天的教程-运动检测和流式传输到Web浏览器-是此类微型项目的一个很好的起点。 我希望现在您已经完成了本教程,并且就如何将这个项目扩展到自己的应用程序中集思广益。

但是,如果您有兴趣学习更多……

我的新书《用于计算机视觉的Raspberry Pi》包含40多个与嵌入式计算机视觉+物联网(IoT)相关的项目。 您可以在本书中的项目基础上解决家庭,企业甚至客户的问题。 这些项目均着重于:

边干边学。
卷起袖子。
在代码和实现中动手实践。
使用Raspberry Pi构建实际的实际项目。 

一些突出的项目包括:

白天和夜晚的野生动植物监测
流量统计和车速检测
深度学习分类,目标检测和实例分割资源受限设备
手势识别
基本的机器人导航
安全应用
课堂上课
…还有很多! 

该书还介绍了使用Google Coral和Intel Movidius NCS协处理器(Hacker + Complete Bundles )进行的深度学习。 当需要更多的深度学习功能时,我们还将引入NVIDIA Jetson Nano ( 完整捆绑包 )。

  • 1
    点赞
  • 0
    评论
  • 19
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值