Базовый графический интерфейс с OpenCV | Кодементор

В этой статье мы собираемся создать базовый пользовательский интерфейс с OpenCV. Пользовательский интерфейс OpenCV позволяет нам создавать окна, добавлять в них изображения, а также перемещать, изменять размер и уничтожать их. Пользовательский интерфейс находится в модуле highui OpenCV. В следующем коде мы узнаем, как создавать и отображать два изображения, нажимая клавишу для отображения нескольких окон с перемещением изображения в окне на нашем рабочем столе.

Не беспокойтесь о чтении полного кода; мы собираемся объяснить это небольшими кусками:

#include <iostream> 
#include <string> 
#include <sstream> 
using namespace std; 
 
// OpenCV includes 
#include <opencv2/core.hpp> 
#include <opencv2/highgui.hpp> 
using namespace cv; 
 
int main(int argc, const char** argv) 
{ 
   // Read images 
   Mat lena= imread("../lena.jpg"); 
   # Checking if Lena image has been loaded
   if (!lena.data) {
 cout << "Lena image missing!" << enld;
 return -1;
   }
   Mat photo= imread("../photo.jpg"); 
   # Checking if Lena image has been loaded
if (!photo.data) {
 cout << "Lena image missing!" << enld;
 return -1;
 }
    
   // Create windows 
   namedWindow("Lena", WINDOW_NORMAL); 
   namedWindow("Photo", WINDOW_AUTOSIZE); 
 
   // Move window 
   moveWindow("Lena", 10, 10); 
   moveWindow("Photo", 520, 10); 
    
   // show images 
   imshow("Lena", lena); 
   imshow("Photo", photo);  
 
   // Resize window, only non autosize 
   resizeWindow("Lena", 512, 512);  
 
   // wait for any key press 
   waitKey(0); 
 
   // Destroy the windows 
   destroyWindow("Lena"); 
   destroyWindow("Photo"); 
 
   // Create 10 windows 
   for(int i =0; i< 10; i++) 
   { 
         ostringstream ss; 
         ss << "Photo" << i; 
         namedWindow(ss.str()); 
         moveWindow(ss.str(), 20*i, 20*i); 
         imshow(ss.str(), photo); 
   } 
 
   waitKey(0); 
   // Destroy all windows 
   destroyAllWindows(); 
   return 0; 
} 

Давайте разберемся с кодом:

  1. Первая задача, которую мы должны выполнить, чтобы упростить графический интерфейс пользователя, — это импортировать модуль highui OpenCV:
#include <opencv2/highgui.hpp> 
  1. Теперь, когда мы готовы создать наши новые окна, мы должны загрузить несколько изображений:
// Read images 
Mat lena= imread("../lena.jpg"); 
Mat photo= imread("../photo.jpg"); 
  1. Для создания окон мы используем функцию namedWindow. Эта функция имеет два параметра; первая — это постоянная строка с именем окна, а вторая — требуемые флаги. Этот второй параметр является необязательным:
namedWindow("Lena", WINDOW_NORMAL); 
namedWindow("Photo", WINDOW_AUTOSIZE);
  1. В нашем случае мы создаем два окна: первое называется Лена, а второе — Фото.

По умолчанию для Qt и нативных есть три флага:
• WINDOW_NORMAL: этот флаг позволяет пользователю изменять размер окна.
• WINDOW_AUTOSIZE: если этот флаг установлен, размер окна автоматически настраивается в соответствии с отображаемым изображением, и изменить размер окна невозможно.
• WINDOW_OPENGL: этот флаг включает поддержку OpenGL.

Qt имеет ряд дополнительных флагов:
• WINDOW_FREERATIO или WINDOW_KEEPRATIO: если установлено значение WINDOW_FREERATIO, изображение настраивается без учета соотношения сторон. Если установлено значение WINDOW_FREEERATIO, изображение корректируется в соответствии с его пропорциями.
• WINDOW_GUI_NORMAL или WINDOW_GUI_EXPANDED: первый флаг обеспечивает базовый интерфейс без строки состояния и панели инструментов. Второй флаг обеспечивает наиболее продвинутый графический интерфейс пользователя со строкой состояния и панелью инструментов.

Примечание
Если мы компилируем OpenCV с Qt, все окна, которые мы создаем, по умолчанию находятся в расширенном интерфейсе, но мы можем использовать нативные интерфейсы и более простые, добавив флаг CV_GUI_NORMAL. По умолчанию установлены флаги WINDOW_AUTOSIZE, WINDOW_KEEPRATIO и WINDOW_GUI_EXPANDED.

  1. Когда мы создаем несколько окон, они накладываются друг на друга, но мы можем перемещать окна в любую область нашего рабочего стола с помощью функции moveWindow следующим образом:
// Move window 
moveWindow("Lena", 10, 10); 
moveWindow("Photo", 520, 10); 
  1. В нашем коде мы перемещаем окно Лены на 10 пикселей влево и на 10 пикселей вверх, а окно Фото на 520 пикселей влево и на 10 пикселей вверх:
// show images 
imshow("Lena", lena); 
imshow("Photo", photo);  
// Resize window, only non autosize 
resizeWindow("Lena", 512, 512);
  1. После показа изображений, которые мы загрузили ранее с помощью функции imshow, мы изменяем размер окна Лены до 512 пикселей, вызывая функцию resizeWindow. Эта функция имеет три параметра: имя окна, ширину и высоту.
**Note**
The specific window size is for the image area. Toolbars are not counted. Only windows without the WINDOW_AUTOSIZE flag enabled can be resized.
  1. После ожидания нажатия клавиши с помощью функции waitKey мы собираемся удалить или удалить наши окна с помощью функции destroyWindow, где имя окна является единственным требуемым параметром:
waitKey(0); 
 
// Destroy the windows 
destroyWindow("Lena"); 
destroyWindow("Photo"); 
  1. В OpenCV есть функция удаления всех окон, которые мы создаем, всего за один вызов. Функция называется destroyAllWindows. Чтобы продемонстрировать, как это работает, мы создаем 10 окон в нашем образце и ждем нажатия клавиши. Когда пользователь нажимает любую клавишу, он уничтожает все окна:
 // Create 10 windows 
for(int i =0; i< 10; i++) 
{ 
   ostringstream ss; 
   ss << "Photo" << i; 
   namedWindow(ss.str()); 
   moveWindow(ss.str(), 20*i, 20*i); 
   imshow(ss.str(), photo); 
} 
 
waitKey(0); 
// Destroy all windows 
destroyAllWindows(); 

В любом случае, OpenCV автоматически уничтожает все окна, когда приложение завершается, и нет необходимости вызывать эту функцию в конце нашего приложения.

Результат всего этого кода можно увидеть на следующих изображениях за два шага. Во-первых, он показывает два окна:

1.png

После нажатия любой клавиши приложение продолжает работу и рисует несколько окон, меняя свое положение:

2.png

С помощью нескольких строк кода мы можем создавать окна и манипулировать ими, а также отображать изображения. Теперь мы готовы упростить взаимодействие пользователя с изображениями и добавить элементы управления пользовательского интерфейса.

Добавление событий слайдера и мыши в наши интерфейсы

События мыши и управление ползунком очень полезны в компьютерном зрении и OpenCV. Используя этих управляющих пользователей, мы можем напрямую взаимодействовать с интерфейсом и изменять свойства входных изображений или переменных. В этом разделе мы собираемся представить события мыши и элементы управления ползунком для основных взаимодействий. Чтобы облегчить правильное понимание, мы создали следующий код, с помощью которого мы будем рисовать зеленые круги на изображении, используя события мыши, и размывать изображение с помощью ползунка:

// Create a variable to save the position value in track 
int blurAmount=15; 
 
// Trackbar call back function 
static void onChange(int pos, void* userInput); 
 
//Mouse callback 
static void onMouse(int event, int x, int y, int, void* userInput); 
 
int main(int argc, const char** argv) 
{ 
   // Read images 
   Mat lena= imread("../lena.jpg"); 
    
   // Create windows 
   namedWindow("Lena"); 
    
   // create a trackbar 
   createTrackbar("Lena", "Lena", &blurAmount, 30, onChange, &lena); 
    
   setMouseCallback("Lena", onMouse, &lena); 
 
   // Call to onChange to init 
   onChange(blurAmount, &lena); 
          
   // wait app for a key to exit 
   waitKey(0); 
    
   // Destroy the windows 
   destroyWindow("Lena"); 
    
   return 0; 
} 

Давайте разбираться в коде!

Во-первых, мы создаем переменную для сохранения положения ползунка. Нам нужно сохранить положение ползунка для доступа из других функций:

// Create a variable to save the position value in track 
int blurAmount=15;

Теперь мы определяем наши обратные вызовы для нашего ползунка и события мыши, необходимые для функций OpenCV setMouseCallback и createTrackbar:

// Trackbar call back function 
static void onChange(int pos, void* userInput); 
 
//Mouse callback 
static void onMouse(int event, int x, int y, int, void* userInput); 

В основной функции мы загружаем изображение и создаем новое окно с именем Лена:

int main(int argc, const char** argv) 
{ 
   // Read images 
   Mat lena= imread("../lena.jpg"); 
    
   // Create windows 
   namedWindow("Lena"); 

Теперь пришло время создать слайдер. OpenCV имеет функцию createTrackbar для создания ползунка со следующими параметрами по порядку:

  1. Название трекбара.
  2. Имя окна.
  3. Целочисленный указатель для использования в качестве значения; этот параметр является необязательным. Если он установлен, ползунок занимает это положение при создании.
  4. Максимальное положение на слайдере.
  5. Функция обратного вызова при изменении положения ползунка.
  6. Пользовательские данные для отправки в callback. Его можно использовать для отправки данных в обратные вызовы без использования глобальных переменных.

К этому коду мы добавляем трекбар для окна Лены и вызываем трекбар Лены тоже, чтобы размыть изображение. Значение трекбара сохраняется в целочисленном значении blurAmount, которое мы передаем как указатель и устанавливаем максимальное значение бара равным 30. Мы настраиваем onChange как функцию обратного вызова и отправляем изображение lena mat в качестве пользовательских данных:

   // create a trackbar 
   createTrackbar("Lena", "Lena", &blurAmount, 30, onChange, &lena);

После создания ползунка мы добавляем события мыши для рисования кругов, когда пользователь нажимает левую кнопку мыши. OpenCV имеет функцию setMouseCallback. Эта функция имеет три параметра:

• Имя окна, в котором мы получаем события мыши.
• Функция обратного вызова для вызова при любом взаимодействии с мышью.
• Пользовательские данные: это любые данные, которые будут отправлены функции обратного вызова при ее запуске. В нашем примере мы отправим изображение Лены целиком.

Используя следующий код, мы можем добавить обратный вызов мыши в окно Lena и настроить onMouse в качестве функции обратного вызова, передав изображение коврика lena в качестве пользовательских данных:

setMouseCallback("Lena", onMouse, &lena); 

Чтобы доработать только основную функцию, нам нужно инициализировать изображение с тем же параметром, что и ползунок. Чтобы выполнить инициализацию, нам нужно только вызвать функцию обратного вызова onChange и дождаться событий, прежде чем закрывать окна с помощью destroyWindow, как видно из следующего кода:

// Call to onChange to init
onChange(blurAmount, &lena); 
          
// wait app for a key to exit 
waitKey(0); 
    
// Destroy the windows 
destroyWindow("Lena"); 

Обратный вызов ползунка применяет базовый фильтр размытия к изображению, используя значение ползунка в качестве величины размытия:

// Trackbar call back function 
static void onChange(int pos, void* userData) { 
    if(pos <= 0) return; 
    // Aux variable for result 
    Mat imgBlur; 
    // Get the pointer input image     
    Mat* img= (Mat*)userInput; 
    // Apply a blur filter 
    blur(*img, imgBlur, Size(pos, pos)); 
    // Show the result 
    imshow("Lena", imgBlur); 
}

Эта функция проверяет, равно ли значение ползунка 0, используя переменную pos. В этом случае мы не применяем фильтр, потому что он приводит к плохому выполнению. Мы также не можем применить размытие 0 пикселей. После проверки значения ползунка мы создаем пустую матрицу с именем imgBlur для хранения результата размытия. Чтобы получить изображение, отправленное через пользовательские данные в функции обратного вызова, мы должны привести void* userData к правильному указателю типа изображения Mat*.

Теперь у нас есть правильные переменные для применения фильтра размытия. Функция размытия применяет базовый медианный фильтр к входному изображению, в нашем случае *img; к выходному изображению последний обязательный параметр — это размер ядра размытия (ядро — это небольшая матрица, используемая для вычисления средств свертки между ядром и изображением), которое мы хотим применить. В нашем случае мы используем квадратное ядро ​​размера pos. Наконец, нам нужно только обновить интерфейс изображения с помощью функции imshow.

Обратный вызов события мыши имеет пять входных параметров: первый параметр определяет тип события; второй и третий определяют положение мыши; четвертый параметр определяет движение колеса; а пятый параметр определяет вводимые пользователем данные.

Типы событий мыши следующие:

Захват.JPG

В нашем примере мы управляем только событиями, возникающими в результате щелчка левой кнопкой мыши, и любое событие, кроме EVENT_LBUTTONDOWN, отбрасывается. После отбрасывания других событий мы получаем входное изображение, подобное этому, с обратным вызовом ползунка и с кругом на изображении с помощью функции Circle OpenCV:

//Mouse callback 
static void onMouse(int event, int x, int y, int, void* userInput) 
{ 
   if(event != EVENT_LBUTTONDOWN) 
           return; 
 
   // Get the pointer input image 
   Mat* img= (Mat*)userInput; 
    
   // Draw circle 
   circle(*img, Point(x, y), 10, Scalar(0,255,0), 3); 
 
   // Call on change to get blurred image 
   onChange(blurAmount, img); 
 
} 

Надеюсь, вам понравилось читать эту статью. Если вы хотите узнать больше об OpenCV, вам следует изучить статью «Изучение OpenCV путем создания проектов — второе издание». Learn OpenCV by Building Projects — Second Edition — это практическое руководство с множеством советов, в котором основное внимание уделяется разработке приложений компьютерного зрения с помощью OpenCV. На протяжении всей книги разрабатываются примеры приложений, которые вы можете запускать и использовать в своих собственных проектах.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *