Обработка изображений с эффектом «рыбий глаз»

В последние годы набирают популярность сверхширокоугольные (панорманые) камеры типа «fish eye»(«рыбий глаз»), угол зрения которых может достигать 180 градусов и больше. Областями применения данных устройств являются следующие:

- общий контроль помещения большой площади в условиях ограничений по прокладке интерфейсов к видеокамерам или монтажа;

- подсчет посетителей или «тепловых зон»;

- наблюдение в труднодоступных местах;

- в системах помощи владельцу автомобиля.

Но данная характеристика подобных камер приводит к искажениям зарегистрированного изображения, в частности прямые линии превращаются в кривые дуги. Для большинства задач распознавания образов исходные данные панорамных камер не подходят вследствие сильного искажения. В связи с этим, требуется осуществление их предварительно обработки. В дорогостоящих камерах реализована функция восстановления изображения. Но при использовании более бюджетных решений данная функция должна быть реализована в ПО видеоанализа.

Цель данной работы  - формирование навыков восстановления изображений с эффектом «рыбий глаз» с использованием библиотеки OpenCV.

В библиотеке OpenCV реализованы готовые функции для восстановления изображения из данных с эффектом рыбий глаз. В данной работе рассматриваются функции интерфейса языка С.

Для восстановления искаженного изображения обычно необходимо определить параметры камеры, с которой получено изображение. К данным параметрам относятся как характеристики связанные с объективом камеры, так и его ориентацией.

Процесс определения внутренних и внешних параметров называется калибровкой камеры.  Внутренние и внешние параметры камеры позволяют моделировать процесс проекции трехмерных точек на плоскость изображения. Модель проецирования в OpenCV определяется следующим выражением:

где u,v –координаты точки на плоскости изображения;

          X,Y,Z – трехмерные координаты точки;

сx,cy- координаты центра изображения;

fx,fy –фокусные расстояние ;

A –матрица внутренних параметров камеры;

R|t –матрица вращения/смещения камеры.

Процесс восстановления, таким образом, включает следующие этапы:

-калибровка камеры. Для этого требуются данные о координатах нескольких точек в трехмерном пространстве и на плоскости изображения;

- непосредственно восстановление изображения, используя вычисленные значения модели камеры.

В библиотеке OpenCV реализованы функции, позволяющие реализовать все вышеуказанные действия. 

Для калибровки изображения требуются калибровочные геометрические объекты, с известными размерами. Наиболее популярным калибровочным объектом является шахматная доска. Это обусловлено тем, что углы квадратов достаточно хорошо распознаются. Кроме того, могут применяться определенные узоры из окружностей. Для корректного решения системы линейных уравнений, чтобы определить параметры камеры, требуется несколько снимков калибровочного объекта. В примерах библиотеки OpenCV используют не менее 10 снимков. Для нахождения координат углов  шахматных квадратов используется функция cv::findChessboardCorners. В данной работе рассмотрим пример использования в качестве калибровочного объекта узор из окружностей, представленный на рисунке 1.

Рисунок 1 – Объект для калибровки

 

Ниже приведен код для генерации тестового изображения:

//======================================================

       IplImage * img = cvCreateImage(CvSize{ width_img ,height_img }, 8, 3);

//создаем объект для хранения рисунка с заданными размерами и с тремя каналами

       cvSet(img, cvScalar(255, 255, 255));

       //закрашиваем белым цветом

       //определяем интервалы между окружностями

       int dx = width_img / (board_w + 1);

       int dy = height_img / (board_h + 1);

       //в цикле прорисовываем окружности и закрашиваем их

       for (int i=0;i<board_w;i++)

             for (int j = 0; j < board_h; j++)

             {

                    cvCircle(img, CvPoint{dx*(i+1),dy*(j+1)},radius,CV_RGB(0,0,0),2);

                    cvFloodFill(img, CvPoint{ dx*(I + 1),dy*(j + 1) }, CV_RGB(0, 0, 0));

             }

       cvShowImage(“calibrate_img”, img); //отображаем рисунок

       cvWaitKey(0);

       cvSaveImage(“calibrate_img.jpg”, img); //сохраняем тестовое изображение

cvReleaseImage(&img);

 

При отсутствии камеры для получения эффекта «рыбий глаз» можно воспользоваться следующим кодом[3]:

       cv::Mat img;

       img=cv::imread(NameFileImg);

       int w, h;

       w = img.cols;

       h = img.rows;

       cv::Mat intrinsic_matrix=cv::Mat::zeros(3, 3, CV_64F);

       cv::Mat newCamMtx = cv::Mat::zeros(3, 3, CV_64F);

       cv::Mat distortion_coeffs = cv::Mat::zeros(1, 4, CV_64F);

       intrinsic_matrix.at<double>(0, 0) = 3500;

       intrinsic_matrix.at<double>(1, 1) = 3500;

       intrinsic_matrix.at<double>(2, 2) = 1.0;

       intrinsic_matrix.at<double>(0, 2) = w/2;

       intrinsic_matrix.at<double>(1, 2) = h/2;

 

       newCamMtx.at<double>(0, 0) = 3500;

       newCamMtx.at<double>(1, 1) = 3500;

       newCamMtx.at<double>(2, 2) = 1.0;

       newCamMtx.at<double>(0, 2) = w / 2;

       newCamMtx.at<double>(1, 2) = h / 2;

 

       //distortion_coeffs.at<double>(0, 0) = -7.0;

       distortion_coeffs.at<double>(0, 0) = 100.0;

       distortion_coeffs.at<double>(0, 1) = 0.0;

       distortion_coeffs.at<double>(0, 2) = 0.0;

       distortion_coeffs.at<double>(0, 3) = 0.0;

 

       cv::Size image_size=img.size();

       cv::Mat map1, map2;

       cv::initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs,

             cv::Mat(), newCamMtx, image_size,

             CV_16SC2, map1, map2);

       cv::Mat img_res;

       cv::remap(img, img_res, map1, map2, cv::INTER_LINEAR,

             cv::BORDER_CONSTANT, cv::Scalar());

       cv::imshow(“Original”, img);

       cv::imshow(“Distorted”, img_res);

       cv::waitKey(0);

       cv::imwrite(“distorted.jpeg”, img_res);

Для формирования данного эффекта используются две основные функции: initUndistortRectifyMap и remap. Первая формирует карты преобразования пикселей для функции remap на основе следующих данных:

- внутренних параметров камеры intrinsic_matrix, при помощи которой получено исходное изображение;

- внутренних параметров камеры newCamMtx, при помощи которой будет генерироваться новое изображение. В данной задаче они одинаковые;

- вектор коэффициентов искажения. Меняя значения коэффициентов искажения можно получить различные эффекты, в том числе и «рыбий глаз».

На рисунке 2 а) и 2 б) представлены результаты данного преобразования.

а)

б)

Рисунок 2 –Результаты формирования эффекта «рыбий глаз»

Код верхнего уровня, осуществляющий калибровку камеры на основе одного кадра изображения представлен ниже:

int board_n = board_sz.width*board_sz.height; //количество окружностей на калибровочном

//объекте

CvPoint2D32f* corners = new CvPoint2D32f[board_n]; //координаты на плоском изображении

//Заполняются функцией  FindCirclesGrid

       IplImage *gray_image = cvCreateImage(cvGetSize(img), 8, 1); //серая копия кадра

       cvCvtColor(img, gray_image, CV_BGR2GRAY);

bool res = FindCirclesGrid(gray_image, board_sz, corners);//поиск центров окружностей на //искаженном изображении

       if (res)//если нашли координаты всех окружностей

       {

             CvMat* intrinsic_matrix = cvCreateMat(3, 3, CV_32FC1);

             CvMat* distortion_coeffs = cvCreateMat(5, 1, CV_32FC1);

             CalcMatrix(corners, cvGetSize(img), board_sz,

                    intrinsic_matrix, distortion_coeffs);//непосредственная калибровка

             cvSave(ntrinsic_name, intrinsic_matrix);

             cvSave(distortion_name, distortion_coeffs);

             cvReleaseMat(&intrinsic_matrix);

             cvReleaseMat(&distortion_coeffs);

       }

       else

       {

             printf(“Not find all circles\n!”);

} 

На первом этапе осуществляется поиск всех окружностей на искаженном изображении на основе функций поиска контуров, определения центра масс окружностей и сортировки найденных кругов в исходном порядке. Данные операции выполняются функцией FindCirclesGrid. При успешном определении координат окружностей осуществляется расчет параметров камеры функцией CalcMatrix:

int board_n = board_sz.width*board_sz.height;//количестов окружностей

       CvMat* object_points2 = cvCreateMat(board_n, 3, CV_32FC1);//массив для хранения трехмерных //координат окружностей

       CvMat* image_points2 = cvCreateMat(board_n, 2, CV_32FC1); //массив для хранения //координат окружностей на плоскости

       CvMat* point_counts2 = cvCreateMat(1, 1, CV_32SC1);

       //заполняем координаты в цикле

       for (int I = 0; I < board_n; i++) {

             CV_MAT_ELEM(*image_points2, float, I, 0) = corners[i].x;

             CV_MAT_ELEM(*image_points2, float, I, 1) = corners[i].y;

 

             CV_MAT_ELEM(*object_points2, float, I, 0) = (float)(I / board_sz.width);

             CV_MAT_ELEM(*object_points2, float, I, 1) = (float)(i% board_sz.width);

             CV_MAT_ELEM(*object_points2, float, I, 2) = 0.0f;

       }

       CV_MAT_ELEM(*point_counts2, int, 0, 0) = board_n;

       CV_MAT_ELEM(*intrinsic_matrix, float, 0, 0) = 1.0f;

       CV_MAT_ELEM(*intrinsic_matrix, float, 1, 1) = 1.0f;

       cvCalibrateCamera2(object_points2, image_points2, point_counts2,img_size,

             intrinsic_matrix, distortion_coeffs, NULL, NULL, CV_CALIB_FIX_ASPECT_RATIO);

       cvReleaseMat(&object_points2);

       cvReleaseMat(&image_points2);

cvReleaseMat(&point_counts2);

Калибровка камеры осуществляется функцией cvCalibrateCamera2, в которую передаются матрицы с координатами окружностей object_points2, координатами на плоскости в искаженном изображении и количеством окружностей. Трехмерные координаты объектов определяются на основе шага между окружностями, а координата z приравнивается 0. Выходными данными являются внутренние (intrinsic_matrix) и внешние параметры камеры (distortion_coeffs).

Ниже представлен участок кода восстановления изображения:

img = cvLoadImage(NameFileImg);//загрузка искаженного файла

CvMat *intrinsic = (CvMat*)cvLoad(NameFileIntrinsics); //загрузка внутренних параметров //камеры

       CvMat *distortion = (CvMat*)cvLoad(NameFileDistortion); //загрузка внешних параметров //камеры

//определяем карты преобразования пикселей для функции remap

       IplImage* mapx = cvCreateImage(cvGetSize(img), IPL_DEPTH_32F, 1);

       IplImage* mapy = cvCreateImage(cvGetSize(img), IPL_DEPTH_32F, 1);

       cvInitUndistortMap(intrinsic, distortion, mapx, mapy);      

       IplImage *t = cvCloneImage(img);

#ifdef _DEBUG

       cvShowImage(«Calibration», img); //показываем исходное изображение

#endif

       cvRemap(t, img, mapx, mapy); // восстанавливаем изображение

       cvReleaseImage(&t);

#ifdef _DEBUG

       cvShowImage(“Undistort”, img); //

#endif

       cvSaveImage(NameFileImgRes, img);

#ifdef _DEBUG

       cvWaitKey(0);

#endif

       cvReleaseMat(&intrinsic);

       cvReleaseMat(&distortion);

cvReleaseImage(&img);

Метод cvInitUndistortMap формирует карты преобразований на основе внутренних параметров камеры и данных об искажениях Непосредственное восстановление осуществляется функцией cvRemap. Пример результата восстановления изображения представлен на рисунке 3.

Рисунок 3 – Результат восстановления изображения

 

Таким образом, в данной работе представлены описание применения функций библиотеки OpenCV для восстановления изображений с эффектом «рыбий глаз».