内容介绍
内容介绍
2023小脚丫寒假一起练项目三
项目需求
- 目标:完成一个基于Sipeed M1s Dock 的网络相机
- 具体要求:完成相机驱动,定时拍摄图片,并将图片通过网络传到电脑或服务器,实现长时间拍摄通过电脑端编程将图片合成为一个视频
结果展示
-
烧录附件中的固件
firmware.bin
,再烧录附件中的camera_streaming_through_wifi.bin
,M1s Dock驱动相机进行拍摄,之后将图片通过tcp连接传输至上位机,之后上位机会将图片合成为视频
- 烧录附件中的固件
firmware_delay1000.bin
,再重新烧录附件中的camera_streaming_through_wifi.bin
,发现现在相机每1秒拍摄一次 -
按q结束拍摄,或者录制30s(最长录制时间可通过修改python代码的全局变量
RECORD_TIME
进行设置)后自动结束。结束后会显示推荐帧率(因为受网络影响,图片传输的速率会有不同),需将python代码中的全局变量FPS
修改为推荐帧率
设计思路
- 烧录例程,发现没反应,通过串口打印的调试信息,发现停在
Socket connect..
- 全局搜索
Socket connect..
,发现在SDKcomponents\sipeed\e907\m1s_e907_xram\srcm1s_e907_xram_wifi.c
的upload_stream_task
- 阅读该函数发现开发者失误将IP与端口写死,修改代码为自己要设的IP与端口,重新烧录固件,发现可以正常通信。(之后官方对代码也进行了修复,修复了此bug)
- 继续阅读代码,发现会先发送4字节的图片长度,收到回复后发送图片,之后重新获取相机拍摄图片,重复上述内容
- 根据此逻辑编写python代码接收图片并拼接为视频
代码实现
python部分
1.1 全局变量
SAVE_PATH = "D:/vedio_test/" # 视频保存地址
RECORD_TIME = 30 # 视频最大录制时间
FPS = 9 # 视频帧率
2.1 确认发送的图片长度的格式是否正确
def check_length(length_byte):
if length_byte[-2:] != b'\x00\x00' and len(length_byte) != 4:
return -1
return 0
2.2 接收图片
def socket_receive(conn, length_require):
content = conn.recv(length_require)
rev_len = len(content)
while rev_len != length_require:
content += conn.recv(length_require - rev_len)
rev_len = len(content)
# print(f"receive length: {rev_len}")
return content
3.1 视频录制相关:
class Video(threading.Thread):
def __init__(self, save_dir: str, img_size: tuple, fps: int):
threading.Thread.__init__(self)
self.now_time = None
self.flag = None
self.start_time = None
self.screen_file_path = None
self.record_time = RECORD_TIME
self.fps = fps # 帧率为25,可以调节
self.save_dir = save_dir
self.get_video_path()
self.video = cv2.VideoWriter(self.screen_file_path, cv2.VideoWriter_fourcc(*'MJPG'), self.fps,
img_size)
def start_record(self):
self.start_time = time.time()
# 最长录制时间
def video_record(self, img):
self.video.write(img)
self.now_time = time.time()
delay = self.now_time - self.start_time
if delay >= self.record_time:
print("---超过指定时间,录制结束!---")
return 0
return 1
def stop_record(self):
self.video.release()
cv2.destroyAllWindows()
def get_video_path(self):
# 录屏保存的文件目录路径
if not os.path.exists(self.save_dir):
os.makedirs(self.save_dir)
# 得到录屏保存的文件路径 按照时间创建文件夹
file_name = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') + '_screen.avi'
# 文件路径
self.screen_file_path = os.path.join(self.save_dir, file_name)
# 找到合适的帧率
def get_fps(self):
video = VideoCapture(self.screen_file_path)
old_fps = video.get(CAP_PROP_FPS)
Count = video.get(CAP_PROP_FRAME_COUNT)
size = (int(video.get(CAP_PROP_FRAME_WIDTH)), int(video.get(CAP_PROP_FRAME_HEIGHT)))
if old_fps == 0 or self.now_time == self.start_time:
print("录制时间过短")
return -1
print("=========================")
print('视频帧率=%.1f' % old_fps)
print('视频的帧数=%.1f' % Count)
print('视频的分辨率', size)
print('视频时间=%.3f秒' % (int(Count) / old_fps))
print('视频的录制时间=%.3f秒' % (self.now_time - self.start_time))
new_fps = int(Count) / (self.now_time - self.start_time)
print('推荐帧率=%.2f' % (new_fps))
return 0
3.2 计算推荐帧率:
def get_fps(self):
video = VideoCapture(self.screen_file_path)
old_fps = video.get(CAP_PROP_FPS)
Count = video.get(CAP_PROP_FRAME_COUNT)
size = (int(video.get(CAP_PROP_FRAME_WIDTH)), int(video.get(CAP_PROP_FRAME_HEIGHT)))
if old_fps == 0 or self.now_time == self.start_time:
print("录制时间过短")
return -1
print("=========================")
print('视频帧率=%.1f' % old_fps)
print('视频的帧数=%.1f' % Count)
print('视频的分辨率', size)
print('视频时间=%.3f秒' % (int(Count) / old_fps))
print('视频的录制时间=%.3f秒' % (self.now_time - self.start_time))
new_fps = int(Count) / (self.now_time - self.start_time)
print('推荐帧率=%.2f' % (new_fps))
return 0
4 主函数
4.1 建立socket连接
s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None)
s.bind(('192.168.89.82', 8888))
s.listen(5) # 等待客户端连接
conn, addr = s.accept() # 建立客户端连接
print(f"connected to {addr}")
4.2 获取图片长度并回复
length_byte = socket_receive(conn, 4)
# print(f"length_byte: {length_byte}")
# 确认length的格式是否正确
if check_length(length_byte) != 0:
continue
length = struct.unpack('i', length_byte)[0]
# print(f"image length: {length}")
# print("--------------------------")
4.3 录制视频
img_b = socket_receive(conn, length)
img = cv2.imdecode(np.frombuffer(img_b, np.uint8), cv2.IMREAD_COLOR)
cv2.imshow('test', img)
key = cv2.waitKey(25)
ret = video.video_record(img)
if key == ord('q') or ret == 0:
video.flag = True
video.stop_record()
video.get_fps()
break
C语言部分
驱动1相机并通过wifi发送图片
bl_cam_mipi_mjpeg_init();
m1s_xram_wifi_init();
m1s_xram_wifi_connect("xxx", "xxx");
m1s_xram_wifi_upload_stream("x.x.x.x", 8888);
根据要求要定时拍摄,而原本代码是连续拍摄,可以在每次拍摄前添加时延以实现定时拍摄
vTaskDelay(1000);
ret = bl_cam_mjpeg_get(&pic, &len);
遇到的问题
- SDK中建立socket连接的代码有bug
解决:修正代码
- python接收图片时没接收完就开始接收下一张图片
通过一个while循环,每次接收后检查剩余需接收的长度,直到接收完为止
未来的计划
-
- 添加图形界面,使操作更加便捷
- 这次由于时间安排没来的及去做项目二至三,接下来会去尝试完成
附件:
- 可烧录的二进制文件(按成果展示中的步骤烧录):链接:https://pan.baidu.com/s/1MsKDigVceksZ717xLo97UQ
提取码:73mq
--来自百度网盘超级会员V2的分享 - python源码:链接:https://pan.baidu.com/s/1S5-fSrxQQ-JH0lKkjxNtJw
提取码:vaxh
--来自百度网盘超级会员V2的分享 - C源码:链接:https://pan.baidu.com/s/1ClFEg_APGPcrlIxea6eRJw
提取码:mr40
--来自百度网盘超级会员V2的分享
团队介绍
还没想好~~
团队成员
sqzr
评论
0 / 100
查看更多
猜你喜欢
基于 Sipeed M1s Dock 实现网络相机使用 Sipeed M1s Dock 的板载摄像头定时捕获当前画面,并通过 WiFi 将图片发送到同一网络下的 PC 服务程序。
topgear
1244
基于Sipeed M1s Dock实现的网络相机本项目基于Sipeed M1s Dock,运行PikaScript环境,应用Python控制开发板,通过摄像头实现照片的拍摄,通过网络传输拍摄的照片,实现为一个网络相机。同时提供桌面应用接收数据,控制拍照和视频录制。
HonestQiao
697
基于Sipeed M1s Dock实现网络相机目标:完成一个基于Sipeed M1s Dock 的网络相机
具体要求:
完成相机驱动,定时拍摄图片,并将图片通过网络传到电脑或服务器,实现长时间拍摄
通过电脑端编程将图片合成为一个视频
Kita-Ikuyo
922