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
//
// https://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 — матрица трансформации 3×3 или 4×4

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
//
// https://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/Матрица_перехода


0 комментариев на «“30. OpenCV шаг за шагом. Трансформация изображения — аффинные преобразования, гомография”»

    • увеличить размер картинки для хранения результата 🙂

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

    • Есть какие-то предложение как это преодолеть?

    • поворачивайте по центру, а не от координат 0,0

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

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение