Новосибирский Государственный Университет
Опубликован: 20.08.2013 | Доступ: свободный | Студентов: 831 / 24 | Длительность: 14:11:00
Лекция 4:

Начало работы с библиотекой OpenCV

< Лекция 3 || Лекция 4: 1234 || Лекция 5 >

1.4. OpenCV на C/C++

Для того, чтобы использовать OpenCV на C/C++ понадобится Visual Studio (для Windows), XCode (для OS X и iOS) или компилятор GCC вместе с утилитой make (для Linnux). По возможности, не используйте MinGW, Borland C++, Sun Studio и разные экзотические компиляторы. OpenCV довольно активно использует разные возможности компиляторов и не все они адекватно справляются с этой задачей.

Также необходима утилита СМake (под Windows не обязательна, но желательна). СМake – это система сборки программного обеспечения из исходного кода. Она не выполняет саму сборку, а создает файлы (проектные или make-файлы), управляющие этой сборкой.

1.4.1. Первая OpenCV-программа на C++

Первая программа на C++ будет делать то же, что и программа first.py на Python'е.

#include "opencv2/opencv.hpp" 
using namespace cv; 
int main(int argc, char** argv) 
{ 
 Mat img, gray, edges; // Объявление матриц 
 img = imread(argv[1], 1); // Читаем изображение 
 imshow("original", img); // Отрисовываем изображение 
 cvtColor(img, gray, COLOR_BGR2GRAY); 
  // Конвертируем в монохромный формат 
 GaussianBlur(gray, gray, Size(7, 7), 1.5); 
  // Устраняем размытие 
 Canny(gray, edges, 0, 50); 
  // Запускаем детектор ребер 
 imshow("edges", edges); 
  // Отрисовываем изображение 
 waitKey();  
  //Ожидаем нажатия клавиши 
 return 0; 
}  
   

Как скомпилировать и запустить эту программу см. инструкции в учебнике http://docs.opencv.org/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.html.

1.4.2. Работа с матрицами

Как было сказано ранее, если хочется провести небольшой эксперимент с OpenCV, как правило Питон будет наилучшим выбором. К C++ же имеет смысл обращаться, когда хочется сделать свой алгоритм работающий непосредственно с пикселями изображений, и делающий это эффективно, или для реализации уже конечного приложения, не прототипа. В любом случае, как только вы начинаете использовать OpenCV из C++, вы немедленно столкнетесь с типом cv::Mat (матрица), который в OpenCV используется для хранения почти всех объектов – изображений, массивов точек, векторных полей вычисляемых алгоритмами оптического поток, обычных матриц и т.д. Собственно, это аналогично MATLAB, где почти все данные хранятся в массивах. Итак, класс cv::Mat реализует многомерные многоканальные массивы.


Рис. 4.5.

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

Изображение – это двумерный массив. Все его элементы расположены подряд по строкам. Кроме числа строк и числа столбцов имеется параметр step, равный расстоянию между строками. Этот параметр не меньше числа столбцов, но может быть его больше. В последнем случае в конце каждой строки имеется неиспользуемое пространство.

Предположим, вы создали некоторую матрицу (матрица A на рисунке выше), указав число ее строк и столбцов и опционально начальное значение для элементов матрицы. Параметр step вычисляется автоматически, выделяется буфер память нужного размера, при необходимости заполняется заданным значением. Счетчик ссылок приравнивается к 1. Если эта матрица присваивается другой матрице (матрица B на рисунке), то физического копирования данных не происходит, но на 1 увеличивается счетчик ссылок, а указатель данных указывает на то же место в памяти. Как только вы завершаете работу с каким либо объектом, счетчик ссылок уменьшается на 1. При достижении значения 0 память освобождается.

Допустим, вы нашли лицо на изображении и хотите найти на нем глаза. В этом случае вам нужно не все изображение, а только его часть, соответствующая лицу. Для этого удобно создать матрицу, являющуюся частью другой матрицы, при этом можно избежать копирования данных (матрица C на рисунке). Заметим, что параметр step для такой подматрицы будет равен параметру step исходной матрицы. Указатель на данные будет указывать на начало подматрицы. Счетчик ссылок после создания такой подматрицы также увеличивается на 1.

Если необходимо настоящее копирование данных, нужно использовать метод clone(), например:

cv::Mat M2 = M.clone();    
   

Любому элементу матрицы можно присвоить значение. Делается это с помощью метода .at(y,x), где y – номер строки, x – номер столбца. Рассмотрим небольшой пример.

Mat M(480,640,CV_8UC1); // Создаем изображение 640x480 
Rect roi(100, 200, 20, 20); // Определяем ROI 
Mat subM = M(roi); // Выделяем ROI в отдельную матрицу 
subM.at<uchar>(y,x)=255; 
 // Изменяем пиксель (x,y) в subM и (x+100, y+200) в M    
   

Так как в данном примере данные одни и те же, то изменяя подматрицу subM, вы изменяете и матрицу M.

Продолжим этот пример и покажем, как работать с итераторами. Пусть в выбранном ROI необходимо оценить "резкость", определяемую как сумму модулей градиентов, вычисленных в каждом пикселе. Эта задача может быть полезной для реализации автофокуса (автофокус соответствует локальному максимуму функции "резкости"). Необходимо пробежаться по всем пикселам, найти в каждом из них градиент, вычислить его модуль и прибавить к сумматору. Для того чтобы пройтись по всем элементам матрицы, удобно использовать итераторы, похожие на итераторы из стандартной библиотеке stl:

Mat_<Vec3b>::iterator it= subM.begin<Vec3b>(), 
   itEnd = subM.end<Vec3b>(); 
float contrast = 0.f; 
for(; it != itEnd; ++it) 
{ 
 uchar* ptr = & (*it); 
 int dx = ptr[1] – ptr[-1], 
  dy = ptr[subM.step] – ptr[-subM.step]; 
 contrast += sqrtf((float)(dx*dx + dy*dy)); 
}   
   

Кроме матриц библиотека OpenCV умеет работать с обычными STL векторами. При этом внутри библиотеки вектора часто обрабатываются как обычные матрицы (cv::Mat). То есть, для входного вектора внутри библиотеки дополнительно создается заголовок cv::Mat, который указывает на те же данные (данные не копируются). По завершении обработки временный заголовок удаляется.


Рис. 4.6.
< Лекция 3 || Лекция 4: 1234 || Лекция 5 >
Александра Максимова
Александра Максимова

При прохождении теста 1 в нем оказались вопросы, который во-первых в 1 лекции не рассматривались, во-вторых, оказалось, что вопрос был рассмаотрен в самостоятельно работе №2. Это значит, что их нужно выполнить перед прохождением теста? или это ошибка?
 

Алена Борисова
Алена Борисова

В лекции по обработке полутоновых изображений (http://www.intuit.ru/studies/courses/10621/1105/lecture/17979?page=2) увидела следующий фильтр:


    \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 2 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array} - \frac{1}{9} \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 1 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array}

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

Что вижу я в конструкции фильтра (скорее всего ошибочно): F(x, y) = 2 * I(x, y) - 1/9 I(x, y) = 17/9 * I(x, y), где F(x, y) - яркость отфильтрованного пикселя, а I(x, y) - яркость исходного пикселя с координатами (x, y). Что означает обычное повышение яркости изображения, при этом без учета соседних пикселей (так как их множители равны 0).

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

Дмитрий Григорович
Дмитрий Григорович
Россия, Москва
Владислав Филатов
Владислав Филатов
Россия