34781浏览
查看: 34781|回复: 1

[教程] 树莓派无线WiFi采集摄像头数据的三种方法

[复制链接]
本帖最后由 不脱发的程序猿 于 2023-12-22 09:15 编辑

1、方法一:Python OpenCV
使用Python和OpenCV实现树莓派/PC实时摄像头数据共享,主要分为服务器和客户端两部分。

服务器Demo如下:

  1. <font color="#000000">#服务器端
  2. import socket
  3. import threading
  4. import struct
  5. import time
  6. import cv2
  7. import numpy
  8. class Carame_Accept_Object:
  9.     def __init__(self,S_addr_port=("",8880)):
  10.         self.resolution=(640,480)       #分辨率
  11.         self.img_fps=15                 #每秒传输多少帧数
  12.         self.addr_port=S_addr_port
  13.         self.Set_Socket(self.addr_port)
  14.     #设置套接字
  15.     def Set_Socket(self,S_addr_port):
  16.         self.server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  17.         self.server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #端口可复用
  18.         self.server.bind(S_addr_port)
  19.         self.server.listen(5)
  20.         #print("the process work in the port:%d" % S_addr_port[1])
  21. def check_option(object,client):
  22.     #按格式解码,确定帧数和分辨率
  23.     info=struct.unpack('lhh',client.recv(8))
  24.     if info[0]>888:
  25.         object.img_fps=int(info[0])-888          #获取帧数
  26.         object.resolution=list(object.resolution)
  27.         # 获取分辨率
  28.         object.resolution[0]=info[1]
  29.         object.resolution[1]=info[2]
  30.         object.resolution = tuple(object.resolution)
  31.         return 1
  32.     else:
  33.         return 0
  34. def RT_Image(object,client,D_addr):
  35.     if(check_option(object,client)==0):
  36.         return
  37.     camera=cv2.VideoCapture(0)                                #从摄像头中获取视频
  38.     img_param=[int(cv2.IMWRITE_JPEG_QUALITY),object.img_fps]  #设置传送图像格式、帧数
  39.     while(1):
  40.         time.sleep(0.1)             #推迟线程运行0.1s
  41.         _,object.img=camera.read()  #读取视频每一帧
  42.         object.img=cv2.resize(object.img,object.resolution)     #按要求调整图像大小(resolution必须为元组)
  43.         _,img_encode=cv2.imencode('.jpg',object.img,img_param)  #按格式生成图片
  44.         img_code=numpy.array(img_encode)                        #转换成矩阵
  45.         object.img_data=img_code.tostring()                     #生成相应的字符串
  46.         try:
  47.             #按照相应的格式进行打包发送图片
  48.             client.send(struct.pack("lhh",len(object.img_data),object.resolution[0],object.resolution[1])+object.img_data)
  49.         except:
  50.             camera.release()        #释放资源
  51.             return
  52. if __name__ == '__main__':
  53.     camera=Carame_Accept_Object()
  54.     while(1):
  55.         client,D_addr=camera.server.accept()
  56.         clientThread=threading.Thread(None,target=RT_Image,args=(camera,client,D_addr,))
  57.         clientThread.start()</font>
复制代码

客户端Demo如下:

  1. <font color="#000000">#客户端
  2. import socket
  3. import cv2
  4. import threading
  5. import struct
  6. import numpy
  7. class Camera_Connect_Object:
  8.     def __init__(self,D_addr_port=["",8880]):
  9.         self.resolution=[640,480]
  10.         self.addr_port=D_addr_port
  11.         self.src=888+15                 #双方确定传输帧数,(888)为校验值
  12.         self.interval=0                 #图片播放时间间隔
  13.         self.img_fps=100               #每秒传输多少帧数
  14.     def Set_socket(self):
  15.         self.client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  16.         self.client.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  17.     def Socket_Connect(self):
  18.         self.Set_socket()
  19.         self.client.connect(self.addr_port)
  20.         print("IP is %s:%d" % (self.addr_port[0],self.addr_port[1]))
  21.     def RT_Image(self):
  22.         #按照格式打包发送帧数和分辨率
  23.         self.name=self.addr_port[0]+" Camera"
  24.         self.client.send(struct.pack("lhh", self.src, self.resolution[0], self.resolution[1]))
  25.         while(1):
  26.             info=struct.unpack("lhh",self.client.recv(8))
  27.             buf_size=info[0]                    #获取读的图片总长度
  28.             if buf_size:
  29.                 try:
  30.                     self.buf=b""                #代表bytes类型
  31.                     temp_buf=self.buf
  32.                     while(buf_size):            #读取每一张图片的长度
  33.                         temp_buf=self.client.recv(buf_size)
  34.                         buf_size-=len(temp_buf)
  35.                         self.buf+=temp_buf      #获取图片
  36.                         data = numpy.fromstring(self.buf, dtype='uint8')    #按uint8转换为图像矩阵
  37.                         self.image = cv2.imdecode(data, 1)                  #图像解码
  38.                         gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
  39.                         cv2.imshow(self.name, self.image)                   #展示图片
  40.                 except:
  41.                     pass;
  42.                 finally:
  43.                     if(cv2.waitKey(10)==27):        #每10ms刷新一次图片,按‘ESC’(27)退出
  44.                         self.client.close()
  45.                         cv2.destroyAllWindows()
  46.                         break
  47.     def Get_Data(self,interval):
  48.         showThread=threading.Thread(target=self.RT_Image)
  49.         showThread.start()
  50. if __name__ == '__main__':
  51.     camera=Camera_Connect_Object()
  52.     camera.addr_port[0]=input("Please input IP:")
  53.     camera.addr_port=tuple(camera.addr_port)
  54.     camera.Socket_Connect()
  55.     camera.Get_Data(camera.interval)</font>
复制代码

运行效果还是比较流畅的,因为传输网络视频流,所以画质欠佳。

树莓派无线WiFi采集摄像头数据的三种方法图5

2、方法二:Python picamera
接下来使用Python和picamera实现树莓派/PC实时摄像头数据共享,主要也可分为服务器和客户端两部分。
服务器(PC/树莓派)Demo如下:
  1. <font color="#000000">import numpy as np
  2. import cv2
  3. import socket
  4. class VideoStreamingTest(object):
  5.     def __init__(self, host, port):
  6.         self.server_socket = socket.socket()
  7.         self.server_socket.bind((host, port))
  8.         self.server_socket.listen(0)
  9.         self.connection, self.client_address = self.server_socket.accept()
  10.         self.connection = self.connection.makefile('rb')
  11.         self.host_name = socket.gethostname()
  12.         self.host_ip = socket.gethostbyname(self.host_name)
  13.         self.streaming()
  14.     def streaming(self):
  15.         try:
  16.             print("Host: ", self.host_name + ' ' + self.host_ip)
  17.             print("Connection from: ", self.client_address)
  18.             print("Streaming...")
  19.             print("Press 'q' to exit")
  20.             # need bytes here
  21.             stream_bytes = b' '
  22.             while True:
  23.                 stream_bytes += self.connection.read(1024)
  24.                 first = stream_bytes.find(b'\xff\xd8')
  25.                 last = stream_bytes.find(b'\xff\xd9')
  26.                 if first != -1 and last != -1:
  27.                     jpg = stream_bytes[first:last + 2]
  28.                     stream_bytes = stream_bytes[last + 2:]
  29.                     image = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
  30.                     cv2.imshow('image', image)
  31.                     if cv2.waitKey(1) & 0xFF == ord('q'):
  32.                         break
  33.         finally:
  34.             self.connection.close()
  35.             self.server_socket.close()
  36. if __name__ == '__main__':
  37.     # host, port
  38.     h, p = "192.168.0.4", 8000
  39.     VideoStreamingTest(h, p)</font>
复制代码
客户端(树莓派)Demo如下:
  1. <font color="#000000">import io
  2. import socket
  3. import struct
  4. import time
  5. import picamera
  6. # create socket and bind host
  7. client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  8. client_socket.connect(('192.168.2.104', 8000))
  9. connection = client_socket.makefile('wb')
  10. try:
  11.     with picamera.PiCamera() as camera:
  12.         camera.resolution = (320, 240)      # pi camera resolution
  13.         camera.framerate = 15               # 15 frames/sec
  14.         time.sleep(2)                       # give 2 secs for camera to initilize
  15.         start = time.time()
  16.         stream = io.BytesIO()
  17.         
  18.         # send jpeg format video stream
  19.         for foo in camera.capture_continuous(stream, 'jpeg', use_video_port = True):
  20.             connection.write(struct.pack('<L', stream.tell()))
  21.             connection.flush()
  22.             stream.seek(0)
  23.             connection.write(stream.read())
  24.             if time.time() - start > 600:
  25.                 break
  26.             stream.seek(0)
  27.             stream.truncate()
  28.     connection.write(struct.pack('<L', 0))
  29. finally:
  30.     connection.close()
  31.     client_socket.close()</font>
复制代码
无论是运行速度还是画质都是比较理想的。
树莓派无线WiFi采集摄像头数据的三种方法图1
3、方法三:搭建网络摄像头
搭建网络摄像头有两种方法。
3.1、motion方式
第一步:安装motion包
  1. <font color="#000000">sudo apt-get install motion
  2. </font>
复制代码
第二步:打开 motion daemon 守护进程,让它可以一直在后台运行
  1. <font color="#000000">sudo nano /etc/default/motion
  2. #no修改成yes:
  3. start_motion_daemon=yes</font>
复制代码
第三步:修改motion的配置文件
  1. <font color="#000000">sudo vim /etc/motion/motion.conf
  2. #deamon off 改成 on
  3. deamon on
  4. #设置分辨率
  5. width 800
  6. height 600
  7. #关闭 localhost 的限制
  8. webcam_localhost off</font>
复制代码
第四步:运行 motion
  1. <font color="#000000">sudo motion</font>
复制代码
安装好USB摄像头,运行后重启树莓派,现在我们的摄像头已经变成了一台网络摄像头,在chrome浏览器下访问 http://[your.domain]:8081 即可看到摄像头当前拍摄的画面,需要注意有时候重启树莓派后网络摄像头可能未启动,所以需要重新运行motion。
需要注意,第一种网络摄像头搭建方式不便于Android手机端的访问,所以这里我并未使用,这里仅做拓展讲解。
PC端使用Python访问非常简单,直接使用OpenCv模块的VideoCapture()方法,输入网络摄像头URL即可。使用伪代码如下:
  1. <font color="#000000">'''
  2. VideoCapture()中参数是0,表示打开电脑内置摄像头,
  3. 参数是1或2打开外部摄像头
  4. 参数是视频文件路径则打开视频文件
  5. 参数是URL打开网络摄像头
  6. '''
  7. cap = cv2.VideoCapture(URL)</font>
复制代码
3.2、MJPG-streamer方式
第一步:安装所需模块
  1. <font color="#000000">sudo apt-get updatesudo
  2. sudo apt-get install subversionsudo
  3. sudo apt-get install libjpeg8-devsudo
  4. sudo apt-get install imagemagicksudo
  5. sudo apt-get install libv4l-devsudo
  6. sudo apt-get install cmakesudo
  7. sudo apt-get install git</font>
复制代码
第二步:git一个开源的project:MJPG-streamer
  1. <font color="#000000">sudo git clone github.com/jacksonliam/mjpg-streamer.git
  2. cd mjpg-streamer/mjpg-streamer-experimentalsudo
  3. make all
  4. sudo make install</font>
复制代码
git过程比较缓慢,大家可以耐心等待一段时间,等待100%后确认完成后再执行下一条命令,启动USB摄像头,指令如下:
  1. <font color="#000000">./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"</font>
复制代码
树莓派无线WiFi采集摄像头数据的三种方法图2


这里有一点需要注意,配置USB摄像头和树莓派专用摄像头指令不一样,这里我配置的是USB摄像头,具体指令分别如下:
  1. <font color="#000000">指令启动普通USB摄像头指令:
  2. ./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"  
  3. 启动树莓派专用摄像头RaspiCamera的指令:
  4. ./mjpg_streamer -i "./input_raspicam.so" -o "./output_http.so -w ./www"</font>
复制代码
并且使用树莓派专用摄像头需要开启专用摄像头配置的,输入指令如下:
  1. <font color="#000000">sudo raspi-config</font>
复制代码
树莓派无线WiFi采集摄像头数据的三种方法图3


点击摄像头使能开启即可。
以上就实现了全部的网络摄像头搭建。不过每次我们启动树莓派的时候还是需要手动去执行对应的命令,比较麻烦。那么接下来我们把当前项设置为一个系统自启动项。
第三步:设置自启动
cd到系统home目录下,新建一个sh可执行文件,输入如下命令即可:
  1. <font color="#000000">sudo nano videoStart.sh</font>
复制代码
videoStart.sh文件中输入内容如下:
  1. <font color="#000000">cd /home/pi/mjpg-streamer/mjpg-streamer-experimental/
  2. ./mjpg_streamer -i "./input_uvc.so" -d /dev/video0 -f 30 -r 1280x720 " -o
  3. "./output_http.so -p 8080 -w ./www"</font>
复制代码
然后退出保存,最后记得再给个可执行权限,输入命令:
  1. <font color="#000000">sudo chmod +x /home/pi/videoStart.sh</font>
复制代码
接着再创建一个目录,输入命令如下:
  1. <font color="#000000">sudo mkdir .config/autostart</font>
复制代码
继续新建一个文件,输入对应命令如下:
  1. <font color="#000000">sudo nano .config/autostart/my.desktop</font>
复制代码
文件内容如下:
  1. <font color="#000000">[Desktop Entry]
  2. Type=shell
  3. Exec=/home/pi/videoStart.sh</font>
复制代码
最后保存退出,这样每次开机就自动执行对应文件命令,省去了每次手动执行的麻烦。
第四步:测试
查看图像,在PC端打开浏览器,输入下面网址可以看到静态截图:
http://<树莓派IP>:8080/?action=snapshot

树莓派无线WiFi采集摄像头数据的三种方法图4

动态图像的这个地址在有的浏览器不太好使,可以使用下面这个网址:
http://<树莓派IP>:8080/javascript_simple.html
经过不断探索树莓派和PC视频数据通信“三部曲”到此结束,希望对小伙伴们有帮助~


ASH腻  管理员

发表于 2024-1-11 11:26:12

很好的教程 赞
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail