ОБНОВЛЕНИЕ СТАТУСА ЩЕДРОСТИ:
Я обнаружил, как отобразить линейную линзу, от
destination
координаты кsource
координаты.Как Вы вычисляете радиальное расстояние от центра для движения от подозрительного взгляда до прямолинейного?
1). Я на самом деле изо всех сил пытаюсь инвертировать его и отобразить исходные координаты на координаты назначения. Какова инверсия в коде в стиле функций преобразования, которые я отправил?
2). Я также вижу, что мое неискажение несовершенно на некоторых линзах - по-видимому, те, которые не строго линейны. Каков эквивалентный to-from источник-и-координаты-назначения для тех линз? Снова, больше кода, чем просто математические формулы...
Вопрос, как первоначально указано:
У меня есть некоторые точки, которые описывают положения в снимке, сделанном с линзой подозрительного взгляда.
Я хочу преобразовать эти точки в прямолинейные координаты. Я хочу не исказить изображение.
Я нашел это описание того, как генерировать эффект подозрительного взгляда, но не, как инвертировать его.
Существует также сообщение в блоге, которое описывает, как использовать инструменты, чтобы сделать это; эти изображения от этого:
(1):
SOURCE
Исходная фото ссылка
Вход: Исходное изображение с искажением подозрительного взгляда для фиксации.(2):
DESTINATION
Исходная фото ссылка
Вывод: Исправленное изображение (технически также с перспективным исправлением, но это - отдельный шаг).Как Вы вычисляете радиальное расстояние от центра для движения от подозрительного взгляда до прямолинейного?
Мой функциональный тупик похож на это:
Point correct_fisheye(const Point& p,const Size& img) { // to polar const Point centre = {img.width/2,img.height/2}; const Point rel = {p.x-centre.x,p.y-centre.y}; const double theta = atan2(rel.y,rel.x); double R = sqrt((rel.x*rel.x)+(rel.y*rel.y)); // fisheye undistortion in here please //... change R ... // back to rectangular const Point ret = Point(centre.x+R*cos(theta),centre.y+R*sin(theta)); fprintf(stderr,"(%d,%d) in (%d,%d) = %f,%f = (%d,%d)\n",p.x,p.y,img.width,img.height,theta,R,ret.x,ret.y); return ret; }
С другой стороны, я мог так или иначе преобразовать изображение от подозрительного взгляда до прямолинейного прежде, чем найти точки, но я полностью сбит с толку документацией OpenCV. Существует ли простой способ сделать это в OpenCV, и это работает достаточно хорошо, чтобы сделать это к живому видео каналу?
описание, которое вы упоминаете, гласит, что проекция камерой с булавочным отверстием (которая не вносит искажения объектива) моделируется
R_u = f*tan(theta)
, а проекция обычными камерами с объективом «рыбий глаз» (то есть искаженная) моделируется
R_d = 2*f*sin(theta/2)
Вы уже знаете, R_d и тета, и если бы вы знали фокусное расстояние камеры (представленное f), то исправление изображения было бы вычислительным R_u с точки зрения R_d и теты. Другими словами,
R_u = f*tan(2*asin(R_d/(2*f)))
— это формула, которую вы ищете. Оценка фокусного расстояния f может быть решена путем калибровки камеры или других средств, таких как предоставление пользователю обратной связи о том, насколько хорошо изображение исправлено, или использование знаний из исходной сцены.
Чтобы решить ту же проблему с помощью OpenCV, вам нужно будет получить внутренние параметры камеры и коэффициенты искажения объектива. См., например, Глава 11 Изучение OpenCV (не забудьте проверить исправление ). Затем вы можете использовать такую программу, как эта (написанная с привязками Python для OpenCV), чтобы обратить вспять искажение объектива:
#!/usr/bin/python
# ./undistort 0_0000.jpg 1367.451167 1367.451167 0 0 -0.246065 0.193617 -0.002004 -0.002056
import sys
import cv
def main(argv):
if len(argv) < 10:
print 'Usage: %s input-file fx fy cx cy k1 k2 p1 p2 output-file' % argv[0]
sys.exit(-1)
src = argv[1]
fx, fy, cx, cy, k1, k2, p1, p2, output = argv[2:]
intrinsics = cv.CreateMat(3, 3, cv.CV_64FC1)
cv.Zero(intrinsics)
intrinsics[0, 0] = float(fx)
intrinsics[1, 1] = float(fy)
intrinsics[2, 2] = 1.0
intrinsics[0, 2] = float(cx)
intrinsics[1, 2] = float(cy)
dist_coeffs = cv.CreateMat(1, 4, cv.CV_64FC1)
cv.Zero(dist_coeffs)
dist_coeffs[0, 0] = float(k1)
dist_coeffs[0, 1] = float(k2)
dist_coeffs[0, 2] = float(p1)
dist_coeffs[0, 3] = float(p2)
src = cv.LoadImage(src)
dst = cv.CreateImage(cv.GetSize(src), src.depth, src.nChannels)
mapx = cv.CreateImage(cv.GetSize(src), cv.IPL_DEPTH_32F, 1)
mapy = cv.CreateImage(cv.GetSize(src), cv.IPL_DEPTH_32F, 1)
cv.InitUndistortMap(intrinsics, dist_coeffs, mapx, mapy)
cv.Remap(src, dst, mapx, mapy, cv.CV_INTER_LINEAR + cv.CV_WARP_FILL_OUTLIERS, cv.ScalarAll(0))
# cv.Undistort2(src, dst, intrinsics, dist_coeffs)
cv.SaveImage(output, dst)
if __name__ == '__main__':
main(sys.argv)
Также обратите внимание, что OpenCV использует совершенно другую модель искажения объектива, чем та, на которую вы ссылаетесь.
Если вы считаете, что ваши формулы точны, вы можете вычислить точную формулу с помощью триггера, например:
Rin = 2 f sin(w/2) -> sin(w/2)= Rin/2f
Rout= f tan(w) -> tan(w)= Rout/f
(Rin/2f)^2 = [sin(w/2)]^2 = (1 - cos(w))/2 -> cos(w) = 1 - 2(Rin/2f)^2
(Rout/f)^2 = [tan(w)]^2 = 1/[cos(w)]^2 - 1
-> (Rout/f)^2 = 1/(1-2[Rin/2f]^2)^2 - 1
Однако, как говорит @jmbr, фактическое искажение камеры будет зависеть от объектива и увеличения. Вместо того, чтобы полагаться на фиксированную формулу, вы можете попробовать разложение полинома:
Rout = Rin*(1 + A*Rin^2 + B*Rin^4 + ...)
Настраивая сначала A, а затем коэффициенты более высокого порядка, вы можете вычислить любую разумную локальную функцию (форма разложения использует преимущество симметрии эта проблема). В частности, должна быть возможность вычислить начальные коэффициенты для аппроксимации теоретической функции, приведенной выше.
Кроме того, для получения хороших результатов вам потребуется использовать фильтр интерполяции для создания исправленного изображения. Если искажение не слишком велико, вы можете использовать фильтр, который вы использовали бы для линейного масштабирования изображения без особых проблем.
Изменить: согласно вашему запросу, эквивалентный коэффициент масштабирования для приведенной выше формулы:
(Rout/f)^2 = 1/(1-2[Rin/2f]^2)^2 - 1
-> Rout/f = [Rin/f] * sqrt(1-[Rin/f]^2/4)/(1-[Rin/f]^2/2)
Если вы нанесете указанную выше формулу рядом с tan (Rin / f), вы увидите, что они очень похожи по форме. По сути, искажение от касательной становится серьезным до того, как sin (w) станет сильно отличаться от w.
Обратная формула должна выглядеть примерно так:
Rin/f = [Rout/f] / sqrt( sqrt(([Rout/f]^2+1) * (sqrt([Rout/f]^2+1) + 1) / 2 )
(Оригинальный постер, предоставляющий альтернативу)
Следующая функция отображает координаты места назначения (прямолинейные) на координаты источника (искаженные рыбой). (Буду признателен за помощь в обратном преобразовании)
Я пришел к этому путем проб и ошибок: Я не очень понимаю, почему этот код работает, объяснения и повышение точности приветствуются!
def dist(x,y):
return sqrt(x*x+y*y)
def correct_fisheye(src_size,dest_size,dx,dy,factor):
""" returns a tuple of source coordinates (sx,sy)
(note: values can be out of range)"""
# convert dx,dy to relative coordinates
rx, ry = dx-(dest_size[0]/2), dy-(dest_size[1]/2)
# calc theta
r = dist(rx,ry)/(dist(src_size[0],src_size[1])/factor)
if 0==r:
theta = 1.0
else:
theta = atan(r)/r
# back to absolute coordinates
sx, sy = (src_size[0]/2)+theta*rx, (src_size[1]/2)+theta*ry
# done
return (int(round(sx)),int(round(sy)))
При использовании с коэффициентом 3.0 он успешно искажает изображения, используемые в качестве примеров (я не пытался сделать качественную интерполяцию):
Dead link
(А это из записи в блоге, для сравнения:)
Я слепо реализовал формулы из здесь , поэтому я не могу гарантировать, что он сделает то, что вам нужно.
Используйте auto_zoom
, чтобы получить значение для параметра zoom
.
def dist(x,y):
return sqrt(x*x+y*y)
def fisheye_to_rectilinear(src_size,dest_size,sx,sy,crop_factor,zoom):
""" returns a tuple of dest coordinates (dx,dy)
(note: values can be out of range)
crop_factor is ratio of sphere diameter to diagonal of the source image"""
# convert sx,sy to relative coordinates
rx, ry = sx-(src_size[0]/2), sy-(src_size[1]/2)
r = dist(rx,ry)
# focal distance = radius of the sphere
pi = 3.1415926535
f = dist(src_size[0],src_size[1])*factor/pi
# calc theta 1) linear mapping (older Nikon)
theta = r / f
# calc theta 2) nonlinear mapping
# theta = asin ( r / ( 2 * f ) ) * 2
# calc new radius
nr = tan(theta) * zoom
# back to absolute coordinates
dx, dy = (dest_size[0]/2)+rx/r*nr, (dest_size[1]/2)+ry/r*nr
# done
return (int(round(dx)),int(round(dy)))
def fisheye_auto_zoom(src_size,dest_size,crop_factor):
""" calculate zoom such that left edge of source image matches left edge of dest image """
# Try to see what happens with zoom=1
dx, dy = fisheye_to_rectilinear(src_size, dest_size, 0, src_size[1]/2, crop_factor, 1)
# Calculate zoom so the result is what we wanted
obtained_r = dest_size[0]/2 - dx
required_r = dest_size[0]/2
zoom = required_r / obtained_r
return zoom