30. OpenCV шаг за шагом. Трансформация изображения - аффинные преобразования, гомография


Оглавление
1. OpenCV шаг за шагом. Введение.
2. Установка.
3. Hello World.
4. Загрузка картинки.
...
27. Детектор границ Кенни (Canny)
28. Преобразование Хафа
29. Интегральное изображение
30. Трансформация изображения — аффинные преобразования, гомография

Преобразование плоскости(изображения) называется аффинным, если оно взаимно однозначно и образом любой прямой является прямая.
Взаимно однозначное преобразование, переводит каждую точку плоскости(изображения) I в другую точку плоскости(изображения) I', таким образом, что каждой точке I соответствует какая-то точка I'.

Примеры аффинных преобразований:
* обычное движение — фактически движение является параллельным переносом
* повороты
* растяжения или сжатия относительно прямой

Для осуществления аффинных преобразований, обычно используется матрица перехода.

В OpenCV аффинные преобразования осуществляются функцией cvWarpAffine():

CVAPI(void)  cvWarpAffine( const CvArr* src, CvArr* dst, const CvMat* map_matrix,
                           int flags CV_DEFAULT(CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS),
                           CvScalar fillval CV_DEFAULT(cvScalarAll(0)) );
— применение аффинной трансформации
src — исходное изображение
dst — целевое изображение
map_matrix — матрица трансформации 2х3
flags — комбинация флагов метода интерполяции и флагов:
#define  CV_WARP_FILL_OUTLIERS 8
#define  CV_WARP_INVERSE_MAP  16


CV_WARP_FILL_OUTLIERS — заполнить все пиксели целевого изображения (если пиксели отсутствуют на исходном изображени используются fillval)
CV_WARP_INVERSE_MAP — используется обратная трансформация из dst в src

fillval — значение для заполнения пикселей вне исходного изображения
dst(x', y') = src(x; y) 


Важным параметром функции cvWarpAffine() является map_matrix — матрица трансформации.
Сгенерировать эту матрицу можно с помощью двух методов — cvGetAffineTransform() или cv2DRotationMatrix():

CVAPI(CvMat*) cvGetAffineTransform( const CvPoint2D32f * src,
                                    const CvPoint2D32f * dst,
                                    CvMat * map_matrix );
— рассчёт матрицы аффинной трансформации из трёх пар точек
src — 3 координаты исходного изображения
dst — 3 соотносящихся координаты целевого изображения
map_matrix — получаемая матрица трансформации 2х3

CVAPI(CvMat*)  cv2DRotationMatrix( CvPoint2D32f center, double angle,
                                   double scale, CvMat* map_matrix );
— рассчитывает аффинную матрицу 2D-вращения
center — центр вращения на исходном изображении
angle — угол поворота в гардусах (положительная величина означает вращение против часовой стрелки)
scale — масштаб
map_matrix — указатель на получаемую матрицу 2х3

a 	b 	(1 - a)*center.x - b*center.y
b-1 	a 	center.x - (1 - a)*center.y

, где
a = scale - cos(angle)
b = scale - sin(angle)


Пример программы, которая выполняет 2 аффинных преобразования — сначала по матрице, полученной из 3 пар точек, а затем по сгенерированной матрице вращения (поворот вокруг центра изображения на 60 градусов по часовой стрелке с масштабом 0.7).

//
// модифицированный пример Example 6-2. Аффинные трансформации
//
// из книги:
//   Learning OpenCV: Computer Vision with the OpenCV Library
//     by Gary Bradski and Adrian Kaehler
//     Published by O'Reilly Media, October 3, 2008
//
// http://robocraft.ru
//

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
	IplImage *src=0, *dst=0;

	// имя картинки задаётся первым параметром
	char* filename = argc == 2 ? argv[1] : "Image0.jpg";
	// получаем картинку
	src = cvLoadImage(filename,1);

	printf("[i] image: %s\n", filename);
	assert( src != 0 );

	// покажем изображение
	cvNamedWindow( "image", 1 );
	cvShowImage( "image", src );

	CvPoint2D32f srcTri[3], dstTri[3];
	// матрицы трансформации
	CvMat* rot_mat = cvCreateMat(2, 3, CV_32FC1);
	CvMat* warp_mat = cvCreateMat(2, 3, CV_32FC1);

	// клонируем изображение
	dst = cvCloneImage(src);

#if 1

	//
	// трансформация по точкам
	//

	// по заданным точкам 
	// вычислим матрицу преобразования
	srcTri[0].x = 0;          //src Top left
	srcTri[0].y = 0;
	srcTri[1].x = src->width - 1;    //src Top right
	srcTri[1].y = 0;
	srcTri[2].x = 0;          //src Bottom left
	srcTri[2].y = src->height - 1;
	//- - - - - - - - - - - - - - -//
	dstTri[0].x = src->width*0.0;    //dst Top left
	dstTri[0].y = src->height*0.33;
	dstTri[1].x = src->width*0.85; //dst Top right
	dstTri[1].y = src->height*0.25;
	dstTri[2].x = src->width*0.15; //dst Bottom left
	dstTri[2].y = src->height*0.7;
	// получаем матрицу
	cvGetAffineTransform(srcTri,dstTri,warp_mat);
	// выполняем трансформацию
	cvWarpAffine(src,dst,warp_mat);
#endif

	// сохраним результат трансформации
	cvCopy(dst, src);

#if 1
	//
	// поворот изображения
	//

	// рассчёт матрицы вращения
	CvPoint2D32f center = cvPoint2D32f(src->width/2, src->height/2);
	double angle = -60.0;	// на 60 градусов по часовой стрелке
	double scale = 0.7;		// масштаб
	cv2DRotationMatrix(center,angle,scale,rot_mat);

	// выполняем вращение
	cvWarpAffine(src, dst, rot_mat);
#endif

	// показываем
	cvNamedWindow( "cvWarpAffine");
	cvShowImage( "cvWarpAffine", dst );

	// ждём нажатия клавиши
	cvWaitKey(0);

	// освобождаем ресурсы
	cvReleaseMat(&rot_mat);
	cvReleaseMat(&warp_mat);

	cvReleaseImage(&src);
	cvReleaseImage(&dst);

	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

скачать иcходник (30-cvWarpAffine.cpp)


Именно афинное преобразование я использовал для поворота картинки, получаемой с управляемой камеры, конструкция которой вынудила закрепить её боком:


для этого я просто написал функцию-обёртку:

// функция поворота изображения на заданный угол
void rotate(IplImage* _image, double _angle=90)
{
        // матрицы трансформации
        CvMat* rot_mat = cvCreateMat(2, 3, CV_32FC1);
        // вращение относительно центра изображения
        CvPoint2D32f center = cvPoint2D32f(_image->width/2, _image->height/2);
        double angle = _angle;
        double scale = 1;
        cv2DRotationMatrix(center,angle,scale,rot_mat);
        
        IplImage* Temp = 0;
        Temp = cvCreateImage(cvSize(_image->width, _image->height) , _image->depth, _image->nChannels);

        // выполняем вращение
        cvWarpAffine(_image,Temp,rot_mat);

        // сохраняем результат
        cvCopy(Temp, _image);

        cvReleaseImage(&Temp);
        cvReleaseMat(&rot_mat);
}


Перспективная трансформация (гомография)

CVAPI(void)  cvPerspectiveTransform( const CvArr* src, CvArr* dst,
                                     const CvMat* mat );
— выполнение перспективной трансформации каждого элемента массива
src — исходный массив (32FC3)
dst — целевой массив (32FC3)
mat — матрица трансформации 3x3 или 4x4

CVAPI(void)  cvWarpPerspective( const CvArr* src, CvArr* dst, const CvMat* map_matrix,
                                int flags CV_DEFAULT(CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS),
                                CvScalar fillval CV_DEFAULT(cvScalarAll(0)) );
— применение перспективной трансформации изображения
src — исходное изображение
dst — целевое изображение
map_matrix — матрица трансформации 3х3
flags — комбинация флагов метода интерполяции и флагов:
#define  CV_WARP_FILL_OUTLIERS 8
#define  CV_WARP_INVERSE_MAP  16


CV_WARP_FILL_OUTLIERS — заполнить все пиксели целевого изображения (если пиксели отсутствуют на исходном изображени используются fillval)
CV_WARP_INVERSE_MAP — используется обратная трансформация из dst в src

fillval — значение для заполнения пикселей вне исходного изображения

CVAPI(CvMat*) cvGetPerspectiveTransform( const CvPoint2D32f* src,
                                         const CvPoint2D32f* dst,
                                         CvMat* map_matrix );
— рассчёт матрицы перспективной трансформации из 4 пар точек
src — 4 координаты исходного изображения
dst — 4 соотносящихся координаты целевого изображения
map_matrix — получаемая матрица трансформации 3х3

//
// модифицированный пример Example 6-3. Перспективная трансформация
//
// из книги:
//   Learning OpenCV: Computer Vision with the OpenCV Library
//     by Gary Bradski and Adrian Kaehler
//     Published by O'Reilly Media, October 3, 2008
//
// http://robocraft.ru
//

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
	IplImage *src=0, *dst=0;

	// имя картинки задаётся первым параметром
	char* filename = argc == 2 ? argv[1] : "Image0.jpg";
	// получаем картинку
	src = cvLoadImage(filename,1);

	printf("[i] image: %s\n", filename);
	assert( src != 0 );

	// покажем изображение
	cvNamedWindow( "image", 1 );
	cvShowImage( "image", src );

	// точки
	CvPoint2D32f srcQuad[4], dstQuad[4];
	// матрица преобразования
	CvMat* warp_matrix = cvCreateMat(3,3,CV_32FC1);

	// клонируем картинку
	dst = cvCloneImage(src);

	// задаём точки
	srcQuad[0].x = 0;           //src Top left
	srcQuad[0].y = 0;
	srcQuad[1].x = src->width - 1;  //src Top right
	srcQuad[1].y = 0;
	srcQuad[2].x = 0;           //src Bottom left
	srcQuad[2].y = src->height - 1;
	srcQuad[3].x = src->width - 1;  //src Bot right
	srcQuad[3].y = src->height - 1;
	//- - - - - - - - - - - - - -//
	dstQuad[0].x = src->width*0.05;  //dst Top left
	dstQuad[0].y = src->height*0.33;
	dstQuad[1].x = src->width*0.9;  //dst Top right
	dstQuad[1].y = src->height*0.25;
	dstQuad[2].x = src->width*0.2;  //dst Bottom left
	dstQuad[2].y = src->height*0.7;      
	dstQuad[3].x = src->width*0.8;  //dst Bot right
	dstQuad[3].y = src->height*0.9;

	// получаем матрицу преобразования
	cvGetPerspectiveTransform(srcQuad,dstQuad,warp_matrix);
	// преобразование перспективы
	cvWarpPerspective(src,dst,warp_matrix);

	// показываем
	cvNamedWindow( "cvWarpPerspective", 1 );
	cvShowImage( "cvWarpPerspective", dst );

	// ждём нажатия клавиши
	cvWaitKey(0);

	// освобождаем ресурсы
	cvReleaseMat(&warp_matrix);

	cvReleaseImage(&src);
	cvReleaseImage(&dst);

	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

скачать иcходник (30-cvWarpPerspective.cpp)


Ещё методы OpenCV для трансформации изображения:

CVAPI(void)  cvRemap( const CvArr* src, CvArr* dst,
                      const CvArr* mapx, const CvArr* mapy,
                      int flags CV_DEFAULT(CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS),
                      CvScalar fillval CV_DEFAULT(cvScalarAll(0)) );
— применяет базовую геометрическую трансформацию, используя специальную координатную карту
формула:
dst(x, y) = src(mapx(x, y), mapy(x, y))

src — исходное изображение
dst — целевое изображение
mapx — карта координат по x (изображение типа 32FC1)
mapy — карта координат по y (изображение типа 32FC1)
flags — флаг — комбинация флагов метода интерполяции и следующего флага:
#define  CV_WARP_FILL_OUTLIERS 8


CV_WARP_FILL_OUTLIERS — заполнить все пиксели целевого изображения (если пиксели отсутствуют на исходном изображени используются fillval)
fillval — значение для заполнения пикселей вне исходного изображения

— используется для устранения дисторсии

CVAPI(void)  cvTransform( const CvArr* src, CvArr* dst,
                          const CvMat* transmat,
                          const CvMat* shiftvec CV_DEFAULT(NULL));
#define cvMatMulAddS cvTransform
— матричная трансформация каждого элемента массива
src — исходный массив
dst — целевой массив
transmat — матрица трансформации (типа float)
shiftvec — вектор сдвига (типа float)

формула:
dst(I) = transmat*src(I) + shiftvec

Каждый элемент N-мерного массива src представляется N-элементным вектором, который трансформируется MxN матрицой transmat и вектором сдвига shiftvec.

CVAPI(void)  cvGetQuadrangleSubPix( const CvArr* src, CvArr* dst,
                                    const CvMat* map_matrix );
— получение изображения с субпиксельной точностью
src — исходное изображение
dst — получаемый quadrangle
map_matrix — матрица трансформации 2х3

dst(x, y) = src(A11x' + A12y' + b1, A21x' + A22y' + b2)

, где
x' = x - (width(dst) - 1)/2
y' = y - (height(dst) - 1)/2

map_matrix =
A11 A12 b1
A21 A22 b2


Далее: 31. Типы данных OpenCV — хранилище памяти, последовательность

Ссылки:
http://ru.wikibooks.org/wiki/Аффинные_преобразования
http://ru.wikipedia.org/wiki/Аффинное_преобразование
http://ru.wikipedia.org/wiki/Матрица_перехода
  • +1
  • 12 августа 2011, 07:33
  • noonv

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

RSS свернуть / развернуть
+
0
объясните пли разницу между аффинными и перспективными преобразованиями
avatar

tipabot

  • 15 сентября 2011, 18:51
+
0
А почему вы видео уроки не делаете?
avatar

korvova

  • 25 сентября 2011, 12:33
+
0
как избежать обрезки углов при повороте?
avatar

basek

  • 19 мая 2013, 14:35
+
0
увеличить размер картинки для хранения результата :)
avatar

noonv

  • 19 мая 2013, 19:59
+
0
Пробывал, но изображение все равно обрезается с лева + не нашел адекватного способа определения размеров результата операции.
avatar

basek

  • 19 мая 2013, 21:35
+
0
Есть какие-то предложение как это преодолеть?
avatar

basek

  • 28 мая 2013, 16:51
+
0
поворачивайте по центру, а не от координат 0,0
avatar

tubsids

  • 10 февраля 2014, 09:24
+
0
ВОПРОС!
А как получить изображение из кадра с FISH EYE в панорамную картинку?
Спасбо
avatar

tubsids

  • 15 января 2014, 15:10
+
0
Подсажите плис:
Когда мы сделали преобразование — каким способом возможно «перспективное» перемещение, т.е. наблюдатель находится в состоянии покоя, а картинка перемещается (например как в навигационных картах при движении)?
avatar

tubsids

  • 10 февраля 2014, 09:22
комментарий был удален


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