1. OpenCV шаг за шагом. Введение.
2. Установка.
3. Hello World.
4. Загрузка картинки.
5. Вывод видео
6. Ползунок
7. Захват видео с камеры
8. Запись видео
9. События от мышки
10. Обработка изображения — сглаживание
11. Обработка изображения — изменение размеров
12. ROI — интересующая область изображения
13. Типы данных OpenCV
14. Матрица
CvMat — матрица
Рассмотрим ещё немножко основ. Функции можно просто пробежать глазами — самое интересное в этом шаге — в самом конце приводятся куски кода обхода элементов матрицы и пикселей изображения.
CvArr — массив — его можно считать «абстрактным базовым классом» для CvMat и далее IplImage (CvArr->CvMat->IplImage)
typedef void CvArr;
Вы уже, наверное, обратили внимание, что прототипы функций OpenCV принимают в качестве параметров указатель на CvArr. Фактически это означает, что они принимают массив CvMat* или изображение IplImage*.
// // матрица // typedef struct CvMat { int type; int step; /* for internal use only */ int* refcount; int hdr_refcount; union { uchar* ptr; short* s; int* i; float* fl; double* db; } data; union { int rows; int height; }; union { int cols; int width; }; } CvMat; // // конструктор: // (данные не резервируются - для этого нужно использовать cvCreateData() // или просто использовать cvCreateMat() ) // CV_INLINE CvMat cvMat( int rows, int cols, int type, void* data CV_DEFAULT(NULL)) { CvMat m; assert( (unsigned)CV_MAT_DEPTH(type) <= CV_64F ); type = CV_MAT_TYPE(type); m.type = CV_MAT_MAGIC_VAL | CV_MAT_CONT_FLAG | type; m.cols = cols; m.rows = rows; m.step = m.cols*CV_ELEM_SIZE(type); m.data.ptr = (uchar*)data; m.refcount = NULL; m.hdr_refcount = 0; return m; }
пример создания матрицы:
CvMat* mat = cvCreateMatHeader( rows, cols, type ); cvCreateData( mat );
CVAPI(CvMat*) cvCreateMatHeader( int rows, int cols, int type );
— создание загловка матрицы (без выделения памяти под данные)
rows — число строк матрицы
cols — число столбцов матрицы
type — тип формата матрицы
CVAPI(void) cvCreateData( CvArr* arr ); CVAPI(void) cvReleaseData( CvArr* arr );
— резервирует/освобождает массив данных
arr — указатель на заголовок массива
CVAPI(CvMat*) cvCreateMat( int rows, int cols, int type );
— создане заголовка матрицы и резервирование данных
rows — число строк матрицы
cols — число столбцов матрицы
type — тип формата матрицы:
CV_<глубина в битах><s|u|f>C<число каналов>,
S — знаковый
U — беззнаковый
F — float
например: CV_8UC1 (8-битный, беззнакоый, 1-канальный)</s|u|f>
CVAPI(void) cvReleaseMat( CvMat** mat );
— освобождает матрицу (данные и заголовок)
mat — двойной указатель на матрицу
CVAPI(void) cvCreateData( CvArr* arr );
— резервирование данных массива
arr — указатель на заголовок массива
CVAPI(void) cvReleaseData( CvArr* arr );
— освобождает данных массива
arr — указатель на заголовок массива
CVAPI(void) cvSetData( CvArr* arr, void* data, int step );
— привязывает данные к заголовку массива (должен быть инициализирован ранее)
arr — указатель на заголовок массива
data — данные
step — полная длина строки в байтах
Рассмотрим 3 способа получения заданного элемента матрицы:
1 — с использованием макроса CV_MAT_ELEM
2 — с использованием функций cvGet2D(), cvGetReal2D()
3 — с использованием прямого доступа к элементам матрицы
// макросы доступа к элементу матрицы #define CV_MAT_ELEM_PTR_FAST( mat, row, col, pix_size ) \ (assert( (unsigned)(row) < (unsigned)(mat).rows && \ (unsigned)(col) < (unsigned)(mat).cols ), \ (mat).data.ptr + (size_t)(mat).step*(row) + (pix_size)*(col)) #define CV_MAT_ELEM_PTR( mat, row, col ) \ CV_MAT_ELEM_PTR_FAST( mat, row, col, CV_ELEM_SIZE((mat).type) ) #define CV_MAT_ELEM( mat, elemtype, row, col ) \ (*(elemtype*)CV_MAT_ELEM_PTR_FAST( mat, row, col, sizeof(elemtype)))
CV_MAT_ELEM — возвращает заданный элемент матрицы
mat — матрица
elemtype — тип элементов матрицы
row — номер строки
col — номер столбца
CVAPI(uchar*) cvPtr1D( const CvArr* arr, int idx0, int* type CV_DEFAULT(NULL)); CVAPI(uchar*) cvPtr2D( const CvArr* arr, int idx0, int idx1, int* type CV_DEFAULT(NULL) ); CVAPI(uchar*) cvPtr3D( const CvArr* arr, int idx0, int idx1, int idx2, int* type CV_DEFAULT(NULL)); CVAPI(uchar*) cvPtrND( const CvArr* arr, const int* idx, int* type CV_DEFAULT(NULL), int create_node CV_DEFAULT(1), unsigned* precalc_hashval CV_DEFAULT(NULL));
— возвращает указатель на заданный элемент массива
arr — указатель на массив
idx0, idx1, idx2 — индекс элемента
idx — массив индексов
type — указатель для получения типа элемента
CVAPI(CvScalar) cvGet1D( const CvArr* arr, int idx0 ); CVAPI(CvScalar) cvGet2D( const CvArr* arr, int idx0, int idx1 ); CVAPI(CvScalar) cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 ); CVAPI(CvScalar) cvGetND( const CvArr* arr, const int* idx );
— возвращает заданный элемент массива
arr — указатель на массив
idx0, idx1, idx2 — индекс элемента
idx — массив индексов
CVAPI(double) cvGetReal1D( const CvArr* arr, int idx0 ); CVAPI(double) cvGetReal2D( const CvArr* arr, int idx0, int idx1 ); CVAPI(double) cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 ); CVAPI(double) cvGetRealND( const CvArr* arr, const int* idx );
— возвращает заданный элемент 1-канального массива
arr — указатель на 1-канальный массив
idx0, idx1, idx2 — индекс элемента
idx — массив индексов элемента
CVAPI(void) cvSet1D( CvArr* arr, int idx0, CvScalar value ); CVAPI(void) cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value ); CVAPI(void) cvSet3D( CvArr* arr, int idx0, int idx1, int idx2, CvScalar value ); CVAPI(void) cvSetND( CvArr* arr, const int* idx, CvScalar value );
— изменение заданного элемента массива
arr — указатель на массив
idx0, idx1, idx2 — индекс элемента
idx — массив индексов
value — новое значение
CVAPI(void) cvSetReal1D( CvArr* arr, int idx0, double value ); CVAPI(void) cvSetReal2D( CvArr* arr, int idx0, int idx1, double value ); CVAPI(void) cvSetReal3D( CvArr* arr, int idx0, int idx1, int idx2, double value ); CVAPI(void) cvSetRealND( CvArr* arr, const int* idx, double value );
— изменение заданного элемента массива
arr — указатель на массив
idx0, idx1, idx2 — индекс элемента
idx — массив индексов
value — новое значение
double cvmGet( const CvMat* mat, int row, int col )
— возвращает элемент массива (1-канальной матрицы типа float)
arr — указатель на массив
row — номер строки
col — номер столбца
void cvmSet( CvMat* mat, int row, int col, double value );
— устанавливает элемент массива (1-канальной матрицы типа float)
arr — указатель на массив
row — номер строки
col — номер столбца
value — новое значение
3 способа получения заданного элемента матрицы:
// покажем содержимое матрицы // int i=0, j=0; // 1 вариант: с использованием макроса CV_MAT_ELEM for(i=0; i<matrix->rows; i++){ for(j=0; j<matrix->cols; j++){ printf("%.0f ", CV_MAT_ELEM( *matrix, float, i, j)); } printf("\n"); } printf("-----\n"); // 2 вариант: с использованием cvGet2D(), cvGetReal2D() for(i=0; i<matrix->rows; i++){ for(j=0; j<matrix->cols; j++){ printf("%.0f ", cvGet2D(matrix, i, j));//cvGetReal2D(matrix, i, j)); } printf("\n"); } printf("-----\n"); // 3 вариант: прямой доступ к элементам for(i=0; i<matrix->rows; i++){ float* ptr = (float*)(matrix->data.ptr + i*matrix->step); for(j=0; j<matrix->cols; j++){ printf("%.0f ", ptr[j]); } printf("\n"); }
Код для обхода всех пикселей 3-канального изображения формата RGB (вернее BGR):
// пробегаемся по всем пикселям изображения for( int y=0; yheight; y++ ) { uchar* ptr = (uchar*) (image->imageData + y * image->widthStep); for( int x=0; xwidth; x++ ) { // 3 канала ptr[3*x] = 0; // B - синий ptr[3*x+1] = 0; // G - зелёный ptr[3*x+2] = 255; // R - красный } }
Читать далее: 15. OpenCV шаг за шагом. Сохранение данных в XML
16 комментариев на «“14. OpenCV шаг за шагом. Матрица”»
Скажи мне, а в последнем примере у тебя image задан как IplImage, CvMat или CvArr?
IplImage
а, это я дибил. IplImage*.
А вот вопрос от чайника: как загрузить картинку из файла в IplImage пример есть. А как загрузить картинку из файла в cvMat?
И можно ли конвертировать IplImage в cvMat и обратно?
разумеется, можно.
можно и из обычного массива грузить данные:
или так:
, при этом raw_buf — это массив ваших данных в правильной укладке порядка пикселей (BGR)
Спасибо за ответ!
Тяжко на шестом десятке въезжать в С и пытаться его переложить на более привычный Basic 🙂
Вообще то мне хотелось бы применить cvMinAreaRect для захваченного с web-камеры изображения (микросхемы) чтобы определить угол ее поворота.
Захват, определение границ контура (по картинке из файла) работают, теперь надо это все вместе соединить и voila 🙂
Добрался до 32 статьи цикла, похоже это там?
Не совсем понятно, что происходит в строчке
uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
Не могли бы пояснить?
данные картики хранятся в массиве памяти (imageData), в котором они укладываются строчка за строчкой (при этом в widthStep хранится длина одной такой строчки).
т.о. в коде
мы получаем указатель на y-ю строчку пикселей изображения (IPL_DEPTH_8U — 8-битного беззнакового — стандартный тип для загруженных картинок)
В последнем примере (Код для обхода всех пикселей 3-канального изображения) ошибка, правда при данных условиях (тип изображения 8UC) она не проявится, а проявится если, например создать картинку с данными типа float:
cvCreateImage(cvSize(cols, rows), IPL_DEPTH_32F, 1);
Короче вместо
uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
следует писать так
uchar* ptr = (uchar*) (image->imageData) + y * image->widthStep;
Т.е. сперва привести указатель на данные к нужному типу, а потом уже его использовать.
За статью спасибо, в своё время здорово помогла, особенно последний пример! 🙂
Сорри за повторение. Но хотелось бы внести и свою лепту. Как новичок в этом деле долго ( что-то около 15 минут 🙂 ) разбирался в последнем куске кода 🙂
Чтобы было более понятно, приведу этот кусок кода с некоторыми комментариями.
Подскажите, будьте добры, если я загружаю изображение, как
и мне нужно работать с массивом получившихся оттенков серого (изменить, выгрузить в, к примеру, числовой массив), как лучше всего это сделать?
к отдельному элементу можно так:
к отдельной строчке через
, а к массиву данных через
см.cv::Mat
Я пробовал такой вариант, но возвращает какие-то нечленораздельные символы. Вот пример:
Слева пример вывода в файл, справа — на консоль.
Отпишитесь, пожалуйста, кто знает.
тогда пишите нафорум — что хотите сделать, что делаете и т.д.