OpenCV (бібліоткека Open Source Computer Vision: http://opencv.org) є бібліотекою з ліцензією BSD та відкритим кодом, що включає декілька сотен алоритмів комп'ютерного бачення. Документ описує так званий OpenCV 2.x API, що загалом є C++ API, на відміну від базованого на C OpenCV 1.x API, який описаний в opencv1x.pdf.
OpenCV має модулярну структуру, що означає, що пакунок включає декілька загальних та статичних бібліотек. Доступні даступні модулі:
- core - компактний модуль, що визначає базові структури даних, включаючи щільні багатовимірні матриці Mat та базові функції, що використувуються в інших модулях.
- imgproc - модуль обробки зображень, що включає лінійне та неленійне фільтрування зображень, геометричні трансформації зображень (маштабування, афінні та перспективні викривлення, базові основані на таблицях перетворення), перетворення просторів кольорів, гістограми, таке інше.
- video - модуль аналізу відео, що включає очікування руху, видалення фону та алгоритми відсліжування об'єктів.
- calib3d - базові багато-переглядні геометричні алгоритми, моно та стерео калібрація, очікування позиції об'єктів, алгоритми стерео відповідності та елементи 3D реконструкції.
- features2d - виявлення особливостей, дескрипторів, та порівняння дескрипторів.
- objdetect - детекція об'єктів та екземплярів заданих класів (наприклад, обличь, очей, чашок, людей, машин, і так далі).
- highgui - простий у використанні інтерфейс для захвату відео, кодеків зображень та відео, а також прості можливості користувацького інтерфейсу.
- gpu - акселеровані на GPU інтерфейси з різних модулей OpenCV.
- ... деякі інші допоміжні модулі, такі як FLANN та тестові обертки Google, прив'зка до Python, та інші.
Подальші глави цього документу описують функціональність кожного модуля. Але зпершу переконаймося, що ви знайомі з загальними концепціями API, що використовуються в бібліотеці.
Всі класи та функції 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;
}
Ключовим компонентом цієї технології є метод Mat::create. Він отримує потрібний розмір та тип масиву. Якщо масив вже має такий розмір та тип, метод нічого не робить. Інакше попередній масив буде знищено, якщо такий був (ця частина включає зменшення числа посилань, та порівняння його з нулем), та потім розміщення нового буфера потрібного розміру. Більшість функцій викликають метод Mat::create для кожного вихідного масиву, і таким чином працює автоматичне розміщення.
Деякі помітні виключення з цієї схеми є cv::mixChannels, cv::RNG::fill, та декілька інших функцій та методів. Вони не в змозі розміщувати вихідні дані, так що вам треба зробити це заздалегідь.
Як бібліотека комп'ютерного бачення, OpenCV має справи здебільшого з пікселями зображень, що часто закодовані як компактні, 8- або 16-біт-на-канал, та, таким чином, має дуже обмежений диапазон значень. Більше того, деякі операції над зображеннями, як перетворення простору кольорів, підлаштування яркості та контрасту, ретушування, складні інтерполяції (бі-кубічні, Lanczos) можуть продукувати значення за межами цього диапазону. Якщо ви тільки зберігаєте меньші 8 (16) біт результату, це призведе до визуальних артифактів, та може впливати на подальший аналіз зображення. Щоб розрішити проблему, використовується так звана арифметика насиченості. Наприклад, щоб зберігти результат операції в 8-бітному зображенні, ви обираєте найближче значення з диапазону 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-бітні з плаваючою крапкою.
Підмножини підтримуваних типів для кожної функції були визначені з практичних потреб, та можуть бути розширені в майбутньому, базуючись на запитах користувачів.
Багато функцій 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 може бути використаний в різних потоках, оскільки операції підрахунка посилань використовують специфічні для архитектури атомарні інструкції.
Шаблонний "гуртовий" клас для примітивних типів даних 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 цім шляхом) для реалізацій узагальнених алгоритмів.
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 крапок задається в координатах та
. Екземпляр класу є взаємозамінним зі структурами 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;
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 крапок задає координати ,
та
. Екземпляр класу є взаємозамінним з структурою C CvPoint2D32f
. Подібно до Point_
, координати крапки 3D можуть бути перетворені на інший тип.
Векторна арифметика, а такоже оператори порівняння також
підтримуються.
Наступні псевдоніми до Point3_<> доступні:
typedef Point3_<int> Point3i;
typedef Point3_<float> Point3f;
typedef Point3_<double> Point3d;
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;
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, описує наступні параметри:
OpenCV звичайно вважає, що верхня та ліва границя включається, тоді як права та нижні - ні. Наприклад, метод Rect_::contains повертає true, якщо
Практично кожний цикл по зображенню 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++)
{
// ...
}
На додаток до членів класу, також реалізвані наступні операції для прямокутників:
Цей приклад показує, як можна встановити деяку іхрархію
прямокутників (rect1 rect2):
template<typename _Tp> inline bool
operator <= (const Rect_<_Tp>& r1, const Rect_<_Tp>& r2)
{
return (r1 & r2) == r1;
}
Для вашої зручності доступні такі псевдоніми Rect_<>:
typedef Rect_<int> Rect;
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);
Дивіться також:
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; // бажана точність
};
Клас визначає критерій припинення для ітеративних алгоритмів. Ви можете ініціалізувати його конструктором по замовчанню, та потім перевизначити любі параметри, або структура може бути повністю ініціалізована з використанням просунутого варіанту конструктора.
Конструктори.
Параметри: |
|
---|
Перетворює до застарілого формату CvTermCriteria.
Шаблонний клас для малих матриць, чиї тип та розмір відомі під час компіляції:
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;
Шаблонний клас для коротких числових векторів, окремий випадок 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.
Також реалізовані всі векторі операції:
Клас Vec загально викорисовується для піксельних типів та багато-канальних масивів. Дивіться Mat щодо деталей.
Шаблонний клас для 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 для передачі піксельних значень.
Шаблонний клас, що вказує на під-послідовність (шматок) послідовності.
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
зправа виключається з диапазону. Такий напів-відкритий інтервал
звичано нотується як .
Статичний метод Range::all() повертає спеціальну змінну, що означає “повна послідовність” або “повний диапазон”, як ” : ” в Matlab або ” ... ” в Python. Всі методи та фукнції в OpenCV, що сприймають Range, підтримують це спеціальне значення Range::all(). Але, звичайно, в випадку, коли ви самі робите обробку, ви будете виконувати перевірку та обробляти відповідно:
void my_function(..., const Range& r, ....)
{
if(r == Range::all()) {
// обробляємо всі дані
}
else {
// обробляємо [r.start, r.end)
}
}
Шаблонний клас для розумних вказівників з підрахунком посилань
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.
Цей клас провадить наступні опції:
Клас 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.
Оператор присвоювання.
Параметри: |
|
---|
Зменшує власний лічильник посилань (за допомогою release()) та збільшує лічильник посилань ptr.
Збільшує лічильник посилань.
Зменьшує лічильник посилань; коли він стає 0, викликається delete_obj().
Задана користувачем операція видалення об'єкту. По замовчанню викликається delete obj;.
Повертає true, якщо obj == 0;
bool empty() const;
Провадить доступ до полів та методів об'єкту.
Повертає підлеглий вказівник об'єкту. Завдяки методу, Ptr<_Tp>може бути використаний замість _Tp*.
Клас 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), оскільки новий заголовок посилається на існуючі дані. Ви можете насправді модифікувати частиму масива з використанням цієї можливості, наприклад:
// додати 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() на виділених суб-матрицях.
Створення заголовку для розміщених користувачем даних. Це може бути корисно для наступного:
Обробка “чужинних” даних з використанням 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);
}
Швидко ініціалізувати невеликі матриці, та/або отримати над-швидкий доступ до елементу.
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() .
Зауваження
Це перелік реалізованих матричних операцій, що можуть бути скомбіновані в вирази довільної складності (тут 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
Параметри: |
|
---|
Ці різні конструктори створюють матрицю. Як вказано в Автоматичне розташування вихідних даних, часто конструктора по замовчанню досить, та потрібна матриця буде розташована функцією OpenCV. Сконструйованії матриці може в подальшому бути присвоєна інша матриця, або матричний вираз, або може бути розміщена за допомогою Mat::create(). В останньому випадку старий вміст втратить всі посилання.
Провадить оператор присвоєння матриці.
Параметри: |
|
---|
Це всі доступні оператори присвоєння матриць. Оскльки вони не дуже різні, будьте впевнені, що ознайомились з описом параметрів оператору.
Створює заголовок матриці для окремого рядка.
Параметри: |
|
---|
Метод створює новий заголовок для окремого рядка, та повертає його. Це операція 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));
Створює заголовок матриці для обраного стовпчика.
Параметри: |
|
---|
Метод створює новий заголовок для вказаного стовпчика матриці, та повертає його. Це операція O(1), що не залежить від розміру матриці. Підлеглі дані нової матриці поділяються з оригінальною матрицею. Дивіться також опис Mat::row().
Створює заголовок для певного диапазону рядків.
Параметри: |
|
---|
Метод створює новий заголовок для вказаного диапазону рядків матриці. Подібно до Mat::row() та Mat::col() , це операція O(1).
Створює заголовок матриці для заданого диапазону стовпчиків.
Параметри: |
|
---|
Метод створює новий заголовок для сказанного диапазону стовпчиків. Подібно до Mat::row() та Mat::col() , це операція O(1).
Виділяє диагональ з матриці, або створює диагональну матрицю.
Параметри: |
|
---|
Метод створює заголовок для вказаної диагоналі. Нова матриця представляється як один стовпчик. Подібно до Mat::row() та Mat::col() , це операція O(1).
Створює повну копію масива та всіх даних.
Метод створює повну копію масиву. Оригінальний step[] не береться до уваги. Таким чином, якщо матриця щільня, її розмір total()*elemSize()байт .
Копіює матрицю в іншу.
Параметри: |
|
---|
Метод копіює дані матриці в іншу матрицю. Перед копіювання метод викликає
m.create(this->size(), this->type());
Таким чином матриця призначення буде реалокована, якщо потрібно. Тоді як m.copyTo(m); робить без питань, функція не обробляє випадок, коли матриці джерела та призачення частково перетинаються.
Коли задана матриця операції, якщо виклик Mat::create реалокує матрицю, нова матриця буде заповнена нулями перед копіювання даних.
Перетворює матрицю на інший тип даних з опціональним маштабуванням.
Параметри: |
|
---|
Метод конвертує піксельні значення джерела на вихідний тип даних. saturate_cast<> додається наприкінці, щоб запобігти можливому переповненню:
Провадить функціональну форму для convertTo.
Параметри: |
|
---|
Це метод, що використовується зсередини в Матричних виразах.
Встановлює всі або деякі з елементів матриці в задане значення.
Параметри: |
|
---|
Змінює форму та/або число каналів для двомірної матриці без копіювання даних.
Параметри: |
|
---|
Метод створює новий заголовок матриці для елементів *this. Нова матриця може мати інший розмір та/або число каналів. Любі комбінації можливі, якщо:
Наприклад, якщо є набір 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 A1 = A + Mat::eye(A.size(), A.type())*lambda;
Mat C = A1.t()*A1; // обчислює (A + lambda*I)^t * (A + lamda*I)
Інвертує (обертає) матрицю.
Параметри: |
|
---|
Метод виконує інверсію матриці в термінах матричних виразів. Це означає, що метод повертає тимчасовий об'єкт інвертованої матриці, що може використовуватись в більш складних виразах, або може бути присвоєна матриці.
Виконує поелементне множенн або ділення двох матриць.
Параметри: |
|
---|
Метод повертає тимчасовий об'єкт, що відповідає поелементному множенню. Зауважте, що це не множенн матриць, що відповідає оператору “*”.
Приклад:
Mat C = A.mul(5/B); // еквівалентно divide(A, B, C, 5)
Обчислює кросс-множення двох 3-елементних векторів.
Параметри: |
|
---|
Метод обчислює кросс-множення двох 3-елементних векторів. Вектори мусять бути 3-елементними векторами з плаваючою крапкою, тієї ж форми та розміру. Результат також 3-елементний вектор того ж типу, що і операнди.
Обчислює крапка-множення двох векторів.
Параметри: |
|
---|
Метод обчислює крапка-множення двох матриць. Якщо матриці не одно-стовпчикові або одно-рядкові вектори, використовується зверху-до низу, та зліва-на право порядок сканування, щоб вважати іх за одномірні вектори. Вектори мають бути того ж розміру та типу. Якщо матриці мають більше одного каналу, крапкове множення підсумовує всі канали разом.
Повертає масив з нулів вказаного розміру та типу.
Параметри: |
|
---|
Метод повертає матрицю в стилі Matlab, що ініціалізована нулями. Це може використано для швидкого формування сталої матриці як параметру функції, частину матричного виразу, або як ініціалізатор матриці.
Mat A;
A = Mat::zeros(3, 3, CV_32F);
В пркладі вище нова матриця розміщується тільки, якщо A не матриця 3x3 з плаваючую точкою. Інакше, існуюча матриця A буде заповнена нулями.
Повертає масив з усіх 1, вказаного розміру та типу.
Параметри: |
|
---|
Метод повертає ініціалізатор матриці в стилі одномірної Matlab, подібно до Mat::zeros(). Зауважте, що використовуючи цей метод, ви можете ініціалізувати масив з довільним значенням, з використанням ідіоми Matlab:
Mat A = Mat::ones(100, 100, CV_8U)*3; // створити матрицю 100x100 заповнену значенням 3.
Операція вище не створює матрицю 100x100 з 1, що помножені на 3. Замість цього вона тільки пам'ятає фактом маштабування 3, та використовує його до насправді ініціалізатора матриці.
Повертає одиничну матрицю вказаного розміру та типу.
Параметри: |
|
---|
Метод повертає ініціалізатор одиничної матриці в стилі Matlab, подібно до Mat::zeros(). Подібно до Mat::ones(), ви можете використовувати операцію маштабування, щоб ефективно створити маштабовану одиничну матрицю:
// створити диагональну матрицю 4x4 з 0.1 по диагоналі.
Mat A = Mat::eye(4, 4, CV_32F)*0.1;
Створює нову матрицю, якщо потрібно.
Параметри: |
|
---|
Це ключовий з нових методів Mat. Більшість з нового стилю функцій та методів OpenCV, що продукують матриці, викликають цей метод для кожної вихідної матриці. Цей метод реалізує наступний алгоритм:
Така схема робить управління пам'яттю надійним та ефективним, та в той же час допомагає вам запобігти зайвого кодування. Це означає, що звичайно вам не треба явно розміщувати вихідні масиви, Тобто заміть друкувати таке:
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::Mat() ), лічильник посилань є NULL, та метод не має ефекту в цьому випадку. Звичайно, щоб запобігти втраті пам'яті, метод не має викликатись безпосередньо. Він викликається неявно, через оператор присвоєння матриці. Збільшення лічильника посилань є атомарною операцією на платформах, що підтримують це. Таким чином безпечно оперувати матрицями асинхронно в різних потоках.
Зменьшує лічильник посилань, та вивільняє матрицю в разі потреби.
Метод зменшує лічильник посилань, асоційований з даними матриці, Коли лічильник посилань досягає нуля, дані матриці деалокуються (вивільняються), та вказивники на дані та лічильник посилань встановлюються в NULL. Якщо заголовок вказує на зовнішні дані (дивіться Mat::Mat() ), лічильник посилань є NULL, та метод не має жодної дії цьому випадку.
Цей метод може бути викликаний вручну, щоб примусово вивільнити дані матриці. Але оскільки цей метод автоматично викликається з деструктора, або любим іншим методом, що змінює вказівник на дані, це, зазвичай, не потрібне. Зменьшення лічильника посилань та перевірка на 0 є атомарною операцією на платформах, що підтримують це. Таким чином безпечно оперувати матрицями асинхронно в різних потоках.
Змінює число рядків матриці.
Параметри: |
|
---|
Метод змінює число рядків матриці. Якщо матриця реалокується, зберігаються перші min(Mat::rows, sz) рядків. Метод емулює відповідні методи векторного класу STL.
Резервує простір для вказаної кількості рядків.
Параметри: |
|
---|
Метод резервує простір для sz рядків. Якщо матриця вже має простір для щоб зберігати sz рядків, нвічого не відбувається. Якщо матриці реалокується, зберігаються перші Mat::rows. Метод емулює відповідний метод векторного класу STL.
Додає елементи в кінець матриці.
Параметри: |
|
---|
Метод додає один або більше елементів в кінець матриці. Вони емулюють відповідні методи векторного класу STL. Коли elem є Mat , його тип та число стовпчиків повинни бути тими ж, що і матриця-контейнер.
Видаляє елементи з кінця матриці.
Параметри: |
|
---|
Метод видаляє один або більше рядків з кінця матриці.
Розміщує заголовок матриці зсередини батьківської матриці.
Параметри: |
|
---|
Після того, як ви виділили підматрицю з матриці з використанням Mat::row(), Mat::col(), Mat::rowRange(), Mat::colRange() , та інших, утворена матриця вказує на частину оригінальної матриці. Однак кожна субматриця містить інформацію (що представлена полями datastart та dataend), що допомагає реконструвати розмір оригінальної матриці та позицію субматриці в оригінальній матриці. Метод locateROI робить саме це.
Підлаштовує розмір субматриці та позицію в батьківській матриці.
Параметри: |
|
---|
Метод є доповненням до Mat::locateROI() . Типове використання ціх функцій є визначення позиції субматриці в батьківській матриці, та потім сзунути її деяким чином. Звичайно це потрібно для операцій фільтрування. коли тре прийняти до уваги також пікселі за межами ROI. Коли всі параметри методу позитивні, ROI буде зростати в усіх напрямках на задане значення, наприклад:
A.adjustROI(2, 2, 2, 2);
В цьому прикладі розмір матриці збільшується на чотири в кожному напрямку. Матрия отриимує 2 елементи зліва, зправа, зверху та знизу відповідно, що надає їй всі необхідні пікселі для фільтрування з ядром 5x5.
adjustROI змушує підлаштований ROI бути всередині батьківської матриці, тобто новий ROI обмежений границями батьківської матриці. Наприклад, якщо субматриця A розміщена на першому рядку батьківськох матриці, та ви викликаєте A.adjustROI(2, 2, 2, 2) , тоді це не дасть збільшення згори.
Функція використовується внутрішньо в функціях фільтрування OpenCV, таких як filter2D() , морфологічних операція, тощо.
Також дивіться
Виділяє прямокутну суб-матрицю.
Параметри: |
|
---|
Оператори створюють новий заголовок для заданого суб-масиву для *this . Вони є найбільш загальною формою для Mat::row(), Mat::col(), Mat::rowRange() та Mat::colRange(). Наприклад, A(Range(0, 10), Range::all()) еквівалентно до A.rowRange(0, 10). Подібно до всіх попередніх, ці оператори O(1), тобто дані матриці не копіюються.
Створює заголовок CvMatдля матриці .
Оператор створює заголовок CvMat для матриці без копіювання даних. Лічильник посилань не приймається до уваги цією операцією. Таким чином, ви повинні впевнитись, що оригінальна матриця не відвантажена, коли використовуєте заголовок CvMat. Оператор корисний для одночасного використання нового та старого OpenCV API, наприклад:
Mat img(Size(320, 240), CV_8UC3);
...
CvMat cvimg = img;
mycvOldFunc( &cvimg, ...);
де mycvOldFunc є функція написана для роботи з структурами даних OpenCV 1.x.
Створює заголовок IplImage для матриці.
Оператор створює заголовок IplImage для матриці, без копіювання даних. Ви повинні впевнитись, що оригінальна матриця не відвантажена, доки використовується заголовок IplImage. Подібно до Mat::operator CvMat, оператор корисний для одночасного використання нового та старого OpenCV API.
Повертає загальне число елементів масива.
Метод повертає число елементів масива (число пікселів, якщо масив представляє зображення).
Повідомляє, чи є матриця послідовною або ні.
Метод повертає 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() не завжди розміщує нову матрицю.
Повертає розмір елементу матриці в байтах.
Метод повертає розмір елементу матриці в байтах. Наприклад, якщо тип матриці CV_16SC3 , метод повертає 3*sizeof(short), тобто 6.
Повертає розмір кожного каналу елементу матриці в байтах.
Метод повертає розмір кожного каналу елементу матриці в байтах, тобто ігноруючи число каналів. Наприклад, якщо тип матриці CV_16SC3 , метод повертає sizeof(short), тобто 2.
Повертає тип елементів матриці .
Метод повертає тип елементів матриці. Це індентифікатор, сумісний з системою типів CvMat, як CV_16SC3 для 16-біт знакового 3-канального масиву, і так далі.
Повертає глибину елементів матриці.
Метод повертає ідентифікатор глибини елементу матриці (тип кожного окремого елементу). Наприклад, для матриці з 16-бітними знаковими елементами метод поверне CV_16S . Повний список матричних типів містить наступні значення:
Повертає число каналів матриці.
Метод повертає число каналів матриці.
Повертає нормалізований крок.
Метод повертає крок матриці, поділений на Mat::elemSize1(). Це може бути корисно для швидкого доступу до довільного елементу матриці.
Повертає розмір матриці.
Метод повертає розмір матриці Size(cols, rows). Коли матриця має більше двох вимірів, повертається розмір (-1, -1).
Повертає true, якщо матриця не має елементів.
Метод повертає true, якщо Mat::total() є 0 або Mat::data є NULL. Завдяки методам pop_back() та resize() M.total() == 0 не очікує M.data == NULL.
Повертає вказівник на вказаний рядок матриці.
Параметри: |
|
---|
Метод повертає uchar* або типізований вказівник на вказаний рядок матриці. Дивіться приклад в Mat::isContinuous() щоб подивитись, як використовується цей метод.
Повертає посилання на вказаний елемент матриці.
Параметри: |
|
---|
Шаблонний метод, що повертає вказаний елемент матриці. Для більшої продуктивності перевірка індексів виконується тільки в конфігуації 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);
Повертає матричний ітератор, та встановлює його на перший елемент матриці.
Метод повертає матричні ітератори тільки для читання, або для читання-і-запису. Використання матричних ітераторів дуже подібне до використання дво-направлених ітераторів 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 .
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);
Це проксі клас для передачі вхідних масивів тільки для читання в функції 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() для знаходження числа компонентів (векторів/матриць) зовнішнього вектора.
Цей тип дуже подібний до 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;
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 презентує багатовимірних розріднених числових масивів. Такі розріднені масиви можуть зберігати елементи любого типу, який може зберігати 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.
Провадить операцію присвоєння розрідненої матриці.
Параметри: |
|
---|
Останній варіант еквівалентний до відповідного конструктора з try1d=false.
Створює повну копію матриці.
Копіює всі дані в цільову матрицю. Цільова матриця буде реалокована в разі потреби.
Параметри: |
|
---|
Останній варіант конвертує розріднену 1D або 2D матрицю в щільну 2D матрицю. Якщо розріднена матриця є 1D, результат буде одно-стовпчикова матриця.
Конвертує розріднену матрицю, можливо зі зміною типу та маштабуванням.
Параметри: |
|
---|
Перша версія конвертує довільну розріднену матрицю в щільну матрицю, та множить всі елементи матриці на вказаний скаляр. Друга версія конвертує розрізнену матрицю з опціональним перетворенням типу та маштабуванням. Якщо rtype=-1, цільовий тип елементу буде таким же, що і тип елементу розрідненої матриці. Інакше rtype буде задавати глибину, та число каналів буде залишатись тим же, що і в розрідненій матриці.
Реалокує розріднену матрицю. Якщо вона вже має відповідний розмір та тип, вона просто очищується за допомогою clear(), інакше стара матриця вивільняється (за допомогою release()), та розміщується нова.
Параметри: |
|
---|
Встановлює всі елементи матриці в 0, що означає очищення хеш таблиці.
Вручну збільшує лічильник посилань на заголовок.
Зменшує лічильник посилань на заголовок, що він досягає 0. Заголовок та всі підлеглі дані деалокуються.
Конвертує розріднену матрицю в старий вигляд. Всі елементи копіюються.
Розмір кожного елементу в байтах (вузли матриці будуть більшими, дякуючи індексам елементів та іншим елементам SparseMat::Node).
elemSize()/channels().
Повертає тип елементів матриці.
Метод повертає тип елементів розрідненої матриці. Це ідентифікатор, сумісний з системою типів CvMat, як CV_16SC3 для 16-бітного знакового 3-канального масиву, і таке інше.
Повертає глибину елементу розрідненої матриці.
Метод повертає ідентифікатор глибини елементу матриці (тип для кожного індивідуального каналу). Наприклад, для 16-бітного знакового 3-канального масиву метод повертає CV_16S
Повертає число каналів матриці.
Метод повертає число каналів матриці.
Повертає масив розмірів або розмір матриці по виміру i та 0 , якщо матриця не розміщена.
Параметри: |
|
---|
Повертає матрицю розмірностей.
Повертає число ненульових елементів.
Обчислює хеш значення для елемента за його індексом.
Параметри: |
|
---|
Низькорівневі функції поелементного доступу, спеціальні варіанти для випадків 1D, 2D, 3D, та загальний варіант для випадку n-D.
Параметри: |
|
---|
Повертає вказівник на елемент матриці. Якщо елемент вже є (ненульовий), повертається вказівник на нього. Якщо такого немає, та createMissing=false, повертається вказівник NULL. Якщо такого немає, та createMissing=true, створюється новий елемент та ініціалізується в 0. Вказівник на нього не повертається. Якщо опціональний вказівник не є NULL, значення хешу для елементу не обчислюється, але замість нього береться hashval.
Очищує вказаний елемент матриці. Коли такого елементу немає, метод нічого не робить.
Параметри: |
|
---|
Шаблонний клас розрідненої 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);
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);
Повертає ім'я алгоритму
Повертає параметр алгоритму
Параметри: |
|
---|
Метод повертає значення окремого параметру. Оскільки компілятор не може визначити тип повертаємого параметру, ви маєте вказати його явно в кутових дужках. Ось доступні форми 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 derivative, ви можете перевизначити метод за зберігати деяку додаткову інформацію. Однак це рідко потрібне. Ось деякі приклади:
- детектор особливостей SIFT (з модуля nonfree). Цей клас зберігає тільки параметри алгоритму, але не ключові крапки або іхні дескриптори. Таким чином, достатньо зберігати тільки параметри алгоритму, що і робить Algorithm::write(). Таким чином немає особливого SIFT::write().
- Видалення фону (з модуля video). Він має параметри алгоритму, а також поточну подель фону. Однак модель фону не зберігаєтся. По-перше вона досить велика. Потім, якщо ви навіть маєте збережену модель фону, вона буде невідповідною при наступному запуску (завдяки здвинутій камері, зміні фону, іншому освітленні, таке інше). Таким чином, BackgroundSubtractorMOG та BackgroundSubtractorMOG2 також покладаються не стандатний Algorithm::write() для зберігання тільки параметрів алгоритму.
- Максимізація очікування (з модуля ml). Алгоритм шукає змішення гауссіанів, що найкраще апроксимує дані користувача. В цьому випадку модель може бути використана повторно у наступному запуску щоб перевірити нові дані проти натренованої статистичної моделі. Так чином EM потребує зберігати модель. Однак, оскільки модель описана декількома параметрами, що доступні як параметри алгоритму тільки для читання (тобто вони доступні через EM::get()), EM також покладається на Algorithm::write() для зберігання параметрів EM та моделі (представлене параметрами тільки для читання).
Читає параметри алгоритму з файлового сховища
Параметри: |
|
---|
Метод читає всі параметри алгоритму з вказаного вузла сховища. Подібно до Algorithm::write(), якщо ви реалізуєте алгоритм, що потребує читати деякі додаткові дані та/або переобчислити деякі внутрішні дані, ви можете перекрити цей метод.
Повертає перелік зареєстрованих алгоритмів
Параметри: |
|
---|
Цей статичний метод повертає список з зареєстрованих алгоритмів в алфавітному порядку. Ось як його використовувати
vector<string> algorithms;
Algorithm::getList(algorithms);
cout << "Algorithms: " << algorithms.size() << endl;
for (size_t i=0; i < algorithms.size(); i++)
cout << algorithms[i] << endl;
Створює екземпляр алгоритму за його ім'ям
Параметри: |
|
---|
Цей статичний метод створює новий екземпляр вказаного алгоритму. Якщо такого алгоритму немає, метод тишком повертає вказівник 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() щодо деталей.