基于Luckfox Pico的opencv使用UDP协议与ubuntu传输摄像头数据-小白进阶

使用UDP传输opencv的mat数据并显示

本教程适用于进阶的小白尝试
先说一下背景吧,正在工作的我,突然间看到淘宝上有个很漂亮的价格还不错的linux小板子,遂买下。没错,工作太无聊以至于开始摸鱼学习~
但奈何每天工作完回家就像躺着,所以板子到手都快半年了才开始研究
实现了简陋的摄像头传输,所以如果有大佬们有更优的代码请评论区讨论,必有3Q回复~
话不多说请看效果图
在这里插入图片描述

器材准备

硬件:
1.Luckfox Pico小板一个
2.Luckfox Pico配套摄像头一个
3.一个小TF卡(我用的是16G的)
软件:
1.带ubuntu的虚拟机
2.ubuntu安装opencv
3.win下的终端我用的MobaXterm
4.luckfox官方教程网页

小板子我是看了价格便宜买的,才四十,感觉linux的学习成本越来越低喽
在这里插入图片描述
摄像头五十拿下
在这里插入图片描述

基础说明

0.需要将tf卡按照官方教程烧录好
1.此教程是基于Luckfox Pico的 “优质社区分享”-----“opencv-mobile” 篇章做的延申,所以可以先将opencv-mobile篇章做完,这样可以确定摄像头到开发板到电脑的链路是通的。luckfox的opencv-mobile篇章
ubuntu
2.虚拟机中的opencv需要安装好,以便ubuntu调用显示 opencv安装教程及说明
3.还会用到利用opencv的编译 opencv的cmake最简单编译
以上四步完成后,下面的操作简直就易如反掌了

win与虚拟机网络设置

我当前的做法是将开发板连接到win电脑,然后通过虚拟机的桥接模式连接到虚拟机,做法如下

WIN连接

其实官方有说明了,不过为了方便大家我还是列出来吧
开发板本身的地址是172.32.0.93
开发板通过typec连接到电脑后电脑会自动多出一个网络连接
名称是“Remote NDIS based Internet Sharing Device”
在这里插入图片描述
将此网络的IPV4的ID改成172.32.0.100,这是为了让电脑与开发板在同一网段下可以相互访问
在这里插入图片描述
至此,便可以使用MobaXterm连接至开发板
用户名:root
密码:luckfox
在这里插入图片描述

虚拟机连接

首先打开虚拟网络编辑器
在这里插入图片描述
将桥接模式的网络设置为开发板的网络(别忘应用哦)
在这里插入图片描述
其次将虚拟机的设置指定为刚设置好的桥接
在这里插入图片描述
然后在ubuntu系统中将网络地址手动设置为172.32.0.101(其实和win下类似,不过是防止与win冲突)
在这里插入图片描述
至此网络设置完美落幕

开发板程序

直接基于官方的luckfox的opencv-mobile篇章内的代码稍作改动(这也是为什么建议先将官方的opencv例子跑通)
拓展:需要注意的是,官方的opencv精简,所以无法使用此opencv库来发送已有的图片,因为官方的opencv精简到无法读取图片哈哈哈哈

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#include <unistd.h>   // sleep()
 

#define SERVER_IP "172.32.0.101" // 服务器IP地址,这里使用本地地址作为示例
#define SERVER_PORT 90     // 服务器端口号,根据你的需要设置

int main()
{
    cv::VideoCapture cap;
    cap.set(cv::CAP_PROP_FRAME_WIDTH, 320);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, 240);
    cap.open(0);

    // cv::Mat images = cv::imread("123.jpg");
    // if(images.empty()){
    //     printf("oprn .jpg error!\n");
    //     return -1;
    // }

    const int w = cap.get(cv::CAP_PROP_FRAME_WIDTH);
    const int h = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
    fprintf(stderr, "%d x %d\n", w, h);

    cv::Mat bgr;

        cap >> bgr;

        sleep(1);


    cap.release();


        cv::Mat out(h , w , CV_8UC3);
        bgr.copyTo(out(cv::Rect(0, 0, w, h)));

        cv::imwrite("out.jpg", out);



#if 1
    int sockfd;
    struct sockaddr_in server_addr;
    char *message = "Hello, UDP Server!"; // 要发送的消息内容
    int bytes_sent, bytes_received;
 
    // 创建UDP socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }
    printf("Socket created successfully.\n");
 
    // 设置服务器地址信息
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET; // 使用IPv4地址族
    server_addr.sin_port = htons(SERVER_PORT); // 设置端口号(网络字节序)
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr); // 设置服务器IP地址
 
      // 将图片编码为JPG格式
    std::vector<uchar> buf;
    cv::imencode(".jpg", bgr, buf);

    uint32_t size_buf = buf.size();

    // 发送数据到服务器
    bytes_sent = sendto(sockfd, &size_buf, sizeof(buf.size()), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (bytes_sent < 0) {
        perror("Sendto failed");
        exit(EXIT_FAILURE);
    }
    printf("Data sent to server successfully.\n");
 
    sleep(1);
    
    bytes_sent = sendto(sockfd, buf.data(), buf.size(), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (bytes_sent < 0) {
        perror("Sendto failed");
        exit(EXIT_FAILURE);
    }
    printf("Data sent to server successfully.\n");

    // 这里可以添加接收服务器响应的代码(使用recvfrom函数)...(略)...
    // ...处理接收到的数据...(略)...
 
    close(sockfd); // 关闭socket连接
#endif

    return 0;
}

ubuntu程序

这是我的ubuntu的程序,大概意思就是先接收图片的大小,然后再按照大小接收图片的数据。
备注:有时候接收的大小也会乱掉,大家多跑几次就通了哈哈哈,毕竟不是精装房~

先看我的代码结构,因为这也是用cmake来做的编译
在这里插入图片描述
然后是cmakelists的代码,其实套路就是 opencv的cmake最简单编译

project(test)
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 11)

SET(CMAKE_C_COMPILER "g++")
SET(CMAKE_CXX_COMPILER "g++")

#set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/usr/lib/x86_64-linux-gnu/cmake/opencv4")
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

add_executable(test opencv.cpp)

target_link_libraries(test ${OpenCV_LIBS})

以下才是真正的代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
 
#define BUFFER_SIZE (1024 * 16)
#define PORT 90
 
int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[BUFFER_SIZE];
    int received_bytes;
 
    // 创建socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }
 
    // 设置服务器地址结构
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 
    // 绑定socket到服务器地址
    if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
 
    printf("Waiting for data on port %d\n", PORT);
    uint32_t len = 0;
    // 接收数据
    while (1) {
        while(1)
        {
            memset(buffer, 0, 4);
            received_bytes = recvfrom(sockfd, buffer, 4, 0, NULL, NULL);
            if (received_bytes < 0) {
                perror("recvfrom failed");
                exit(EXIT_FAILURE);
            }
            if((buffer[1]<0x40)&&(buffer[1]>0x10))
            {
                if((buffer[2]==0x00)&&(buffer[3]==0x00))
                {
                    len = ((uint32_t)buffer[3]<<24)|((uint32_t)buffer[2]<<16)|((uint32_t)buffer[1]<<8)|((uint32_t)buffer[0]);
                    printf("Received length: %x\n", len);
                    break;
                }
                else
                {
                    printf("buffer 2-3 is not 0x00 ! %x %x %x %x\n",buffer[0],buffer[1],buffer[2],buffer[3]);
                }

            }
            else
            {
                printf("buffer 0-1 is not 0x20-0x25 ! %x %x %x %x\n",buffer[0],buffer[1],buffer[2],buffer[3]);
            }

        }
        memset(buffer, 0, len);
        received_bytes = recvfrom(sockfd, buffer, len, 0, NULL, NULL);
        if (received_bytes < 0) {
            perror("recvfrom failed");
            exit(EXIT_FAILURE);
        }
        printf("Received message!\n");

        cv::Mat image = cv::imdecode(cv::Mat(1, received_bytes, CV_8UC1, buffer), cv::IMREAD_COLOR);
        printf("Received image!\n");
        if (!image.empty()) {
                // 显示或处理图像
                cv::imshow("Received Image", image);
                cv::waitKey(0);
            } else {
                printf("Failed to decode image\n");
            }

    }
 
    close(sockfd);
    return 0;
}

效果

先运行虚拟机上build里的test:

sudo ./test

然后运行开发板上已经给过去的opencv-mobile-test

./opencv-mobile-test

然后就会弹出照片框喽
在这里插入图片描述

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
LuckFox Pico是一款基于ARM架构的嵌入式开发板,它支持烧录Linux操作系统。下面是烧录LinuxLuckFox Pico的一般步骤: 1. 准备开发环境:首先,你需要安装交叉编译工具链和相关的开发工具,例如GCC编译器、Make工具等。 2. 获取Linux内核源代码:你可以从Linux内核官方网站或者其他适配了LuckFox Pico的开源项目中获取适用于该开发板的Linux内核源代码。 3. 配置内核:进入内核源代码目录,使用交叉编译工具链进行配置。你可以使用make menuconfig命令来选择适合LuckFox Pico的配置选项,例如选择正确的处理器架构、设备驱动等。 4. 编译内核:使用make命令进行内核编译。这个过程可能需要一些时间,取决于你的电脑性能和内核源代码的大小。 5. 生成根文件系统:除了内核,你还需要一个根文件系统来运行Linux。你可以选择使用已有的根文件系统,或者自己构建一个。根文件系统包含了Linux所需的各种库、工具和配置文件。 6. 烧录内核和根文件系统:将编译好的内核和根文件系统烧录到LuckFox Pico的存储介质中,例如SD卡或者eMMC。你可以使用烧录工具,如dd命令或者专门的烧录软件来完成这个步骤。 7. 启动LuckFox Pico:将烧录好的存储介质插入LuckFox Pico,然后通过串口或者其他方式连接到开发板。启动开发板后,它将加载内核并运行Linux操作系统。 希望以上步骤对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值