В данной статье будет рассказываться о применении библиотеки машинного зрения (openCV) для удаления эффекта радиального искажения (дисторсии) с фото и видео. Данный эффект также известен как эффект рыбьего глаза (fisheye) или distortion. Решение написать данную статью было принято после нескольких дней поиска информации в интернете. Не смотря на то, что есть гайды на английском языке, они не объясняют как правильно установить openCV, чтобы все работало. В статье присутствует готовый код.
Сразу привожу фото итогового результата. Слева оригинальное фото, справа — обработанное:
Сборка и установка openCV
Первое, что нужно сделать, это грамотно установить библиотеку openCV. Для этого скачиваем из официального репозитория два проекта — openCV и opencv_contrib.
git clone https://github.com/opencv/opencv.git
git clone https://github.com/opencv/opencv_contrib.git
Пока загружается openCV, устанавливаем видеокодек ffmpeg:
sudo apt-get install ffmpeg
Заходим в папку openCV, создаем подпапку buid и заходим в нее. Вся работа по сборке и установке библиотеки openCV будет производиться из этой директории.
cd opencv
mkdir build
cd build/
Для сборки библиотеки выполняем следующие команды:
cmake .. -DOPENCV_EXTRA_MODULES_PATH=/путь к папке opencv_contrib/modules/ /путь к папке opencv/
make -j5
sudo make install
У меня сборка заняла около полутора часов, установка — несколько минут. Обратите внимание: если у вас возникла ошибка при сборке(выполнение команды cmake), для нового запуска необходимо удалить файл CMakeCache.txt. После установки можем проверить все ли правильно получилось. Для этого можно вызвать рабочую среду python и импортировать библиотеку openCV. Если никаких ошибок не произошло, то вы все сделали правильно. Вторая строчка покажет, какая версия у вас установлена. На момент написания статьи я пользовался 3 версией библиотеки.
import cv2
print ("OpenCV version : {0}".format(cv2.__version__))
Калибровка камеры
Для того, чтобы убрать искажения, нам необходимо определить калибровочные коэффициенты для нашей камеры. Для этого необходимо скачать картинку с шахматной доской, сделать 5-6 снимков на камеру, изображения с которой мы хотим обработать. Все изображения необходимо конвертировать в формат png. Далее выполняем следующий код:
from __future__ import print_function
import numpy as np
import cv2
from common import splitfn
import os
if __name__ == '__main__':
import sys
import getopt
from glob import glob
args, img_mask = getopt.getopt(sys.argv[1:], '', ['debug=', 'square_size='])
args = dict(args)
args.setdefault('--debug', '/рабочая директория/')
args.setdefault('--square_size', 1.0)
if not img_mask:
img_mask = '/папка с изображениями/*.png'
else:
img_mask = img_mask[0]
img_names = glob(img_mask)
debug_dir = args.get('--debug')
if not os.path.isdir(debug_dir):
os.mkdir(debug_dir)
square_size = float(args.get('--square_size'))
pattern_size = (9, 6)
pattern_points = np.zeros((np.prod(pattern_size), 3), np.float32)
pattern_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2)
pattern_points *= square_size
obj_points = []
img_points = []
h, w = 0, 0
img_names_undistort = []
for fn in img_names:
print('processing %s... ' % fn, end='')
img = cv2.imread(fn, 0)
if img is None:
print("Failed to load", fn)
continue
h, w = img.shape[:2]
found, corners = cv2.findChessboardCorners(img, pattern_size)
if found:
term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.1)
cv2.cornerSubPix(img, corners, (5, 5), (-1, -1), term)
if not found:
print('chessboard not found')
continue
img_points.append(corners.reshape(-1, 2))
obj_points.append(pattern_points)
print('ok')
rms, camera_matrix, dist_coefs, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, (w, h), None, None)
print("\nRMS:", rms)
print("camera matrix:\n", camera_matrix)
print("distortion coefficients: ", dist_coefs.ravel())
cv2.destroyAllWindows()
В результате выполнения данного скрипта в консоли появится сообщение об обработанных фото и отобразятся два важных параметра — camera matrix и distortion coefficients. Это и есть те калибровочные коэффициенты, которые нам нужны.
Обработка фото и видео
Для обработки фото и/или видио необходимо выполнить скрипты, приведенные ниже. В скриптах нужно указать свои калибровочные параметры и рабочие папки.
from __future__ import print_function
import numpy as np
import cv2
import glob
from matplotlib import pyplot as plt
from common import splitfn
import os
img_names_undistort = [img for img in glob.glob("/путь до папки с фотографиями/*.png")]
new_path = "/путь для сохранения обработанных изображений/"
camera_matrix = np.array([[1.26125746e+03, 0.00000000e+00, 9.40592038e+02],
[0.00000000e+00, 1.21705719e+03, 5.96848905e+02],
[0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]);
dist_coefs = np.array([-0.49181345, 0.25848255, -0.01067125, -0.00127517, -0.01900726]);
i = 0
#for img_found in img_names_undistort:
while i < len(img_names_undistort):
img = cv2.imread(img_names_undistort[i])
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(camera_matrix, dist_coefs, (w, h), 1, (w, h))
dst = cv2.undistort(img, camera_matrix, dist_coefs, None, newcameramtx)
dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)
# crop and save the image
x, y, w, h = roi
dst = dst[y:y+h-50, x+70:x+w-20]
name = img_names_undistort[i].split("/")
name = name[6].split(".")
name = name[0]
full_name = new_path + name + '.jpg'
#outfile = img_names_undistort + '_undistorte.png'
print('Undistorted image written to: %s' % full_name)
cv2.imwrite(full_name, dst)
i = i + 1
from __future__ import print_function
import numpy as np
import cv2
import glob
from matplotlib import pyplot as plt
from common import splitfn
import os
FILENAME_IN = "videoin.mp4"
FILENAME_OUT = "videoout.mp4"
CODEC = 'mp4v'
camera_matrix = np.array([[1.26125746e+03, 0.00000000e+00, 9.40592038e+02],
[0.00000000e+00, 1.21705719e+03, 5.96848905e+02],
[0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]);
dist_coefs = np.array([-3.18345478e+01, 7.26874187e+02, -1.20480816e-01, 9.43789095e-02, 5.28916586e-01]);
print ("OpenCV version : {0}".format(cv2.__version__))
print((cv2.__version__).split('.'))
# Load video
video = cv2.VideoCapture(FILENAME_IN)
fourcc = cv2.VideoWriter_fourcc(*list(CODEC))
fps = video.get(cv2.CAP_PROP_FPS)
frame_count = video.get(cv2.CAP_PROP_FRAME_COUNT)
size = (int(video.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)))
sizew = (1676, 846)
writer = cv2.VideoWriter(FILENAME_OUT, fourcc, 25, sizew)
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(camera_matrix, dist_coefs, (size[0], size[1]), 1, (size[0], size[1]))
x, y, w, h = roi
M = cv2.getRotationMatrix2D((size[0]/2,size[1]/2),5,1)
while video.grab() is True:
print("On frame %i of %i."%(video.get(cv2.CAP_PROP_POS_FRAMES), frame_count))
frame = video.retrieve()[1]
frame = cv2.undistort(frame, camera_matrix, dist_coefs, None, newcameramtx)
frame = frame[y:y+h-50, x+70:x+w-20]
writer.write(frame)
video.release()
writer.release()
Комментарии (19)
SyavaSyava
29.10.2017 00:41Стесняюсь спросить, а где эта чудесная программа взяла детали, недостающие на фото слева, которые потом добавила на фото справа?
Если что – речь про коричневый фон вокруг мишени слева в средней части. На левом фото его точно нет…
Про всё остальное ничего сказать не могу – не понял совершенно ничего… Но это не ваша вина )))
iroln
29.10.2017 02:13В статье ни слова о теории, об используемом алгоритме коррекции дисторсии.
А зачем contrib понадобился? Калибровка камеры входит в официальный OpenCV.
Решение написать данную статью было принято после нескольких дней поиска информации в интернете. Не смотря на то, что есть гайды на английском языке, они не объясняют как правильно установить openCV, чтобы все работало. В статье присутствует готовый код.
Всё тут:
https://docs.opencv.org/3.1.0/dc/dbb/tutorial_py_calibration.html
Устанавлиать opencv очень просто:
pip install opencv-python
или если нужен contrib
pip install opencv-contrib-python
import getopt
Серьёзо? argparse надо использовать.
И да, в статье слишком много слова "данный". Ужасное слово. :)
TheShock
29.10.2017 08:40+1Сразу привожу фото итогового результата. Слева оригинальное фото, справа — обработанное:
На хабре принято выкладывать фото на web.habrastorage.org, а не во вконтакте (он у многих заблочен)
kraidiky
29.10.2017 15:36Очень интересно, теперь бы прибавить к этому какой-нибудь путный алгоритм стабилизации изображений и мой склад видео с экшенкамеры перестанет лежать мёртвым грузом. :)
muxa_ru
29.10.2017 23:05+1А есть такой же, но
с перламутровыми пуговицамивыравнивающий фотографию изогнутых книжных страниц?Psychopompe
29.10.2017 23:13+1Тогда нужен обобщённый метод для пакетной обработки произвольных, но однотипных искажений.
HerrDonUlt
pip install opencv-python
Zenker
Ставить opencv из пипа чревато, там кросплатформенная сборка, в которой не работает, например, cv2.VideoCapture(), причём без всяких сообщений об ошибках, что может подарить незабываемые часы отладки. Могу предположить, что это не единственный такой сюрприз. Поэтому ставить надо, по возможности, из репозитория своего дистрибутива. Собирать самому есть смысл разве что если нужна свежая версия или есть требования к производительности. Вот тут есть немного про оптимизацию под raspberry: www.pyimagesearch.com/2017/10/09/optimizing-opencv-on-the-raspberry-pi
veveve
Тоже недавно сталкивался. А вы не в курсе, почему именно не работает? Чем эта сборка отличается?
Zenker
А это так и задумано, у них на гитхабе об этом прямо написано: