Merge pull request #25551 from Kumataro:fix25550

highgui: wayland: fix to pass highgui test #25551

Close #25550

- optimize Mat to XRGB8888 conversion with OpenCV functions
  - extend to support CV_8S/16U/16S/32F/64F
  - extend to support 1/4 channels
- fix to update value timing
 - initilize slider_ value if value is not nullptr.
 - Update user-ptr value and call on_change() function if cv_wl_trackbar::draw() is not called.
- Update usage of WAYLAND/XDG macro to avoid reference undefined macro.
- Update documents

### 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
- [x] 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
This commit is contained in:
Kumataro 2024-05-13 20:26:24 +09:00 committed by GitHub
parent 1d9a4120fa
commit 8dc9ff5624
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 132 additions and 30 deletions

View File

@ -366,6 +366,11 @@ for image display). **waitKey(25)** will display a frame and wait approximately
press (suitable for displaying a video frame-by-frame). To remove the window, use cv::destroyWindow.
@note [__Windows Backend Only__] Pressing Ctrl+C will copy the image to the clipboard. Pressing Ctrl+S will show a dialog to save the image.
@note [__Wayland Backend Only__] Supoorting format is extended.
- If the image is 8-bit signed, the pixels are biased by 128. That is, the
value range [-128,127] is mapped to [0,255].
- If the image is 16-bit signed, the pixels are divided by 256 and biased by 128. That is, the
value range [-32768,32767] is mapped to [0,255].
@param winname Name of the window.
@param mat Image to be shown.
@ -394,6 +399,8 @@ CV_EXPORTS_W void resizeWindow(const String& winname, const cv::Size& size);
@param winname Name of the window.
@param x The new x-coordinate of the window.
@param y The new y-coordinate of the window.
@note [__Wayland Backend Only__] This function is not supported by the Wayland protocol limitation.
*/
CV_EXPORTS_W void moveWindow(const String& winname, int x, int y);
@ -404,6 +411,8 @@ The function setWindowProperty enables changing properties of a window.
@param winname Name of the window.
@param prop_id Window property to edit. The supported operation flags are: (cv::WindowPropertyFlags)
@param prop_value New value of the window property. The supported flags are: (cv::WindowFlags)
@note [__Wayland Backend Only__] This function is not supported.
*/
CV_EXPORTS_W void setWindowProperty(const String& winname, int prop_id, double prop_value);
@ -421,6 +430,8 @@ The function getWindowProperty returns properties of a window.
@param prop_id Window property to retrieve. The following operation flags are available: (cv::WindowPropertyFlags)
@sa setWindowProperty
@note [__Wayland Backend Only__] This function is not supported.
*/
CV_EXPORTS_W double getWindowProperty(const String& winname, int prop_id);
@ -431,6 +442,8 @@ The function getWindowImageRect returns the client screen coordinates, width and
@param winname Name of the window.
@sa resizeWindow moveWindow
@note [__Wayland Backend Only__] This function is not supported by the Wayland protocol limitation.
*/
CV_EXPORTS_W Rect getWindowImageRect(const String& winname);

View File

@ -98,6 +98,7 @@ void cvSetModeWindow_WinRT(const char* name, double prop_value);
CvRect cvGetWindowRect_W32(const char* name);
CvRect cvGetWindowRect_GTK(const char* name);
CvRect cvGetWindowRect_COCOA(const char* name);
CvRect cvGetWindowRect_WAYLAND(const char* name);
double cvGetModeWindow_W32(const char* name);
double cvGetModeWindow_GTK(const char* name);

View File

@ -436,6 +436,8 @@ cv::Rect cv::getWindowImageRect(const String& winname)
return cvGetWindowRect_GTK(winname.c_str());
#elif defined (HAVE_COCOA)
return cvGetWindowRect_COCOA(winname.c_str());
#elif defined (HAVE_WAYLAND)
return cvGetWindowRect_WAYLAND(winname.c_str());
#else
return Rect(-1, -1, -1, -1);
#endif

View File

@ -84,20 +84,73 @@ static int xkb_keysym_to_ascii(xkb_keysym_t keysym) {
return static_cast<int>(keysym & 0xff);
}
static void write_mat_to_xrgb8888(cv::Mat const &img_, void *data) {
// Validate destination data.
CV_CheckFalse((data == nullptr), "Destination Address must not be nullptr.");
static void draw_xrgb8888(void *d, uint8_t a, uint8_t r, uint8_t g, uint8_t b) {
*((uint32_t *) d) = ((a << 24) | (r << 16) | (g << 8) | b);
}
// Validate source img parameters.
CV_CheckFalse(img_.empty(), "Source Mat must not be empty.");
const int ncn = img_.channels();
CV_CheckType(img_.type(),
( (ncn == 1) || (ncn == 3) || (ncn == 4)),
"Unsupported channels, please convert to 1, 3 or 4 channels"
);
static void write_mat_to_xrgb8888(cv::Mat const &img, void *data) {
CV_Assert(data != nullptr);
CV_Assert(img.isContinuous());
// The supported Mat depth is according to imshow() specification.
const int depth = CV_MAT_DEPTH(img_.type());
CV_CheckDepth(img_.type(),
( (depth == CV_8U) || (depth == CV_8S) ||
(depth == CV_16U) || (depth == CV_16S) ||
(depth == CV_32F) || (depth == CV_64F) ),
"Unsupported depth, please convert to CV_8U"
);
for (int y = 0; y < img.rows; y++) {
for (int x = 0; x < img.cols; x++) {
auto p = img.at<cv::Vec3b>(y, x);
draw_xrgb8888((char *) data + (y * img.cols + x) * 4, 0x00, p[2], p[1], p[0]);
}
// Convert to CV_8U
cv::Mat img;
const int mtype = CV_MAKE_TYPE(CV_8U, ncn);
switch(CV_MAT_DEPTH(depth))
{
case CV_8U:
img = img_; // do nothing.
break;
case CV_8S:
// [-128,127] -> [0,255]
img_.convertTo(img, mtype, 1.0, 128);
break;
case CV_16U:
// [0,65535] -> [0,255]
img_.convertTo(img, mtype, 1.0/255. );
break;
case CV_16S:
// [-32768,32767] -> [0,255]
img_.convertTo(img, mtype, 1.0/255. , 128);
break;
case CV_32F:
case CV_64F:
// [0, 1] -> [0,255]
img_.convertTo(img, mtype, 255.);
break;
default:
// it cannot be reachable.
break;
}
CV_CheckDepthEQ(CV_MAT_DEPTH(img.type()), CV_8U, "img should be CV_8U");
// XRGB8888 in Little Endian(Wayland Request) = [B8:G8:R8:X8] in data array.
// X is not used to show. So we can use cvtColor() with GRAY2BGRA or BGR2BGRA or copyTo().
cv::Mat dst(img.size(), CV_MAKE_TYPE(CV_8U, 4), (uint8_t*)data);
if(ncn == 1)
{
cvtColor(img, dst, cv::COLOR_GRAY2BGRA);
}
else if(ncn == 3)
{
cvtColor(img, dst, cv::COLOR_BGR2BGRA);
}
else
{
CV_CheckTrue(ncn==4, "Unexpected channels");
img.copyTo(dst);
}
}
@ -232,10 +285,10 @@ private:
&handle_pointer_axis, &handle_pointer_frame,
&handle_pointer_axis_source, &handle_pointer_axis_stop,
&handle_pointer_axis_discrete,
#if WL_POINTER_AXIS_VALUE120_SINCE_VERSION >= 8
#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION
&handle_axis_value120,
#endif
#if WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION >= 9
#ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION
&handle_axis_relative_direction,
#endif
};
@ -284,7 +337,7 @@ private:
CV_UNUSED(discrete);
}
#if WL_POINTER_AXIS_VALUE120_SINCE_VERSION >= 8
#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION
static void
handle_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) {
CV_UNUSED(data);
@ -294,7 +347,7 @@ private:
}
#endif
#if WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION >= 9
#ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION
static void
handle_axis_relative_direction(void *data, struct wl_pointer *wl_pointer, uint32_t axis, uint32_t direction) {
CV_UNUSED(data);
@ -686,7 +739,7 @@ public:
void show_image(cv::Mat const &image);
void create_trackbar(std::string const &name, int *value, int count, CvTrackbarCallback2 on_change, void *userdata);
int create_trackbar(std::string const &name, int *value, int count, CvTrackbarCallback2 on_change, void *userdata);
weak_ptr<cv_wl_trackbar> get_trackbar(std::string const &) const;
@ -723,10 +776,10 @@ private:
struct xdg_toplevel *xdg_toplevel_;
struct xdg_toplevel_listener xdgtop_listener_{
&handle_toplevel_configure, &handle_toplevel_close,
#if XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION >= 4
#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
&handle_toplevel_configure_bounds,
#endif
#if XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION >= 5
#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION
&handle_toplevel_wm_capabilities,
#endif
};
@ -775,7 +828,7 @@ private:
static void handle_toplevel_close(void *data, struct xdg_toplevel *surface);
#if XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION >= 4
#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
static void
handle_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height)
{
@ -786,7 +839,7 @@ private:
}
#endif
#if XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION >= 5
#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION
static void
handle_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities)
{
@ -1527,13 +1580,7 @@ cv_wl_viewer::cv_wl_viewer(cv_wl_window *window, int flags)
}
void cv_wl_viewer::set_image(cv::Mat const &image) {
if (image.type() == CV_8UC1) {
cv::Mat bgr;
cv::cvtColor(image, bgr, CV_GRAY2BGR);
image_ = bgr.clone();
} else {
image_ = image.clone();
}
image_ = image.clone();
image_changed_ = true;
}
@ -1642,6 +1689,11 @@ cv_wl_trackbar::cv_wl_trackbar(cv_wl_window *window, std::string name,
on_change_.value = value;
on_change_.data = data;
on_change_.callback = on_change;
// initilize slider_.value if value is not nullptr.
if (value != nullptr){
set_pos(*value);
}
}
std::string const &cv_wl_trackbar::name() const {
@ -1657,6 +1709,12 @@ void cv_wl_trackbar::set_pos(int value) {
slider_.value = value;
slider_moved_ = true;
window_->show();
// Update user-ptr value and call on_change() function if cv_wl_trackbar::draw() is not called.
if(slider_moved_) {
on_change_.update(slider_.value);
on_change_.call(slider_.value);
}
}
}
@ -1666,6 +1724,12 @@ void cv_wl_trackbar::set_max(int maxval) {
slider_.value = maxval;
slider_moved_ = true;
window_->show();
// Update user-ptr and call on_change() function if cv_wl_trackbar::draw() is not called.
if(slider_moved_) {
on_change_.update(slider_.value);
on_change_.call(slider_.value);
}
}
}
@ -1836,8 +1900,9 @@ void cv_wl_window::show_image(cv::Mat const &image) {
this->show();
}
void cv_wl_window::create_trackbar(std::string const &name, int *value, int count, CvTrackbarCallback2 on_change,
int cv_wl_window::create_trackbar(std::string const &name, int *value, int count, CvTrackbarCallback2 on_change,
void *userdata) {
int ret = 0;
auto exists = this->get_trackbar(name).lock();
if (!exists) {
auto trackbar =
@ -1846,7 +1911,9 @@ void cv_wl_window::create_trackbar(std::string const &name, int *value, int coun
);
widgets_.emplace_back(trackbar);
widget_geometries_.emplace_back(0, 0, 0, 0);
ret = 1;
}
return ret;
}
weak_ptr<cv_wl_trackbar> cv_wl_window::get_trackbar(std::string const &trackbar_name) const {
@ -2324,6 +2391,7 @@ std::string const &cv_wl_core::get_window_name(void *handle) {
}
bool cv_wl_core::create_window(std::string const &name, int flags) {
CV_CheckTrue(display_ != nullptr, "Display is not connected.");
auto window = std::make_shared<cv_wl_window>(display_, name, flags);
auto result = windows_.insert(std::make_pair(name, window));
handles_[window.get()] = window->get_title();
@ -2402,6 +2470,13 @@ CV_IMPL void cvResizeWindow(const char *name, int width, int height) {
throw_system_error("Could not get window name", errno)
}
CvRect cvGetWindowRect_WAYLAND(const char* name)
{
CV_UNUSED(name);
CV_LOG_ONCE_WARNING(nullptr, "Function not implemented: User cannot get window rect in Wayland");
return cvRect(-1, -1, -1, -1);
}
CV_IMPL int cvCreateTrackbar(const char *name_bar, const char *window_name, int *value, int count,
CvTrackbarCallback on_change) {
CV_UNUSED(name_bar);
@ -2416,10 +2491,11 @@ CV_IMPL int cvCreateTrackbar(const char *name_bar, const char *window_name, int
CV_IMPL int cvCreateTrackbar2(const char *trackbar_name, const char *window_name, int *val, int count,
CvTrackbarCallback2 on_notify, void *userdata) {
int ret = 0;
if (auto window = CvWlCore::getInstance().get_window(window_name))
window->create_trackbar(trackbar_name, val, count, on_notify, userdata);
ret = window->create_trackbar(trackbar_name, val, count, on_notify, userdata);
return 0;
return ret;
}
CV_IMPL int cvGetTrackbarPos(const char *trackbar_name, const char *window_name) {

View File

@ -48,6 +48,16 @@ inline void verify_size(const std::string &nm, const cv::Mat &img)
{
EXPECT_NO_THROW(imshow(nm, img));
EXPECT_EQ(-1, waitKey(200));
// see https://github.com/opencv/opencv/issues/25550
// Wayland backend is not supported getWindowImageRect().
string framework;
EXPECT_NO_THROW(framework = currentUIFramework());
if(framework == "WAYLAND")
{
return;
}
Rect rc;
EXPECT_NO_THROW(rc = getWindowImageRect(nm));
EXPECT_EQ(rc.size(), img.size());