Подтвердить что ты не робот

Как оценить/определить поверхностные нормали и касательные плоскости в точках изображения глубины?

У меня есть изображение глубины, которое я создал с использованием данных 3D CAD. Это изображение глубины также можно взять с датчика глубины изображения, такого как Kinect или стереокамера. Таким образом, в основном это карта глубины точек, видимых в представлении изображений. Другими словами, это сегментированное облако точек объекта с определенного вида.

Я хотел бы определить (оценивая также) нормали поверхности каждой точки, затем найти тангенциальную плоскость этой точки.

Как я могу это сделать? Я провел некоторое исследование и нашел некоторые методы, но не понял их хорошо (я не мог его реализовать). Что еще более важно, как я могу это сделать в Matlab или OpenCV? Я не мог этого сделать, используя команду surfnorm. AFAIK ему нужна одиночная поверхность, и у меня есть частичные поверхности в изображении глубины.

Это пример изображения глубины.

enter image description here

[EDIT]

Что я хочу сделать, после того, как я получу нормальную поверхность в каждой точке, я создам тангенциальные плоскости в этих точках. Затем используйте эти касательные плоскости, чтобы решить, идет ли эта точка из плоской области или нет, взяв сумму расстояний соседних точек к касательной плоскости.

[EDIT]

4b9b3361

Ответ 1

Итак, в вашем вопросе есть несколько вещей, которые undefined, но я сделаю все возможное, чтобы набросать ответ.

Основная идея того, что вы хотите сделать, - это взять градиент изображения, а затем применить преобразование к градиенту, чтобы получить нормальные векторы. Снять градиент в Matlab легко:

[m, g] = imgradient(d);

дает нам величину (m) и направление (g) градиента (относительно горизонтали и измеренного в градусах) изображения в каждой точке. Например, если мы отображаем величину градиента для вашего изображения, это выглядит так:

enter image description here

Теперь, чем сложнее будет взять эту информацию о градиенте и превратить ее в нормальный вектор. Чтобы сделать это правильно, нам нужно знать, как преобразовать из координат изображения в мировые координаты. Для CAD-сгенерированного изображения, подобного вашему, эта информация содержится в преобразовании проекции, используемом для создания изображения. Для изображения реального мира, подобного тому, который вы получили от Kinect, вам придется искать спецификацию для устройства захвата изображения.

Ключевой частью информации, которая нам нужна, является то, насколько широкий пиксель в реальных координатах? Для не ортонормированных проекций (например, тех, которые используются в реальных устройствах захвата изображений) мы можем приблизить это, предположив, что каждый пиксель представляет свет в пределах фиксированного угла реального мира. Если мы знаем этот угол (назовем его p и измерим его в радианах), то расстояние в реальном мире, покрытое пикселем, равно sin(p) .* d или приблизительно p .* d, где d - глубина изображения при каждый пиксель.

Теперь, если у нас есть эта информация, мы можем построить 3 компонента нормальных векторов:

width = p .* d;
gradx = m .* cos(g) * width;
grady = m .* sin(g) * width;

normx = - gradx;
normy = - grady;
normz = 1;

len = sqrt(normx .^ 2 + normy .^ 2 + normz .^ 2);
x = normx ./ len;
y = normy ./ len;
z = normz ./ len;

Ответ 2

Что предлагает малтенпорт, может быть сделано в пиксельном шейдере. В каждом пиксельном шейдере вы вычисляете два вектора A и B, а перекрестное произведение векторов даст вам нормальное значение. То, как вы вычисляете два вектора, выглядит так:

float2 du //values sent to the shader based on depth image width and height
float2 dv //normally du = float2(1/width, 0) and dv = float2(0, 1/height)
float D = sample(depthtex, uv)
float D1 = sample(depthtex, uv + du)
float D2 = sample(depthtex, uv + dv)
float3 A = float3(du*width_of_image, 0, D1-D)
float3 B = float3(0, dv*height_of_image, D2-D)
float3 normal = AXB
return normal

Это сломается, когда есть разрывы в значениях глубины.

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

float D = sample(depthtex, uv)
float D1 = sample(depthtex, uv + du)
float D3 = sample(depthtex, uv - du)

float dx1 = (D1 - D)/du
float dx2 = (D - D3)/du
float dxx = (dx2 - dx1)/du

Таким же образом вы должны вычислить dyy, dxy and dyx. Поверхность плоская, если dxx = dyy = dxy = dyx = 0.

Как правило, вы выбираете du и dv как 1/ширину и 1/высоту изображения глубины.

Все это происходит на графическом процессоре, который делает все очень быстрым. Но если вам все равно, вы можете запустить этот метод и в CPU. Единственная проблема будет заключаться в том, чтобы вы заменили функцию типа sample и внедрили свою собственную версию. Он примет значение глубины и значения u, v в качестве входных данных и вернет значение глубины в выбранной точке.

Edit:

Здесь существует гипотетическая функция выборки, которая выполняет выборку ближайших соседей на процессоре.

float Sample(const Texture& texture, vector_2d uv){
    return texture.data[(int)(uv.x * texture.width + 0.5)][(int)(uv.y * texture.height + 0.5];
}

Ответ 3

Я опишу, что я думаю, что вам нужно сделать концептуально и предоставить ссылки на соответствующие части opencv,

Чтобы определить нормаль данной (3d) точки в pointcloud: