基于M1s dock实现的网络相机
基于该速矽公司板卡完成的网络相机,完成了相机驱动并拍摄图片通过网络上传到电脑的过程。并在电脑端完成了照片合成视频的工作。
标签
嵌入式系统
Python
2023寒假在家练
yang15611106899
更新2023-03-28
北京理工大学
300

1.项目需求

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

2.完成的功能

  1. 相机驱动并且拍摄照片
  2. 将图片通过网络上传到电脑
  3. 通过电脑端编程将图片合成为一个视频

3.设计思路

  1. 因为该开发板仅支持在Linux下开发,搭建Linux为基础的Ubuntu虚拟机,在虚拟机上搭建硬件编程的环境。
  2. 下载远程仓库的example与SDK后,在example的c906app内有开源的demo,使用其中的camera_streaming_through_wifi的demo,改写其内的main.c程序以满足项目要求,烧录进开发板内,来完成开发板与主机的通信准备。烧录完成后实质上已经完成了相机的驱动并且拍摄照片这一目标。
  3. 在确认开发板已经与主机接入到同一WiFi下后,开始进行主机侧的上位机服务端编写。
  4. 在服务端编写完成后,经过测试已经将客户端也就是开发板采集的照片数据传输到电脑中后,使用python的opencv库来进行图片的拟合,输出一个.avi为结尾的视频。
  5. 查看视频发现视频能够正常播放,完成该项目。

4.实现过程

Fr1-gvIb5U9bLJqHLWt-bVU8u0a6

4.1 虚拟机与环境的搭建

虚拟机的搭建过程对于我这个从来没用过Liunx系统的人来说,是一种全新的的体验。首先,我下载了一个用于运行虚拟机的框架Vmware station,下载完成后,又从Ubuntu的官方网站上下载了Ubuntu的虚拟光驱,经过了一段时间的摸索后完成了Ubuntu的基本安装。

在安装完Ubuntu后,接下来的工作就是搭建编译环境,根据直播课程以及相关文档一步一步的搭建,因为对于虚拟机以及Linux系统都不太熟悉,在环境搭建的过程中走了许多的弯路。比如怎么在主机与虚拟机之间复制粘贴,如何去传递文件,如何挂载mnt。在一段时间的学习后,克服了这些很基础的小问题,然后便是给固件要进行编译烧写的文件进行修改了。

#include <stdbool.h>
#include <stdio.h>

/* FreeRTOS */
#include <FreeRTOS.h>
#include <task.h>

/* bl808 c906 std driver */
#include <bl808_glb.h>
#include <bl_cam.h>

#include <m1s_c906_xram_wifi.h>

void main()
{
    vTaskDelay(1);
    bl_cam_mipi_mjpeg_init();

    m1s_xram_wifi_init();
    m1s_xram_wifi_connect("yxy", "yxy596800");
    m1s_xram_wifi_upload_stream("192.168.3.224", 8888);
}

这一部分代码是前面所提到的camera_streaming_through_wifi的demo内的main.c内容,将其build之后便可在build_out的文件家里找到相应的bin文件,将其以优盘拖拽的形式便可以直接烧录进开发板了,十分的便捷。其中的m1s_xram_wifi_connect("yxy", "yxy596800"); 语句,对应的分别是要连接WiFi的ssid和连接所需的密码。m1s_xram_wifi_upload_stream("192.168.3.224", 8888);则是通过WiFi数据流上传的地址以及接口。该地址与主机连接的WiFi地址是一致的。在主机观测到开发板的上线与下线后,确定了开发板已经连入了同一WiFi的8888端口进行数据的传递。

FiogddMig7mjzhL5Mv7BcT5vUqo5

4.2主机服务端的构建并且接受数据文件

在完成了下位机的准备工作后,现在要做的便是编写上位机程序,因此我在这里用python编写了一个tcp服务器,来进行数据的收取。

# 服务器建立
tcpserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host="192.168.3.224"
port=8888
tcpserver.bind((host, port))
tcpserver.listen()

print("tcpServer 正在监听 192.168.3.224:8888")

print("等待建立连接中.........")

counter = 1

首先我构建了服务器,并且将地址与接口都写入服务器的套接字,完成基础的构建。

如果构建成功,便能够看到。

while True:
    client, address = tcpserver.accept()
    print(f"成功地同 {address} 建立连接")

 

    while True:
        # accept four bytes data to represent the piclength
        length_bytes = client.recv(4)
        if not length_bytes:
        break

然后用四个字节数据来表示图片的长度。如果收到的不是字节长度就跳出本段程序,依此进行一个循环收取的过程。

 

# Unicode four bytes data
        length = int.from_bytes(length_bytes, byteorder='little', signed=True)
        print(f"收到图片的长度为: {length}")
        client.send(b'1')

然后对收到的四个字符进行数据解码的过程,将解码得出的长度打印而出。

 

# accept picture data
        image_data = b''
        while len(image_data) < length:
            chunk = client.recv(length - len(image_data))
            if not chunk:
                break
            image_data += chunk
在解码完成之后,便进行数据收取的过程。

# save pictures
        out_folder = 'collected_pictures'
        if not os.path.exists(out_folder):
            os.makedirs(out_folder)
        image_file = os.path.join(out_folder, f'image{counter}.jpg')
        while os.path.exists(image_file):
            counter += 1
            image_file = os.path.join(out_folder, f'image{counter}.jpg')
        
        with open(image_file, "wb") as f:
            f.write(image_data)
        
        counter += 1

    client.close()

server.close()

在数据的收取完成后,此段代码进行收取图片的保存,创建了一个名为“collected_pictures”的文件夹,并将收到的图片收取到此文件夹中,在所有工作结束后,关闭服务器。

4.3对收取的图片进行视频拟合

在该过程中,使用了opencv的数据库来进行拟合的工作,首先要做的是定“collected_pictures”的文件夹的位置

# 设置图像目录
img_dir = 'collected_pictures/'

由此获得图像目录位置。

在获取了文件位置之后,从中提取文件。

# 获取图像
images = [img_dir + f for f in os.listdir(img_dir) if f.endswith('.jpg')]
images.sort()

然后设置输出视频的码率,文件名称等基本属性。值得注意的是,码率的改变对视频生成的流畅度有很大的帮助,因为经过简略的计算,下位机上传图片的速度大约是6帧,因此,视频帧率在这个值附近视频较为流畅,但因其基础采样值依旧过于小,所以生成的视频观看感受难免有卡顿之感。

# 设置视频帧率
fps = 4

# 设置输出视频文件
output_file = 'pictures_fusion.avi'

接着获得图片属性并且初始化视频写入器。

# 获取首张图片属性
frame = cv2.imread(images[0])
height, width, _ = frame.shape

# 初始化视频写入器
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
video_writer = cv2.VideoWriter(output_file, fourcc, fps, (width, height))

最后遍历所有图像并将其导入视频,结束进程。

# 循环遍历图像并将其写入视频
for image in images:

    frame = cv2.imread(image)

    video_writer.write(frame)

# 结束进程
video_writer.release()

4.4完整代码展示

tcp服务器代码

import socket
import os
# 服务器建立
tcpserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host="192.168.3.224"
port=8888
tcpserver.bind((host, port))
tcpserver.listen()

print("tcpServer 正在监听 192.168.3.224:8888")

print("等待建立连接中.........")

counter = 1

while True:
    client, address = tcpserver.accept()
    print(f"成功地同 {address} 建立连接")

    while True:
        # accept four bytes data to represent the piclength
        length_bytes = client.recv(4)
        if not length_bytes:
          break

        # Unicode four bytes data
        length = int.from_bytes(length_bytes, byteorder='little', signed=True)
        print(f"收到图片的长度为: {length}")
        client.send(b'1')

        # accept picture data
        image_data = b''
        while len(image_data) < length:
            chunk = client.recv(length - len(image_data))
            if not chunk:
                break
            image_data += chunk

        # save pictures
        out_folder = 'collected_pictures'
        if not os.path.exists(out_folder):
            os.makedirs(out_folder)
        image_file = os.path.join(out_folder, f'image{counter}.jpg')
        while os.path.exists(image_file):
            counter += 1
            image_file = os.path.join(out_folder, f'image{counter}.jpg')
        
        with open(image_file, "wb") as f:
            f.write(image_data)
        
        counter += 1

    client.close()

tcpserver.close()

 

图片输出视频代码

import cv2
import os

# 设置图像目录
img_dir = 'collected_pictures/'

# 获取图像
images = [img_dir + f for f in os.listdir(img_dir) if f.endswith('.jpg')]
images.sort()

# 设置视频帧率
fps = 4

# 设置输出视频文件
output_file = 'pictures_fusion.avi'

# 获取首张图片属性
frame = cv2.imread(images[0])
height, width, _ = frame.shape

# 初始化视频写入器
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
video_writer = cv2.VideoWriter(output_file, fourcc, fps, (width, height))

# 循环遍历图像并将其写入视频
for image in images:

    frame = cv2.imread(image)

    video_writer.write(frame)

# 结束进程
video_writer.release()

5.主要遇到的难题

  1. 在完成下位机工作时,在对c906的demo烧录以及e907的firmware烧录后,发现开发板一开始并不能连接到同一WiFi下,在经过查看后,发现SDK内关于WiFi的部分将ip地址写成了固定的,因此将其改变之后,重新编译烧录了firmware后,才解决了这个问题。
  2. 上位机的程序编写时,在建立了客户端与服务器的链接之后,如何将数据读取进来成为了最主要的问题,最后发现主机给客户端发去一个字节之后开始tcp通信,并且以四个字节为基础进行解码才最终解决了这个问题。
  3. 在将图片拟合成视频这一步,最开始视频很卡以为是拟合的问题,在经过不断的尝试后,发现因为下位机采集传输上来的图片本来就不够快因此出现较为明显的卡顿是正常现象,因此也就不再纠结,反而是最开始的时候将视频帧率设置太高,导致视频一闪而过。

6.未来的计划

  1. 该网络相机最主要的问题在于不能得知你拍摄的对象是什么,我曾尝试将摄像头信号在开发板自带的电容屏上显示出来,效果很良好,我也知道我拍摄的是什么,但是在上传数据并且解码的之后,得到的图片却变成了两张照片一左一右,我设想是因为电容屏也将显示的信号进行了上传,在未来或许能够改进使得网络相机所见即所得。
  2. 想让视频的流畅度提升,但这或许需要在硬件方面下手,提高数据采集的帧率,得到更加流畅的视频。但是显然现在还没有这样的能力去完成,笑。
附件下载
tcpserver.py
tcp服务器代码文件
WiFi_camera.rar
下位机固件demo
putout_video.py
图片生成视频代码文件
团队介绍
个人项目,由来自北京理工大学电子信息专业的本科生完成
团队成员
杨昕宇
北京理工大学本科生
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号