Merge pull request #25661 from itlab-vision:framebuffer

Highgui backend on top of Framebuffer #25661

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [ x] I agree to contribute to the project under Apache 2 License.
- [ x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [ x] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ x] The feature is well documented and sample code can be built with the project CMake

Environment variables used:
OPENCV_UI_BACKEND - you need to add the value “FB”
OPENCV_UI_PRIORITY_FB - requires priority indication
OPENCV_HIGHGUI_FB_MODE={FB|XVFB|EMU} - mode of using Framebuffer (default "FB")
- FB - Linux Framebuffer
- XVFB - virtual Framebuffer
- EMU - emulation (images are not displayed)
OPENCV_HIGHGUI_FB_DEVICE (FRAMEBUFFER) - path to the Framebuffer file (default "/dev/fb0").

Examples of using:

sudo OPENCV_UI_BACKEND=FB ./opencv_test_highgui
sudo OPENCV_UI_PRIORITY_FB=1111 ./opencv_test_highgui
OPENCV_UI_BACKEND=FB OPENCV_HIGHGUI_FB_MODE=EMU ./opencv_test_highgui
sudo OPENCV_UI_BACKEND=FB OPENCV_HIGHGUI_FB_MODE=FB ./opencv_test_highgui

export DISPLAY=:99
Xvfb $DISPLAY -screen 0 1024x768x24 -fbdir /tmp/ -f /tmp/user.xvfb.auth&
sudo -u sipeed XAUTHORITY=/tmp/user.xvfb.auth x11vnc -display $DISPLAY -listen localhost&
DISPLAY=:0 gvncviewer localhost&

FRAMEBUFFER=/tmp/Xvfb_screen0 OPENCV_UI_BACKEND=FB OPENCV_HIGHGUI_FB_MODE=XVFB ./opencv_test_highgui
pull/25819/head
kozinove 5 months ago committed by GitHub
parent 76a1d26bcd
commit efa4d9176a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 11
      CMakeLists.txt
  2. 9
      cmake/checks/framebuffer.cpp
  3. 10
      modules/highgui/CMakeLists.txt
  4. 14
      modules/highgui/cmake/detect_framebuffer.cmake
  5. 2
      modules/highgui/cmake/init.cmake
  6. 4
      modules/highgui/src/backend.hpp
  7. 4
      modules/highgui/src/registry.impl.hpp
  8. 798
      modules/highgui/src/window_framebuffer.cpp
  9. 135
      modules/highgui/src/window_framebuffer.hpp
  10. 6
      modules/highgui/test/test_gui.cpp

@ -306,6 +306,10 @@ OCV_OPTION(WITH_GTK "Include GTK support" ON
OCV_OPTION(WITH_GTK_2_X "Use GTK version 2" OFF
VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID
VERIFY HAVE_GTK AND NOT HAVE_GTK3)
OCV_OPTION(WITH_FRAMEBUFFER "Include framebuffer support" OFF
VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID)
OCV_OPTION(WITH_FRAMEBUFFER_XVFB "Include virtual framebuffer support" OFF
VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID)
OCV_OPTION(WITH_WAYLAND "Include Wayland support" OFF
VISIBLE_IF UNIX AND NOT APPLE AND NOT ANDROID
VERIFY HAVE_WAYLAND)
@ -1462,6 +1466,13 @@ if(WITH_GTK OR HAVE_GTK)
endif()
endif()
if(WITH_FRAMEBUFFER OR HAVE_FRAMEBUFFER)
status(" Framebuffer UI:" HAVE_FRAMEBUFFER THEN YES ELSE NO)
if(WITH_FRAMEBUFFER_XVFB OR HAVE_FRAMEBUFFER_XVFB)
status(" Virtual framebuffer UI:" HAVE_FRAMEBUFFER_XVFB THEN YES ELSE NO)
endif()
endif()
if(WITH_OPENGL OR HAVE_OPENGL)
status(" OpenGL support:" HAVE_OPENGL THEN "YES (${OPENGL_LIBRARIES})" ELSE NO)
endif()

@ -0,0 +1,9 @@
#include <X11/XWDFile.h>
#include <X11/X.h>
int main(void)
{
XWDFileHeader *xwd_header;
XWDColor *xwd_colors;
return 0;
}

@ -49,6 +49,16 @@ list(REMOVE_ITEM highgui_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${n
set(OPENCV_HIGHGUI_BUILTIN_BACKEND "")
if(WITH_FRAMEBUFFER AND HAVE_FRAMEBUFFER)
set(OPENCV_HIGHGUI_BUILTIN_BACKEND "FB")
add_definitions(-DHAVE_FRAMEBUFFER)
list(APPEND highgui_srcs ${CMAKE_CURRENT_LIST_DIR}/src/window_framebuffer.cpp)
list(APPEND highgui_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/window_framebuffer.hpp)
if(HAVE_FRAMEBUFFER_XVFB)
add_definitions(-DHAVE_FRAMEBUFFER_XVFB)
endif()
endif()
if(WITH_WAYLAND AND HAVE_WAYLAND)
set(OPENCV_HIGHGUI_BUILTIN_BACKEND "Wayland")
add_definitions(-DHAVE_WAYLAND)

@ -0,0 +1,14 @@
# --- FB ---
set(HAVE_FRAMEBUFFER ON)
if(WITH_FRAMEBUFFER_XVFB)
try_compile(HAVE_FRAMEBUFFER_XVFB
"${CMAKE_CURRENT_BINARY_DIR}"
"${OpenCV_SOURCE_DIR}/cmake/checks/framebuffer.cpp")
if(HAVE_FRAMEBUFFER_XVFB)
message(STATUS "Check virtual framebuffer - done")
else()
message(STATUS
"Check virtual framebuffer - failed\n"
"Please install the xorg-x11-proto-devel or x11proto-dev package\n")
endif()
endif()

@ -39,6 +39,8 @@ endmacro()
add_backend("gtk" WITH_GTK)
add_backend("win32ui" WITH_WIN32UI)
add_backend("wayland" WITH_WAYLAND)
add_backend("framebuffer" WITH_FRAMEBUFFER)
# TODO cocoa
# TODO qt
# TODO opengl

@ -127,6 +127,10 @@ std::shared_ptr<UIBackend> createUIBackendGTK();
std::shared_ptr<UIBackend> createUIBackendQT();
#endif
#ifdef HAVE_FRAMEBUFFER
std::shared_ptr<UIBackend> createUIBackendFramebuffer();
#endif
#endif // BUILD_PLUGIN
} // namespace highgui_backend

@ -44,6 +44,10 @@ std::vector<BackendInfo>& getBuiltinBackendsInfo()
DECLARE_DYNAMIC_BACKEND("GTK2")
#endif
#ifdef HAVE_FRAMEBUFFER
DECLARE_STATIC_BACKEND("FB", createUIBackendFramebuffer)
#endif
#if 0 // TODO
#ifdef HAVE_QT
DECLARE_STATIC_BACKEND("QT", createUIBackendQT)

@ -0,0 +1,798 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "precomp.hpp"
#include "window_framebuffer.hpp"
#include <opencv2/core/utils/configuration.private.hpp>
#include <opencv2/core/utils/logger.defines.hpp>
#ifdef NDEBUG
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1
#else
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1
#endif
#include <opencv2/core/utils/logger.hpp>
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fb.h>
#include <linux/input.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include "opencv2/imgproc.hpp"
#ifdef HAVE_FRAMEBUFFER_XVFB
#include <X11/XWDFile.h>
#include <X11/X.h>
#define C32INT(ptr) ((((unsigned char*)ptr)[0] << 24) | (((unsigned char*)ptr)[1] << 16) | \
(((unsigned char*)ptr)[2] << 8) | (((unsigned char*)ptr)[3] << 0))
#endif
namespace cv {
namespace highgui_backend {
std::shared_ptr<UIBackend> createUIBackendFramebuffer()
{
return std::make_shared<FramebufferBackend>();
}
static std::string& getFBMode()
{
static std::string fbModeOpenCV =
cv::utils::getConfigurationParameterString("OPENCV_HIGHGUI_FB_MODE", "FB");
return fbModeOpenCV;
}
static std::string& getFBFileName()
{
static std::string fbFileNameFB =
cv::utils::getConfigurationParameterString("FRAMEBUFFER", "/dev/fb0");
static std::string fbFileNameOpenCV =
cv::utils::getConfigurationParameterString("OPENCV_HIGHGUI_FB_DEVICE", "");
if (!fbFileNameOpenCV.empty()) return fbFileNameOpenCV;
return fbFileNameFB;
}
FramebufferWindow::FramebufferWindow(FramebufferBackend &_backend, int _flags):
backend(_backend), flags(_flags)
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::FramebufferWindow()");
FB_ID = "FramebufferWindow";
windowRect = Rect(0,0, backend.getFBWidth(), backend.getFBHeight());
}
FramebufferWindow::~FramebufferWindow()
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::~FramebufferWindow()");
}
void FramebufferWindow::imshow(InputArray image)
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::imshow(InputArray image)");
currentImg = image.getMat().clone();
CV_LOG_INFO(NULL, "UI: InputArray image: "
<< cv::typeToString(image.type()) << " size " << image.size());
if (currentImg.empty())
{
CV_LOG_WARNING(NULL, "UI: image is empty");
return;
}
CV_CheckEQ(currentImg.dims, 2, "UI: dims != 2");
Mat img = image.getMat();
switch (img.channels())
{
case 1:
{
Mat tmp;
switch(img.type())
{
case CV_8U:
tmp = img;
break;
case CV_8S:
cv::convertScaleAbs(img, tmp, 1, 127);
break;
case CV_16S:
cv::convertScaleAbs(img, tmp, 1/255., 127);
break;
case CV_16U:
cv::convertScaleAbs(img, tmp, 1/255.);
break;
case CV_32F:
case CV_64F: // assuming image has values in range [0, 1)
img.convertTo(tmp, CV_8U, 255., 0.);
break;
}
Mat rgb(img.rows, img.cols, CV_8UC3);
cvtColor(tmp, rgb, COLOR_GRAY2RGB);
img = rgb;
}
break;
case 3:
case 4:
{
Mat tmp(img.rows, img.cols, CV_8UC3);
convertToShow(img, tmp, true);
img = tmp;
}
break;
default:
CV_Error(cv::Error::StsBadArg, "Bad image: wrong number of channels");
}
{
Mat bgra(img.rows, img.cols, CV_8UC4);
cvtColor(img, bgra, COLOR_RGB2BGRA, bgra.channels());
img = bgra;
}
int newWidth = windowRect.width;
int newHeight = windowRect.height;
int cntChannel = img.channels();
cv::Size imgSize = currentImg.size();
if (flags & WINDOW_AUTOSIZE)
{
windowRect.width = imgSize.width;
windowRect.height = imgSize.height;
newWidth = windowRect.width;
newHeight = windowRect.height;
}
if (flags & WINDOW_FREERATIO)
{
newWidth = windowRect.width;
newHeight = windowRect.height;
}
else //WINDOW_KEEPRATIO
{
double aspect_ratio = ((double)img.cols) / img.rows;
newWidth = windowRect.width;
newHeight = (int)(windowRect.width / aspect_ratio);
if (newHeight > windowRect.height)
{
newWidth = (int)(windowRect.height * aspect_ratio);
newHeight = windowRect.height;
}
}
if ((newWidth != img.cols) && (newHeight != img.rows))
{
Mat imResize;
cv::resize(img, imResize, cv::Size(newWidth, newHeight), INTER_LINEAR);
img = imResize;
}
CV_LOG_INFO(NULL, "UI: Formated image: "
<< cv::typeToString(img.type()) << " size " << img.size());
if (backend.getMode() == FB_MODE_EMU)
{
CV_LOG_WARNING(NULL, "UI: FramebufferWindow::imshow is used in EMU mode");
return;
}
if (backend.getFBPointer() == MAP_FAILED)
{
CV_LOG_ERROR(NULL, "UI: Framebuffer is not mapped");
return;
}
int xOffset = backend.getFBXOffset();
int yOffset = backend.getFBYOffset();
int fbHeight = backend.getFBHeight();
int fbWidth = backend.getFBWidth();
int lineLength = backend.getFBLineLength();
int img_start_x;
int img_start_y;
int img_end_x;
int img_end_y;
int fb_start_x;
int fb_start_y;
if (windowRect.y - yOffset < 0)
{
img_start_y = - (windowRect.y - yOffset);
}
else
{
img_start_y = 0;
}
if (windowRect.x - xOffset < 0)
{
img_start_x = - (windowRect.x - xOffset);
}
else
{
img_start_x = 0;
}
if (windowRect.y + yOffset + img.rows > fbHeight)
{
img_end_y = fbHeight - windowRect.y - yOffset;
}
else
{
img_end_y = img.rows;
}
if (windowRect.x + xOffset + img.cols > fbWidth)
{
img_end_x = fbWidth - windowRect.x - xOffset;
}
else
{
img_end_x = img.cols;
}
if (windowRect.y + yOffset >= 0)
{
fb_start_y = windowRect.y + yOffset;
}
else
{
fb_start_y = 0;
}
if (windowRect.x + xOffset >= 0)
{
fb_start_x = windowRect.x + xOffset;
}
else
{
fb_start_x = 0;
}
for (int y = img_start_y; y < img_end_y; y++)
{
std::memcpy(backend.getFBPointer() +
(fb_start_y + y - img_start_y) * lineLength + fb_start_x * cntChannel,
img.ptr<unsigned char>(y) + img_start_x * cntChannel,
(img_end_x - img_start_x) * cntChannel);
}
}
double FramebufferWindow::getProperty(int /*prop*/) const
{
CV_LOG_WARNING(NULL, "UI: getProperty (not supported)");
return 0.0;
}
bool FramebufferWindow::setProperty(int /*prop*/, double /*value*/)
{
CV_LOG_WARNING(NULL, "UI: setProperty (not supported)");
return false;
}
void FramebufferWindow::resize(int width, int height)
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::resize(int width "
<< width <<", height " << height << ")");
CV_Assert(width > 0);
CV_Assert(height > 0);
if (!(flags & WINDOW_AUTOSIZE))
{
windowRect.width = width;
windowRect.height = height;
if (!currentImg.empty())
{
imshow(currentImg);
}
}
}
void FramebufferWindow::move(int x, int y)
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::move(int x " << x << ", y " << y <<")");
windowRect.x = x;
windowRect.y = y;
if (!currentImg.empty())
{
imshow(currentImg);
}
}
Rect FramebufferWindow::getImageRect() const
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::getImageRect()");
return windowRect;
}
void FramebufferWindow::setTitle(const std::string& /*title*/)
{
CV_LOG_WARNING(NULL, "UI: setTitle (not supported)");
}
void FramebufferWindow::setMouseCallback(MouseCallback /*onMouse*/, void* /*userdata*/)
{
CV_LOG_WARNING(NULL, "UI: setMouseCallback (not supported)");
}
std::shared_ptr<UITrackbar> FramebufferWindow::createTrackbar(
const std::string& /*name*/,
int /*count*/,
TrackbarCallback /*onChange*/,
void* /*userdata*/)
{
CV_LOG_WARNING(NULL, "UI: createTrackbar (not supported)");
return nullptr;
}
std::shared_ptr<UITrackbar> FramebufferWindow::findTrackbar(const std::string& /*name*/)
{
CV_LOG_WARNING(NULL, "UI: findTrackbar (not supported)");
return nullptr;
}
const std::string& FramebufferWindow::getID() const
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::getID()");
return FB_ID;
}
bool FramebufferWindow::isActive() const
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::isActive()");
return true;
}
void FramebufferWindow::destroy()
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::destroy()");
}
int FramebufferBackend::fbOpenAndGetInfo()
{
std::string fbFileName = getFBFileName();
CV_LOG_INFO(NULL, "UI: FramebufferWindow::The following is used as a framebuffer file: \n"
<< fbFileName);
int fb_fd = open(fbFileName.c_str(), O_RDWR);
if (fb_fd == -1)
{
CV_LOG_ERROR(NULL, "UI: can't open framebuffer");
return -1;
}
if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &fixInfo))
{
CV_LOG_ERROR(NULL, "UI: can't read fix info for framebuffer");
return -1;
}
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &varInfo))
{
CV_LOG_ERROR(NULL, "UI: can't read var info for framebuffer");
return -1;
}
CV_LOG_INFO(NULL, "UI: framebuffer info: \n"
<< " red offset " << varInfo.red.offset << " length " << varInfo.red.length << "\n"
<< " green offset " << varInfo.green.offset << " length " << varInfo.green.length << "\n"
<< " blue offset " << varInfo.blue.offset << " length " << varInfo.blue.length << "\n"
<< "transp offset " << varInfo.transp.offset << " length " <<varInfo.transp.length << "\n"
<< "bits_per_pixel " << varInfo.bits_per_pixel);
if ((varInfo.red.offset != 16) && (varInfo.red.length != 8) &&
(varInfo.green.offset != 8) && (varInfo.green.length != 8) &&
(varInfo.blue.offset != 0) && (varInfo.blue.length != 8) &&
(varInfo.bits_per_pixel != 32) )
{
close(fb_fd);
CV_LOG_ERROR(NULL, "UI: Framebuffer format is not supported "
<< "(use BGRA format with bits_per_pixel = 32)");
return -1;
}
fbWidth = varInfo.xres;
fbHeight = varInfo.yres;
fbXOffset = varInfo.xoffset;
fbYOffset = varInfo.yoffset;
fbBitsPerPixel = varInfo.bits_per_pixel;
fbLineLength = fixInfo.line_length;
fbScreenSize = max(varInfo.xres, varInfo.xres_virtual) *
max(varInfo.yres, varInfo.yres_virtual) *
fbBitsPerPixel / 8;
fbPointer = (unsigned char*)
mmap(0, fbScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
if (fbPointer == MAP_FAILED)
{
CV_LOG_ERROR(NULL, "UI: can't mmap framebuffer");
return -1;
}
return fb_fd;
}
int FramebufferBackend::XvfbOpenAndGetInfo()
{
int fb_fd = -1;
#ifdef HAVE_FRAMEBUFFER_XVFB
std::string fbFileName = getFBFileName();
CV_LOG_INFO(NULL, "UI: FramebufferWindow::The following is used as a framebuffer file: \n"
<< fbFileName);
fb_fd = open(fbFileName.c_str(), O_RDWR);
if (fb_fd == -1)
{
CV_LOG_ERROR(NULL, "UI: can't open framebuffer");
return -1;
}
XWDFileHeader *xwd_header;
xwd_header = (XWDFileHeader*)
mmap(NULL, sizeof(XWDFileHeader), PROT_READ, MAP_SHARED, fb_fd, 0);
if (xwd_header == MAP_FAILED)
{
CV_LOG_ERROR(NULL, "UI: can't mmap xwd header");
return -1;
}
if (C32INT(&(xwd_header->pixmap_format)) != ZPixmap)
{
CV_LOG_ERROR(NULL, "Unsupported pixmap format: " << xwd_header->pixmap_format);
return -1;
}
if (xwd_header->xoffset != 0)
{
CV_LOG_ERROR(NULL, "UI: Unsupported xoffset value: " << xwd_header->xoffset );
return -1;
}
unsigned int r = C32INT(&(xwd_header->red_mask));
unsigned int g = C32INT(&(xwd_header->green_mask));
unsigned int b = C32INT(&(xwd_header->blue_mask));
fbWidth = C32INT(&(xwd_header->pixmap_width));
fbHeight = C32INT(&(xwd_header->pixmap_height));
fbXOffset = 0;
fbYOffset = 0;
fbLineLength = C32INT(&(xwd_header->bytes_per_line));
fbBitsPerPixel = C32INT(&(xwd_header->bits_per_pixel));
CV_LOG_INFO(NULL, "UI: XVFB info: \n"
<< " red_mask " << r << "\n"
<< " green_mask " << g << "\n"
<< " blue_mask " << b << "\n"
<< "bits_per_pixel " << fbBitsPerPixel);
if ((r != 16711680 ) && (g != 65280 ) && (b != 255 ) &&
(fbBitsPerPixel != 32))
{
CV_LOG_ERROR(NULL, "UI: Framebuffer format is not supported "
<< "(use BGRA format with bits_per_pixel = 32)");
return -1;
}
xvfb_len_header = C32INT(&(xwd_header->header_size));
xvfb_len_colors = sizeof(XWDColor) * C32INT(&(xwd_header->ncolors));
xvfb_len_pixmap = C32INT(&(xwd_header->bytes_per_line)) *
C32INT(&(xwd_header->pixmap_height));
munmap(xwd_header, sizeof(XWDFileHeader));
fbScreenSize = xvfb_len_header + xvfb_len_colors + xvfb_len_pixmap;
xwd_header = (XWDFileHeader*)
mmap(NULL, fbScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
fbPointer = (unsigned char*)xwd_header;
fbPointer_dist = xvfb_len_header + xvfb_len_colors;
#else
CV_LOG_WARNING(NULL, "UI: To use virtual framebuffer, "
<< "compile OpenCV with the WITH_FRAMEBUFFER_XVFB=ON");
#endif
return fb_fd;
}
fb_var_screeninfo &FramebufferBackend::getVarInfo()
{
return varInfo;
}
fb_fix_screeninfo &FramebufferBackend::getFixInfo()
{
return fixInfo;
}
int FramebufferBackend::getFramebuffrerID()
{
return fbID;
}
int FramebufferBackend::getFBWidth()
{
return fbWidth;
}
int FramebufferBackend::getFBHeight()
{
return fbHeight;
}
int FramebufferBackend::getFBXOffset()
{
return fbXOffset;
}
int FramebufferBackend::getFBYOffset()
{
return fbYOffset;
}
int FramebufferBackend::getFBBitsPerPixel()
{
return fbBitsPerPixel;
}
int FramebufferBackend::getFBLineLength()
{
return fbLineLength;
}
unsigned char* FramebufferBackend::getFBPointer()
{
return fbPointer + fbPointer_dist;
}
Mat& FramebufferBackend::getBackgroundBuff()
{
return backgroundBuff;
}
OpenCVFBMode FramebufferBackend::getMode()
{
return mode;
}
FramebufferBackend::FramebufferBackend():mode(FB_MODE_FB), fbPointer_dist(0)
{
CV_LOG_DEBUG(NULL, "UI: FramebufferWindow::FramebufferBackend()");
std::string fbModeStr = getFBMode();
if (fbModeStr == "EMU")
{
mode = FB_MODE_EMU;
CV_LOG_WARNING(NULL, "UI: FramebufferWindow is trying to use EMU mode");
}
if (fbModeStr == "FB")
{
mode = FB_MODE_FB;
CV_LOG_WARNING(NULL, "UI: FramebufferWindow is trying to use FB mode");
}
if (fbModeStr == "XVFB")
{
mode = FB_MODE_XVFB;
CV_LOG_WARNING(NULL, "UI: FramebufferWindow is trying to use XVFB mode");
}
fbID = -1;
if (mode == FB_MODE_FB)
{
fbID = fbOpenAndGetInfo();
}
if (mode == FB_MODE_XVFB)
{
fbID = XvfbOpenAndGetInfo();
}
CV_LOG_INFO(NULL, "UI: FramebufferWindow::fbID " << fbID);
if (fbID == -1)
{
mode = FB_MODE_EMU;
fbWidth = 640;
fbHeight = 480;
fbXOffset = 0;
fbYOffset = 0;
fbBitsPerPixel = 0;
fbLineLength = 0;
CV_LOG_WARNING(NULL, "UI: FramebufferWindow is used in EMU mode");
return;
}
CV_LOG_INFO(NULL, "UI: Framebuffer's width, height, bits per pix: "
<< fbWidth << " " << fbHeight << " " << fbBitsPerPixel);
CV_LOG_INFO(NULL, "UI: Framebuffer's offsets (x, y), line length: "
<< fbXOffset << " " << fbYOffset << " " << fbLineLength);
backgroundBuff = Mat(fbHeight, fbWidth, CV_8UC4);
int cntChannel = 4;
for (int y = fbYOffset; y < backgroundBuff.rows + fbYOffset; y++)
{
std::memcpy(backgroundBuff.ptr<unsigned char>(y - fbYOffset),
getFBPointer() + y * fbLineLength + fbXOffset * cntChannel,
backgroundBuff.cols * cntChannel);
}
}
FramebufferBackend::~FramebufferBackend()
{
CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::~FramebufferBackend()");
if(fbID == -1) return;
if (fbPointer != MAP_FAILED)
{
int cntChannel = 4;
for (int y = fbYOffset; y < backgroundBuff.rows + fbYOffset; y++)
{
std::memcpy(getFBPointer() + y * fbLineLength + fbXOffset * cntChannel,
backgroundBuff.ptr<cv::Vec4b>(y - fbYOffset),
backgroundBuff.cols * cntChannel);
}
munmap(fbPointer, fbScreenSize);
}
close(fbID);
}
void FramebufferBackend::destroyAllWindows() {
CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::destroyAllWindows()");
}
// namedWindow
std::shared_ptr<UIWindow> FramebufferBackend::createWindow(
const std::string& winname,
int flags)
{
CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::createWindow("
<< winname << ", " << flags << ")");
return std::make_shared<FramebufferWindow>(*this, flags);
}
void FramebufferBackend::initTermios(int echo, int wait)
{
tcgetattr(0, &old);
current = old;
current.c_lflag &= ~ICANON;
current.c_lflag &= ~ISIG;
current.c_cc[VMIN] = wait;
if (echo)
{
current.c_lflag |= ECHO;
}
else
{
current.c_lflag &= ~ECHO;
}
tcsetattr(0, TCSANOW, &current);
}
void FramebufferBackend::resetTermios(void)
{
tcsetattr(0, TCSANOW, &old);
}
int FramebufferBackend::getch_(int echo, int wait)
{
int ch;
initTermios(echo, wait);
ch = getchar();
if (ch < 0)
{
rewind(stdin);
}
resetTermios();
return ch;
}
bool FramebufferBackend::kbhit()
{
int byteswaiting = 0;
initTermios(0, 1);
if (ioctl(0, FIONREAD, &byteswaiting) < 0)
{
CV_LOG_ERROR(NULL, "UI: Framebuffer ERR byteswaiting" );
}
resetTermios();
return byteswaiting > 0;
}
int FramebufferBackend::waitKeyEx(int delay)
{
CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::waitKeyEx(int delay = " << delay << ")");
int code = -1;
if (delay <= 0)
{
int ch = getch_(0, 1);
CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " << (int)ch);
code = ch;
while ((ch = getch_(0, 0)) >= 0)
{
CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = "
<< (int)ch << " (additional code on <stdin>)");
code = ch;
}
}
else
{
bool f_kbhit = false;
while (!(f_kbhit = kbhit()) && (delay > 0))
{
delay -= 1;
usleep(1000);
}
if (f_kbhit)
{
CV_LOG_INFO(NULL, "UI: FramebufferBackend kbhit is True ");
int ch = getch_(0, 1);
CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " << (int)ch);
code = ch;
while ((ch = getch_(0, 0)) >= 0)
{
CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = "
<< (int)ch << " (additional code on <stdin>)");
code = ch;
}
}
}
CV_LOG_INFO(NULL, "UI: FramebufferBackend::waitKeyEx() result code = " << code);
return code;
}
int FramebufferBackend::pollKey()
{
CV_LOG_DEBUG(NULL, "UI: FramebufferBackend::pollKey()");
int code = -1;
bool f_kbhit = false;
f_kbhit = kbhit();
if (f_kbhit)
{
CV_LOG_INFO(NULL, "UI: FramebufferBackend kbhit is True ");
int ch = getch_(0, 1);
CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = " << (int)ch);
code = ch;
while ((ch = getch_(0, 0)) >= 0)
{
CV_LOG_INFO(NULL, "UI: FramebufferBackend::getch_() take value = "
<< (int)ch << " (additional code on <stdin>)");
code = ch;
}
}
return code;
}
const std::string FramebufferBackend::getName() const
{
return "FB";
}
}} // cv::highgui_backend::

@ -0,0 +1,135 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_HIGHGUI_WINDOWS_FRAMEBUFFER_HPP
#define OPENCV_HIGHGUI_WINDOWS_FRAMEBUFFER_HPP
#include "backend.hpp"
#include <linux/fb.h>
#include <linux/input.h>
#include <termios.h>
namespace cv {
namespace highgui_backend {
enum OpenCVFBMode{
FB_MODE_EMU,
FB_MODE_FB,
FB_MODE_XVFB
};
class FramebufferBackend;
class FramebufferWindow : public UIWindow
{
FramebufferBackend &backend;
std::string FB_ID;
Rect windowRect;
int flags;
Mat currentImg;
public:
FramebufferWindow(FramebufferBackend &backend, int flags);
virtual ~FramebufferWindow();
virtual void imshow(InputArray image) override;
virtual double getProperty(int prop) const override;
virtual bool setProperty(int prop, double value) override;
virtual void resize(int width, int height) override;
virtual void move(int x, int y) override;
virtual Rect getImageRect() const override;
virtual void setTitle(const std::string& title) override;
virtual void setMouseCallback(MouseCallback onMouse, void* userdata /*= 0*/) override;
virtual std::shared_ptr<UITrackbar> createTrackbar(
const std::string& name,
int count,
TrackbarCallback onChange /*= 0*/,
void* userdata /*= 0*/
) override;
virtual std::shared_ptr<UITrackbar> findTrackbar(const std::string& name) override;
virtual const std::string& getID() const override;
virtual bool isActive() const override;
virtual void destroy() override;
}; // FramebufferWindow
class FramebufferBackend: public UIBackend
{
OpenCVFBMode mode;
struct termios old, current;
void initTermios(int echo, int wait);
void resetTermios(void);
int getch_(int echo, int wait);
bool kbhit();
fb_var_screeninfo varInfo;
fb_fix_screeninfo fixInfo;
int fbWidth;
int fbHeight;
int fbXOffset;
int fbYOffset;
int fbBitsPerPixel;
int fbLineLength;
long int fbScreenSize;
unsigned char* fbPointer;
unsigned int fbPointer_dist;
Mat backgroundBuff;
int fbOpenAndGetInfo();
int fbID;
unsigned int xvfb_len_header;
unsigned int xvfb_len_colors;
unsigned int xvfb_len_pixmap;
int XvfbOpenAndGetInfo();
public:
fb_var_screeninfo &getVarInfo();
fb_fix_screeninfo &getFixInfo();
int getFramebuffrerID();
int getFBWidth();
int getFBHeight();
int getFBXOffset();
int getFBYOffset();
int getFBBitsPerPixel();
int getFBLineLength();
unsigned char* getFBPointer();
Mat& getBackgroundBuff();
OpenCVFBMode getMode();
FramebufferBackend();
virtual ~FramebufferBackend();
virtual void destroyAllWindows()override;
// namedWindow
virtual std::shared_ptr<UIWindow> createWindow(
const std::string& winname,
int flags
)override;
virtual int waitKeyEx(int delay /*= 0*/)override;
virtual int pollKey() override;
virtual const std::string getName() const override;
};
}} // cv::highgui_backend::
#endif

@ -148,7 +148,8 @@ static void Foo(int, void* counter)
&& !defined HAVE_WIN32UI \
&& !defined HAVE_WAYLAND \
) \
|| defined(__APPLE__) // test fails on Mac (cocoa)
|| defined(__APPLE__) /* test fails on Mac (cocoa) */ \
|| defined HAVE_FRAMEBUFFER /* trackbar is not supported */
TEST(Highgui_GUI, DISABLED_trackbar_unsafe)
#else
TEST(Highgui_GUI, trackbar_unsafe)
@ -188,7 +189,8 @@ void testTrackbarCallback(int pos, void* param)
&& !defined HAVE_WIN32UI \
&& !defined HAVE_WAYLAND \
) \
|| defined(__APPLE__) // test fails on Mac (cocoa)
|| defined(__APPLE__) /* test fails on Mac (cocoa) */ \
|| defined HAVE_FRAMEBUFFER /* trackbar is not supported */
TEST(Highgui_GUI, DISABLED_trackbar)
#else
TEST(Highgui_GUI, trackbar)

Loading…
Cancel
Save