如何从 Linux 帧缓冲区获取 RGB 像素值?

Posted

技术标签:

【中文标题】如何从 Linux 帧缓冲区获取 RGB 像素值?【英文标题】:How to get RGB pixel values from Linux framebuffer? 【发布时间】:2017-10-21 18:42:05 【问题描述】:

我想使用 Linux 以最有效的方式获取屏幕像素的 RGB 值。所以我决定使用 C 中的帧缓冲库 (fb.h) 来访问帧缓冲设备 (/dev/fb0) 并直接从中读取。

这是代码:

#include <stdint.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

int main() 

    int fb_fd;
    struct fb_fix_screeninfo finfo;
    struct fb_var_screeninfo vinfo;
    uint8_t *fb_p;

    /* Open the frame buffer device */
    fb_fd = open("/dev/fb0", O_RDWR);
    if (fb_fd < 0) 
        perror("Can't open /dev/fb0\n");
        exit(EXIT_FAILURE);
    

    /* Get fixed info */
    if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) < 0) 
        perror("Can't get fixed info\n");
        exit(EXIT_FAILURE);
    

    /* Get variable info */
    if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) 
        perror("Can't get variable info\n");
        exit(EXIT_FAILURE);
    

    /* To access to the memory, it can be mapped*/
    fb_p = (uint8_t *) mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
    if (fb_p == MAP_FAILED) 
        perror("Can't map memory\n");
        exit(EXIT_FAILURE);
    

    /* Print each byte of the frame buffer */
    for (int i = 0; i < finfo.smem_len; i++) 
        printf("%d\n", *(fb_p + i));

        // for (int j = 0; j < 500000000; j++);       /* Delay */
    

    munmap(fb_p, 0);
        close(fb_fd);

    return 0;

但是当我打印这些值时,我没有得到我所期望的......

如果我使用 grabc 之类的工具选择像素 (0, 0) 的 RGB 值,我会得到:

#85377e
133,55,126

但我的代码的第一次打印是:

126
145
198
...

看起来我很好地获得了第一个像素的第一个值,对应于蓝色,但其余的都是错误的。

【问题讨论】:

我没有使用 Linux 帧缓冲区的经验,但是您获得的像素缓冲区是否不是从您认为的 (0,0) 开始的?例如。您选择了左上角的值,但缓冲区从右下角开始。 当您尝试读取帧缓冲区数据时,您是否在 X Window 系统中?也许这是不可能的。 关键问题是 XWindow 可能根本不使用内核中的帧缓冲区。据我所知,像 amd 或 nvidia 这样的 gpu 不会在内核中的 fb 驱动程序上进行中继。但是当通过 ctrl+alt+fn 切换到控制台时,我相信你可以得到正确的帧缓冲区 rgb 值。 fbgrab -b 24 test.png 是否达到了您的预期? 无论如何,如果您使用 X Window System,this 应该会有所帮助。只需打开 /tmp/fbe_buffer 而不是 /dev/fb0 【参考方案1】:

如果您查看 grabc 源代码 (https://www.muquit.com/muquit/software/grabc/grabc.html),您会发现他正在查询 X Windows(并且不是硬件帧缓冲区本身)。

root_window=XRootWindow(display,XDefaultScreen(display));
target_window=selectWindow(display,&x,&y);
...
ximage=XGetImage(display,target_window,x,y,1,1,AllPlanes,ZPixmap);
...
color->pixel=XGetPixel(ximage,0,0);
XDestroyImage(ximage);

无论它值多少钱 - 出于几个不同的原因 - 我强烈建议您考虑这样做。

您可能还对以下内容感兴趣:

Intro to Low-Level Graphics on Linux

DirectFB

【讨论】:

我认为这个问题与 Linux 控制台有关;不是 X Window 系统。 我对这个解决方案不感兴趣。我想直接从设备上读取……不知道有没有可能。 是的,这是可能的。它只是提出了一些“挑战”。我引用的两个链接都处理低级“帧缓冲区 I/O”。 DirectFB 库可能有助于简化您的任务。几年前我自己也尝试过同样的事情......最终使用 Xlib 而不是 FB。另一个链接:ummon.eu/Linux/API/Devices/framebuffer.html @Sergio /dev/fb 不是“直接来自设备”。是司机送的可能对设备实际在做什么一无所知的驱动程序 - 因为 X 服务器正在直接与设备对话。 @Wumpus Q. Wumbley - 优秀的分数。此外,值得强调的是 /dev/fb STILL 是一个“抽象层”(如 XLib),HIDES(可能有用)详细信息,并导致 绩效处罚。另请注意,/dev/fb 可能未配置为与任何 Xlib(即可见)图形相关!【参考方案2】:

在一些使用 fbdev 的罕见错误设备中,您可能需要将 mmap 返回的指针与下一个 system page 对齐,以便正确获取第一个像素,这样做您可能还需要再映射一个页面。

我不认为这是您的情况,您似乎正在尝试通过使用 linux fbdev 获取 xserver 桌面像素,除非您的 xserver 配置为使用 fbdev 驱动程序(即使这样我也不确定会工作)是行不通的。 如果读取桌面像素是您的目标,您应该查看一些屏幕截图工具。

此外,fbdev 驱动程序通常非常基础,是一种低级访问,但在映射和读取像素方面没有任何效率,Xlib 或任何更高级别的图形系统可能会意识到并支持您的图形卡加速并且效率更高(也许使用 DMA 将整个帧缓冲区读取到系统内存而不加载 cpu)。

【讨论】:

【参考方案3】:

ios 上工作时,有一个私有框架 iOMobileFrameBuffer,并为屏幕创建了一个可爱的录制软件。我以前涉足过这个,所以我会给你我所知道的。 iOS 显然与 Linux FrameBuffer 不同(缺乏文档非常相似......)但我个人会看一下this 的答案。尽管该问题不符合您的要求,但该答案解释了 PixelBuffer 以及您所要求的一个小示例。阅读并回答任何问题,我会尽我所能回答。

【讨论】:

以上是关于如何从 Linux 帧缓冲区获取 RGB 像素值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在opengl es 3+中获取我的离屏帧缓冲区内的底层对象的像素颜色?

OpenCV 访问 MAT 对象中的 RGB 值

如何从 OpenGL 中的帧缓冲区纹理中采样像素?

从视频流中获取像素数据

WebGL 帧缓冲区 ClearColor 仅影响 (0,0) 像素

从(视频)帧英特尔实感获取 RGB 值时出现指针异常