Перекладено українською: Арсеній Чеботарьов, Ніжин 2015. ac2epsilon@gmail.com

Інструкція з OpenCV

Вступ

OpenCV (бібліоткека Open Source Computer Vision: http://opencv.org) є бібліотекою з ліцензією BSD та відкритим кодом, що включає декілька сотен алоритмів комп'ютерного бачення. Документ описує так званий OpenCV 2.x API, що загалом є C++ API, на відміну від базованого на C OpenCV 1.x API, який описаний в opencv1x.pdf.

OpenCV має модулярну структуру, що означає, що пакунок включає декілька загальних та статичних бібліотек. Доступні даступні модулі:

Подальші глави цього документу описують функціональність кожного модуля. Але зпершу переконаймося, що ви знайомі з загальними концепціями API, що використовуються в бібліотеці. 

Концепції API

Простір імен cv

Всі класи та функції OpenCV розміщені в просторі імен cv. Таким чином, щоб отримати доступ до функціональності з коду, використовуйте специфікатор cv:: або директиву using namespace cv;:

#include "opencv2/core/core.hpp"
...
cv::Mat H = cv::findHomography(points1, points2, CV_RANSAC, 5);
...

або

#include "opencv2/core/core.hpp"
using namespace cv;
...
Mat H = findHomography(points1, points2, CV_RANSAC, 5 );
...

Деякі з поточних або майбутніх зовнішніх імен OpenCV можуть конфліктувати з STL або іншими бібліотеками. В цьому випадку використовуйте явне задання простору імен, щоб іникати конфліктів:

Mat a(100, 100, CV_32F);
randu(a, Scalar::all(1), Scalar::all(std::rand()));
cv::log(a, a);
a /= std::log(2.);

Автоматичне управління пам'яттю

OpenCV обробляє всю пам'ять автоматично.

З самого початку, std::vector, Mat, та інші структури даних, що використовуються в функціях та методах, мають деструктори, що повертають пам'ять об'єктів по мірі необхідності. Це означає, що деструктори не завжди повертають буфер, як в випадку Mat. Вони приймають до уваги можливе сумісне застосування пам'яті. Деструктор зменшує лічильник посилань, асоційованих з буфером даних матриці. Буфер повертається до пулу тільки в випадку, коли кількість посилань стає рівною нулю, тобто, коли коли ніяка інша структура більше не посилається на буфер. Подібно до цього, коли екземпляр Mat копіюється, ніякі дані насправді не копіюються. Замість цього лічильник посилань збільшується, щоб запам'ятати, що дані використовуються ще десь в іншому місці. Також є метод Mat::clone, що створює повну копію даних матриці. Дивіться приклад нижче:

// створюємо велику матрицю розміром 8Mb
Mat A(1000, 1000, CV_64F);

// створюємо інший заголовок для тієї ж матриці;
// це безпосередня операція, безвідносно до розміру матриці.
Mat B = A;
// створюємо інший заголовок дл 3-го рядка A; дані не копіюються
Mat C = B.row(3);
// створюємо окрему копію матриці
Mat D = B.clone();
// копіюємо 5й рядок B в C, тобто 5й рядок A в 3й рядок A.
B.row(5).copyTo(C);
// тепер дозволяємо A та D мати загальні дані; після цього модифікована A 
// все ще має посилання з B та C.
A = D; // Тепер робимо B пустою матрицею, без асоційованих буферів // Але C все ще посилається на A, тому що C це тільки рядок в оригінальній A B.release(); // Тепер робимо повну копію C. Як результат, велика матриця відвантажується // бо більше на неї немає посилань C = C.clone();

Ви бачите, що використання Mat та інших базових структур є простим. Але що до класів вищого рівня, та навіть користувацькіх структур даних, до не беруть до уваги автоматичне керування пам'яттю? Для них OpenCV пропонує шаблон класу Ptr<>, що подібний до std::shared_ptr з C++ TR1. Так що, замість використання прямих вказівників:

T* ptr = new T(...);

ви можете використовувати:

Ptr<T> ptr = new T(...);

Тобто  Ptr<T> ptr містить вазівник на екземпляр T, та лічильник посилань на цей вказівник. Дивіться опис Ptr для отримання деталей.

Автоматичне розміщення вихідних даних

OpenCV вивільняє пам'ять автоматично, так само, як автоматично резервує пам'ять для параметрів функцій виводу, у більшості випадків. Таким чином, якщо функція має один або більше вхідних масивів (екземплярів cv::Mat), та деякі вихідні масиви, вихідні масиви автоматично розміщуються або вивільняються. Розмір та тип вихідних масивів визначаються з розміру та типу вхідних масивів. Якщо виникає потреба, функція приймає інші параметри, що допомагають здогадатись властивості вихідного массиву. 

Приклад:

#include "cv.h"
#include "highgui.h"

using namespace cv;

int main(int, char**)
{
    VideoCapture cap(0);
    if(!cap.isOpened()) return -1;

    Mat frame, edges;
    namedWindow("edges",1);
    for(;;)
    {
        cap >> frame;
        cvtColor(frame, edges, CV_BGR2GRAY);
        GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
        Canny(edges, edges, 0, 30, 3);
        imshow("edges", edges);
        if(waitKey(30) >= 0) break;
    }
    return 0;
}
Масив frame автоматично розміщується оператором >>, оскільки модуль захоплення відео знає розподільну здатність та бітову глибину відео фрейма. Масив edges автоматично розміщується функцією cvtColor. Він буде того ж розміру та глибини кольору, що і вхідний масив. Число каналів є 1, оскільки перетворення кольору CV_BGR2GRAY вже відбулось, що означає, що кольори зведені до відтінків сірого. Зауважте, що frame та edges розміщені тільки одного разу, на протязі першого виконання тіла циклу, оскількі подальші відео фрагменти матимуть ту розподільну здатність. Якщо ви якось зміните розподільну здатність відео, масиви автоматично змінять розміри.

Ключовим компонентом цієї технології є метод Mat::create. Він отримує потрібний розмір та тип масиву. Якщо масив вже має такий розмір та тип, метод нічого не робить. Інакше попередній масив буде знищено, якщо такий був (ця частина включає зменшення числа посилань, та порівняння його з нулем), та потім розміщення нового буфера потрібного розміру. Більшість функцій викликають метод Mat::create для кожного вихідного масиву, і таким чином працює автоматичне розміщення. 

Деякі помітні виключення з цієї схеми є cv::mixChannels, cv::RNG::fill, та декілька інших функцій та методів. Вони не в змозі розміщувати вихідні дані, так що вам треба зробити це заздалегідь.

Арифметика насиченості

Як бібліотека комп'ютерного бачення, OpenCV має справи здебільшого з пікселями зображень, що часто закодовані як компактні, 8- або 16-біт-на-канал, та, таким чином, має дуже обмежений диапазон значень. Більше того, деякі операції над зображеннями, як перетворення простору кольорів, підлаштування яркості та контрасту, ретушування, складні інтерполяції (бі-кубічні, Lanczos) можуть продукувати значення за межами цього диапазону. Якщо ви тільки зберігаєте меньші 8 (16) біт результату, це призведе до визуальних артифактів, та може впливати на подальший аналіз зображення. Щоб розрішити проблему, використовується так звана арифметика насиченості. Наприклад, щоб зберігти результат операції в 8-бітному зображенні, ви обираєте найближче значення з диапазону 0..255:

I(x,y)= \min ( \max (\textrm{round}(r), 0), 255)

Подібні правила також застосовуються до 8-бітних зі знаком, 16-бітних зі знаком, та беззнакових типів. Ця сементика використовується завжди в бібліотеці. В коді C++ це робиться за допомогою функцій saturate_cast<>, що копіюють операції преведення типів в стандартному C++. Дивіться нижче реалізацію формули, що надана вище:

I.at<uchar>(y, x) = saturate_cast<uchar>(r);

де cv::uchar є OpenCV 8-бітним беззнаковим цілого типу. В оптимізованому коді SIMD використовуються інструкції SSE2, такі як paddusb, packuswb, таке інше. Вони допомагають досягти саме такої поведінки, як і код C++.

Зауваження

Насичення не застосовується, коли результат є 32-бітним цілим.

Фіксовані типи пікселів. Обмежене застосування шаблонів

Шаблони є чудовою можливістю C++, що дозволяє реалізацію дуже потужних, ефективних, та безпечних структур даних та алгоритмів. Однак екстенсивне застосування шаблонів може драматично збільшити час компіляції та розмір коду. Окрім цього, складно відрізнити інтерфейс від реалізації, коли використовуються виключно шаблони. Це може бути гарним для простих алгоритмів, але негарно для бібліотек комп'юторного зору, де один алгоритм може займати впродовж тисяч рядків коду. Тому, а також щоб спростити розробку прив'язок для інших мов, як Python, Java, Matlab, що взагалі не мають шаблонів, або обмежені можливості шаблонів, поточна реалізація OpenCV базується на поліморфізмі та маршрутизації часу виконання, замість шаблонів. В тих місцях, де маршрутизація часу виконання також дуже повільна (як операції доступу до пікселів), неможлива (реалізація Ptr<>), або просто дуже незручна (saturate_cast<>()), поточна реалізація вводить маленькі шаблонні класи, методи та функції. В усіх інших місцях поточна версія OpenCV використання шаблонів обмежено.

Відповідно, є обмежений набір примітивних типів даних, якими може оперувати бібліотека. Тобто, елементи масивів мають бути одного з наступних типів:

  • 8-бітові беззнакові цілі (uchar)
  • 8-бітові знакові цілі (schar)
  • 16-бітові беззнакові цілі (ushort)
  • 16-бітові знакові цілі (short)
  • 32-бітові знакові цілі (int)
  • 32-бітові з плаваючою крапкою (float)
  • 64-бітові з плаваючою крапкою (double)
  • кортежі з декількох елементів, де всі елементи мають той же тип (один з перелічених). Масив, де елементами є такі кортежі, називають багатоканальними масивами, на відміну від одноканальних масивів, чиї елементи є скалярними значеннями. Максимально можливе число каналів визначене в константі CV_CN_MAX, що начасі встановлено в 512.

Для ціх базових типів застосовується наступний перелік:

enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };

Мультиканальні (n-канальні) типи можуть бути задані з використанням наступних опцій:

Зауваження

CV_32FC1 == CV_32F, CV_32FC2 == CV_32FC(2) == CV_MAKETYPE(CV_32F, 2), та CV_MAKETYPE(depth, n) == (depth&7) + ((n-1)<<3). Це означає, що тип констант формується з depth, беручи молодші 3 біти, та числа каналів мінус 1, беручи наступні log2(CV_CN_MAX) біт.

Приклади:

Mat mtx(3, 3, CV_32F); // створює матрицю 3x3 з плаваючою точкою
Mat cmtx(10, 1, CV_64FC2); // створює матрицю 10x1 з 2-каналів з плаваючою точкою
                           // (комплексний вектор з 10 елементів)
Mat img(Size(1920, 1080), CV_8UC3); // створює 3-канальне зображення (з трьох кольорів)
                                    // з 1920 колонок та 1080 рядків.
Mat grayscale(image.size(), 
CV_MAKETYPE
(image.depth(), 1)); // створює 1-канальне зображення // такого ж розміру та типу, як image

Масиви з більш складними елементами не можуть бути сконструйовані або оброблені в OpenCV. Більше того, кожна функція аюл метод може обробляти тільки одну підмножину всіх можливих типів масивів. Звичайно, чим складнішим є алгоритм, тим менша підмножина форматів. Дивіться нижче типові приклади таких обмежень:

  • Алгоритм розпізнавання обличчя робить тільки з 8-бітними відтінками сірого або кольоровими зображення.
  • Функції лінійної алгебри, та більшість алгоритмів навчання, роблять тільки з масивами з плаваючою крапкою.
  • Базові функції, такі як cv::add, підтримують всі типи.
  • Перетворення простору кольорів підтримують 8-беззнакові, 16-бітові беззнакові, та 32-бітні з плаваючою крапкою.

Підмножини підтримуваних типів для кожної функції були визначені з практичних потреб, та можуть бути розширені в майбутньому, базуючись на запитах користувачів.

InputArray та OutputArray

Багато функцій OpenCV обробляють щільні 2-вимірні або багато-вимірні числові масиви. Звичайно, такі функції сприймають cpp:class:Mat в якості параметрів, але в деяких випадках більш зручно використовувати std::vector<> (для набору крапок, наприклад) або Matx<> (для гомографічних матриць 3x3, та подібного). Щоб виключити багато дублікацій в  API, були введені спецальні "проксі" класи. Базови й проксі клас є InputArray. Він використовується для передачі масивів тільки для читання на вхід функції. Похідний від InputArray клас OutputArray використовується для задання вихідного масиву від функції. Звичайно ви не турбуватиметесь про ці проміжні типи (і ви не декларуватимите змінні ціх типів явно) - вони просто будуть робити автоматично. Ви можете очікувати, що замість InputArray/OutputArray ви можете завжди використовувати Mat, std::vector<>, Matx<>, Vec<> або Scalar. Коли функція має опціональну матрицю на вході або виході, що ви не маєте або не бажаєте вказувати, передайте cv::noArray().

Обробка помилок

OpenCV використовує виключення для повідомлення про критичні помилки. Коли вхідні дані мають коректний формат, та належать до вказаного диапазону, але алгоритм не може подолати завдання з різних причин (наприклад, алгоритм оптимізації не доходить згоди), він повертає спеціальний код помилки (типово, логічне значення).

Виключення можуть бути екземплярами класу cv::Exception або похідних класів. В свою чергу, cv::Exception походить від std::exception. Так що воно може оброблятись в коді з використанням компонент стандартної бібліотеки C++.

Виключення типово спрацьовує або з використанням макро CV_Error(errcode, description), або його аналога CV_Error_(errcode, printf-spec, (printf-args)), або з використанням макро CV_Assert(condition), що перевіряє умову, та викликає виключення, коли умова не виконується. Для критичних фрагментів коду є CV_DbgAssert(condition), що щось робить тільки в конфігурації налаштування. Через автоматичне керування пам'ятю всі проміжні буфери автоматично вивільняються в випадку несподіваної помилки. Вам тільки треба додати try для відлову виключень, якщо треба:

try
{
    ... // виклики OpenCV
}
catch( cv::Exception& e )
{
    const char* err_msg = e.what();
    std::cout << "exception caught: " << err_msg << std::endl;
}

Багатопоточність та реентерабільність

Поточна реалізація OpenCV повністю реентерабельна. Тобто та ж функція, той же constant метод екземпляра класа, або той же non-constant метод різних екземплярів класу, може бути викликаний з різних потоків. Також, той же cv::Mat може бути використаний в різних потоках, оскільки операції підрахунка посилань використовують специфічні для архитектури атомарні інструкції.

Базові структури

Типи даних

class DataType

Шаблонний "гуртовий" клас для примітивних типів даних OpenCV. Примітивний тип даних OpenCV є unsigned char, bool, signed char, unsigned short, signed short, int, float, double, або кортеж зі значень одного з ціх типів, де всі значення в кортежі мають однаковий тип. Любий примітивний тип зі списку може бути визначений по ідентифікатору в формі CV_<bit-depth>{U|S|F}C(<number_of_channels>), наприклад: uchar ~ CV_8UC1, 3-елементий тип з плаваючою крапкою ~ CV_32FC3, і так далі. Універсальна структура OpenCV, що дозволяє зберігати один екземпляр такого примітиву, є Vec. Декілька екземплярів такого типу зберігаються в std::vector, Mat, Mat_, SparseMat, SparseMat_, або іншого контейнера, що здатен зберігати екземпляри Vec.

Клас DataType в основному використовуєтья для впровадження опису такого примітивного типу даних, без додавання полів або методів до відповідних класів (та, насправді, неможливо додати будь-що до примітивних типів даних C/C++). Цей прийом відомий в C++ як успадкування класів (class traits, класи, що складаються з самих методів, що під'єднуються до інших для надання функціональності, дещо схожої на мікс-іни або інтерфейси в інших мовах. прим. перекл.). Клас DataType не використовується сам по собі, але його спеціалізовані версії, як, наприклад:

template<> class DataType<uchar>
{
    typedef uchar value_type;
    typedef int work_type;
    typedef uchar channel_type;
    enum { channel_type = CV_8U, channels = 1, fmt='u', type = CV_8U };
};
...
template<typename _Tp> DataType<std::complex<_Tp> >
{
    typedef std::complex<_Tp> value_type;
    typedef std::complex<_Tp> work_type;
    typedef _Tp channel_type;
    // DataDepth ще один допоміжний клас
    enum { depth = DataDepth<_Tp>::value, channels=2,
        fmt=(channels-1)*256+DataDepth<_Tp>::fmt,
        type=CV_MAKETYPE(depth, channels) };
};
...

Головне призначення цього класу є перетворення інформації про типи часу компіляції на сумісні з OpenCV ідентифікатори типів даних, наприклад:

// розміщує матрицю 30x40 з плаваючою крапкою 
Mat A(30, 40, DataType<float>::type);

Mat B = Mat_<std::complex<double> >(3, 3);
// твердження нижче друкує 6, 2 /*, тобто depth == CV_64F, channels == 2 */
cout << B.depth() << ", " << B.channels() << endl;

Таким чином, такі успадкування використовуються, щоб сказати OpenCV, з яким типом даних робити, навіть якщо такий тип не є природним для OpenCV. Наприклад, ініціалізація матриці B вище компілюється, оскільки OpenCV визначає відповідний спеціальний клас шаблону DataType<complex<_Tp> > . Цей механізм також корисний (та використовується в OpenCV цім шляхом) для реалізацій узагальнених алгоритмів.

Point_

class Point_

template<typename _Tp> class CV_EXPORTS Point_
{
public:
    typedef _Tp value_type;

    // різні конструктори
    Point_();
    Point_(_Tp _x, _Tp _y);
    Point_(const Point_& pt);
    Point_(const CvPoint& pt);
    Point_(const CvPoint2D32f& pt);
    Point_(const Size_<_Tp>& sz);
    Point_(const Vec<_Tp, 2>& v);

    Point_& operator = (const Point_& pt);
    //! перетворення на інші типи даних
    template<typename _Tp2> operator Point_<_Tp2>() const;

    //! перетворення на стару структуру в стилі C 
    operator CvPoint() const;
    operator CvPoint2D32f() const;
    operator Vec<_Tp, 2>() const;

    //! множення крапок
    _Tp dot(const Point_& pt) const;
    //! множення крапок з подвійною точністю
    double ddot(const Point_& pt) const;
    //! кросс-множення
    double cross(const Point_& pt) const;
    //! перевірка на належність до прямокутника
    bool inside(const Rect_<_Tp>& r) const;

    _Tp x, y; //< координати крапки
};

Клас шаблону для 2D крапок задається в координатах x та y . Екземпляр класу є взаємозамінним зі структурами C, CvPoint та CvPoint2D32f . Також є оператор приведення, щоб привести координати крапки до вказаного типу. Перетворення з плаваючих координат до цілих відбувається з округленням. Зазвичай, перетворення використовує цю операцію для кожної з координат. Окрім членів класу, перелічених в декларації вище, наступні операції також реалізовані для крапок:

pt1 = pt2 + pt3;
pt1 = pt2 - pt3;
pt1 = pt2 * a;
pt1 = a * pt2;
pt1 += pt2;
pt1 -= pt2;
pt1 *= a;
double value = norm(pt); // L2 norm
pt1 == pt2;
pt1 != pt2;

Для вашої зручності визначені наступні типи:

typedef Point_<int> Point2i;
typedef Point2i Point;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;

Приклад:

Point2f a(0.3f, 0.f), b(0.f, 0.4f);
Point pt = (a + b)*10.f;
cout << pt.x << ", " << pt.y << endl;

Point3_

class Point3_

template<typename _Tp> class CV_EXPORTS Point3_
{
public:
    typedef _Tp value_type;

    // різноманітні конструктори
    Point3_();
    Point3_(_Tp _x, _Tp _y, _Tp _z);
    Point3_(const Point3_& pt);
    explicit Point3_(const Point_<_Tp>& pt);
    Point3_(const CvPoint3D32f& pt);
    Point3_(const Vec<_Tp, 3>& v);

    Point3_& operator = (const Point3_& pt);
    //! перетворення на інший тип даних
    template<typename _Tp2> operator Point3_<_Tp2>() const;
    //! перетворення на старий CvPoint...
    operator CvPoint3D32f() const;
    //! перетворення на cv::Vec<>
    operator Vec<_Tp, 3>() const;

    //! множення крапок
    _Tp dot(const Point3_& pt) const;
    //! множення крапок з подвійною точністю
    double ddot(const Point3_& pt) const;
    //! кросс-множення двох 3D крапок
    Point3_ cross(const Point3_& pt) const;

    _Tp x, y, z; //< координати крапки
};

Клас шаблону для 3D крапок задає координати x, y та z . Екземпляр класу є взаємозамінним з структурою C CvPoint2D32f . Подібно до Point_ , координати крапки 3D можуть бути перетворені на інший тип. Векторна арифметика, а такоже оператори порівняння також підтримуються.

Наступні псевдоніми до Point3_<> доступні:

typedef Point3_<int> Point3i;
typedef Point3_<float> Point3f;
typedef Point3_<double> Point3d;

Size_

class Size_

template<typename _Tp> class CV_EXPORTS Size_
{
public:
    typedef _Tp value_type;

    //! різноманітні конструктори
    Size_();
    Size_(_Tp _width, _Tp _height);
    Size_(const Size_& sz);
    Size_(const CvSize& sz);
    Size_(const CvSize2D32f& sz);
    Size_(const Point_<_Tp>& pt);

    Size_& operator = (const Size_& sz);
    //! область (width*height)
    _Tp area() const;

    //! перетворення на інший тип даних.
    template<typename _Tp2> operator Size_<_Tp2>() const;

    //! перетворення на старі типи OpenCV
    operator CvSize() const;
    operator CvSize2D32f() const;

    _Tp width, height; // ширина та висота
};

Шаблонний клас для задання розміру зображення або прямокутника. Клас включає два члени, width та height. Структура може бути перетворена з та на старі структури OpenCV CvSize та CvSize2D32f . Той же набір операцій арифметичних та порівнянь, як і для Point_ є доступними.

OpenCV визначає наступні псевдоніми для Size_<>:

typedef Size_<int> Size2i;
typedef Size2i Size;
typedef Size_<float> Size2f;

Rect_

class Rect_

template<typename _Tp> class CV_EXPORTS Rect_
{
public:
    typedef _Tp value_type;

    //! різноманітні конструктори
    Rect_();
    Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height);
    Rect_(const Rect_& r);
    Rect_(const CvRect& r);
    Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz);
    Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2);

    Rect_& operator = ( const Rect_& r );
    //! верхній лівий кут
    Point_<_Tp> tl() const;
    //! нижній правий кут
    Point_<_Tp> br() const;

    //! size (width, height) прямокутника
    Size_<_Tp> size() const;
    //! площа (width*height) прямокутника
    _Tp area() const;

    //! перетворення на інший тип даних
    template<typename _Tp2> operator Rect_<_Tp2>() const;
    //! перетворення на старий CvRect
    operator CvRect() const;

    //! перевіряє, чи прямокутник містить крапку
    bool contains(const Point_<_Tp>& pt) const;

    _Tp x, y, width, height; //< верхній лівий кут, а також ширина та висота прямокутника
};

Шаблонний клас для прямокутників 2D, описує наступні параметри:

  • Координати верхнього лівого кута. Це інтерпретація по замовчанню для Rect_::x та Rect_::y в OpenCV. Однак в ваших алгоритмах ви можете вважати x та y в нижньому лівому куті. 
  • Ширина та висота прямокутника.

OpenCV звичайно вважає, що верхня та ліва границя включається, тоді як права та нижні - ні. Наприклад, метод Rect_::contains повертає true, якщо

x  \leq pt.x < x+width,
      y  \leq pt.y < y+height

Практично кожний цикл по зображенню ROI (Region Of Integest, тобто підмножина зображення) в OpenCV (де ROI задається як Rect_<int> ), реалізується наступним чином:

for(int y = roi.y; y < roi.y + rect.height; y++)
    for(int x = roi.x; x < roi.x + rect.width; x++)
    {
        // ...
    }

На додаток до членів класу, також реалізвані наступні операції для прямокутників:

  • \texttt{rect} = \texttt{rect} \pm \texttt{point} (зсув прямокутника на деяку відстань)
  • \texttt{rect} = \texttt{rect} \pm \texttt{size} (збільшення або зтискання прямокутника на деякий додаток)
  • rect += point, rect -= point, rect += size, rect -= size (операції корегування)
  • rect = rect1 & rect2 (вкладання прямокутника)
  • rect = rect1 | rect2 (мінімальна область, що містить обоє прямокутники)
  • rect &= rect1, rect |= rect1 (та відповідна операція корегування)
  • rect == rect1, rect != rect1 (порівняння прямокутників)

Цей приклад показує, як можна встановити деяку іхрархію прямокутників (rect1 \subseteq rect2):

template<typename _Tp> inline bool
operator <= (const Rect_<_Tp>& r1, const Rect_<_Tp>& r2)
{
    return (r1 & r2) == r1;
}

Для вашої зручності доступні такі псевдоніми Rect_<>:

typedef Rect_<int> Rect;

RotatedRect

class RotatedRect

class CV_EXPORTS RotatedRect
{
public:
    //! різноманітні конструктори
    RotatedRect();
    RotatedRect(const Point2f& center, const Size2f& size, float angle);
    RotatedRect(const CvBox2D& box);

    //! повертає 4 кути прямокутника
    void points(Point2f pts[]) const;
    //! повертає мінімальний верхній правий-нижній (звичайний) прямокутник, що містить обертаючийся прямокутник
    Rect boundingRect() const;
    //! перетворення на старий CvBox2D
    operator CvBox2D() const;

    Point2f center; //< центр маси прямокутника
    Size2f size;    //< ширина та висота прямокутника
    float angle;    //< кут обертання. Коли кут 0, 90, 180, 270 etc., прямокутник стає звичайним
};

Клас предствляє обернутий (тобто, не правий-нижній) прямокутник на площині. Кожний прямокутник задається через центральну крапку (центр маси), довжину кожної сторони (представленої структурою cv::Size2f) та вуглом нахилу в градусах.

C++: RotatedRect::RotatedRect()

C++: RotatedRect::RotatedRect(const Point2f& center, const Size2f& size, float angle)

C++: RotatedRect::RotatedRect(const CvBox2D& box)
Параметри:
  • center – Центр маси прямокутника.
  • size – Ширина та висота прямокутника.
  • angle – Вугол обертання за часоою стрілкою. Коли вугол є 0, 90, 180, 270 і так далі, прямокутник стає звичайним.
  • box – Параметри обернутого прямокутника як застаріла структура CvBox2D.
C++: void RotatedRect::points(Point2f pts[]) const

C++: Rect RotatedRect::boundingRect() const

C++: RotatedRect::operator CvBox2D() const
Параметри:
  • pts – Масив крапок, що містить вершини прямокутника.

Приклад нижче демонструє, як використовувати RotatedRect:

Mat image(200, 200, CV_8UC3, Scalar(0));
RotatedRect rRect = RotatedRect(Point2f(100,100), Size2f(100,50), 30);

Point2f vertices[4];
rRect.points(vertices);
for (int i = 0; i < 4; i++)
    line(image, vertices[i], vertices[(i+1)%4], Scalar(0,255,0));

Rect brect = rRect.boundingRect();
rectangle(image, brect, Scalar(255,0,0));

imshow("rectangles", image);
waitKey(0);
../../../_images/rotatedrect.png

Дивіться також:

CamShift() , fitEllipse() , minAreaRect() , CvBox2D

TermCriteria

class TermCriteria

class CV_EXPORTS TermCriteria
{
public:
    enum
    {
        COUNT=1, //!< максимальне число ітерацій або елементів до обчислення
        MAX_ITER=COUNT, //!< ditto
        EPS=2 //!< бажана точність або зміна в параметрах, по якому алгоритм зупиняється 
    };

    //! конструктор по замовчанню
    TermCriteria();
    //! повний конструктор
    TermCriteria(int type, int maxCount, double epsilon);
    //! перетворення з CvTermCriteria
    TermCriteria(const CvTermCriteria& criteria);
    //! перетворення на CvTermCriteria
    operator CvTermCriteria() const;

    int type; //!< тип критерії завершення: COUNT, EPS або COUNT + EPS
    int maxCount; // максимальне число ітерації/елементів
    double epsilon; // бажана точність
};

Клас визначає критерій припинення для ітеративних алгоритмів. Ви можете ініціалізувати його конструктором по замовчанню, та потім перевизначити любі параметри, або структура може бути повністю ініціалізована з використанням просунутого варіанту конструктора.

TermCriteria::TermCriteria

Конструктори.

C++: TermCriteria::TermCriteria()

C++: TermCriteria::TermCriteria(int type, int maxCount, double epsilon)

C++: TermCriteria::TermCriteria(const CvTermCriteria& criteria)
Параметри:
  • type – тип критерію завершення: TermCriteria::COUNT, TermCriteria::EPS or TermCriteria::COUNT + TermCriteria::EPS.
  • maxCount – Максимальне число ітерацій, або елементів обчислення.
  • epsilon – Бажана точність або зміна параметрів, коли ітерації повинно зупинити.
  • criteria – Критерій завершення в застарілому форматі CvTermCriteria.

TermCriteria::operator CvTermCriteria

Перетворює до застарілого формату CvTermCriteria.

C++: TermCriteria::operator CvTermCriteria() const

Matx

class Matx

Шаблонний клас для малих матриць, чиї тип та розмір відомі під час компіляції:

template<typename _Tp, int m, int n> class Matx {...};

typedef Matx<float, 1, 2> Matx12f;
typedef Matx<double, 1, 2> Matx12d;
...
typedef Matx<float, 1, 6> Matx16f;
typedef Matx<double, 1, 6> Matx16d;

typedef Matx<float, 2, 1> Matx21f;
typedef Matx<double, 2, 1> Matx21d;
...
typedef Matx<float, 6, 1> Matx61f;
typedef Matx<double, 6, 1> Matx61d;

typedef Matx<float, 2, 2> Matx22f;
typedef Matx<double, 2, 2> Matx22d;
...
typedef Matx<float, 6, 6> Matx66f;
typedef Matx<double, 6, 6> Matx66d;

Якщо вам треба більше гнучкий тип, використовуйте Mat . Елементи матриці M доступні в нотації M(i,j). Більшість з загальних операцій з матрицями також доступні. Щоб виконати операцію з Matx , що начасі не реалізована, ви можете просто перевторити матрицю на Mat, а потім назад.

Matx33f m(1, 2, 3,
          4, 5, 6,
          7, 8, 9);
cout << sum(Mat(m*m.t())) << endl;

Vec

class Vec

Шаблонний клас для коротких числових векторів, окремий випадок Matx:

template<typename _Tp, int n> class Vec : public Matx<_Tp, n, 1> {...};

typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;

typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;

typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;

typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;

typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

Можливо перетворити Vec<T,2> до/з Point_, Vec<T,3> до/з Point3_ , та Vec<T,4> на CvScalar або Scalar_. Використовуйте operator[] для доступу до елементів Vec.

Також реалізовані всі векторі операції:

  • v1 = v2 + v3
  • v1 = v2 - v3
  • v1 = v2 * scale
  • v1 = scale * v2
  • v1 = -v2
  • v1 += v2 and other augmenting operations
  • v1 == v2, v1 != v2
  • norm(v1) (euclidean norm)

Клас Vec загально викорисовується для піксельних типів та багато-канальних масивів. Дивіться Mat щодо деталей.

Scalar_

class Scalar_

Шаблонний клас для 4-елементного вектору, що походить від Vec.

template<typename _Tp> class CV_EXPORTS Scalar_ : public Vec<_Tp, 4>
{
public:
    //! різноманітні конструктори
    Scalar_();
    Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);
    Scalar_(const CvScalar& s);
    Scalar_(_Tp v0);

    //! повертає скаляр зі всіма елементами до v0
    static Scalar_<_Tp> all(_Tp v0);
    //! перетворення на старий CvScalar
    operator CvScalar() const;

    //! перетворення на інший тип даних
    template<typename T2> operator Scalar_<T2>() const;

    //! поелементне множення
    Scalar_<_Tp> mul(const Scalar_<_Tp>& t, double scale=1 ) const;

    // повертає (v0, -v1, -v2, -v3)
    Scalar_<_Tp> conj() const;

    // повертає true, якщо v1 == v2 == v3 == 0
    bool isReal() const;
};

typedef Scalar_<double> Scalar;

Походячи від Vec<_Tp, 4> , Scalar_ та Scalar можуть використовуватись як типові 4-елементні вектори. Додатково, вони можуть бути перетворені на/з CvScalar . Типовий Scalar широко використовується в OpenCV для передачі піксельних значень.

Range

class Range

Шаблонний клас, що вказує на під-послідовність (шматок) послідовності.

class CV_EXPORTS Range
{
public:
    Range();
    Range(int _start, int _end);
    Range(const CvSlice& slice);
    int size() const;
    bool empty() const;
    static Range all();
    operator CvSlice() const;

    int start, end;
};

Клас використовується для задання диапазону рядків або стовчиків в матриці ( Mat ), та в багатьох інших цілях. Range(a,b) загалом те ж саме, що і a:b в Matlab або a..b в Python. Як і в Python, start зліва включається, end зправа виключається з диапазону. Такий напів-відкритий інтервал звичано нотується як [start,end) .

Статичний метод Range::all() повертає спеціальну змінну, що означає “повна послідовність” або “повний диапазон”, як ” : ” в Matlab або ” ... ” в Python. Всі методи та фукнції в OpenCV, що сприймають Range, підтримують це спеціальне значення Range::all(). Але, звичайно, в випадку, коли ви самі робите обробку, ви будете виконувати перевірку та обробляти відповідно:

void my_function(..., const Range& r, ....)
{
    if(r == Range::all()) {
        // обробляємо всі дані
    }
    else {
        // обробляємо [r.start, r.end)
    }
}

Ptr

class Ptr

Шаблонний клас для розумних вказівників з підрахунком посилань

template<typename _Tp> class Ptr
{
public:
    // конструктор по замовчанню
    Ptr();
    // конструктор, що обертає вказівник на об'єкт
    Ptr(_Tp* _obj);
    // destructor: calls release()
    ~Ptr();
    // копіюючий конструктор; збільшує число посилань
    Ptr(const Ptr& ptr);
    // оператор присвоювання; зменшує власний лічильник посилань
    // (за допомогою release()) та збільшує лічильник посилань ptr's
    Ptr& operator = (const Ptr& ptr);
    // збільшує лічильник посилань
    void addref();
    // зменьшує лічильник посилань; коли він стає 0, викликається delete_obj()
    void release();
    // задана користувачем операція видалення. по замовчанню
    // викликається "delete obj;"
    void delete_obj();
    // повертає true якщо obj == 0;
    bool empty() const;

    // провадить доступ до полів та методів об'єкта
    _Tp* operator -> ();
    const _Tp* operator -> () const;

    // повертає підлеглий вказівник об'єкту;
    // завдяки методам Ptr<_Tp> може бути використаний замість _Tp*
    operator _Tp* ();
    operator const _Tp*() const;
protected:
    // інкапсульований вказівник об'єкта
    _Tp* obj;
    // асоційований лічильник посилань
    int* refcount;
};

Клас Ptr<_Tp> є шаблонний клас, що огортає вказівники відповідних типів. Це подібно до shared_ptr, що є частиною бібліотеки Boost (http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/shared_ptr.htm), а також частиною стандарта C++0x.

Цей клас провадить наступні опції:

  • Конструктор по замовчанню, та оператор присвоювання для довільного класу C++ або структури C. Для деякіх об'єктів, як файли, вікна, мьютекси, сокети, та інші, конструктор копіювання або оператор присвоювання складно визначити. Для деякіх інших об'єктів, як складні классифікатори в OpenCV, конструктори копіювання відсутні, та нелегкі до реалізації. Нарешті, деякі складні OpenCV та ваші структури даних можуть бути написані на C. Однак, конструктори копіювання та конструктори по замовчанню можуть значно спростити програмування. Крім цього, часто вони просто необхідні (наприклад, контейнерами STL). Обертаючи вказівник на такий складний об'єкт TObj в Ptr<TObj>, ви автоматично отримуєте всі потрібні конструктори та оператор присвоювання.
  • O(1) складність для зазначених вище операторів. Хоча деякі структури, як std::vector, провадять конструктор копіювання та оператор присвоювання, операції можуть зайняти помітний час, якщо структури даних великі. Але якщо структури покладені в Ptr<>, накладні розходи малі, та не залежать від розміру даних.
  • Автоматичне руйнування, навіть для структур C. Дивіться приклад нижче з FILE*.
  • Гетерогенні колекції об'єктів. Стандарт STL, та більшість інших контейнерів C++ та OpenCV можуть зберігати тільки об'єкти одного типу, та одного розміру. Класичне рішення зберігати об'єкти різних типів в одному контейнері, це зберігання вказівників на базовий клас base_class_t*, але тоді ви втрачаєте автоматичние керуквання пам'яттю. Знову, використання Ptr<base_class_t>() замість простих вказівників може вирішити цю проблему.

Клас Ptr трактує вкладений об'єкт як чорну скриньку. Лічильник посилань розміщений та керується окремо. Одна річ, яку треба знати класу вказівника про об'єкт, це як відвантажити його. Це знання інкапсульоване в методі Ptr::delete_obj(), що викликається, коли лічильник посилань стає 0. Якщо об'єкт є екземпляром класа C++, немає потреби в додатковому кодуванні, оскільки реалізація по замовчанню викликає delete obj;. Однак, якщо об'єкт відвантажується в інший спосіб, повинно створити спеціальний метод. Наприклад, якщо ви бажаєте огорнути FILE, delete_objможе бути реалізоване наступним чином :

template<> inline void Ptr<FILE>::delete_obj()
{
    fclose(obj); // немає потреби очищати вказівник після цього, це робиться ззовні.
}
...

// тепер використаємо це:
Ptr<FILE> f(fopen("myfile.txt", "r"));
if(f.empty())
    throw ...;
fprintf(f, ....);
...
// файл буде закритий автоматично, деструктором Ptr<FILE>.

Зауваження

Операції збільшення та зменшення лічильника посилань реалізовано як атомарні операції, і, таким чином, нормально безпечно використовувати класи в багатопоточних застосунках. Те ж вірно для Mat та інших C++ класів OpenCV, що оперують з лічильниками посилань.

Ptr::Ptr

Різні конструктори Ptr.

C++: Ptr::Ptr()

C++: Ptr::Ptr(_Tp* _obj)

C++: Ptr::Ptr(const Ptr& ptr)
Параметри:
  • _obj – Об'єкт для копіювання.
  • ptr – Об'єкт для копіювання.

Ptr::~Ptr

Деструктор Ptr.

C++: Ptr::~Ptr()

Ptr::operator =

Оператор присвоювання.

C++: Ptr& Ptr::operator=(const Ptr& ptr)
Параметри:
  • ptr – Об'єкт для копіювання.

Зменшує власний лічильник посилань (за допомогою release()) та збільшує лічильник посилань ptr.

Ptr::addref

Збільшує лічильник посилань.

C++: void Ptr::addref()

Ptr::release

Зменьшує лічильник посилань; коли він стає 0, викликається delete_obj().

C++: void Ptr::release()

Ptr::delete_obj

Задана користувачем операція видалення об'єкту. По замовчанню викликається delete obj;.

C++: void Ptr::delete_obj()

Ptr::empty

Повертає true, якщо obj == 0;

bool empty() const;

Ptr::operator ->

Провадить доступ до полів та методів об'єкту.

C++: template<typename _Tp> _Tp* Ptr::operator->()

C++: template<typename _Tp> const _Tp* Ptr::operator->() const

Ptr::operator _Tp*

Повертає підлеглий вказівник об'єкту. Завдяки методу, Ptr<_Tp>може бути використаний замість _Tp*.

C++: template<typename _Tp> Ptr::operator _Tp*()

C++: template<typename _Tp> Ptr::operator const _Tp*() const

Mat

class Mat

Клас OpenCV C++ n-вимірної щільної матриці

class CV_EXPORTS Mat
{
public:
    // ... безліч методів ...
    ...

    /*! включає декілька бітових полів:
         - магічну сигнатуру
         - флаг подовження
         - глубину
         - число каналів
     */
    int flags;
    //! розмірність масива, >= 2
    int dims;
    //! число рядків та стовпчиків або (-1, -1), коли масив більше двох вимірів
    int rows, cols;
    //! вказівник на дані
    uchar* data;

    //! вказівник на лічильник посилань;
    // коли масив вказує на розміщені користувачем дані, вказівник NULL
    int* refcount;

    // інші члени
    ...
};

Клас Mat представляє n-вимірну щільний числовий одноканальний або багатоканальний масив. Він може бути використаний для векторів або матриць для дійсних або комплексних значень, сірих або кольорових зображень, значень вокселів (об'ємних пікселів), компонент вектору, хмар з крапок, тензорів, гістограм (хоча дуже високо-вимірні гістограми краще зберігатимуться в SparseMat ). Розташування даних в матриці визначається масивом M.step[].

Зауважте, що M.step[i] >= M.step[i+1] (фактично, M.step[i] >= M.step[i+1]*M.size[i+1] ). Це означає, що двовимірні матриці зберігаються рядок за рядком, тривимірні площина за площиною, і так далі. M.step[M.dims-1] є мінімальним, та завжди дорівнює розміру елемента M.elemSize() .

Таким чином, розташування в Mat є повністю сумісним з типами CvMat, IplImage, та CvMatND з OpenCV 1.x. Воно також сумісне з більшістю типів щільних матриць зі стандартних тулкітів та SDK, таких як Numpy (ndarray), Win32 (незалежна бітова карта пристрою), та інші, тобто, з масивами, що використовують кроки (або стрибки) для обчислення позиції пікселів. Завдяки цій сумісності можливо зробити заголовок Mat для розміщених користувачем даних, та обробляти його на місці за допомогою функцій OpenCV.

Є багато різноманітних шляхів для створення об'єкта Mat. Найбільш популярні опції наступні:

  • Використання методу create(nrows, ncols, type) або подібного конструктора Mat(nrows, ncols, type[, fillValue]). Буде розміщено новий масив заданого розміру та типу. type має те ж значення, як і в методі cvCreateMat. Наприклад, CV_8UC1 означає 8-бітовий одноканальний масив, CV_32FC2 означає двоканальний (комплексний) масив з плаваючою точкою, і так далі.

    // створити комплексну матрицю 7x7, заповнену 1+3j.
    Mat M(7,7,CV_32FC2,Scalar(1,3));
    // та тепер перетворити M на 100x60 15-канальну 8-бітну матрицю.
    // Старий вміст буде утилізовано
    M.create(100,60,CV_8UC(15));
    

    Як зазначалось у вступі до цієї глави, create() розміщує тільки новий масив, коли розміри та тип поточного масиву відрізняється від вказаного. 

  • Створення багато-розмірного масиву:

    // створити 100x100x100 8-бітний масив
    int sz[] = {100, 100, 100};
    Mat bigCube(3, sz, CV_8U, Scalar::all(0));
    

    (ремарка незрозуміла, прим. перекл.) It passes the number of dimensions =1 to the Mat constructor but the created array will be 2-dimensional with the number of columns set to 1. So, Mat::dims is always >= 2 (can also be 0 when the array is empty).

  • Використання конструктуру копіювання або оператора присвоювання, де зправа масив або вираз (дивіться нижче). Як зазначено у вступі, присвоєння масиву є операцією O(1), оскільки вона копіює тільки заголовок, та збільшує лічильник посилань. Метод Mat::clone() може бути використаний для отримання повної (глибокої) копії масива, коли це потрібно.
  • Конструювання заголовоку для частини іншого масиву. Це може бути один рядок, один стовбчик, декільки рядків, декілька стовбчиків, прямокутна область масива (в алгебрі називається мінор) або діагональ. Такі операції є також O(1), оскільки новий заголовок посилається на існуючі дані. Ви можете насправді модифікувати частиму масива з використанням цієї можливості, наприклад:

    // додати 5-тий рядок, помножений на з, до 3-го рядка
    M.row(3) = M.row(3) + M.row(5)*3;
    
    // скопіювати колонку 7 в колонку 1
    // M.col(1) = M.col(7); // це не буде працювати
    Mat M1 = M.col(1);
    M.col(7).copyTo(M1);
    
    // створити зображення 320x240
    Mat img(Size(320,240),CV_8UC3);
    // обрати ROI (регіон, що нас цікавить)
    Mat roi(img, Rect(10,10,100,100));
    // заповнити його (0,255,0) (що в просторі RGB є зеленим);
    // оригінальне зображення буде модифіковане
    roi = Scalar(0,255,0);
    

    Завдяки додатковим членам datastart та dataend, можливо обчислити відносту позицію суб-масиву в головному контейнерному масиві, використовуючи locateROI():

    Mat A = Mat::eye(10, 10, CV_32S);
    // виділити стовпчики з A, 1 (включно) to 3 (виключно).
    Mat B = A(Range::all(), Range(1, 3));
    // виділити з B рядки, 5 (включно) to 9 (виключно).
    // тобто, C ~ A(Range(5, 9), Range(1, 3))
    Mat C = B(Range(5, 9), Range::all());
    Size size; Point ofs;
    C.locateROI(size, ofs);
    // розмір буде (width=10,height=10) та ofs (offset, відступ) буде (x=1, y=5)
    

    Як і в випадку цілих матриць, якщо вам треба глибока копія, використовуйте метод clone() на виділених суб-матрицях.

  • Створення заголовку для розміщених користувачем даних. Це може бути корисно для наступного:

    1. Обробка “чужинних” даних з використанням OpenCV (наприклад, коли ви реалізуєте фільтр DirectShow*, або обробляєте модуль для gstreamer, і так далі). Наприклад:

      void process_video_frame(const unsigned char* pixels,
                               int width, int height, int step)
      {
          Mat img(height, width, CV_8UC3, pixels, step);
          GaussianBlur(img, img, Size(7,7), 1.5, 1.5);
      }
      
    2. Швидко ініціалізувати невеликі матриці, та/або отримати над-швидкий доступ до елементу.

      double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
      Mat M = Mat(3, 3, CV_64F, m).inv();
      

    Частково дуже загальні випадки розміщених користувачем даних є перетворення з CvMat та IplImage до Mat. Для ціх цілей є спеціальні конструктори, що приймають вказівник на CvMat або IplImage, та опціональний флаг, що вказує, чи треба копіювати дані, або ні.

    Зворотнє перетворення з Mat до CvMat або IplImage провадиться через операцію кастингу Mat::operator CvMat() const та Mat::operator IplImage(). Оператори НЕ копіюють дані.

    IplImage* img = cvLoadImage("greatwave.jpg", 1);
    Mat mtx(img); // конвертувати IplImage* -> Mat
    CvMat oldmat = mtx; // конвертувати Mat -> CvMat
    CV_Assert(oldmat.cols == img->width && oldmat.rows == img->height &&
        oldmat.data.ptr == (uchar*)img->imageData && oldmat.step == img->widthStep);
    
  • Використання ініціалізаторів масивів в стилі MATLAB, zeros(), ones(), eye(), наприклад:

    // створити одиничну матрицю з подвійною точністю, та додати її до M.
    M += Mat::eye(M.rows, M.cols, CV_64F);
    
  • Використання ініціалізатора через кому:

    // створити одиничну матрицю 3x3 з подвійною точністю
    Mat M = (Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);
    

    З цім підходом, ви спершу викликаєте конструктор класу Mat_ з відповідними параметрами, та потім просто даєте оператор << з послідовністю розділених комою значень, що можуть бути константами, змінними, виразами, тощо. Також зауважте на додаткові дужки, що потрібні для запобігання помилок компіляції.

Коли масив створено, він автоматично керується через механізм підрахунку посилань. Якщо заголовок масиву побудований на даних користувача, ви повинні самі обробляти дані самостійно. Дані масива вивільняються, коли немає посилань на них. Якщо ви бажаєте вивільнити дані, на які вказує заголовок, перед тим, як буде викликаний деструктор, використовуйте Mat::release() .

Наступна важлива річ щодо класу масиву, це дступ до елементів. Ця інструкція вже описувала, як обчислюється адреса кожного елементу. Звичайно, вам не треба використовувати формулу прямо в коді. Якщо ви знаєте тип елеентів масива (що може бути отриманий з використанням методу Mat::type() ), ви можете отримати доступ до елементу двовимірного масиву так:

M.at<double>(i,j) += 1.f;

де M очікується є масив з подвійною точністю плаваючої крапки. Є декілька варіантів методу at для різного числа вимірів.

Якщо вам треба обробити цілий рядок двовимірної матриці, найбільш ефективним шляхом буде спершу отримати вказівник на рядок, та потім скористатись звичайним оператором C [] :

// обчислити суму позитивних елементів матриці
// (очікуємо, що M є матрицею з подвійною точністю)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
    const double* Mi = M.ptr<double>(i);
    for(int j = 0; j < M.cols; j++)
        sum += std::max(Mi[j], 0.);
}

Деякі операції, як наведена вище, насправді не залежать від форми матриці. Вони просто обробляють елементи матриці один за одним (або елементи з багатьох матриць, що мають ті ж координати, наприклад, при додаванні матриць). Такі операції називають поелементними. Має сенс перевірити, що всі вхідні/вихідні матриці безперервні, тобто не мають провалів в кінці кожного рядка. Якщо це так, обробляємо так, якби це був один рядок:

// обчислюємо суму позитивних елементів матриці, оптимізований варіант
double sum=0;
int cols = M.cols, rows = M.rows;
if(M.isContinuous())
{
    cols *= rows;
    rows = 1;
}
for(int i = 0; i < rows; i++)
{
    const double* Mi = M.ptr<double>(i);
    for(int j = 0; j < cols; j++)
        sum += std::max(Mi[j], 0.);
}

В випадку безперервної матриці зовнішній цикл виконається тільки один раз. Накладні розходи мешьші, що особливо помітно для малих матриць. (Насправді, ще оптимізованіше було б включити спрощений однорівневий цикл в if, а загальний варіант розмістити в else, прим перекл.)

Нарешті, є ітератори в стилі STL, що досить розумні для пропуску провалів між послідовними рядками:

// обчислити суму позитивних елементів матриці, на базі ітераторів 
double sum=0;
MatConstIterator_<double> it = M.begin<double>(), it_end = M.end<double>();
for(; it != it_end; ++it)
    sum += std::max(*it, 0.);

Матричні ітератори є ітератори з довільним доступом, так що вони можуть бути передані до любого алгоритму STL, включаючи std::sort() .

Зауваження

  • Приклад, що демонструє можливості послідовного виводу cv::Mat може бути знайдений в opencv_source_code/samples/cpp/cout_mat.cpp

Матричні вирази

Це перелік реалізованих матричних операцій, що можуть бути скомбіновані в вирази довільної складності (тут A, B означають матриці ( Mat ), sє скаляр  ( Scalar ), alpha є сталяр дійсного типу  ( double )):

  • Додавання, віднимання, зміна знаку: A+B, A-B, A+s, A-s, s+A, s-A, -A

  • Маштабування: A*alpha

  • Поелементне множення та ділення: A.mul(B), A/B, alpha/A

  • Множення матриць: A*B

  • Транспонування: A.t() (означає AT)

  • Інверсія матриць та псевдо-інверсія, рішення лінійних систем, та проблеми найменьших квадратів:

    A.inv([method]) (~ A-1) ,   A.inv([method])*B (~ X: AX=B)

  • Порівняння: A cmpop B, A cmpop alpha, alpha cmpop A, де cmpopодне з наступного  :  >, >=, ==, !=, <=, <. Результат порівняння є 8-бітна одноканальна маска, чий елементи встановлені в 255, якщо елемент або пара елементів задовільняє умові, та 0 якщо ні.

  • Побітові операції: A logicop B, A logicop s, s logicop A, ~A, де logicop є одним з наступного:  &, |, ^.

  • Поелементі мінімум та максимум: min(A, B), min(A, alpha), max(A, B), max(A, alpha)

  • Поелементі абосолютні значення: abs(A)

  • Крос-множення, крапка-множення: A.cross(B) A.dot(B)

  • Люба функція матриці або матриць та скалярів, що повертає матрицю або скаляр, такі як norm, mean, sum, countNonZero, trace, determinant, repeat, та інші.

  • Матричні ініціалізатори ( Mat::eye(), Mat::zeros(), Mat::ones() ), матричні розділені комою ініцііалізатори, матричні конструктори та оператори, що виокремлюють суб-матриці (дивіться опис Mat).

  • Mat_<destination_type>() конструктори, що приводять результат до потрібного типу.

Зауваження

Розділені комою ініціалізатори, та можливо інші операції, потребують додаткового явного виклику конструктора Mat() або Mat_<T>(), щоб розрішити можливі сумніви щодо типу.

Ось приклади виразів з матрицями:

// обчислюємо псевдо-інверсію A, еквівалент до A.inv(DECOMP_SVD)
SVD svd(A);
Mat pinvA = svd.vt.t()*Mat::diag(1./svd.w)*svd.u.t();

// обчислюємо новий вектор параметрів в алгоритмі Levenberg-Marquardt
x -= (A.t()*A + lambda*Mat::eye(A.cols,A.cols,A.type())).inv(DECOMP_CHOLESKY)*(A.t()*err);

// підсилюємо чіткість зображення з використанням алгоритму "маски нечіткості"
Mat blurred; double sigma = 1, threshold = 5, amount = 1;
GaussianBlur(img, blurred, Size(), sigma, sigma);
Mat lowConstrastMask = abs(img - blurred) < threshold;
Mat sharpened = img*(1+amount) + blurred*(-amount);
img.copyTo(sharpened, lowContrastMask);

Нижче наводять формальні дескриптори методів Mat.

Mat::Mat

Різноманітні конструктори Mat

C++: Mat::Mat()

C++: Mat::Mat(int rows, int cols, int type)

C++: Mat::Mat(Size size, int type)

C++: Mat::Mat(int rows, int cols, int type, const Scalar& s)

C++: Mat::Mat(Size size, int type, const Scalar& s)

C++: Mat::Mat(const Mat& m)

C++: Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)

C++: Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)

C++: Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all() )

C++: Mat::Mat(const Mat& m, const Rect& roi)

C++: Mat::Mat(const CvMat* m, bool copyData=false)

C++: Mat::Mat(const IplImage* img, bool copyData=false)

C++: template<typename T, int n> explicit Mat::Mat(const Vec<T, n>& vec, bool copyData=true)

C++: template<typename T, int m, int n> explicit Mat::Mat(const Matx<T, m, n>& vec, bool copyData=true)

C++: template<typename T> explicit Mat::Mat(const vector<T>& vec, bool copyData=false)

C++: Mat::Mat(int ndims, const int* sizes, int type)

C++: Mat::Mat(int ndims, const int* sizes, int type, const Scalar& s)

C++: Mat::Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)

C++: Mat::Mat(const Mat& m, const Range* ranges)
Параметри:
  • ndims – Розмірність масиву.
  • rows – Число рядків в 2D масиві.
  • cols – Число стовпчиків в 2D масиві.
  • roi – Region of interest, регіон зацікавленості.
  • size – Розмір 2D масиву: Size(cols, rows) . В конструкторі Size() число рядків та число стовпчиків ідуть в зворотньому порядку.
  • sizes – Масив цілих, що задає розміри n-вимірного масиву.
  • type – Тип масиву. Використовуйте CV_8UC1, ..., CV_64FC4 для створення 1-4 канальних матриць, або CV_8UC(n), ..., CV_64FC(n) для створення мульті-канальних матриць (до CV_CN_MAX каналів).
  • s – Опціональне значення для ініціалізації кожного елементу матриці.  Щоб встановити всі елементи матриці в окреме значення після створення, використовуйте оператор присвоювання Mat::operator=(const Scalar& value) .
  • data – Вказівник на користувацькі дані. Констркутори матриць, що сприймають параметри data та step, не розміщують дані матриці. Замість цього, вони тільки ініціалізують заголовок матриці, що вказує на вказані дані, що значить, що дані не будуть скопійовані. Ця операція дуже ефективна, та може використовуватись для обробки зовнішніх даних з використанням функцій OpenCV. Зовнішні дані не будуть автоматично вивільнятись, так що ви повинні потурбуватись про це.
  • step – Число байтів, яке займає кожний рядок матриці. Значення має включати байти вирівнювання в кінці кожного рядка, якщо такі є. Якщо параметр відсутній (встановлений в AUTO_STEP ), вирівнювання не очікується, на справжній крок обчислюється як cols*elemSize(). Дивіться Mat::elemSize() .
  • steps – Масив з ndims-1 кроків, в випадку багатовимірної матриці (останній крок завжди встановлений в розмір елементу). Якщо не вказано, матриця очікується як безперервна.
  • m – Масив що (цілком або частково) призначається створюваній матриці. Дані не копіюються ціми конструкторами. Замість цього створюється заголовок, що вказує на дані m або на суб-матрицю, та асоціюється з нею. Лічильник посилань, якщо є, збільшується. Таким чином. коли ви модифікуєте матрицю, що утворилась під час дії цього конструктора, ви також модифікуєте відповідні елементи m . Якщо ви бажаєте мати незалежну копію суб-масива, використовуйте Mat::clone() .
  • img – Вказівник на стару структуру зображення IplImage. По замовчанню, дані розділяються між оригінальним зображенням та новою матрицею. Але коли встановлений copyData, буде створена повна копія даних зображення.
  • vec – Вектор STL, чиї елементи формують матрицю. Матриця має один стовпчик та число рядків, що рівне числу елементів вектора. Тип матриці співпадає з типом елементів вектора. Конструктор може обробляти довільні типи, для яких є відповідно задекларований DataType . Це означає, що елементи вектора мусять бути примітивними числами, або юні-типовими кортежами з чисел. Структури змішеного типу не підтримуються. Оскільки вектори STLне конвертуються автоматично в екземпляри Mat, ви повинні писати Mat(vec) явно. Якщо ви явно не копіюєте дані в матрицю ( copyData=true ), нові елементи не будуть додані до вектора, оскільки це може потенційно викликати релокацію вектора, і, таким чином, вказівник на матрицю буде недійсним.
  • copyData – Флаг, що вказує, чи дані в форматі STL вектора, або старих CvMat або  IplImage повинно скопіювати (true) або розділені (false) з новоствореною матрицею. Коли дані копіюються, розміщений буфер обробляєься з використанням лічильника посилань Mat. Коли дані розділені, лічильник посилань є NULL, та ви не повинні знищцвати дані, поки не викликаний деструктор матриці.
  • rowRange – Диапазон рядків m. Як звичайно, початок диапазону включений, та кінець виключений. Використовуйте Range::all() щоб отримати всі рядки.
  • colRange – Диапазон стовпчиків m. Використовуйте  Range::all() для отримання всіх стовпчиків.
  • ranges – Масив з вибраних диапазонів m по кожному виміру.

Ці різні конструктори створюють матрицю. Як вказано в Автоматичне розташування вихідних даних, часто конструктора по замовчанню досить, та потрібна матриця буде розташована функцією OpenCV. Сконструйованії матриці може в подальшому бути присвоєна інша матриця, або матричний вираз, або може бути розміщена за допомогою Mat::create(). В останньому випадку старий вміст втратить всі посилання.

Mat::~Mat

Деструктор Mat.

C++: Mat::~Mat()

Деструктор матриці викликає Mat::release() .

Mat::operator =

Провадить оператор присвоєння матриці.

C++: Mat& Mat::operator=(const Mat& m)

C++: Mat& Mat::operator=(const MatExpr& expr)

C++: Mat& Mat::operator=(const Scalar& s)
Параметри:
  • m – Право-стороння матриця, що присвоюється. Присвоєння матриці - операція O(1), що означає сталий час, незалежно від кількості елементів. Це досягається створенням нового заголовку, копіюванням посилання на дані, та збільшенням лічильника посилань, якщо такій існує. Перед присвоєнням старе посилання знищується за допомогою Mat::release() .
  • expr – Об'єкт присвоєнної матриці. На відміну від першої форми, друга форма може повторно використати вже розташовану матрицю, якщо вона має потрібний розмір та тип, що підходять для результату. Це автоматично виконується дійсною функцією, до якої розширюється вираз. Наприклад, C=A+B перетворюється на add(A, B, C), та add() турбується про автоматичну релокацію C.
  • s – Скалар, присвоєний кожному елементу матриці. Розмір матриці та тип на не змінюються. 

Це всі доступні оператори присвоєння матриць. Оскльки вони не дуже різні, будьте впевнені, що ознайомились з описом параметрів оператору.

Mat::row

Створює заголовок матриці для окремого рядка.

C++: Mat Mat::row(int y) const
Параметри:
  • y – Індекс, починаючи з 0.

Метод створює новий заголовок для окремого рядка, та повертає його. Це операція O(1), що не залежить від роміру матриці. Підлеглі дані нової матриці поділяються з первинною матрицею. Ось приклад одного з класичних базових операторів з матрицями, axpy, що використовується в LU та багатьох інших алгоритмах:

inline void matrix_axpy(Mat& A, int i, int j, double alpha)
{
    A.row(i) += A.row(j)*alpha;
}

Зауваження: В поточній реалізації наступний код не робить як очікується:

Mat A;
...
A.row(i) = A.row(j); // це не робить

Це відбувається, тому що A.row(i) формує тимчасовий заголовок, що в подальшому присвоюється іншому заголовку. Пам'ятайте, що кожна з ціх операцій O(1), тобто дані не копіюються. Таким чином, присвоєння не є дійсним, якщо ви очікували присвоїти j-рядок до i-рядка. Щоб досягти цього вам треба або перетворити це просте присвоєння на вираз, або використовувати метод Mat::copyTo():

Mat A;
...
// робить, але виглядає трохи таємниче.
A.row(i) = A.row(j) + 0;

// це довше, але є рекомендованим методом.
A.row(j).copyTo(A.row(i));

Mat::col

Створює заголовок матриці для обраного стовпчика.

C++: Mat Mat::col(int x) const
Параметри:
  • x – Інедкс стовпчика, починаючи з 0.

Метод створює новий заголовок для вказаного стовпчика матриці, та повертає його. Це операція O(1), що не залежить від розміру матриці. Підлеглі дані нової матриці поділяються з оригінальною матрицею. Дивіться також опис Mat::row().

Mat::rowRange

Створює заголовок для певного диапазону рядків.

C++: Mat Mat::rowRange(int startrow, int endrow) const

C++: Mat Mat::rowRange(const Range& r) const
Параметри:
  • startrow – Індекс початку диапазону, починаючи з 0, включно.
  • endrow – Індекс кінці диапазону, виключно.
  • rRange структура, що містить індекси початкц і кінця.

Метод створює новий заголовок для вказаного диапазону рядків матриці. Подібно до Mat::row() та Mat::col() , це операція O(1).

Mat::colRange

Створює заголовок матриці для заданого диапазону стовпчиків.

C++: Mat Mat::colRange(int startcol, int endcol) const

C++: Mat Mat::colRange(const Range& r) const
Параметри:
  • startcol – Включний початковий індекс стовпчика, починаючи з 0.
  • endcol – Виключний, починаючи з 0, кінцевий індекс стовпчика.
  • rRange стркутура, що містить індекси початку та кінця.

Метод створює новий заголовок для сказанного диапазону стовпчиків. Подібно до Mat::row() та Mat::col() , це операція O(1).

Mat::diag

Виділяє диагональ з матриці, або створює диагональну матрицю.

C++: Mat Mat::diag(int d=0 ) const

C++: static Mat Mat::diag(const Mat& d)
Параметри:
  • d – Одно-стовпчикова матриця, що формує диагональ або індекс диагоналі, з наступними значеннями:
    • d=0 головна диагональ.
    • d>0 диагональ з нижньої половини. Наприклад, d=1 означає диагональ прямо нижче головної.
    • d<0 Диагональ з верхньої половини. Наприклад, d=1 означає диагональ прямо над головною.

Метод створює заголовок для вказаної диагоналі. Нова матриця представляється як один стовпчик. Подібно до Mat::row() та Mat::col() , це операція O(1).

Mat::clone

Створює повну копію масива та всіх даних.

C++: Mat Mat::clone() const

Метод створює повну копію масиву. Оригінальний step[] не береться до уваги. Таким чином, якщо матриця щільня, її розмір total()*elemSize()байт .

Mat::copyTo

Копіює матрицю в іншу.

C++: void Mat::copyTo(OutputArray m) const

C++: void Mat::copyTo(OutputArray m, InputArray mask) const
Параметри:
  • m – Матриця призначення. Якщо вона не має потрібного розміру або типу, вона буде релокована. 
  • mask – Маска операції. Її ненульові елементи вказують, які елементи треба копіювати.

Метод копіює дані матриці в іншу матрицю. Перед копіювання метод викликає 

m.create(this->size(), this->type());

Таким чином матриця призначення буде реалокована, якщо потрібно. Тоді як  m.copyTo(m); робить без питань, функція не обробляє випадок, коли матриці джерела та призачення частково перетинаються.

Коли задана матриця операції, якщо виклик Mat::create реалокує матрицю, нова матриця буде заповнена нулями перед копіювання даних.

Mat::convertTo

Перетворює матрицю на інший тип даних з опціональним маштабуванням.

C++: void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 ) const
Параметри:
  • m – вихідна матрия; якщо вона не має потрібний розмір або тип, вона буде реалокована.
  • rtype – потрібний тип вихідної матриці, або, глубина, якщо потрібне число каналів те є саме, що і на вході. Якщо rtype негативне, вихідна матриця матиме той же тип, що і вхідна.
  • alpha – опціональний коефіцієнт маштабування.
  • beta – опціональна дельта, що дадається до маштабованих значень.

Метод конвертує піксельні значення джерела на вихідний тип даних. saturate_cast<> додається наприкінці, щоб запобігти можливому переповненню:

m(x,y) = saturate \_ cast<rType>( \alpha (*this)(x,y) +  \beta )

Mat::assignTo

Провадить функціональну форму для convertTo.

C++: void Mat::assignTo(Mat& m, int type=-1 ) const
Параметри:
  • m – Цільовий масив.
  • type – Бажана глибина цільового масива (або -1, якщо вона має бути такою ж, як і для вхідної матриці).

Це метод, що використовується зсередини в Матричних виразах.

Mat::setTo

Встановлює всі або деякі з елементів матриці в задане значення.

C++: Mat& Mat::setTo(InputArray value, InputArray mask=noArray() )
Параметри:
  • value – Слаляр, приведений до дійсного значення матриці.
  • mask – Матриця операції такого ж розміру, що і *this. Це розширений варіант оператору Mat::operator=(const Scalar& s).

Mat::reshape

Змінює форму та/або число каналів для двомірної матриці без копіювання даних.

C++: Mat Mat::reshape(int cn, int rows=0) const
Параметри:
  • cn – Нове число каналів. Якщо параметр є 0, число каналів не змінюється.
  • rows – Нове число рядків. Якщо параметр 0, число рядків не змінюється.

Метод створює новий заголовок матриці для елементів *this. Нова матриця може мати інший розмір та/або число каналів. Любі комбінації можливі, якщо:

  • Нові елементи не включаються в нову матрицю, та жодні елементи не видаляються. Відповідно, rows*cols*channels() повинно залишатись тим же після перетворення.
  • Дані не копіюються, тобто це операція O(1). Відповідно, якщо ви зміните число рядків, або операція змінює індекси рядків якимось іншим шляхом, матриця повинна бути послідовною. Дивіться Mat::isContinuous() .

Наприклад, якщо є набір 3D крапок, що зберігаються як STL вектор, та ви бажаєте представити крапки як матриця 3xN, зробіть наступне:

std::vector<Point3f> vec;
...

Mat pointMat = Mat(vec). // перетворює вектор на Mat, операція O(1)
                  reshape(1). // робить з 1-канальної матриці Nx3 нову Nx1 3-канальну.
                              // також операція O(1)
                     t(); // транспонує матрицю Nx3. Це включає копіювання елементів. 

Mat::t

Транспонує матрицю.

C++: MatExpr Mat::t() const

Метод виконує транспозицію матриці для використання в матричних виразах. Він не виконує справжньої танспозиції, але повертає тимчасову матрицю, що в подальшому може використовуватись як частина більш складного матричного виразу, або може бути присвоєна матриці:

Mat A1 = A + Mat::eye(A.size(), A.type())*lambda;
Mat C = A1.t()*A1; // обчислює (A + lambda*I)^t * (A + lamda*I)

Mat::inv

Інвертує (обертає) матрицю.

C++: MatExpr Mat::inv(int method=DECOMP_LU) const
Параметри:
  • method

    Метод інверсії матриці. Можливі значення:

    • DECOMP_LU декомпозиція LU. Матриця повинна бути несингулярною.
    • DECOMP_CHOLESKY декомпозиція Cholesky, тільки для симметричних, позитивно визначених матриць. Цей тип приблизно в два рази швидший за LU на великих матрицях.
    • DECOMP_SVD декомпозиція SVD. Якщо матриця сингулярна, або навіть не квадратна, обчислюється псевдо інверсія.

Метод виконує інверсію матриці в термінах матричних виразів. Це означає, що метод повертає тимчасовий об'єкт інвертованої матриці, що може використовуватись в більш складних виразах, або може бути присвоєна матриці. 

Mat::mul

Виконує поелементне множенн або ділення двох матриць.

C++: MatExpr Mat::mul(InputArray m, double scale=1) const
Параметри:
  • m – Інший масив того ж типу та розміру, що і *this, або матричний вираз.
  • scale – Опціональний фактор маштабування.

Метод повертає тимчасовий об'єкт, що відповідає поелементному множенню. Зауважте, що це не множенн матриць, що відповідає оператору “*”.

Приклад:

Mat C = A.mul(5/B); // еквівалентно divide(A, B, C, 5)

Mat::cross

Обчислює кросс-множення двох 3-елементних векторів.

C++: Mat Mat::cross(InputArray m) const
Параметри:
  • m – Інший операнд для кросс-множення.

Метод обчислює кросс-множення двох 3-елементних векторів. Вектори мусять бути 3-елементними векторами з плаваючою крапкою, тієї ж форми та розміру. Результат також 3-елементний вектор того ж типу, що і операнди.

Mat::dot

Обчислює крапка-множення двох векторів.

C++: double Mat::dot(InputArray m) const
Параметри:
  • m – інший операнд крапка-множення.

Метод обчислює крапка-множення двох матриць. Якщо матриці не одно-стовпчикові або одно-рядкові вектори, використовується зверху-до низу, та зліва-на право  порядок сканування, щоб вважати іх за одномірні вектори. Вектори мають бути того ж розміру та типу. Якщо матриці мають більше одного каналу, крапкове множення підсумовує всі канали разом. 

Mat::zeros

Повертає масив з нулів вказаного розміру та типу.

C++: static MatExpr Mat::zeros(int rows, int cols, int type)

C++: static MatExpr Mat::zeros(Size size, int type)

C++: static MatExpr Mat::zeros(int ndims, const int* sz, int type)
Параметри:
  • ndims – Розмірність масиву.
  • rows – Число рядків.
  • cols – Число стовпчиків.
  • size – Альтернативний метод задати розмір матриці Size(cols, rows) .
  • sz – Масив з цілих, що задає форму матриці.
  • type – тип стовонюваної матриці.

Метод повертає матрицю в стилі Matlab, що ініціалізована нулями. Це може використано для швидкого формування сталої матриці як параметру функції, частину матричного виразу, або як ініціалізатор матриці.

Mat A;
A = Mat::zeros(3, 3, CV_32F);

В пркладі вище нова матриця розміщується тільки, якщо A не матриця 3x3 з плаваючую точкою. Інакше, існуюча матриця A буде заповнена нулями.

Mat::ones

Повертає масив з усіх 1, вказаного розміру та типу.

C++: static MatExpr Mat::ones(int rows, int cols, int type)

C++: static MatExpr Mat::ones(Size size, int type)

C++: static MatExpr Mat::ones(int ndims, const int* sz, int type)
Параметри:
  • ndims – Розмірність матриці.
  • rows – Число рядків.
  • cols – Число стовпчиків.
  • size – Альтеранивний спосіб задати розмір матриці Size(cols, rows) .
  • sz – Масив цілих, що вказує розмір масиву.
  • type – Створити матрицю цього типу.

Метод повертає ініціалізатор матриці в стилі одномірної Matlab, подібно до Mat::zeros(). Зауважте, що використовуючи цей метод, ви можете ініціалізувати масив з довільним значенням, з використанням ідіоми Matlab:

Mat A = Mat::ones(100, 100, CV_8U)*3; // створити матрицю 100x100 заповнену значенням 3.

Операція вище не створює матрицю 100x100 з 1, що помножені на 3. Замість цього  вона тільки пам'ятає фактом маштабування 3, та використовує його до насправді ініціалізатора матриці.

Mat::eye

Повертає одиничну матрицю вказаного розміру та типу.

C++: static MatExpr Mat::eye(int rows, int cols, int type)

C++: static MatExpr Mat::eye(Size size, int type)
Параметри:
  • rows – Число рядків.
  • cols – Число стовпчиків.
  • size – Альтернативний спосіб задати розмір матриці, як Size(cols, rows) .
  • type – Тип стоворюванюї матриці.

Метод повертає ініціалізатор одиничної матриці в стилі Matlab, подібно до Mat::zeros(). Подібно до Mat::ones(), ви можете використовувати операцію маштабування, щоб ефективно створити маштабовану одиничну матрицю:

// створити диагональну матрицю 4x4 з 0.1 по диагоналі.
Mat A = Mat::eye(4, 4, CV_32F)*0.1;

Mat::create

Створює нову матрицю, якщо потрібно.

C++: void Mat::create(int rows, int cols, int type)

C++: void Mat::create(Size size, int type)

C++: void Mat::create(int ndims, const int* sizes, int type)
Параметри:
  • ndims – Розмірність нової матриці.
  • rows – Нове число рядків.
  • cols – Нове число стовпчиків.
    • size – Альтернативний спосіб задати розмір: Size(cols, rows)
  • sizes – Масив цілих, що задає новий розмір матриці.
  • type – Новий тип матриці.

Це ключовий з нових методів Mat. Більшість з нового стилю функцій та методів OpenCV, що продукують матриці, викликають цей метод для кожної вихідної матриці. Цей метод реалізує наступний алгоритм:

  1. Якщо форма поточної матриці та тип співпадає з новим, повернутиь безпосередньо. Інакше видалити посилання на існуючи дані, викликавши Mat::release().
  2. Ініціалізувати новий заголовок.
  3. Розмістити нові дані з total()*elemSize() байт.
  4. Розмістити новий, асоційований з даними, лічильник, та встановити його в 1.

Така схема робить управління пам'яттю надійним та ефективним, та в той же час допомагає вам запобігти зайвого кодування. Це означає, що звичайно вам не треба явно розміщувати вихідні масиви, Тобто заміть друкувати таке:

Mat color;
...
Mat gray(color.rows, color.cols, color.depth());
cvtColor(color, gray, CV_BGR2GRAY);

ви просто пишете:

Mat color;
...
Mat gray;
cvtColor(color, gray, CV_BGR2GRAY);

оскільки cvtColor , так само як і більшість функцій OpenCV, викликають Mat::create() для внутрішньго створення вихідної матриці.

Mat::addref

Збільшує лічильник посилань.

C++: void Mat::addref()

Метод збільшує лічильник посилань, що асоційований з даними матриці. Якщо заголовок матриці вказує на зовнішній набір даних (дивіться Mat::Mat() ), лічильник посилань є NULL, та метод не має ефекту в цьому випадку. Звичайно, щоб запобігти втраті пам'яті, метод не має викликатись безпосередньо. Він викликається неявно, через оператор присвоєння матриці. Збільшення лічильника посилань є атомарною операцією на платформах, що підтримують це. Таким чином безпечно оперувати матрицями асинхронно в різних потоках.

Mat::release

Зменьшує лічильник посилань, та вивільняє матрицю в разі потреби.

C++: void Mat::release()

Метод зменшує лічильник посилань, асоційований з даними матриці, Коли лічильник посилань досягає нуля, дані матриці деалокуються (вивільняються), та вказивники на дані та лічильник посилань встановлюються в NULL. Якщо заголовок вказує на зовнішні дані (дивіться Mat::Mat() ), лічильник посилань є NULL, та метод не має жодної дії цьому випадку.

Цей метод може бути викликаний вручну, щоб примусово вивільнити дані матриці. Але оскільки цей метод автоматично викликається з деструктора, або любим іншим методом, що змінює вказівник на дані, це, зазвичай, не потрібне. Зменьшення лічильника посилань та перевірка на 0 є атомарною операцією на платформах, що підтримують це. Таким чином безпечно оперувати матрицями асинхронно в різних потоках.

Mat::resize

Змінює число рядків матриці.

C++: void Mat::resize(size_t sz)

C++: void Mat::resize(size_t sz, const Scalar& s)
Параметри:
  • sz – Нова кількість рядків.
  • s – Значеня, що отримують нові елементи.

Метод змінює число рядків матриці. Якщо матриця реалокується, зберігаються перші min(Mat::rows, sz) рядків. Метод емулює відповідні методи векторного класу STL.

Mat::reserve

Резервує простір для вказаної кількості рядків.

C++: void Mat::reserve(size_t sz)
Параметри:
  • sz – Число рядків.

Метод резервує простір для sz рядків. Якщо матриця вже має простір для щоб зберігати sz рядків, нвічого не відбувається. Якщо матриці реалокується, зберігаються перші Mat::rows. Метод емулює відповідний метод векторного класу STL.

Mat::push_back

Додає елементи в кінець матриці.

C++: template<typename T> void Mat::push_back(const T& elem)

C++: void Mat::push_back(const Mat& m)
Параметри:
  • elem – Елементи, що додаються.
  • m – додані рядки.

Метод додає один або більше елементів в кінець матриці. Вони емулюють відповідні методи векторного класу STL. Коли elem є Mat , його тип та число стовпчиків  повинни бути тими ж, що і матриця-контейнер.

Mat::pop_back

Видаляє елементи з кінця матриці.

C++: template<typename T> void Mat::pop_back(size_t nelems=1)
Параметри:
  • nelems – Число рядків, що видаляються. Якщо це більше, ніж загальне число рядків, виникає виключення.

Метод видаляє один або більше рядків з кінця матриці.

Mat::locateROI

Розміщує заголовок матриці зсередини батьківської матриці.

C++: void Mat::locateROI(Size& wholeSize, Point& ofs) const
Параметри:
  • wholeSize – Вихідний параметр, що містить розмір цілої матриці, яка включає *this як частину.
  • ofs – Вихідний параметр, що містить сзув *this в цілій матриці.

Після того, як ви виділили підматрицю з матриці з використанням Mat::row(), Mat::col(), Mat::rowRange(), Mat::colRange() , та інших, утворена матриця вказує на частину оригінальної матриці. Однак кожна субматриця містить інформацію (що представлена полями datastart та dataend),  що допомагає реконструвати розмір оригінальної матриці та позицію субматриці в оригінальній матриці. Метод locateROI робить саме це.

Mat::adjustROI

Підлаштовує розмір субматриці та позицію в батьківській матриці.

C++: Mat& Mat::adjustROI(int dtop, int dbottom, int dleft, int dright)
Параметри:
  • dtop – Зсуває верхівку субматриці догори.
  • dbottom – Зсуває низівку субматриці вниз.
  • dleft – Зсуває ліву границю субматриці вліво.
  • dright – Зсуває праву границю субматриці вправо.

Метод є доповненням до Mat::locateROI() . Типове використання ціх функцій є визначення позиції субматриці в батьківській матриці, та потім сзунути її деяким чином. Звичайно це потрібно для операцій фільтрування. коли тре прийняти до уваги також пікселі за межами ROI. Коли всі параметри методу позитивні, ROI буде зростати в усіх напрямках на задане значення, наприклад:

A.adjustROI(2, 2, 2, 2);

В цьому прикладі розмір матриці збільшується на чотири в кожному напрямку. Матрия отриимує 2 елементи зліва, зправа, зверху та знизу відповідно, що надає їй всі необхідні пікселі для фільтрування з ядром 5x5.

adjustROI змушує підлаштований ROI бути всередині батьківської матриці, тобто новий ROI обмежений границями батьківської матриці. Наприклад, якщо субматриця A розміщена на першому рядку батьківськох матриці, та ви викликаєте A.adjustROI(2, 2, 2, 2) , тоді це не дасть збільшення згори. 

Функція використовується внутрішньо в функціях фільтрування OpenCV, таких як filter2D() , морфологічних операція, тощо.

Також дивіться

copyMakeBorder()

Mat::operator()

Виділяє прямокутну суб-матрицю.

C++: Mat Mat::operator()(Range rowRange, Range colRange) const

C++: Mat Mat::operator()(const Rect& roi) const

C++: Mat Mat::operator()(const Range* ranges) const
Параметри:
  • rowRange – Початковий та останній (виключно) рядок суб-матриці. Для вибору всіх рядків використовуйте Range::all().
  • colRange – Початковий та останній (виключно) стовпчик суб-матриці. Щоб вибрати всі стовпчики використовуйте Range::all().
  • roi – Виділена матриця, задана прямокутником.
  • ranges – Масив обраних диапазонів по кожному з розмірностей.

Оператори створюють новий заголовок для заданого суб-масиву для *this . Вони є найбільш загальною формою для Mat::row(), Mat::col(), Mat::rowRange() та Mat::colRange(). Наприклад, A(Range(0, 10), Range::all()) еквівалентно до  A.rowRange(0, 10). Подібно до всіх попередніх, ці оператори O(1), тобто дані матриці не копіюються.

Mat::operator CvMat

Створює заголовок CvMatдля матриці .

C++: Mat::operator CvMat() const

Оператор створює заголовок CvMat для матриці без копіювання даних. Лічильник посилань не приймається до уваги цією операцією. Таким чином, ви повинні впевнитись, що оригінальна матриця не відвантажена, коли використовуєте заголовок CvMat. Оператор корисний для одночасного використання нового та старого OpenCV API, наприклад:

Mat img(Size(320, 240), CV_8UC3);
...

CvMat cvimg = img;
mycvOldFunc( &cvimg, ...);

де mycvOldFunc є функція написана для роботи з структурами даних OpenCV 1.x.

Mat::operator IplImage

Створює заголовок IplImage для матриці.

C++: Mat::operator IplImage() const

Оператор створює заголовок IplImage для матриці, без копіювання даних. Ви повинні впевнитись, що оригінальна матриця не відвантажена, доки використовується заголовок IplImage. Подібно до Mat::operator CvMat, оператор корисний для одночасного використання нового та старого OpenCV API.

Mat::total

Повертає загальне число елементів масива.

C++: size_t Mat::total() const

Метод повертає число елементів масива (число пікселів, якщо масив представляє зображення).

Mat::isContinuous

Повідомляє, чи є матриця послідовною або ні.

C++: bool Mat::isContinuous() const

Метод повертає true,  якщо елементи матриці зберігаються послідовно, без проміжків в кінці кожного рядка. Інакше повертається false. Очевидно, матриці 1x1 або 1xN завжди послідовні. Матриці, створені за допомогою Mat::create() завжди послідовні. Але якщо ви виділяєте частину матриці з використанням Mat::col(), Mat::diag() , і таке інше, або створюєте заголовок матриці для зовнішніх даних, такі матриці можуть перестати мати цю властивість.

Флаг послідовності зберігається як біт в полі Mat::flags, та обчислюється автоматично, коли ви конструююте заголовок матриці. Таким чином, перевірка послідовності дуже швидка операція, хоча теоретично вона може бути зроблена наступним чином:

// альтернативна реалізація Mat::isContinuous()
bool myCheckMatContinuity(const Mat& m)
{
    // повертає (m.flags & Mat::CONTINUOUS_FLAG) != 0;
    return m.rows == 1 || m.step == m.cols*m.elemSize();
}

Метод використується в декількох функціях OpenCV. В основному це поелементні операції (як арифметичні та логічні операції, математичні функції, альфа блендінг, перетворення кольорових просторів, та інші), що не залежать від геометрії зображення. Таким чином, якщо всі вихідні масиви послідовні, фукнції можуть оброблять їх як дуже довгі однорядкові вектори. Приклад нижче ілюструє, як може бути реалізована функція альфа-блендінгу.

template<typename T>
void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst)
{
    const float alpha_scale = (float)std::numeric_limits<T>::max(),
                inv_scale = 1.f/alpha_scale;

    CV_Assert( src1.type() == src2.type() &&
               src1.type() == CV_MAKETYPE(DataType<T>::depth, 4) &&
               src1.size() == src2.size());
    Size size = src1.size();
    dst.create(size, src1.type());

    // ось ідіома: перевірка на послідовність матриць,
    // та якщо це так, трактувати масиви як одномірні вектори
    if( src1.isContinuous() && src2.isContinuous() && dst.isContinuous() )
    {
        size.width *= size.height;
        size.height = 1;
    }
    size.width *= 4;

    for( int i = 0; i < size.height; i++ )
    {
        // коли матриці послідовні, зовнішній цикл проходить один раз
        const T* ptr1 = src1.ptr<T>(i);
        const T* ptr2 = src2.ptr<T>(i);
        T* dptr = dst.ptr<T>(i);

        for( int j = 0; j < size.width; j += 4 )
        {
            float alpha = ptr1[j+3]*inv_scale, beta = ptr2[j+3]*inv_scale;
            dptr[j] = saturate_cast<T>(ptr1[j]*alpha + ptr2[j]*beta);
            dptr[j+1] = saturate_cast<T>(ptr1[j+1]*alpha + ptr2[j+1]*beta);
            dptr[j+2] = saturate_cast<T>(ptr1[j+2]*alpha + ptr2[j+2]*beta);
            dptr[j+3] = saturate_cast<T>((1 - (1-alpha)*(1-beta))*alpha_scale);
        }
    }
}

Цей підхід, хоча і є дуже простий, може пришвидшити продуктівність простої операції з елементами на 10-20 відсотків, особливо якщо зображення скоріше невелике, а операція досить проста. 

Друга ідіома OpenCV в цій фукнкції, виклик Mat::create() для цільового масива, що розміщує цільовий масив, якщо він вже не має потрібного розміру і типу. Та оскільки новий масив завжди послідовний, вам все ще треба перевірити новий масив, оскільки Mat::create() не завжди розміщує нову матрицю. 

Mat::elemSize

Повертає розмір елементу матриці в байтах.

C++: size_t Mat::elemSize() const

Метод повертає розмір елементу матриці в байтах. Наприклад, якщо тип матриці CV_16SC3 , метод повертає  3*sizeof(short), тобто 6.

Mat::elemSize1

Повертає розмір кожного каналу елементу матриці в байтах.

C++: size_t Mat::elemSize1() const

Метод повертає розмір кожного каналу елементу матриці в байтах, тобто ігноруючи число каналів. Наприклад, якщо тип матриці CV_16SC3 , метод повертає sizeof(short), тобто 2.

Mat::type

Повертає тип елементів матриці .

C++: int Mat::type() const

Метод повертає тип елементів матриці. Це індентифікатор, сумісний з системою типів CvMat, як CV_16SC3 для 16-біт знакового 3-канального масиву, і так далі.

Mat::depth

Повертає глибину елементів матриці. 

C++: int Mat::depth() const

Метод повертає ідентифікатор глибини елементу матриці (тип кожного окремого елементу). Наприклад, для матриці з 16-бітними знаковими елементами метод поверне CV_16S . Повний список матричних типів містить наступні значення:

  • CV_8U - 8-біт беззнакові цілі ( 0..255 )
  • CV_8S - 8-біт знакові цілі ( -128..127 )
  • CV_16U - 16-біт беззнакові цілі ( 0..65535 )
  • CV_16S - 16-біт знакові цілі ( -32768..32767 )
  • CV_32S - 32-біт знакові цілі ( -2147483648..2147483647 )
  • CV_32F - 32-біт з плаваючою точкою ( -FLT_MAX..FLT_MAX, INF, NAN )
  • CV_64F - 64-біт з плаваючою точкою ( -DBL_MAX..DBL_MAX, INF, NAN )

Mat::channels

Повертає число каналів матриці.

C++: int Mat::channels() const

Метод повертає число каналів матриці.

Mat::step1

Повертає нормалізований крок.

C++: size_t Mat::step1(int i=0 ) const

Метод повертає крок матриці, поділений на Mat::elemSize1(). Це може бути корисно для швидкого доступу до довільного елементу матриці. 

Mat::size

Повертає розмір матриці.

C++: Size Mat::size() const

Метод повертає розмір матриці Size(cols, rows). Коли матриця має більше двох вимірів, повертається розмір (-1, -1).

Mat::empty

Повертає true,  якщо матриця не має елементів.

C++: bool Mat::empty() const
 

Метод повертає true, якщо Mat::total() є 0 або Mat::data є NULL. Завдяки методам pop_back() та  resize() M.total() == 0 не очікує M.data == NULL.

Mat::ptr

Повертає вказівник на вказаний рядок матриці.

C++: uchar* Mat::ptr(int i0=0)

C++: const uchar* Mat::ptr(int i0=0) const

C++: template<typename _Tp> _Tp* Mat::ptr(int i0=0)

C++: template<typename _Tp> const _Tp* Mat::ptr(int i0=0) const
Параметри:
  • i0 – Інщдекс рядка, починаючи з 0.

Метод повертає uchar* або типізований вказівник на вказаний рядок матриці. Дивіться приклад в Mat::isContinuous() щоб подивитись, як використовується цей метод.

Mat::at

Повертає посилання на вказаний елемент матриці.

C++: template<typename T> T& Mat::at(int i) const

C++: template<typename T> const T& Mat::at(int i) const

C++: template<typename T> T& Mat::at(int i, int j)

C++: template<typename T> const T& Mat::at(int i, int j) const

C++: template<typename T> T& Mat::at(Point pt)

C++: template<typename T> const T& Mat::at(Point pt) const

C++: template<typename T> T& Mat::at(int i, int j, int k)

C++: template<typename T> const T& Mat::at(int i, int j, int k) const

C++: template<typename T> T& Mat::at(const int* idx)

C++: template<typename T> const T& Mat::at(const int* idx) const
Параметри:
  • i – індекс по виміру 0
  • j – індекс по виміру 1
  • k – індекс по виміру 2
  • pt – позиція елементу як Point(j,i) .
  • idx – масив індексів Mat::dims.

Шаблонний метод, що повертає вказаний елемент матриці. Для більшої продуктивності перевірка індексів виконується тільки в конфігуації Debug.

Зауважте, що варіанти з одним індексом (i) можуть використовуватись для доступу до елементів одно-рядкових або одно-стовчикових двовимірних масивів. Таким чином, наприклад, якщо A  є 1 x N матриця з плаваючою крапкою, та B є M x 1 ціла матриця, ви можете просто записати A.at<float>(k+4) та B.at<int>(2*i+1) замість A.at<float>(0,k+4) та B.at<int>(2*i+1,0), відповідно.

Приклад нижче ініціалізує матрицю Ґілберта:

Mat H(100, 100, CV_64F);
for(int i = 0; i < H.rows; i++)
    for(int j = 0; j < H.cols; j++)
        H.at<double>(i,j)=1./(i+j+1);

Mat::begin

Повертає матричний ітератор, та встановлює його на перший елемент матриці. 

C++: template<typename _Tp> MatIterator_<_Tp> Mat::begin()

C++: template<typename _Tp> MatConstIterator_<_Tp> Mat::begin() const

Метод повертає матричні ітератори тільки для читання, або для читання-і-запису. Використання матричних ітераторів дуже подібне до використання дво-направлених ітераторів STL. В прикладі нижче функція альфа-блендінгу переписана за допомогою ітераторів:

template<typename T>
void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst)
{
    typedef Vec<T, 4> VT;

    const float alpha_scale = (float)std::numeric_limits<T>::max(),
                inv_scale = 1.f/alpha_scale;

    CV_Assert( src1.type() == src2.type() &&
               src1.type() == DataType<VT>::type &&
               src1.size() == src2.size());
    Size size = src1.size();
    dst.create(size, src1.type());

    MatConstIterator_<VT> it1 = src1.begin<VT>(), it1_end = src1.end<VT>();
    MatConstIterator_<VT> it2 = src2.begin<VT>();
    MatIterator_<VT> dst_it = dst.begin<VT>();

    for( ; it1 != it1_end; ++it1, ++it2, ++dst_it )
    {
        VT pix1 = *it1, pix2 = *it2;
        float alpha = pix1[3]*inv_scale, beta = pix2[3]*inv_scale;
        *dst_it = VT(saturate_cast<T>(pix1[0]*alpha + pix2[0]*beta),
                     saturate_cast<T>(pix1[1]*alpha + pix2[1]*beta),
                     saturate_cast<T>(pix1[2]*alpha + pix2[2]*beta),
                     saturate_cast<T>((1 - (1-alpha)*(1-beta))*alpha_scale));
    }
}

Mat::end

Повертає матричний ітератор, та встановює його за останнім елементом.

C++: template<typename _Tp> MatIterator_<_Tp> Mat::end()

C++: template<typename _Tp> MatConstIterator_<_Tp> Mat::end() const

Метод повертає матричні ітератори тільки для читання, або читання-і-запису, встановлені за останнім елементом.

Mat_

class Mat_

Шаблонний клас матриці, що походить від Mat .

template<typename _Tp> class Mat_ : public Mat
{
public:
    // ... деякі специфічні методи
    //         та
    // жодних додаткових полів
};

Клас Mat_<_Tp> є “тонкою” шаблонною огорткою над класом Mat. Він не має жодних додаткових полів. Ні йей клас, ні Mat не мають віртуальних методів. Таким чином, посилання або вказівники на ці два класи можуть бути вільно перетворені один на інший. Наприклад:

// створюємо матрицю 100x100 8-біт
Mat M(100,100,CV_8U);
// це компілюється гарно. конверсії даних не відбувається.
Mat_<float>& M1 = (Mat_<float>&)M;
// програма скоріше всього зазнає краху після наступного твердження
M1(99,99) = 1.f;

Хоча Mat достатньо в більшості випадків, Mat_ може бути більш зручним, якщо ви використовуєте багато операцій доступу до елементів, та якщо ви знаєте тип матриці під час коспіляції. Зауважте, що Mat::at<_Tp>(int y, int x) та  Mat_<_Tp>::operator ()(int y, int x) роблять абсолютно одне і те ж, але останній варіант коротший:

Mat_<double> M(20,20);
for(int i = 0; i < M.rows; i++)
    for(int j = 0; j < M.cols; j++)
        M(i,j) = 1./(i+j+1);
Mat E, V;
eigen(M,E,V);
cout << E.at<double>(0,0)/E.at<double>(M.rows-1,0);

Щоб використовувати Mat_ для багатоканальних зображень/матриць, передайте Vec як параметр Mat_:

// розмістити кольорове зображення 320x240 та заповнити зеленим (в просторі RGB)
Mat_<Vec3b> img(240, 320, Vec3b(0,255,0));
// намалювати диагональну білу лінію
for(int i = 0; i < 100; i++)
    img(i,i)=Vec3b(255,255,255);
// та тепер проставити другий (червоний) канал по всьому зображенню
for(int i = 0; i < img.rows; i++)
    for(int j = 0; j < img.cols; j++)
        img(i,j)[2] ^= (uchar)(i ^ j);

InputArray

class InputArray

Це проксі клас для передачі вхідних масивів тільки для читання в функції OpenCV. Він визначений наступним чином

typedef const _InputArray& InputArray;

де _InputArray є класом, що може може бути побудованим з Mat, Mat_<T>, Matx<T, m, n>, std::vector<T>, std::vector<std::vector<T> > або std::vector<Mat>. Він також може бути побудований як матричний вираз.

Оскільки це здебільшого клас рівня реалізації, та його інтерфейс може змінюватись в наступних реалізаціях, тому ми не описуємо його детально. Однак є декілька головних ключових речей, що треба тримати на увазі:

  • Коли ви бачите в посиланні або в початковому коді OpenCV функцію, що сприймає InputArray, це означає, що насправді ви можете передати Mat, Matx, vector<T> та таке інше. (повний перелік дивіться нижче).
  • Додаткові вхідні аргументи: Якщо вхідний масив може бути пустим, передайте cv::noArray() (або просто cv::Mat(), як ви можливо робили до цього).
  • Клас створено виключно для передачі параметрів. Тобто ви, зазвичай не повинні декларувати члени класів, локальні або глобальні змінні цього типу.
  • Якщо ви бажаєте розробити вашу власну функцію або метод класа, що може оперувати з масивами декількох типів, ви можете використовувати InputArray (бао OutputArray) для відповідних параметрів. Зсередини функції ви повинні використовувати метод _InputArray::getMat() для побудування заголовка матриці для масиву (без копіювання даних). _InputArray::kind() може використовуватись для розрізнення Mat та vector<> і таке інше, але звичайно це не потрібне.

Ось як ви можете використовувати функцію, що сприймає InputArray

std::vector<Point2f> vec;
// крапки або коло
for( int i = 0; i < 30; i++ )
    vec.push_back(Point2f((float)(100 + 30*cos(i*CV_PI*2/5)),
                          (float)(100 - 30*sin(i*CV_PI*2/5))));
cv::transform(vec, vec, cv::Matx23f(0.707, -0.707, 10, 0.707, 0.707, 20));

Таким чином ми формуємо STL вектор, що містить крапки, та застосовуємо афінну трансформацію по місцю до вектора з використанням матриці 2x3, що створена ін-лайн як екземпляр Matx<float, 2, 3>.

Ось як така функція може бути реализована (для спрощення ми реалізуємо дуже специфічний випадок, що закладений як допущення в твердженні всередені)

void myAffineTransform(InputArray _src, OutputArray _dst, InputArray _m)
{
    // отримати заголовки Mat для вхідних масивів. Це операція O(1),
    // якщо _src та/або _m не є матричними виразами.
    Mat src = _src.getMat(), m = _m.getMat();
    CV_Assert( src.type() == CV_32FC2 && m.type() == CV_32F && m.size() == Size(3, 2) );

    // [пере]створити вихідну матрицю, щоб вона мала потрібний тип та розмір.
    // В випадку Mat це викликає Mat::create, в випадку STL вектору це викликає vector::resize.
    _dst.create(src.size(), src.type());
    Mat dst = _dst.getMat();

    for( int i = 0; i < src.rows; i++ )
        for( int j = 0; j < src.cols; j++ )
        {
            Point2f pt = src.at<Point2f>(i, j);
            dst.at<Point2f>(i, j) = Point2f(m.at<float>(0, 0)*pt.x +
                                            m.at<float>(0, 1)*pt.y +
                                            m.at<float>(0, 2),
                                            m.at<float>(1, 0)*pt.x +
                                            m.at<float>(1, 1)*pt.y +
                                            m.at<float>(1, 2));
        }
}

Є інший пов'язаний тип, InputArrayOfArrays, що наразі визначений як синонім до InputArray:

typedef InputArray InputArrayOfArrays;

Він позначає аргументи фукнції, що є або векторами векторів, або векторами матриць. Окремий синонім потрібен для відповідного формування огорток для Python/Java та інших. На рівні реалізації його використання аналогічне, але _InputArray::getMat(idx) треба використовувати для отримання заголовку idx-того компоненту зовнішнього вектору, та використовувати _InputArray::size().area() для знаходження числа компонентів (векторів/матриць) зовнішнього вектора.

OutputArray

class OutputArray : public InputArray

Цей тип дуже подібний до InputArray , за виключенням того, що він використовується для вводу/виводу та вихідних параметрів функцій. Так само як InputArray, користувачі OpenCV не повинні турбуватись щодо OutputArray, вони тільки передають Mat, vector<T> таке інше до функції. Діють ті ж обмеження, що і для InputArray: Не створюйте екземпляри OutputArray власноруч діє і в цьому випадку.

Якщо ви бажаєте зробити вашу функцію поліморфною (тобто таку, що сприймає різні масиви як вихідні параметри), це також не дуже важко зробити. Прийміть той же приклад вище в якості взірця. Зауважте, що _OutputArray::create() потребує бути викликаною перед_OutputArray::getMat(). Таким чином ви гарантуєте, що вихідний масив буде розміщений певним чином. 

Опціональні вихідні параметри. Якщо вам не треба певний вихідний масив, щоб він був обчислений та повернутий, надішліть cv::noArray(), таким же чином, як і в випадку опціонального вхідного масиву. На рівні реалізації використовуйте _OutputArray::needed() для перевірки, чи певний масив потрібно обчислювати, чи ні.

Є декілька синонимів для OutputArray , що використовуються для допомоги в створенні автоматичних генераторів огорток для Python/Java/... :

typedef OutputArray OutputArrayOfArrays;
typedef OutputArray InputOutputArray;
typedef OutputArray InputOutputArrayOfArrays;

NAryMatIterator

class NAryMatIterator

n-арний багаторозмірний ітератор массивів.

class CV_EXPORTS NAryMatIterator
{
public:
    //! конструктор по замовчанню
    NAryMatIterator();
    //! повний конструктор, сприймає довільне число n-розмірних матриць
    NAryMatIterator(const Mat** arrays, Mat* planes, int narrays=-1);
    //! окремий метод ініціалізації ітератора
    void init(const Mat** arrays, Mat* planes, int narrays=-1);

    //! обробляє до наступної площини кожної матриці ітератора
    NAryMatIterator& operator ++();
    //! обробляє до наступної площини кожної матриці ітератора (постфістна операція інкременту)
    NAryMatIterator operator ++(int);

    ...
    int nplanes; // загальна кількість площин
};

Використовуйте цей клас для реалізації унарних, бінарних, та, загалом, n-арних поелементних операцій на багатовимірних масивах. Деякі з аргументів n-арних функцій можуть бути послідовними масивами, деякі можут не бути такими. Можливо використовувати звичайні MatIterator для кожного масиву, збільшення всіх ітераторів після кожної малої операції може бути великим навантаженням. В цьому випадку розгляньте можливість використання NAryMatIterator для ітерації по декільком матрицям одночасно, доки вони мають однакову геометрію (розмірність та всі розміри однакові). Він ітерує по шарах (або площинах), не по елементах, де “шар” є послідовною частиною масиву. На кожній ітерації it.planes[0], it.planes[1] , ... буде шаром відповідної матриці.

Приклад нижче ілюструє, як ви можете обчислити нормалізовану та обмежену 3D гістограму кольорів:

void computeNormalizedColorHist(const Mat& image, Mat& hist, int N, double minProb)
{
    const int histSize[] = {N, N, N};

    // дає впевненість, що гістограма матиме відповідний розмір та тип
    hist.create(3, histSize, CV_32F);

    // та очищує її
    hist = Scalar(0);

    // цикл нижче використовує факт, що зображення
    // є 8-бітним 3-канальним. перевірте це.
    CV_Assert(image.type() == CV_8UC3);
    MatConstIterator_<Vec3b> it = image.begin<Vec3b>(),
                             it_end = image.end<Vec3b>();
    for( ; it != it_end; ++it )
    {
        const Vec3b& pix = *it;
        hist.at<float>(pix[0]*N/256, pix[1]*N/256, pix[2]*N/256) += 1.f;
    }

    minProb *= image.rows*image.cols;

    // ініціалізуємо ітератор (стиль відмінний від STL).
    // після ініціалізації ітератор буде містити
    // число шарів або площин, через які буде проходити ітератор.
    // він одночасно збільшує для деякіх матриць
    // що надаються як завершений null список вказівників
    const Mat* arrays[] = {&hist, 0};
    Mat planes[1];
    NAryMatIterator itNAry(arrays, planes, 1);
    double s = 0;
    // ітерація по матриці. на кожній ітерації itNAry.planes[i] (типу Mat) буде 
// буде встановлене в поточну площину з
i-тої n-розмірної матриці,
// переданої в конструктор ітератора.
for(int p = 0; p < itNAry.nplanes; p++, ++itNAry) { threshold(itNAry.planes[0], itNAry.planes[0], minProb, 0, THRESH_TOZERO); s += sum(itNAry.planes[0])[0]; } s = 1./s; itNAry = NAryMatIterator(arrays, planes, 1); for(int p = 0; p < itNAry.nplanes; p++, ++itNAry) itNAry.planes[0] *= s; }

SparseMat

class SparseMat

Клас SparseMat презентує багатовимірних розріднених числових масивів. Такі розріднені масиви можуть зберігати елементи любого типу, який може зберігати Mat. Розріднені означає, що зберігаються тільки ненульові елементи (хоча в результаті операцій з розрідненими матрицями деякі зі збережених елеентів можеть насправді стати 0. Це покладається на вас, визначати такі елементи та визначати їх з використанням SparseMat::erase ). Ненульові елементи зберігаються в хеш таблиці, що збільшується, коли вона заповнюється, таким чином час пошуку в середньому O(1) (безвідносно від того, чи елемент там, чи ні). Елементи можуть бути доступні за допомогою наступних методів:

  • Операції запиту (SparseMat::ptr , та вищого рівня SparseMat::ref, SparseMat::value та SparseMat::find), наприклад:

    const int dims = 5;
    int size[] = {10, 10, 10, 10, 10};
    SparseMat sparse_mat(dims, size, CV_32F);
    for(int i = 0; i < 1000; i++)
    {
        int idx[dims];
        for(int k = 0; k < dims; k++)
            idx[k] = rand()
        sparse_mat.ref<float>(idx) += 1.f;
    }
    
  • Ітератори розріднених матриць. Вони подібні до MatIterator , але відрізняються від NAryMatIterator. Тобто цикл ітерації знайомий для користувачів STL:

    // друкує елементи розрідненої матриці з плаваючою крапкою
    // та підсумовує елементи.
    SparseMatConstIterator_<float>
        it = sparse_mat.begin<float>(),
        it_end = sparse_mat.end<float>();
    double s = 0;
    int dims = sparse_mat.dims();
    for(; it != it_end; ++it)
    {
        // друкує індекси та значення елементів
        const SparseMat::Node* n = it.node();
        printf("(");
        for(int i = 0; i < dims; i++)
            printf("%d%s", n->idx[i], i < dims-1 ? ", " : ")");
        printf(": %g\n", it.value<float>());
        s += *it;
    }
    printf("Сума елементів %g\n", s);
    

    Якщо ви виконаєте цикл, ви можете помітити, що елементи не пронумеровані в логічному порядку (лексографічному, або щось подібне). Вони ідуть в тому ж порядку, як вони зберігаються в хеш таблиці (умовно-довільному). Ви можете зберігати вказівники не вузли та відсортувати їх, щоб отримати бажане сортування. Однак зауважте, що вказівники на вузли можуть стати недійсними, коли ви додаєти більше елементів до матриці. Це може трапитись через можливу реалокацію буфера.

  • Комбінація двох методів вище, коли вам треба обробити дві або більше розріднених матриць одночасно. Наприклад, ось як можна обчислити ненормалізовану кросс-кореляцію з двох розріднених матриць з плаваючою крапкою:

    double cross_corr(const SparseMat& a, const SparseMat& b)
    {
        const SparseMat *_a = &a, *_b = &b;
        // якщо b містить меньше елементів, ніж a,
        // буде швидше ітерувати вздовж b
        if(_a->nzcount() > _b->nzcount())
            std::swap(_a, _b);
        SparseMatConstIterator_<float> it = _a->begin<float>(),
                                       it_end = _a->end<float>();
        double ccorr = 0;
        for(; it != it_end; ++it)
        {
            // візміть наступний елемент з першої матриці
            float avalue = *it;
            const Node* anode = it.node();
            // та спробувати знайти елемент з тим же індексом в другій матриці.
            // оскільки значення хешу залежить тільки від індексу елементу,
            // повторно використовуємо значення хешу, що зберігається у вузлі
            float bvalue = _b->value<float>(anode->idx,&anode->hashval);
            ccorr += avalue*bvalue;
        }
        return ccorr;
    }
    

SparseMat::SparseMat

Різні конструктори SparseMat.

C++: SparseMat::SparseMat()

C++: SparseMat::SparseMat(int dims, const int* _sizes, int _type)

C++: SparseMat::SparseMat(const SparseMat& m)

C++: SparseMat::SparseMat(const Mat& m)

C++: SparseMat::SparseMat(const CvSparseMat* m)
Параметри:
  • m – Первинна матриця для конструктору копіювання. Якщо m є щільною матрицею  (ocv:class:Mat), тоді вона буде перетворена на розріднену.
  • dims – Розмірність матриці.
  • _sizes – Розмір розрідненої матриці по всіх вимірах.
  • _type – Тип даних розрідненої матриці.

SparseMat::~SparseMat

Деструктор об'єкту SparseMat.

C++: SparseMat::~SparseMat()

SparseMat::operator=

Провадить операцію присвоєння розрідненої матриці.

C++: SparseMat& SparseMat::operator=(const SparseMat& m)

C++: SparseMat& SparseMat::operator=(const Mat& m)
Параметри:
  • m – Матриця для присвоєння.

Останній варіант еквівалентний до відповідного конструктора з try1d=false.

SparseMat::clone

Створює повну копію матриці.

C++: SparseMat SparseMat::clone() const

SparseMat::copyTo

Копіює всі дані в цільову матрицю. Цільова матриця буде реалокована в разі потреби.

C++: void SparseMat::copyTo(SparseMat& m) const

C++: void SparseMat::copyTo(Mat& m) const
Параметри:
  • m – Ціль для копіювання.

Останній варіант конвертує розріднену 1D або 2D матрицю в щільну 2D матрицю. Якщо розріднена матриця є 1D, результат буде одно-стовпчикова матриця.

SparceMat::convertTo

Конвертує розріднену матрицю, можливо зі зміною типу та маштабуванням.

C++: void SparseMat::convertTo(SparseMat& m, int rtype, double alpha=1 ) const

C++: void SparseMat::convertTo(Mat& m, int rtype, double alpha=1, double beta=0 ) const
Параметри:
  • m – Цільова матриця.
  • rtype – Цільовий тип даних.
  • alpha – Множник перетворення.

Перша версія конвертує довільну розріднену матрицю в щільну матрицю, та множить всі елементи матриці на вказаний скаляр. Друга версія конвертує розрізнену матрицю з опціональним перетворенням типу та маштабуванням. Якщо rtype=-1, цільовий тип елементу буде таким же, що і тип елементу розрідненої матриці. Інакше rtype буде задавати глибину, та число каналів буде залишатись тим же, що і в розрідненій матриці. 

SparseMat:create

Реалокує розріднену матрицю. Якщо вона вже має відповідний розмір та тип, вона просто очищується за допомогою clear(), інакше стара матриця вивільняється (за допомогою release()), та розміщується нова.

C++: void SparseMat::create(int dims, const int* _sizes, int _type)
Параметри:
  • dims – Розмірність матриці.
  • _sizes – Розмір розрідненої матриці за всіма вимірами.
  • _type – Тип даних розрідненої матриці.

SparseMat::clear

Встановлює всі елементи матриці в 0, що означає очищення хеш таблиці.

C++: void SparseMat::clear()

SparseMat::addref

Вручну збільшує лічильник посилань на заголовок.

C++: void SparseMat::addref()

SparseMat::release

Зменшує лічильник посилань на заголовок, що він досягає 0. Заголовок та всі підлеглі дані деалокуються.

C++: void SparseMat::release()

SparseMat::CvSparseMat *

Конвертує розріднену матрицю в старий вигляд. Всі елементи копіюються.

C++: SparseMat::operator CvSparseMat*() const

SparseMat::elemSize

Розмір кожного елементу в байтах (вузли матриці будуть більшими, дякуючи індексам елементів та іншим елементам SparseMat::Node).

C++: size_t SparseMat::elemSize() const

SparseMat::elemSize1

elemSize()/channels().

C++: size_t SparseMat::elemSize() const

SparseMat::type

Повертає тип елементів матриці.

C++: int SparseMat::type() const

Метод повертає тип елементів розрідненої матриці. Це ідентифікатор, сумісний з системою типів CvMat, як  CV_16SC3 для 16-бітного знакового 3-канального масиву, і таке інше.

SparseMat::depth

Повертає глибину елементу розрідненої матриці.

C++: int SparseMat::depth() const

Метод повертає ідентифікатор глибини елементу матриці (тип для кожного індивідуального каналу). Наприклад, для 16-бітного знакового 3-канального масиву метод повертає CV_16S

  • CV_8U - 8-біт беззнакові цілі ( 0..255 )
  • CV_8S - 8-біт знакові цілі ( -128..127 )
  • CV_16U - 16-біт беззнакові цілі ( 0..65535 )
  • CV_16S - 16-біт знакові цілі ( -32768..32767 )
  • CV_32S - 32-біт знакові цілі ( -2147483648..2147483647 )
  • CV_32F - 32-біт числа з плаваючою крапкою ( -FLT_MAX..FLT_MAX, INF, NAN )
  • CV_64F - 64-біт числа з плаваючою крапкою ( -DBL_MAX..DBL_MAX, INF, NAN )

SparseMat::channels

Повертає число каналів матриці.

C++: int SparseMat::channels() const

Метод повертає число каналів матриці.

SparseMat::size

Повертає масив розмірів або розмір матриці по виміру i та 0 , якщо матриця не розміщена.

C++: const int* SparseMat::size() const

C++: int SparseMat::size(int i) const
Параметри:
  • i – Індекс розмірності.

SparseMat::dims

Повертає матрицю розмірностей.

C++: int SparseMat::dims() const

SparseMat::nzcount

Повертає число ненульових елементів.

C++: size_t SparseMat::nzcount() const

SparseMat::hash

Обчислює хеш значення для елемента за його індексом.

C++: size_t SparseMat::hash(int i0) const

C++: size_t SparseMat::hash(int i0, int i1) const

C++: size_t SparseMat::hash(int i0, int i1, int i2) const

C++: size_t SparseMat::hash(const int* idx) const
Параметри:
  • i0 – Індекс першої розмірності.
  • i1 – Індекс другої розмірності.
  • i2 – Індекс третьої розмірності.
  • idx – Масив індексів для багатовимірних матриць.

SparseMat::ptr

Низькорівневі функції поелементного доступу, спеціальні варіанти для випадків 1D, 2D, 3D, та загальний варіант для випадку n-D.

C++: uchar* SparseMat::ptr(int i0, bool createMissing, size_t* hashval=0)

C++: uchar* SparseMat::ptr(int i0, int i1, bool createMissing, size_t* hashval=0)

C++: uchar* SparseMat::ptr(int i0, int i1, int i2, bool createMissing, size_t* hashval=0)

C++: uchar* SparseMat::ptr(const int* idx, bool createMissing, size_t* hashval=0)
Параметри:
  • i0 – Індекс першої розмірності.
  • i1 – Індекс другої розмірності.
  • i2 – Індекс третьої розмірності.
  • idx – Масив індексів для багатовимірних матриць.
  • createMissing – Створити новий елемент зі значенням 0, якщо він не існує в SparseMat.

Повертає вказівник на елемент матриці. Якщо елемент вже є (ненульовий), повертається вказівник на нього. Якщо такого немає, та createMissing=false, повертається вказівник NULL. Якщо такого немає, та createMissing=true, створюється новий елемент та ініціалізується в 0. Вказівник на нього не повертається. Якщо опціональний вказівник не є NULL, значення хешу для елементу не обчислюється, але замість нього береться  hashval.

SparseMat::erase

Очищує вказаний елемент матриці. Коли такого елементу немає, метод нічого не робить. 

C++: void SparseMat::erase(int i0, int i1, size_t* hashval=0)

C++: void SparseMat::erase(int i0, int i1, int i2, size_t* hashval=0)

C++: void SparseMat::erase(const int* idx, size_t* hashval=0)
Параметри:
  • i0 – Індекс першої розмірності.
  • i1 – Індекс другої розмірності.
  • i2 – Індекс третьої розмірності.
  • idx – Масив з індексів для багатовимірних матриць.

SparseMat_

class SparseMat_

Шаблонний клас розрідненої n-вимірної матриці, що походить від SparseMat

template<typename _Tp> class SparseMat_ : public SparseMat
{
public:
    typedef SparseMatIterator_<_Tp> iterator;
    typedef SparseMatConstIterator_<_Tp> const_iterator;

    // конструктори;
    // Створена матриця буде мати тип даних type = DataType<_Tp>::type
    SparseMat_();
    SparseMat_(int dims, const int* _sizes);
    SparseMat_(const SparseMat& m);
    SparseMat_(const SparseMat_& m);
    SparseMat_(const Mat& m);
    SparseMat_(const CvSparseMat* m);
    // оператори присвоювання; конверсія типів даних відбувається в разі потреби
    SparseMat_& operator = (const SparseMat& m);
    SparseMat_& operator = (const SparseMat_& m);
    SparseMat_& operator = (const Mat& m);

    // еквівалент до відповідних методів батьківського класу
    SparseMat_ clone() const;
    void create(int dims, const int* _sizes);
    operator CvSparseMat*() const;

    // перекриті методи, що виконують додаткову перевірку типів даних
    int type() const;
    int depth() const;
    int channels() const;

    // більш зручні операції доступу до елементів.
    // ref() відійшло в історію (але специфікація <_Tp> більше не потрібна);
    // оператор () еквівалентний до SparseMat::value<_Tp>
    _Tp& ref(int i0, size_t* hashval=0);
    _Tp operator()(int i0, size_t* hashval=0) const;
    _Tp& ref(int i0, int i1, size_t* hashval=0);
    _Tp operator()(int i0, int i1, size_t* hashval=0) const;
    _Tp& ref(int i0, int i1, int i2, size_t* hashval=0);
    _Tp operator()(int i0, int i1, int i2, size_t* hashval=0) const;
    _Tp& ref(const int* idx, size_t* hashval=0);
    _Tp operator()(const int* idx, size_t* hashval=0) const;

    // ітератори
    SparseMatIterator_<_Tp> begin();
    SparseMatConstIterator_<_Tp> begin() const;
    SparseMatIterator_<_Tp> end();
    SparseMatConstIterator_<_Tp> end() const;
};

SparseMat_ є тонкою огорткою зверху SparseMat , створеною аналогічно Mat_ . Це спрощує нотацію деяких операцій.

int sz[] = {10, 20, 30};
SparseMat_<double> M(3, sz);
...
M.ref(1, 2, 3) = M(4, 5, 6) + M(7, 8, 9);

Algorithm

class Algorithm

class CV_EXPORTS_W Algorithm
{
public:
    Algorithm();
    virtual ~Algorithm();
    string name() const;

    template<typename _Tp> typename ParamType<_Tp>::member_type get(const string& name) const;
    template<typename _Tp> typename ParamType<_Tp>::member_type get(const char* name) const;

    CV_WRAP int getInt(const string& name) const;
    CV_WRAP double getDouble(const string& name) const;
    CV_WRAP bool getBool(const string& name) const;
    CV_WRAP string getString(const string& name) const;
    CV_WRAP Mat getMat(const string& name) const;
    CV_WRAP vector<Mat> getMatVector(const string& name) const;
    CV_WRAP Ptr<Algorithm> getAlgorithm(const string& name) const;

    void set(const string& name, int value);
    void set(const string& name, double value);
    void set(const string& name, bool value);
    void set(const string& name, const string& value);
    void set(const string& name, const Mat& value);
    void set(const string& name, const vector<Mat>& value);
    void set(const string& name, const Ptr<Algorithm>& value);
    template<typename _Tp> void set(const string& name, const Ptr<_Tp>& value);

    CV_WRAP void setInt(const string& name, int value);
    CV_WRAP void setDouble(const string& name, double value);
    CV_WRAP void setBool(const string& name, bool value);
    CV_WRAP void setString(const string& name, const string& value);
    CV_WRAP void setMat(const string& name, const Mat& value);
    CV_WRAP void setMatVector(const string& name, const vector<Mat>& value);
    CV_WRAP void setAlgorithm(const string& name, const Ptr<Algorithm>& value);
    template<typename _Tp> void setAlgorithm(const string& name, const Ptr<_Tp>& value);

    void set(const char* name, int value);
    void set(const char* name, double value);
    void set(const char* name, bool value);
    void set(const char* name, const string& value);
    void set(const char* name, const Mat& value);
    void set(const char* name, const vector<Mat>& value);
    void set(const char* name, const Ptr<Algorithm>& value);
    template<typename _Tp> void set(const char* name, const Ptr<_Tp>& value);

    void setInt(const char* name, int value);
    void setDouble(const char* name, double value);
    void setBool(const char* name, bool value);
    void setString(const char* name, const string& value);
    void setMat(const char* name, const Mat& value);
    void setMatVector(const char* name, const vector<Mat>& value);
    void setAlgorithm(const char* name, const Ptr<Algorithm>& value);
    template<typename _Tp> void setAlgorithm(const char* name, const Ptr<_Tp>& value);

    CV_WRAP string paramHelp(const string& name) const;
    int paramType(const char* name) const;
    CV_WRAP int paramType(const string& name) const;
    CV_WRAP void getParams(CV_OUT vector<string>& names) const;


    virtual void write(FileStorage& fs) const;
    virtual void read(const FileNode& fn);

    typedef Algorithm* (*Constructor)(void);
    typedef int (Algorithm::*Getter)() const;
    typedef void (Algorithm::*Setter)(int);

    CV_WRAP static void getList(CV_OUT vector<string>& algorithms);
    CV_WRAP static Ptr<Algorithm> _create(const string& name);
    template<typename _Tp> static Ptr<_Tp> create(const string& name);

    virtual AlgorithmInfo* info() const /* TODO: make it = 0;*/ { return 0; }
};

Це базовий клас для всіх більш або менш складних алгоритмів OpenCV, особливо для класів алгоритмів, для яких може існувати декілька реалізацій. Прикладами є стерео відповідність (для чого є алгоритми як порівняння блоків, напів-глобальне порівняння блоків, відсікання графів, та інші), видалення фону (що робиться з використанням моделей змішаних гаусіанів, алгоритмів на основі кодових книжок, та інші), оптичного потоку (порівняння блоків, Lucas-Kanade, Horn-Schunck та інші).

Клас провадить наступні можливості для всіх похідних класів:

  • так званий “віртуальний конструктор”. Тобто любий похідний від Algorithm клас реєструється на початку програми, та ви можете отримати перелік зареєстрованих алгоритмів та створити екземпляр окремого алгоритма по його імені (дивіться Algorithm::create). Якщо ви плануєте додати ваші власні алгоритми, буде гарною практикою додати унікальний префікс до ваших алгоритмів, щоб відрізняти їх від інших.
  • встановлення/отримання параметрів алгоритмів по імен. Якщо ви використовуєте фукнціональність відео з модуля OpenCV highgui, ви можете бути знайомі з cvSetCaptureProperty(), cvGetCaptureProperty(), VideoCapture::set() та VideoCapture::get(). Algorithm провадить подібний метод, де замість цілих id ви вказуєте імена параметрів як текстові рядки. Дивіться Algorithm::set та  Algorithm::get щодо деталей.
  • читання та запис параметрів до файлів XML або YAML. Кожний похідний від Algorithm клас може зберігати всі свої параметри, та потім читати їх. Немає потреби реалізовувати це кожного разу. 

Ось приклад використання SIFT в вашому застосунку через інтерфейс Algorithm:

#include "opencv2/opencv.hpp"
#include "opencv2/nonfree/nonfree.hpp"

...

initModule_nonfree(); // завантаження SURF/SIFT 

Ptr<Feature2D> sift = Algorithm::create<Feature2D>("Feature2D.SIFT");

FileStorage fs("sift_params.xml", FileStorage::READ);
if( fs.isOpened() ) // якщо є файл з параметрами, завантажуємо його
{
    sift->read(fs["sift_params"]);
    fs.release();
}
else // інакше модифікуємо параметри та зберігаємо їх; користувач може потім відредагувати файл щоб змінити параметри
{
    sift->set("contrastThreshold", 0.01f); // зменшуємо межу контрасту, порівняно з значенням по замовчанню

    {
    WriteStructContext ws(fs, "sift_params", CV_NODE_MAP);
    sift->write(fs);
    }
}

Mat image = imread("myimage.png", 0), descriptors;
vector<KeyPoint> keypoints;
(*sift)(image, noArray(), keypoints, descriptors);

Algorithm::name

Повертає ім'я алгоритму

C++: string Algorithm::name() const

Algorithm::get

Повертає параметр алгоритму

C++: template<typename _Tp> typename ParamType<_Tp>::member_type Algorithm::get(const string& name) const
Параметри:
  • name – Ім'я параметру.

Метод повертає значення окремого параметру. Оскільки компілятор не може визначити тип повертаємого параметру, ви маєте вказати його явно в кутових дужках. Ось доступні форми get:

  • myalgo.get<int>(“param_name”)
  • myalgo.get<double>(“param_name”)
  • myalgo.get<bool>(“param_name”)
  • myalgo.get<string>(“param_name”)
  • myalgo.get<Mat>(“param_name”)
  • myalgo.get<vector<Mat> >(“param_name”)
  • myalgo.get<Algorithm>(“param_name”) (повертає Ptr<Algorithm>).

В деяких випадках дійсний тип параметру може бути приведений до вказаного типу, тобто цілий параметр може бути приведений до подвійної точності, bool може бути приведений до int. Але “небезпечні” перетворення (рядок<->число, double->int, 1x1 Mat<->число, ...) не виконуються, та метод буде викликати виключення. В випадку параметрів Mat або vector<Mat> метод не клонує дані матриці, і, таким чином, не модифікує матриці. Замість цього використовуйте Algorithm::set - повільніше, але більш безпечно.

Algorithm::set

Встановлює параметр алгоритму

C++: void Algorithm::set(const string& name, int value)

C++: void Algorithm::set(const string& name, double value)

C++: void Algorithm::set(const string& name, bool value)

C++: void Algorithm::set(const string& name, const string& value)

C++: void Algorithm::set(const string& name, const Mat& value)

C++: void Algorithm::set(const string& name, const vector<Mat>& value)

C++: void Algorithm::set(const string& name, const Ptr<Algorithm>& value)
Параметри:
  • name – Ім'я параметру.
  • value – Значення параметру.

Метод встановлює значення окремого параметру. Деякі з параметрів алгоритму можуть бути відмічені як тільки для читання. Якщо ви спробуєте встановити такий параметр, ви отримаєте виключення з відповідним повідомленням.

Algorithm::write

Зберігає параметри алгоритму в файловому сховищі

C++: void Algorithm::write(FileStorage& fs) const
Параметри:
  • fs – Файлове сховище.

Метод зберігає всі параметри алгоритму (в алфавітному порядку) в файлове сховище. Метод є віртуальним. Якщо ви визначаєте власний похідний від Algorithm derivative, ви можете перевизначити метод за зберігати деяку додаткову інформацію. Однак це рідко потрібне. Ось деякі приклади:

  • детектор особливостей SIFT (з модуля nonfree). Цей клас зберігає тільки параметри алгоритму, але не ключові крапки або іхні дескриптори. Таким чином, достатньо зберігати тільки параметри алгоритму, що і робить Algorithm::write(). Таким чином немає особливого SIFT::write().
  • Видалення фону (з модуля video). Він має параметри алгоритму, а також поточну подель фону. Однак модель фону не зберігаєтся. По-перше вона досить велика. Потім, якщо ви навіть маєте збережену модель фону, вона буде невідповідною при наступному запуску (завдяки здвинутій камері, зміні фону, іншому освітленні, таке інше). Таким чином, BackgroundSubtractorMOG та BackgroundSubtractorMOG2 також покладаються не стандатний Algorithm::write() для зберігання тільки параметрів алгоритму.
  • Максимізація очікування (з модуля ml). Алгоритм шукає змішення гауссіанів, що найкраще апроксимує дані користувача. В цьому випадку модель може бути використана повторно у наступному запуску щоб перевірити нові дані проти натренованої статистичної моделі. Так чином EM потребує зберігати модель. Однак, оскільки модель описана декількома параметрами, що доступні як параметри алгоритму тільки для читання (тобто вони доступні через EM::get()), EM також покладається на Algorithm::write() для зберігання параметрів EM та моделі (представлене параметрами тільки для читання).

Algorithm::read

Читає параметри алгоритму з файлового сховища

C++: void Algorithm::read(const FileNode& fn)
Параметри:
  • fn – Файлови вузол для сховища.

Метод читає всі параметри алгоритму з вказаного вузла сховища. Подібно до Algorithm::write(), якщо ви реалізуєте алгоритм, що потребує читати деякі додаткові дані та/або переобчислити деякі внутрішні дані, ви можете перекрити цей метод.

Algorithm::getList

Повертає перелік зареєстрованих алгоритмів

C++: void Algorithm::getList(vector<string>& algorithms)
Параметри:
  • algorithms – Вихідний вектор імен алгоритмів.

Цей статичний метод повертає список з зареєстрованих алгоритмів в алфавітному порядку. Ось як його використовувати

vector<string> algorithms;
Algorithm::getList(algorithms);
cout << "Algorithms: " << algorithms.size() << endl;
for (size_t i=0; i < algorithms.size(); i++)
    cout << algorithms[i] << endl;

Algorithm::create

Створює екземпляр алгоритму за його ім'ям

C++: template<typename _Tp> Ptr<_Tp> Algorithm::create(const string& name)
Параметри:
  • name – Ім'я алгоритму, одне з імен, що повертаються Algorithm::getList().

Цей статичний метод створює новий екземпляр вказаного алгоритму. Якщо такого алгоритму немає, метод тишком повертає вказівник null (що може бути перевірено методом Ptr::empty()). Також ви повинні вказати окремий суб-клас Algorithm як _Tp (або просто Algorithm , якщо ви не знаєте його на цей момент).

Ptr<BackgroundSubtractor> bgfg = Algorithm::create<BackgroundSubtractor>("BackgroundSubtractor.MOG2");

Зауваження.

Важливо зауважити щодо майже містичної поведінки Algorithm::create() , коли він повертає NULL, тоді, коли б, здавалось, не повинен. Причина проста - Algorithm::create() розташований в OpenCV модулі core, але алгоритми реалізовані в інших модулях. Якщо ви створюєте алгоритми динамічно, лінкер C++ може вирішити видалити модулі, де насправді реалізовані алгоритми, оскільки ви не викликаєте жодних функцій з ціх модулів. Щоб уникнути цієї проблеми, вам треба викликти initModule_<modulename>(); десь на початку програми, перед Algorithm::create(). Наприклад, викличте initModule_nonfree() щоб використовувати SURF/SIFT, викличте initModule_ml() для використання максимізації очікування, і так далі.

Створення власних алгоритмів

Зазначені методи завичайно достатні для користувачів. Якщо ви бажаєте зробити ваш власний алгоритм, що походить від Algorithm, вам треба в основному слідувати небагатьом домовленостям, та додати трохи напів-стандартного коду до вашого класу:

  • Зробіть клас та вкажіть Algorithm в якості базового класу.
  • Параметри алгоритму повинні бути членами класу. Дивіться Algorithm::get() щодо можливих типів параметрів.
  • Додайте публічний віртуальний метод AlgorithmInfo* info() const; до вашого класу.
  • Додайте фукнцію конструктора, екземпляр AlgorithmInfo та реалізуйте метод info(). Найпростіший шлях є взяти https://github.com/Itseez/opencv/tree/master/modules/ml/src/ml_init.cpp в скості посилання, та модифікувати його відповідно до списку ваших параметрів.
  • Додайте декіка публічних фукнцій (як initModule_<mymodule>()) , що викликають info() вашого алгоритму, та розташуйте їх в тому ж файлі, що і реалізація info(). Це змусить лінкер C++ включити цей об'єктний файл в цільовий застосунок. Дивіться Algorithm::create() щодо деталей.