使用 OpenCV 写入 /dev/fb0

Posted

技术标签:

【中文标题】使用 OpenCV 写入 /dev/fb0【英文标题】:Writing to /dev/fb0 using OpenCV 【发布时间】:2015-09-22 05:42:03 【问题描述】:

所以对于使用 OpenCV 对映射的帧缓冲设备(例如 /dev/fb0)hwne 执行 memcopy 有点困惑。

例如,查看 C 和 C++ 的 OpenCV 捕获函数之一:

C: IplImage* cvQueryFrame(CvCapture* capture) 
C++: bool VideoCapture::read(Mat& image)

看起来图像数据存储在 C++ 的矩阵结构 (MAT) 和 C 的 CvCapture 黑盒结构中。网上有很多关于如何使用这些的示例,所以那里很好。

但是,我还没有找到任何好的(一致的)代码 sn-ps 来展示如何获取存储在这些结构中的图像数据并将其复制到帧缓冲区。

有谁知道或有此所需转换的示例?

(Linux 和 OpenCV 的新手)

【问题讨论】:

为什么要写入帧缓冲区? 我有一个嵌入式板,带有最小的 Linux 映像 - 没有桌面等。我连接了一个 USB DisplayLink 显示器。最终我想做一些视频处理,但现在我只是想显示来自网络摄像头的图像。 如果你有更好的方法,我愿意接受新的想法 在内核中使用 DRM 会更好吗? 没有;最好转向高级抽象。 【参考方案1】:

不管这种方式好不好,都可以这样(只支持一些像素格式的组合):

/**
 * @file fbwrite_video.cpp
 * Displays OpenCV video on framebuffer.
 * Compile with
 * g++ -o fbwrite_video -lopencv_core -lopencv_highgui -lopencv_imgproc fbwrite_video.cpp
 *
 * Contains code from https://***.com/questions/4722301/writing-to-frame-buffer
 */

#include <iostream> // for std::cerr
#include <opencv2/imgproc/imgproc.hpp>  // for cv::cvtColor
#include <opencv2/highgui/highgui.hpp> // for cv::VideoCapture
#include <fstream> // for std::ofstream
#include <boost/timer/timer.hpp> // for boost::timer::cpu_timer

// this is C :/
#include <stdint.h> // for uint32_t
#include <sys/ioctl.h> // for ioctl
#include <linux/fb.h> // for fb_
#include <fcntl.h> // for O_RDWR
struct framebuffer_info  
    uint32_t bits_per_pixel; uint32_t xres_virtual; 
;
struct framebuffer_info get_framebuffer_info(const char* framebuffer_device_path) 
    struct framebuffer_info info;
    struct fb_var_screeninfo screen_info;
    int fd = -1;
    fd = open(framebuffer_device_path, O_RDWR);
    if (fd >= 0) 
        if (!ioctl(fd, FBIOGET_VSCREENINFO, &screen_info)) 
            info.xres_virtual = screen_info.xres_virtual;
            info.bits_per_pixel = screen_info.bits_per_pixel;
        
    
    return info;
;
// C ends here

int main(int, char**) 
    const int frame_width = 320;
    const int frame_height = 240;
    const int frame_rate = 10;
    framebuffer_info fb_info = get_framebuffer_info("/dev/fb0");
    cv::VideoCapture cap(0);
    if(!cap.isOpened()) 
        std::cerr << "Could not open video device." << std::endl;
        return 1;
     else 
        std::cout << "Successfully opened video device." << std::endl;
        cap.set(CV_CAP_PROP_FRAME_WIDTH,frame_width);
        cap.set(CV_CAP_PROP_FRAME_HEIGHT,frame_height);
        cap.set(CV_CAP_PROP_FPS,frame_rate);
        std::ofstream ofs("/dev/fb0");
        cv::Mat frame;
        while (true) 
            cap >> frame;
            if (frame.depth() != CV_8U) 
                std::cerr << "Not 8 bits per pixel and channel." << std::endl;
             else if (frame.channels() != 3) 
                std::cerr << "Not 3 channels." << std::endl;
             else 
                // 3 Channels (assumed BGR), 8 Bit per Pixel and Channel
                int framebuffer_width = fb_info.xres_virtual;
                int framebuffer_depth = fb_info.bits_per_pixel;
                cv::Size2f frame_size = frame.size();
                cv::Mat framebuffer_compat;
                switch (framebuffer_depth) 
                    case 16:
                        cv::cvtColor(frame, framebuffer_compat, cv::COLOR_BGR2BGR565);
                        for (int y = 0; y < frame_size.height ; y++) 
                            ofs.seekp(y*framebuffer_width*2);
                            ofs.write(reinterpret_cast<char*>(framebuffer_compat.ptr(y)),frame_size.width*2);
                        
                    break;
                    case 32: 
                            std::vector<cv::Mat> split_bgr;
                            cv::split(frame, split_bgr);
                            split_bgr.push_back(cv::Mat(frame_size,CV_8UC1,cv::Scalar(255)));
                            cv::merge(split_bgr, framebuffer_compat);
                            for (int y = 0; y < frame_size.height ; y++) 
                                ofs.seekp(y*framebuffer_width*4);
                                ofs.write(reinterpret_cast<char*>(framebuffer_compat.ptr(y)),frame_size.width*4);
                            
                         break;
                    default:
                        std::cerr << "Unsupported depth of framebuffer." << std::endl;
                
            
        
    

【讨论】:

【参考方案2】:

不要尝试直接访问原始帧缓冲设备。

改为使用一些抽象出实际输出设备的中间库,例如SDL(我不完全确定 SDL2 是否支持帧缓冲区(可能是 does not),但 SDL1 肯定支持。

【讨论】:

这是过度概括吗?是否有一些理由推荐用于嵌入式系统,例如运行 Raspian 的没有桌面(例如没有 X11)或竞争者的 Raspberry Pi,用于专用应用程序......并且只有一个图形设备?那肯定是个例外吧?在这种情况下,为什么我需要抽象的开销?

以上是关于使用 OpenCV 写入 /dev/fb0的主要内容,如果未能解决你的问题,请参考以下文章

mmap /dev/fb0 因“无效参数”而失败

Linux系统编程应用 Linux系统中找不到设备/dev/fb0

显示设备文件接口

Linux下读取framebuffer(/dev/fb0)出来的数据全部是0,为啥会这样

应用程序操作LCD源码分析

帧缓冲设备应用开发