Что не так с моим алгоритмом билинейной интерполяции?

Я использую PHP для проверки моего билинейного алгоритма. Код не оптимизирован для ясности.

Вот что делает код ниже:

  1. Нанесите оригинальные пиксели с изображения 2×2 на пункт назначения 10×10
    образ. Это оставит пустые пиксели.

Примечание. Размеры изображений уменьшены с 10×10 до 100×100 для лучшего просмотра.

изменен

  1. Интерполировать ряд пикселей.

введите описание изображения здесь

  1. Интерполируйте оставшиеся пиксели, идущие слева направо, сверху вниз, используя строку пикселей на шаге 2:

введите описание изображения здесь

Тем не менее, он не совпадает с результатом, который я получаю в Photoshop с помощью билинейной передискретизации:

введите описание изображения здесь

Полный исходный код:

<?php
$image1 = imagecreatefrompng( 'test.png' );

$w1 = imagesx( $image1 );
$h1 = imagesy( $image1 );

$w2 = 10;
$h2 = 10;
$image2 = imagecreatetruecolor( $w2, $h2 );

imagefill($image2, 0, 0, imagecolorallocate($image2, 0x4c, 0x4c, 0x8e)); // added bg for pixels to stand-out

function lerp($v0, $v1, $t) {
return $v0 + $t*($v1-$v0);
}

function getPixel($image, $x, $y){
$rgb = imagecolorat( $image, $x, $y );
$r     = ($rgb >> 16) & 0xFF;
$g     = ($rgb >> 8) & 0xFF;
$b     = $rgb & 0xFF;
return array($r,$g,$b);
}

$maxY1 = $h1 - 1;
$maxX1 = $w1 - 1;
$maxY2 = $h2 - 1;
$maxX2 = $w2 - 1;

// plot original pixels from source to destination
for($y = 0; $y <= $maxY1; $y++) { // loop thru src height

$newY = floor(($y/$maxY1) * $maxY2);

for ($x = 0; $x <= $maxX1; $x++) { // loop thru src width

$newX = floor(($x/$maxX1) * $maxX2);
$rgb = imagecolorat( $image1, $x, $y );
$r1     = ($rgb >> 16) & 0xFF;
$g1     = ($rgb >> 8) & 0xFF;
$b1     = $rgb & 0xFF;

imagesetpixel( $image2, $newX, $newY, imagecolorallocate( $image2, $r1, $g1, $b1 ) );

}
}
imagepng( $image2, 'out1.png' );

// interpolate pixels from pixel[1,0] to pixel[8,0]
$y = 0;
$rgb = imagecolorat( $image2, 0, $y );
$r0     = ($rgb >> 16) & 0xFF;
$g0     = ($rgb >> 8) & 0xFF;
$b0     = $rgb & 0xFF;

$rgb = imagecolorat( $image2, 9, $y );
$r1     = ($rgb >> 16) & 0xFF;
$g1     = ($rgb >> 8) & 0xFF;
$b1     = $rgb & 0xFF;
for($x=1; $x <= 8; $x++){
$t = $x / 9;
$r = lerp($r0, $r1, $t);
$g = lerp($g0, $g1, $t);
$b = lerp($b0, $b1, $t);
imagesetpixel( $image2, $x, $y, imagecolorallocate( $image2, $r, $g, $b ) );
}
imagepng( $image2, 'out2.png' );

// interpolate pixels from pixel[1,9] to pixel[8,9]
$y = 9;
$rgb = imagecolorat( $image2, 0, $y );
$r0     = ($rgb >> 16) & 0xFF;
$g0     = ($rgb >> 8) & 0xFF;
$b0     = $rgb & 0xFF;

$rgb = imagecolorat( $image2, 9, $y );
$r1     = ($rgb >> 16) & 0xFF;
$g1     = ($rgb >> 8) & 0xFF;
$b1     = $rgb & 0xFF;

for($x=1; $x <= 8; $x++){
$t = $x / 9;
$r = lerp($r0, $r1, $t);
$g = lerp($g0, $g1, $t);
$b = lerp($b0, $b1, $t);
imagesetpixel( $image2, $x, $y, imagecolorallocate( $image2, $r, $g, $b ) );
}
imagepng( $image2, 'out3.png' );

// interpolate remaining pixels
for($x=0; $x <= 9; $x++){
$rgb = imagecolorat( $image2, $x, 0 );
$r0     = ($rgb >> 16) & 0xFF;
$g0     = ($rgb >> 8) & 0xFF;
$b0     = $rgb & 0xFF;

$rgb = imagecolorat( $image2, $x, 9 );
$r1     = ($rgb >> 16) & 0xFF;
$g1     = ($rgb >> 8) & 0xFF;
$b1     = $rgb & 0xFF;
for($y = 1; $y <= 8; $y++){
$t = $y / 9;
$r = lerp($r0, $r1, $t);
$g = lerp($g0, $g1, $t);
$b = lerp($b0, $b1, $t);
imagesetpixel( $image2, $x, $y, imagecolorallocate( $image2, $r, $g, $b ) );
}
}
imagepng( $image2, 'out4.png' );

header('Content-type: image/png');
imagepng( $image2);
imagedestroy( $image1 );

Что мне не хватает?

1

Решение

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

2х2:

введите описание изображения здесь

10×10 до интерполяции:

введите описание изображения здесь

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

1

Другие решения

Если вы внимательно посмотрите на результат PS, вы заметите, что до того, как пиксели углов интерполяции были изменены в 9 раз (они занимают 3×3 в углах изображения). Это очевидно сделано для того, чтобы получить более острые края, к лучшему или к худшему.

Если вы добавите логику для генерации промежуточного изображения перед интерполяцией:

промежуточное изображение

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

-1

По вопросам рекламы [email protected]