Базовый графический интерфейс с 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;
}
Давайте разберемся с кодом:
- Первая задача, которую мы должны выполнить, чтобы упростить графический интерфейс пользователя, — это импортировать модуль highui OpenCV:
#include <opencv2/highgui.hpp>
- Теперь, когда мы готовы создать наши новые окна, мы должны загрузить несколько изображений:
// Read images
Mat lena= imread("../lena.jpg");
Mat photo= imread("../photo.jpg");
- Для создания окон мы используем функцию namedWindow. Эта функция имеет два параметра; первая — это постоянная строка с именем окна, а вторая — требуемые флаги. Этот второй параметр является необязательным:
namedWindow("Lena", WINDOW_NORMAL);
namedWindow("Photo", WINDOW_AUTOSIZE);
- В нашем случае мы создаем два окна: первое называется Лена, а второе — Фото.
По умолчанию для 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.
- Когда мы создаем несколько окон, они накладываются друг на друга, но мы можем перемещать окна в любую область нашего рабочего стола с помощью функции moveWindow следующим образом:
// Move window
moveWindow("Lena", 10, 10);
moveWindow("Photo", 520, 10);
- В нашем коде мы перемещаем окно Лены на 10 пикселей влево и на 10 пикселей вверх, а окно Фото на 520 пикселей влево и на 10 пикселей вверх:
// show images
imshow("Lena", lena);
imshow("Photo", photo);
// Resize window, only non autosize
resizeWindow("Lena", 512, 512);
- После показа изображений, которые мы загрузили ранее с помощью функции 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.
- После ожидания нажатия клавиши с помощью функции waitKey мы собираемся удалить или удалить наши окна с помощью функции destroyWindow, где имя окна является единственным требуемым параметром:
waitKey(0);
// Destroy the windows
destroyWindow("Lena");
destroyWindow("Photo");
- В 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 автоматически уничтожает все окна, когда приложение завершается, и нет необходимости вызывать эту функцию в конце нашего приложения.
Результат всего этого кода можно увидеть на следующих изображениях за два шага. Во-первых, он показывает два окна:
После нажатия любой клавиши приложение продолжает работу и рисует несколько окон, меняя свое положение:
С помощью нескольких строк кода мы можем создавать окна и манипулировать ими, а также отображать изображения. Теперь мы готовы упростить взаимодействие пользователя с изображениями и добавить элементы управления пользовательского интерфейса.
Добавление событий слайдера и мыши в наши интерфейсы
События мыши и управление ползунком очень полезны в компьютерном зрении и 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 для создания ползунка со следующими параметрами по порядку:
- Название трекбара.
- Имя окна.
- Целочисленный указатель для использования в качестве значения; этот параметр является необязательным. Если он установлен, ползунок занимает это положение при создании.
- Максимальное положение на слайдере.
- Функция обратного вызова при изменении положения ползунка.
- Пользовательские данные для отправки в 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.
Обратный вызов события мыши имеет пять входных параметров: первый параметр определяет тип события; второй и третий определяют положение мыши; четвертый параметр определяет движение колеса; а пятый параметр определяет вводимые пользователем данные.
Типы событий мыши следующие:
В нашем примере мы управляем только событиями, возникающими в результате щелчка левой кнопкой мыши, и любое событие, кроме 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. На протяжении всей книги разрабатываются примеры приложений, которые вы можете запускать и использовать в своих собственных проектах.