• - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!
подписаться на RSS-ленту

14. OpenCV шаг за шагом. Матрица

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-канальный)

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; y<image->height; y++ ) {
		uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
		for( int x=0; x<image->width; x++ ) {
			// 3 канала 
			ptr[3*x] = 0;     // B - синий
			ptr[3*x+1] = 0;   // G - зелёный
			ptr[3*x+2] = 255; // R - красный
		}
	}


Читать далее: 15. OpenCV шаг за шагом. Сохранение данных в XML
  • 0
  • 27 августа 2010, 10:03
  • noonv

Комментарии (8)

RSS свернуть / развернуть
+
0
Скажи мне, а в последнем примере у тебя image задан как IplImage, CvMat или CvArr?
avatar

gous

  • 14 августа 2011, 07:04
+
0
IplImage
avatar

admin

  • 14 августа 2011, 07:08
+
+1
а, это я дибил. IplImage*.
avatar

gous

  • 14 августа 2011, 07:08
+
0
А вот вопрос от чайника: как загрузить картинку из файла в IplImage пример есть. А как загрузить картинку из файла в cvMat?
И можно ли конвертировать IplImage в cvMat и обратно?
avatar

atlab

  • 20 ноября 2011, 07:52
+
0
разумеется, можно.
можно и из обычного массива грузить данные:
memcpy(cv_image->imageData, raw_buf, raw_buf_size);
или так:
// помещаем данные в матрицу
CvMat image_mat =  cvMat(raw_height, raw_width, CV_8UC3, raw_buf);
IplImage cv_image;
// преобразуем матрицу в изображение
cvGetImage(&image_mat, &cv_image);
, при этом raw_buf — это массив ваших данных в правильной укладке порядка пикселей (BGR)
avatar

noonv

  • 20 ноября 2011, 08:24
+
0
Спасибо за ответ!
Тяжко на шестом десятке въезжать в С и пытаться его переложить на более привычный Basic :)
Вообще то мне хотелось бы применить cvMinAreaRect для захваченного с web-камеры изображения (микросхемы) чтобы определить угол ее поворота.
Захват, определение границ контура (по картинке из файла) работают, теперь надо это все вместе соединить и voila :)
Добрался до 32 статьи цикла, похоже это там?
avatar

atlab

  • 20 ноября 2011, 09:43
+
0
Не совсем понятно, что происходит в строчке
uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
Не могли бы пояснить?
avatar

vargy

  • 2 января 2012, 03:07
+
+1
данные картики хранятся в массиве памяти (imageData), в котором они укладываются строчка за строчкой (при этом в widthStep хранится длина одной такой строчки).
т.о. в коде
uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
мы получаем указатель на y-ю строчку пикселей изображения (IPL_DEPTH_8U — 8-битного беззнакового — стандартный тип для загруженных картинок)
avatar

noonv

  • 2 января 2012, 07:21

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.