基于Sipeed M1s Dock的网络相机
基于Sipeed M1s Dock的网络相机 实现拍摄与查看
标签
嵌入式系统
Python
2023寒假在家练
M1s Dock
远程相机
颜七岁
更新2023-03-28
475

板卡介绍

1、Sipeed M1s Dock 是基于 Sipeed M1s 模组来设计的一款核心板,引出了 MIPI CSI、SPI LCD 等 FPC 接口,免去接线难的烦恼。使用最精简的设计,用于客户对模组进行模组评估,或者爱好者直接上手游玩等用途。

 

FrPYG7Uq0r8ZwM-YURtDk8HYRJ5f

3、板卡特点:

  • 主芯片 BL808 RISC-V 480Mhz + NPU BLAI-100
  • 板载 USB 转 UART 调试器(可实现一键点击烧录,无需按实体按键)
  • 1.69 寸 240x280 电容触摸屏
  • 200W 像素摄像头
  • 支持 2.4G WIFI / BT / BLE
  • 板载 1 个模拟麦克风、1 个 LED、1 个 TF 卡座
  • 引出一路 USB-OTG 到 USB Type-C 接口
  •  

此次板卡还可以连接wifi,实现远程数据上传等物联网方便的任务。是一款非常适合入门学习机器学习,AI识别的开发板

 

任务要求

项目5 -  网络相机

目标:完成一个基于Sipeed M1s Dock 的网络相机
具体要求:

  • 完成相机驱动,定时拍摄图片,并将图片通过网络传到电脑或服务器,实现长时间拍摄
  • 通过电脑端编程将图片合成为一个视频

 

设计思路

我们将Sipeed M1s Dock设备作为下位机,其主要的作用就是连接网络并且拍摄图片,再将拍摄好的照片通过WiFi传输到指定的上位机。这里程序可通过视频讲解进行代码编译,并且下载,本次使用到的下位机程序由官方提供。

由于我的电脑对 8888端口冲突,因此本项目的端口号我改为了8889。

上位机,作为采集照片,并且将采集到的照片进行存储。

上位机采用python编程语言,编译器使用的是pycharm。

 

存储路径:(所存储的路径均为目录文件的相对路径,这样为了以后项目更好的移植等等)

│ ├─m1s code
│ │ │ img
│ │ │ video
│ │ │ main.py

我是用tkinter库作为上位机的界面。

Fo-YNW8oBBe4ICfpx0SqYGPtDehQ

我的设计思路是:

1、下位机连接WiFi,开始等待握手数据包。

2、打开上位机,连接下位机并且开始建立tcp连接,成功之后等待采集照片命令。

3、点击开始拍摄按钮,上位机开始接收下位机发来的图片,并且将图片按照顺序存储到相对路径的img文件夹内

4、点击停止拍摄并播放,这里思路采用逐次执行三个函数,第一个是停止函数,这里采用线程的方法实现开始和暂停。

然后再将img文件夹内的照片进行二次排序,通过opencv库实现图片转换成视频。紧接着在上位机上进行播放视频。

 

代码解释

此次上位机使用python语言来开发。实际上我将停止采集和图片合成视频以及播放写在一个函数当中,可以一键完成视频的播放,更加直观,此次项目使用了一下库:

import socket
import numpy as np
import cv2 as cv
import tkinter as tk
from tkinter import *
import os
from PIL import Image, ImageTk
import threading
from tkinter.messagebox import *

python太好用了!!!!

采集图片

首先与下位机建立TCP链接,在主程序中,通过线程设定一个标志位来实现开始和暂停。

def code_main():
    global stop_flag

    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    tcp_server.bind(("192.168.31.124", 8889))
    tcp_server.listen(128)
    tcp_client, tcp_client_address = tcp_server.accept()

    # 代码执行到此说明连接建立成功
    print("摄像头连接成功,IP端口号为", tcp_client_address)

    index = 1

    while True:
        if stop_flag == 1:
            # 接收客户端发送的数据, 这次接收数据的最大字节数是4
            recv_data = tcp_client.recv(4)
            mjpeg_len = int.from_bytes(recv_data, 'little')
            # print("recv len: ", mjpeg_len)
            tcp_client.send(recv_data)
            recv_data_mjpeg = b''
            remained_bytes = mjpeg_len
            while remained_bytes > 0:
                recv_data_mjpeg += tcp_client.recv(remained_bytes)
                remained_bytes = mjpeg_len - len(recv_data_mjpeg)
            # print("recv stream success")
            if recv_data_mjpeg[:2] != b'\xff\xd8' \
                    or recv_data_mjpeg[-2:] != b'\xff\xd9':
                continue

            mjpeg_data = np.frombuffer(recv_data_mjpeg, 'uint8')
            img = cv.imdecode(mjpeg_data, cv.IMREAD_COLOR)

            # cv.imshow('Online Video', img)

            # 保存图片
            cv.imwrite(path + './img/' + str(index) + ".jpg", img)
            print("已将" + str(index) + ".jpg 图片成功保存!")

            index += 1

        elif stop_flag == 0:
            print("停止获取")
            stop_flag = 1
            Generate_video()

            play_video()
            break

 

转换视频

讲得到的图片先进行排序,按照文件的名称。随之通过CV库来生成视频

def Generate_video():
    for root, dirs, files in os.walk(file_dir):
        for file in files:
            list.append(file)  # 获取目录下文件名列表
            list.sort(key=lambda x: int(x[:-4])) ##文件名按数字排序,不然会卡帧
    # VideoWriter是cv2库提供的视频保存方法,将合成的视频保存到该路径中
    # 'MJPG'意思是支持jpg格式图片
    # fps = 20代表视频的帧频为5,如果图片不多,帧频最好设置的小一点
    # (800,600)是生成的视频像素800*600,一般要与所使用的图片像素大小一致,否则生成的视频无法播放
    # 定义保存视频目录名称和压缩格式,像素为1280*720
    #生成视频 = 存放路径 + 视频格式 + 视频格式类型 + 帧率 + 视频像素
    video = cv.VideoWriter(path +'./video/myvideo.avi',cv.VideoWriter_fourcc(*'MJPG'),10,(800,600))
    for i in range(1, len(list)):
        # 读取图片
        img = cv.imread(path + './img/' + list[i - 1])
        # 将图片转换为1280*720像素大小
        img = cv.resize(img, (800, 600))
        # 写入视频
        video.write(img)

    # 释放资源
    print("视频生成成功" + path +'\\video\\myvideo.avi')#给个弹窗提示吧
    showinfo('信息', '完成')
    video.release()

 

播放视频

将视频通过tkinter界面显示出来

def play_video():
    print("播放视频")
    wait_time = 1000 / 55 #播放速度
    # 刷新一下目录
    while video.isOpened():  # 如果被打开
        result, movieFrame = video.read()  # 读取视频对象的信息 result 代表视频还有没有帧数,如果有,那么True,否则反之,第二个是帧的图片序列
        if result:  # 如果视频没播完
            v_image = cv.cvtColor(movieFrame, cv.COLOR_BGR2RGBA)#视频色差
            image_cv = Image.fromarray(v_image)  # 将帧的图片序列转化为图片对象
            image_tk = ImageTk.PhotoImage(image=image_cv)  # 将图片对象传入tkinter支持的对象
            Label_movie.config(image=image_tk)  # 将Label的图片参数修改为image_tk
            Label_movie.image = image_tk  # 同上,如果少了这句话,那么会导致部分的视频闪烁
            window.update()  # 更新窗口
            cv.waitKey(int(wait_time))
        else:  # 如果视频播完了,那么退出循环
            break

 

 

 

软件框图

FheimjttvTCtbj4b2xRG2dgpR1G6

 

活动心得

感谢硬禾学堂的寒假一起练活动,此次使用Sipeed M1s Dock成功实现了网络相机的任务,此次收获比较多,了解了linux环境,并且通过Python来制作上位机。我从中学到很多在校期间学不到的内容,此次的任务还可以更加完善,继续努力学习。争取把这个项目做的更好。同时更期待硬禾学堂的暑期活动!

附件下载
m1s-寒假一起练.zip
程序代码
团队介绍
基于Sipeed M1s Dock的网络相机 实现拍摄与查看
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号