Искажение объектива камеры в OpenGL

Я пытаюсь смоделировать эффект искажения объектива для своего проекта SLAM. Отсканированное цветное 3D-облако точек уже задано и загружено в OpenGL. То, что я пытаюсь сделать, это визуализировать 2D-сцену в заданной позе и выполнить некоторую визуальную одометрию между реальным изображением с камеры «рыбий глаз» и визуализированным изображением. Поскольку камера имеет серьезные искажения объектива, это также следует учитывать на этапе рендеринга.

Проблема в том, что я понятия не имею, куда поставить дисторсию объектива. Шейдеры?

Я нашел несколько открытых кодов, которые помещают искажение в геометрический шейдер. Но я думаю, что эта модель искажения отличается от модели искажения объектива в сообществе Computer Vision. В сообществе CV дисторсия линзы обычно возникает на плоскости проекции.

Этот очень похож на мою работу, но они не t используется модель искажения.

У кого-нибудь есть хорошая идея?

Я только что нашел другая реализация. Их код реализовал искажение как во фрагментном шейдере, так и в геометрическом шейдере. Но в моей ситуации применим вариант фрагментного шейдера. Таким образом, я предполагаю, что следующее будет работать:

# vertex shader
p'=T.model x T.view x p
p_f = FisheyeProjection(p') // custom fish eye projection

person C.O Park    schedule 11.06.2017    source источник
comment
Другой способ, которым это часто делается, — рендеринг в текстуру, которую вы затем накладываете на прямоугольник, форму которого затем искажаете.   -  person p10ben    schedule 12.06.2017


Ответы (2)


Дисторсия объектива обычно превращает прямые линии в кривые. Однако при растеризации линий и треугольников с помощью OpenGL края примитивов остаются прямыми, независимо от того, как вы преобразуете вершины.

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

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

Другой метод — рендеринг в кубическую карту, а затем использование шейдера для создания эквивалента объектива. Я бы рекомендовал это для создания изображений «рыбий глаз».

Само искажение обычно представляется полиномом порядка 3-5, отображающим неискаженное угловое расстояние от оптической центральной оси в искаженное угловое расстояние.

person datenwolf    schedule 12.06.2017
comment
Привет! Спасибо за идеи. Я использую представление карты на основе серфинга, где каждая точка имеет диск с фиксированным радиусом около 1 см или меньше, что достаточно мало. Итак, я думаю, ваше первое решение без их разделения будет работать для меня. Вторая идея тоже звучит неплохо. Я проверю кубмапу. Спасибо! - person C.O Park; 12.06.2017

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

Вот код, реализующий модель рационального искажения OpenCV (см. https://docs.opencv.org/4.0.1/d9/d0c/group__calib3d.html для формул):

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal_in;
layout (location = 2) in vec2 texture_coordinate_in;
uniform mat4 model_matrix;
uniform mat4 view_matrix;
uniform float dist_coeffs[8];
uniform mat4 projection_matrix;
uniform vec3 light_position;
out vec2 texture_coordinate;
out vec3 normal;
out vec3 light_direction;

// distort the real world vertices using the rational model
vec4 distort(vec4 view_pos)
{
  // normalize
  float z = view_pos[2];
  float z_inv = 1 / z;
  float x1 = view_pos[0] * z_inv;
  float y1 = view_pos[1] * z_inv;
  // precalculations
  float x1_2 = x1*x1;
  float y1_2 = y1*y1;
  float x1_y1 = x1*y1;
  float r2 = x1_2 + y1_2;
  float r4 = r2*r2;
  float r6 = r4*r2;
  // rational distortion factor
  float r_dist = (1 + dist_coeffs[0]*r2 +dist_coeffs[1]*r4 + dist_coeffs[4]*r6) 
    / (1 + dist_coeffs[5]*r2 + dist_coeffs[6]*r4 + dist_coeffs[7]*r6);
  // full (rational + tangential) distortion
  float x2 = x1*r_dist + 2*dist_coeffs[2]*x1_y1 + dist_coeffs[3]*(r2 + 2*x1_2);
  float y2 = y1*r_dist + 2*dist_coeffs[3]*x1_y1 + dist_coeffs[2]*(r2 + 2*y1_2);
  // denormalize for projection (which is a linear operation)
  return vec4(x2*z, y2*z, z, view_pos[3]);
}

void main()
{
  vec4 local_pos = vec4(position, 1.0);
  vec4 world_pos  =  model_matrix * local_pos;
  vec4 view_pos = view_matrix * world_pos;
  vec4 dist_pos = distort(view_pos);
  gl_Position = projection_matrix * dist_pos;
  // lighting on world coordinates not distorted ones
  normal = mat3(transpose(inverse(model_matrix))) * normal_in;
  light_direction = normalize(light_position - world_pos.xyz);
  texture_coordinate = texture_coordinate_in;
}

Важно отметить, что искажения рассчитываются в z-нормализованных координатах, но денормализованы в координаты вида в последней строке искажения. Это позволяет использовать проекционную матрицу, подобную той, что приведена в этом сообщении: .github.io/2013/06/03/calilated_cameras_in_opengl/

Изменить: для всех, кто хочет увидеть код в контексте, я опубликовал код в небольшой библиотеке шейдер искажения используется в этом пример.

person Tuebel    schedule 27.02.2019
comment
Привет, приятель, я реализовал код в своем шейдере, и он работает очень хорошо, за исключением случаев, когда я подхожу слишком близко к объекту, иногда я получаю странные артефакты. Это похоже на то, что когда вершина находится вне определенного домена, она будет проецироваться неправильно. - person user6138759; 21.04.2020
comment
Возможно, вы ближе к своему объекту, чем позволяет усеченная пирамида? - person Tuebel; 23.04.2020
comment
Нет, это не проблема усеченной пирамиды, потому что, когда я делаю проекцию без искажений, проблем не возникает. - person user6138759; 23.04.2020
comment
Я знаю, что ProjectPoints OpenCV имеет свои ограничения, иногда, когда проекция неискаженных точек выходит за пределы определенного эллипса вокруг изображения, искажение дает странные результаты. Так что я думаю, что это та же проблема. - person user6138759; 23.04.2020