【问题标题】:SDL2 how to scale mouse coordinates after window resizing?SDL2如何在窗口调整大小后缩放鼠标坐标?
【发布时间】:2019-03-11 19:29:57
【问题描述】:

我正在为 C++ 应用程序使用 OpenCV 和 SDL2 库。

Opencv 用于从网络摄像头捕获帧,然后将帧转换为在 SDL 窗口上显示。 SDL 用于捕获来自鼠标的输入。

在 SDL 窗口中每次单击时,都会在网络摄像头框架上单击的位置绘制一个圆圈。

问题如下:当应用程序运行时手动调整窗口大小时(从操作系统而不是代码),已经绘制的点将被正确绘制,但如果我左键单击以绘制另一个点调整大小的窗口,点将完全超出窗口范围。

这是主要功能:

#ifndef __OPENCV__
#define __OPENCV__
#include "opencv2/opencv.hpp"
#endif
#include <iostream>
#include <unistd.h>
#include <vector>
#include <SDL.h>
#include <SDL_events.h>

using namespace cv;
using namespace std;


int mouseCallback(SDL_MouseButtonEvent ev, Mat frame);


int main(int argc, char* argv[])
{
    /* Initialise SDL */
    if( SDL_Init( SDL_INIT_VIDEO ) < 0)
    {
        fprintf( stderr, "Could not initialise SDL: %s\n", SDL_GetError() );
        exit( -1 );
    }
    const String sourceReference = argv[1];
    int camNum;
    string sensorName;
    try
    {
        camNum = stoi(sourceReference); // throws std::length_error
    }
    catch (const std::exception& e)// reference to the base of a polymorphic object
    {
        std::cout<<"Exception: " << e.what()<<endl; // information from length_error printed
        return -1;
    }
    cout<<"camera initializing\n";
    VideoSettings cam(camNum + CAP_V4L);
    cout<<"camera initialized\n";
    /* or
    VideoCapture captUndTst;
    captUndTst.open(sourceCompareWith);*/
    cout<<"Ch3ck c4m3ra is 0p3n3d\n";
    if ( !cam.isOpened())
    {
        cout << "Could not open reference " << sourceReference << endl;
        return -1;
    }
    Mat frame;
    cam>>frame;
    SDL_Window* win = SDL_CreateWindow("Camera", 100, 100, frame.cols, frame.rows,
        SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

    SDL_SetWindowTitle(win, "Camera");
    SDL_Renderer * renderer = SDL_CreateRenderer(win, -1, 0);
    SDL_Event genericEvent;

    SDL_Surface* frameSurface;
    SDL_Texture* frameTexture;
    while(cam.isOpened())
    {
        while( SDL_PollEvent(&genericEvent) )
        {
            switch( genericEvent.type )
            {
                case SDL_MOUSEBUTTONDOWN:
                    mouseCallback(genericEvent.button, frame);
                    break;
                /* SDL_QUIT event (window close) */
                case SDL_QUIT:
                    return 0;
                    break;
                default:
                    break;
            }
        }
        cam>>frame;
        draw(frame_out);

        //Convert to SDL_Surface
        frameSurface = SDL_CreateRGBSurfaceFrom((void*)frame_out.data,
            frame_out.size().width, frame_out.size().height,
            24, frame_out.cols *3,
            0xff0000, 0x00ff00, 0x0000ff, 0);

        if(frameSurface == NULL)
        {
            SDL_Log("Couldn't convert Mat to Surface.");
            return -2;
        }

        //Convert to SDL_Texture
        frameTexture = SDL_CreateTextureFromSurface(renderer, frameSurface);
        if(frameTexture == NULL)
        {
            SDL_Log("Couldn't convert Mat(converted to surface) to Texture."); //<- ERROR!!
            return -1;
        }
        //imshow("Camera", frame_out);
        SDL_RenderCopy(renderer, frameTexture, NULL, NULL);
        SDL_RenderPresent(renderer);
        /* A delay is needed to show (it actually wait for an input)*/
        if(waitKey(delay)>delay){;}

    }
    SDL_DestroyTexture(frameTexture);
    SDL_FreeSurface(frameSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(win);
    return 0;
}


int mouseCallback(SDL_MouseButtonEvent ev, Mat frame)
{

    if(ev.button == SDL_BUTTON_LEFT)
    {
        mouseLeft( ev.x, ev.y);
    }
}

单击鼠标左键时,调用的函数是mouseCallback,它正在调用函数mouseLeft,定义为:

void mouseLeft(int x, int y)
{
  Point pt;// = *((Point*)param);
  pt.x = x;
  pt.y = y;
  pts.push_back(pt);
  int ii=0;
  int size = pts.size()-1;
  while(ii<size && size>0)
  {
      if (euclideanDist(pt, pts.at(ii))<minDistance)
      {
          //cout<<pts->size()<<endl;
          pts.erase (pts.begin()+ii);

          // std::vector::end returns an iterator to the element following
          //the last element of the container, not to the last element.
          pts.erase(pts.end()-1);
          size-=2;
      }
      else
      {
          ;
      }
      ii++;

  }
}

pts 是一个 Points 向量(它最初是一个类的成员,但可以在此示例中全局声明)。当mouseLeft 被调用时,如果点击点与pts 中的任何点足够近,则将其从向量中移除,否则将推回一个新点。

函数draw正在绘制存储在pts中的点加上连接点的线:

void draw(Mat &frame)
{
  vector<Point> new_pts = pts; //avoid modifying the elements we are drawing
  int sizeVector = pts.size();
  if (sizeVector==1)
  {
      try
      {
        circle( frame, pts.at(0), radius, Scalar( 0, 0, 0), FILLED, 8,0);
      }
        catch (const out_of_range& e)
        {
            cout<<"Exception with command "<<endl;
            cout<<"circle( frame, pts.at(0), radius, Scalar( 0, 0, 0), FILLED, 8,0)"<<endl;
        }
  }
  else
  {
    int ii=0;
    while(ii<(sizeVector-1) && sizeVector>0)
    {
        try
        {
          circle( frame, pts.at(ii),   radius, Scalar( 0, 0, 0), FILLED, 8,0);
          circle( frame, pts.at(ii+1), radius, Scalar( 0, 0, 0), FILLED, 8,0);
          line(frame, pts.at(ii), pts.at(ii+1), Scalar( 0, 0, 0 ), 4, 8 );
        }
        catch (const out_of_range& e)
        {
          cout<<"Exception with commands: "<<endl;
          cout<<"\tcircle( frame, pts.at(ii),   radius, Scalar( 0, 0, 0), FILLED, 8,0)"<<endl;
          cout<<"\tcircle( frame, pts.at(ii+1), radius, Scalar( 0, 0, 0), FILLED, 8,0);"<<endl;
          cout<<"\tline(frame, pts.at(ii), pts.at(ii+1), Scalar( 0, 0, 0 ), 4, 8 );"<<endl;
        }

        if(pts.size()<=0) break;
        ii++;

        sizeVector= pts.size();
    }
  }
}

如果不是使用SDL_Window* win = SDL_CreateWindow("Camera", 100, 100, frame.cols, frame.rows, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); 创建窗口,而是将其尺寸加倍,例如:SDL_Window* win = SDL_CreateWindow("Camera", 100, 100, 2*frame.cols, 2*frame.rows, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

请注意,发布的代码是对原始代码的重新改编,以使其简单易懂。如果有任何遗漏或不清楚,请告诉我,以便我可以正确编辑问题。

【问题讨论】:

    标签: c++ opencv resize window sdl-2


    【解决方案1】:

    问题如下:基本上从SDL捕获的鼠标坐标是正确的:当我把窗口放大时,坐标甚至可以有很大的值。

    问题是这些坐标是由openCV从凸轮上绘制的,它始终具有相同的尺寸(基本上是凸轮的分辨率)。

    在框架上绘制点之前,我必须在从摄像头拍摄的图像的尺寸范围内重新调整鼠标单击的坐标。

    我找到了一个解决方案,基本上是在定义窗口后将窗口的初始尺寸存储在两个变量中:

    SDL_Window* win = SDL_CreateWindow("Camera", 100, 100, frame.cols, frame.rows,
        SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    int oldWidth = frame.cols, oldHeight= frame.rows;
    int width, height;
    float scaleX=1, scaleY=1;
    

    然后在已经存在的 switch-case 中添加以下事件:

                case SDL_WINDOWEVENT:
                {
                    if (genericEvent.window.event==SDL_WINDOWEVENT_RESIZED)
                    {
                        //oldWidth = width;
                        //oldHeight = height;
                        SDL_GetWindowSize(win, &width, &height);
                        scaleX =  (float)(width)/ float(oldWidth);
                        scaleY = (float)(height) / (float)(oldHeight);
                    }
                    break;
                }
    

    以及修改`mouseCallback as

    int mouseCallback(SDL_MouseButtonEvent ev, float scaleX, float scaleY, Mat frame)
    {
        if(ev.button == SDL_BUTTON_LEFT)
        {
            cout<<scaleX<<" "<<scaleY<<endl;
            int scaled_x =  static_cast<int> ((float)(ev.x)/scaleX);
            int scaled_y = static_cast<int> ((float)(ev.y)/ scaleY);
            std::cout<<"scaled x: "<<scaled_x<<", scaled y: "<<scaled_y<<endl;
            mouseLeft( scaled_x,scaled_y);
        }
    }
    

    【讨论】:

    • 这听起来像是在解决 SDL 中的一个错误。这也可能意味着这个“解决方案”在不同平台或 SDL 版本上运行时实际上会中断。
    • 我不这么认为:这是我的错误。我试图在答案的开头描述它:如果不清楚,请告诉我。
    猜你喜欢
    • 2014-12-02
    • 2012-05-22
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    • 2014-01-11
    • 2011-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多