исправление искажения подозрительного взгляда программно

ОБНОВЛЕНИЕ СТАТУСА ЩЕДРОСТИ:

Я обнаружил, как отобразить линейную линзу, от 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, и это работает достаточно хорошо, чтобы сделать это к живому видео каналу?

49
задан Community 23 May 2017 в 02:17
поделиться

4 ответа

описание, которое вы упоминаете, гласит, что проекция камерой с булавочным отверстием (которая не вносит искажения объектива) моделируется

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 использует совершенно другую модель искажения объектива, чем та, на которую вы ссылаетесь.

33
ответ дан 7 November 2019 в 11:54
поделиться

Если вы считаете, что ваши формулы точны, вы можете вычислить точную формулу с помощью триггера, например:

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 )
4
ответ дан 7 November 2019 в 11:54
поделиться

(Оригинальный постер, предоставляющий альтернативу)

Следующая функция отображает координаты места назначения (прямолинейные) на координаты источника (искаженные рыбой). (Буду признателен за помощь в обратном преобразовании)

Я пришел к этому путем проб и ошибок: Я не очень понимаю, почему этот код работает, объяснения и повышение точности приветствуются!

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

(А это из записи в блоге, для сравнения:)

Using Panotools

8
ответ дан 7 November 2019 в 11:54
поделиться

Я слепо реализовал формулы из здесь , поэтому я не могу гарантировать, что он сделает то, что вам нужно.

Используйте 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
3
ответ дан 7 November 2019 в 11:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: