Рассмотрим ещё немножко основ. Функции можно просто пробежать глазами — самое интересное в этом шаге — в самом конце приводятся куски кода обхода элементов матрицы и пикселей изображения.
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(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 — с использованием прямого доступа к элементам матрицы
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 - красный
}
}
А вот вопрос от чайника: как загрузить картинку из файла в IplImage пример есть. А как загрузить картинку из файла в cvMat?
И можно ли конвертировать IplImage в cvMat и обратно?
Спасибо за ответ!
Тяжко на шестом десятке въезжать в С и пытаться его переложить на более привычный Basic :)
Вообще то мне хотелось бы применить cvMinAreaRect для захваченного с web-камеры изображения (микросхемы) чтобы определить угол ее поворота.
Захват, определение границ контура (по картинке из файла) работают, теперь надо это все вместе соединить и voila :)
Добрался до 32 статьи цикла, похоже это там?
данные картики хранятся в массиве памяти (imageData), в котором они укладываются строчка за строчкой (при этом в widthStep хранится длина одной такой строчки).
т.о. в коде
uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
мы получаем указатель на y-ю строчку пикселей изображения (IPL_DEPTH_8U — 8-битного беззнакового — стандартный тип для загруженных картинок)
Сорри за повторение. Но хотелось бы внести и свою лепту. Как новичок в этом деле долго ( что-то около 15 минут :) ) разбирался в последнем куске кода :)
Чтобы было более понятно, приведу этот кусок кода с некоторыми комментариями.
// пробегаемся по всем пикселям изображения
for( int y=0; y<image->height; y++ ) {
int nChan = image->nChannels; // Определить количество каналов, чтобы реализация не зависела от пользователя.
// Мало ли сколько там каналов :) Не самому же пользователю за всем следить :)
uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
// widthStep - расстояние между соседними по вертикали точками изображения (число байт в одной строчке картинки).
// Хотя это, скорее всего, число элементов (uchar) в одной строке массива данных.
// Соответственно, для данного случая (при image->imageData == начало массива данных) по арифметике указателей
// ptr будет указывать на начало каждой строки массива данных картинки
for( int x=0; x<image->width; x++ ) {
// количество каналов вложили в nChan
// пробегаемся по всем каналам каждого пикселя
// конкретно здесь устанавливаются значения каналов каждого пикселя :)
ptr[nChan*x] = 0; // B - синий
ptr[nChan*x+1] = 0; // G - зелёный
ptr[nChan*x+2] = 255; // R - красный
}
}
В последнем примере (Код для обхода всех пикселей 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;
Т.е. сперва привести указатель на данные к нужному типу, а потом уже его использовать.
За статью спасибо, в своё время здорово помогла, особенно последний пример! :)
Комментарии (16)
RSS свернуть / развернутьgous
admin
gous
И можно ли конвертировать IplImage в cvMat и обратно?
atlab
можно и из обычного массива грузить данные:
или так:
, при этом raw_buf — это массив ваших данных в правильной укладке порядка пикселей (BGR)
noonv
Тяжко на шестом десятке въезжать в С и пытаться его переложить на более привычный Basic :)
Вообще то мне хотелось бы применить cvMinAreaRect для захваченного с web-камеры изображения (микросхемы) чтобы определить угол ее поворота.
Захват, определение границ контура (по картинке из файла) работают, теперь надо это все вместе соединить и voila :)
Добрался до 32 статьи цикла, похоже это там?
atlab
uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
Не могли бы пояснить?
vargy
т.о. в коде
мы получаем указатель на y-ю строчку пикселей изображения (IPL_DEPTH_8U — 8-битного беззнакового — стандартный тип для загруженных картинок)
noonv
Чтобы было более понятно, приведу этот кусок кода с некоторыми комментариями.
DevOS
cvCreateImage(cvSize(cols, rows), IPL_DEPTH_32F, 1);
Короче вместо
uchar* ptr = (uchar*) (image->imageData + y * image->widthStep);
следует писать так
uchar* ptr = (uchar*) (image->imageData) + y * image->widthStep;
Т.е. сперва привести указатель на данные к нужному типу, а потом уже его использовать.
За статью спасибо, в своё время здорово помогла, особенно последний пример! :)
Grunelf
и мне нужно работать с массивом получившихся оттенков серого (изменить, выгрузить в, к примеру, числовой массив), как лучше всего это сделать?
vcxsaz
noonv
Слева пример вывода в файл, справа — на консоль.
vcxsaz
vcxsaz
vcxsaz
noonv
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.