基于Sipeed M1s Dock实现的网络相机
本项目是基于Sipeed M1s Dock实现的网络相机,可实现定时拍摄图片,并将图片通过网络传到电脑或服务器,实现长时间拍摄
标签
嵌入式系统
2023寒假在家练
sqzr
更新2023-03-28
南京信息工程大学
282

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修改为推荐帧率

成果展示2

设计思路

  1. 烧录例程,发现没反应,通过串口打印的调试信息,发现停在Socket connect..
  2. 全局搜索Socket connect..,发现在SDKcomponents\sipeed\e907\m1s_e907_xram\srcm1s_e907_xram_wifi.cupload_stream_task
  3. 阅读该函数发现开发者失误将IP与端口写死,修改代码为自己要设的IP与端口,重新烧录固件,发现可以正常通信。(之后官方对代码也进行了修复,修复了此bug)
  4. 继续阅读代码,发现会先发送4字节的图片长度,收到回复后发送图片,之后重新获取相机拍摄图片,重复上述内容
  5. 根据此逻辑编写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);

遇到的问题
  1. SDK中建立socket连接的代码有bug

解决:修正代码

  1. python接收图片时没接收完就开始接收下一张图片

通过一个while循环,每次接收后检查剩余需接收的长度,直到接收完为止

未来的计划

    1. 添加图形界面,使操作更加便捷
    2. 这次由于时间安排没来的及去做项目二至三,接下来会去尝试完成

 

附件:

  1. 可烧录的二进制文件(按成果展示中的步骤烧录):链接:https://pan.baidu.com/s/1MsKDigVceksZ717xLo97UQ 
    提取码:73mq 
    --来自百度网盘超级会员V2的分享
  2. python源码:链接:https://pan.baidu.com/s/1S5-fSrxQQ-JH0lKkjxNtJw 
    提取码:vaxh 
    --来自百度网盘超级会员V2的分享
  3. C源码:链接:https://pan.baidu.com/s/1ClFEg_APGPcrlIxea6eRJw 
    提取码:mr40 
    --来自百度网盘超级会员V2的分享
团队介绍
还没想好~~
团队成员
sqzr
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号