Перерисовать изображение с 3D-перспективы до 2d - программирование
Подтвердить что ты не робот

Перерисовать изображение с 3D-перспективы до 2d

Мне нужно обратное преобразование перспективы, написанное на Паскале/Дельфи/Лазаресе. См. Следующее изображение:

image process

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

function redraw_3d_to_2d(sourcebitmap:tbitmap, sourceaspect:extended, point_a, point_b, point_c, point_d:tpoint, megapixelcount:integer):tbitmap;
var
   destinationbitmap:tbitmap;
   x,y,sx,sy:integer;
begin
  destinationbitmap:=tbitmap.create;
  destinationbitmap.width=megapixelcount*sourceaspect*???; // I dont how to calculate this
  destinationbitmap.height=megapixelcount*sourceaspect*???; // I dont how to calculate this
  for x:=0 to destinationbitmap.width-1 do
    for y:=0 to destinationbitmap.height-1 do
    begin
        sx:=??;
        sy:=??;
        destinationbitmap.canvas.pixels[x,y]=sourcebitmap.canvas.pixels[sx,sy];
    end;
  result:=destinationbitmap;
end;

Мне нужна настоящая формула... Поэтому решение OpenGL не было бы идеальным...

4b9b3361

Ответ 1

Примечание. Существует версия этого с правильной математической установкой в ​​Math SE.

Вычисление проективного преобразования

Перспектива - это частный случай проективного преобразования который, в свою очередь, определяется четырьмя точками.

Шаг 1: Начиная с 4 позиций исходного изображения с именем (x1,y1) через (x4,y4), вы решаете следующие системы линейных уравнений:

[x1 x2 x3] [λ]   [x4]
[y1 y2 y3]∙[μ] = [y4]
[ 1  1  1] [τ]   [ 1]

Колонки образуют однородные координаты: еще одно измерение, созданное добавлением 1 в качестве последней записи. В последующих шагах кратность этих векторов будет использоваться для обозначения одних и тех же точек. См. Последний шаг для примера того, как вернуть их обратно в двумерные координаты.

Шаг 2: Масштабируйте столбцы с помощью только что вычисленных коэффициентов:

    [λ∙x1 μ∙x2 τ∙x3]
A = [λ∙y1 μ∙y2 τ∙y3]
    [λ    μ    τ   ]

Эта матрица отобразит (1,0,0) в кратность (x1,y1,1), (0,1,0) до кратного (x2,y2,1), (0,0,1) до кратного (x3,y3,1) и (1,1,1) до (x4,y4,1). Таким образом, он будет отображать эти четыре специальных вектора (называемые базисными векторами в последующих объяснениях) в указанные позиции на изображении.

Шаг 3: Повторите шаги 1 и 2 для соответствующих позиций на целевом изображении, чтобы получить вторую матрицу с именем B.

Это карта от базисных векторов к местам назначения.

Шаг 4: Инвертировать B, чтобы получить B⁻¹.

B отображает из базисных векторов в позиции назначения, поэтому обратная матрица отображает в обратном направлении.

Шаг 5: Вычислить в сочетании Матрица C = A∙B⁻¹.

B⁻¹ отображает из мест назначения в базовые векторы, а A отображает оттуда исходные позиции. Таким образом, комбинация отображает позиции назначения в исходные позиции.

Шаг 6: Для каждого пикселя (x,y) целевого изображения вычислите продукт

[x']     [x]
[y'] = C∙[y]
[z']     [1]

Это однородные координаты вашей преобразованной точки.

Шаг 7: Вычислить позицию в исходном изображении следующим образом:

sx = x'/z'
sy = y'/z'

Это называется дегомогенезацией вектора координат.

Вся эта математика будет намного проще читать и писать, если SO будет поддерживать MathJax... ☹

Выбор размера изображения

Вышеприведенное aproach предполагает, что вы знаете расположение ваших углов в целевом изображении. Для этого вам нужно знать ширину и высоту этого изображения, которое также помечено вопросительными знаками в коде. Поэтому допустим, что height вашего выходного изображения было 1, а width - sourceaspect. В этом случае общая площадь будет sourceaspect. Вы должны масштабировать эту область в pixelcount/sourceaspect, чтобы достичь области pixelcount. Это означает, что вам нужно масштабировать каждую длину края на квадратный корень этого фактора. Итак, в конце концов, вы

pixelcount = 1000000.*megapixelcount;
width  = round(sqrt(pixelcount*sourceaspect));
height = round(sqrt(pixelcount/sourceaspect));

Ответ 2

Используйте Graphics32, в частности TProjectiveTransformation (для использования с методом Transform.) Не забудьте оставить прозрачный маркер в исходном изображении, чтобы вы не получили зубчатые края.