mirror of https://github.com/opencv/opencv.git
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_highguipull/25819/head
parent
76a1d26bcd
commit
efa4d9176a
10 changed files with 991 additions and 2 deletions
@ -0,0 +1,9 @@ |
||||
#include <X11/XWDFile.h> |
||||
#include <X11/X.h> |
||||
|
||||
int main(void) |
||||
{ |
||||
XWDFileHeader *xwd_header; |
||||
XWDColor *xwd_colors; |
||||
return 0; |
||||
} |
@ -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() |
@ -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::
|
@ -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 |
Loading…
Reference in new issue