mirror of
https://github.com/opencv/opencv.git
synced 2025-07-20 19:17:36 +08:00
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
This commit is contained in:
parent
76a1d26bcd
commit
efa4d9176a
@ -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()
|
||||
|
9
cmake/checks/framebuffer.cpp
Normal file
9
cmake/checks/framebuffer.cpp
Normal file
@ -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)
|
||||
|
14
modules/highgui/cmake/detect_framebuffer.cmake
Normal file
14
modules/highgui/cmake/detect_framebuffer.cmake
Normal file
@ -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)
|
||||
|
798
modules/highgui/src/window_framebuffer.cpp
Normal file
798
modules/highgui/src/window_framebuffer.cpp
Normal file
@ -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, ¤t);
|
||||
}
|
||||
|
||||
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::
|
135
modules/highgui/src/window_framebuffer.hpp
Normal file
135
modules/highgui/src/window_framebuffer.hpp
Normal file
@ -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…
Reference in New Issue
Block a user