Применяете FXAA в PHP?
Когда я использую библиотеку изображений GD в PHP для рисования фигур, она всегда показывает твердые края. Я пытался использовать GD imageantialias()
функция, но это только для прямых линий.
Чтобы решить эту проблему, я искал некоторые алгоритмы сглаживания и обнаружил, что FXAA работает довольно хорошо, поэтому я собираюсь попробовать. Я попытался перенести фильтр сглаживания FXAA из шейдера GLSL здесь.
Затем, когда я закончил перенос шейдера FXAA на PHP, он не дал мне правильного результата. Я тестирую фильтр FXAA, используя imagecolorallocatealpha()
пример на PHP.net:
<?php
require('fxaa.php');
$size = 300;
$image=imagecreatetruecolor($size, $size);
// something to get a white background with black border
$back = imagecolorallocate($image, 255, 255, 255);
$border = imagecolorallocate($image, 0, 0, 0);
imagefilledrectangle($image, 0, 0, $size - 1, $size - 1, $back);
imagerectangle($image, 0, 0, $size - 1, $size - 1, $border);
$yellow_x = 100;
$yellow_y = 75;
$red_x = 120;
$red_y = 165;
$blue_x = 187;
$blue_y = 125;
$radius = 150;
// allocate colors with alpha values
$yellow = imagecolorallocatealpha($image, 255, 255, 0, 75);
$red = imagecolorallocatealpha($image, 255, 0, 0, 75);
$blue = imagecolorallocatealpha($image, 0, 0, 255, 75);
// drawing 3 overlapped circle
imagefilledellipse($image, $yellow_x, $yellow_y, $radius, $radius, $yellow);
imagefilledellipse($image, $red_x, $red_y, $radius, $radius, $red);
imagefilledellipse($image, $blue_x, $blue_y, $radius, $radius, $blue);
FXAA::process($image);
// don't forget to output a correct header!
header('Content-Type: image/png');
// and finally, output the result
imagepng($image);
imagedestroy($image);
?>
Вот оригинальное изображение:
И это обработанное изображение:
Вот еще одно тестовое изображение. (Слева обрабатывается FXAA, а справа нет.)
Цвет примеров изображений перепутан с фоном, а края слишком сглажены. Это не ожидаемый результат, как я думаю. Я не понимаю, что не так с моим кодом, поэтому я обращаюсь к вам за помощью.
Кроме того, вот класс FXAA, который я написал, и оригинальный шейдер GLSL:
<?php
class FXAA {
const FXAA_REDUCE_MIN = 0.0078125;
const FXAA_REDUCE_MUL = 0.125;
const FXAA_SPAN_MAX = 8;
static $w = 0;
static $h = 0;
private static function add($a, $b){
return array($a[0] + $b[0], $a[1] + $b[1], $a[2] + $b[2]);
}
private static function dot($a, $b){
return $a[0] * $b[0] + $a[1] * $b[1] + $a[2] * $b[2];
}
private static function texture2D($img, $pos){
if(($pos[0] >= self::$w || $pos[0] < 0) || ($pos[1] >= self::$h || $pos[1] < 0)){
return array(0, 0, 0, 0);
}
$color = imagecolorat($img, $pos[0], $pos[1]);
$a = ($color >> 24) & 0xFF;
$r = ($color >> 16) & 0xFF;
$g = ($color >> 8) & 0xFF;
$b = $color & 0xFF;
return array($r, $g, $b, $a);
}
public static function process($img){
self::$w = imagesx($img);
self::$h = imagesy($img);
for($x = 0; $x < imagesx($img); $x++){
for($y = 0; $y < imagesy($img); $y++){
$rgbNW = self::texture2D($img,array($x - 1, $y - 1));
$rgbNE = self::texture2D($img,array($x + 1, $y - 1));
$rgbSW = self::texture2D($img,array($x - 1, $y + 1));
$rgbSE = self::texture2D($img,array($x + 1, $y + 1));
$rgbaM = $rgbM = self::texture2D($img,array($x, $y));
$opacity = array_pop($rgbM);
$luma = array(76, 149, 29);
$lumaNW = self::dot($rgbNW, $luma);
$lumaNE = self::dot($rgbNE, $luma);
$lumaSW = self::dot($rgbSW, $luma);
$lumaSE = self::dot($rgbSE, $luma);
$lumaM = self::dot($rgbM, $luma);
$lumaMin = min($lumaM, min(min($lumaNW, $lumaNE ), min($lumaSW, $lumaSE)));
$lumaMax = max($lumaM, max(max($lumaNW, $lumaNE), max($lumaSW, $lumaSE)));
$dir = array(
-(($lumaNW + $lumaNE) - ($lumaSW + $lumaSE)),
(($lumaNW + $lumaSW) - ($lumaNE + $lumaSE))
);
$dirReduce = max(($lumaNW + $lumaNE + $lumaSW + $lumaSE ) * ( 0.25 * self::FXAA_REDUCE_MUL ), self::FXAA_REDUCE_MIN);
$rcpDirMin = 1 / (min(abs($dir[0]), abs($dir[1])) + $dirReduce);
$dir[0] = min(self::FXAA_SPAN_MAX, max(-self::FXAA_SPAN_MAX, $dir[0] * $rcpDirMin));
$dir[1] = min(self::FXAA_SPAN_MAX, max(-self::FXAA_SPAN_MAX, $dir[1] * $rcpDirMin));
$rgbA = self::add(
self::texture2D($img, array($x + $dir[0] * (1 / 3 - 0.5), $y + $dir[1] * (1 / 3 - 0.5))),
self::texture2D($img, array($x + $dir[0] * (2 / 3 - 0.5), $y + $dir[1] * (1 / 3 - 0.5)))
);
$rgbA[0] *= 0.5;
$rgbA[1] *= 0.5;
$rgbA[2] *= 0.5;
$rgbB = self::add(
self::texture2D($img, array($x + $dir[0] * -0.5, $y + $dir[1] * -0.5)),
self::texture2D($img, array($x + $dir[0] * 0.5, $y + $dir[1] * 0.5))
);
$rgbB[0] = $rgbB[0] * 0.25 + $rgbA[0] * 0.5;
$rgbB[1] = $rgbB[1] * 0.25 + $rgbA[1] * 0.5;
$rgbB[2] = $rgbB[2] * 0.25 + $rgbA[2] * 0.5;
$lumaB = self::dot($rgbB, $luma);
if(($lumaB < $lumaMin) || ($lumaB > $lumaMax)){
imagesetpixel($img, $x, $y, imagecolorallocatealpha($img, $rgbA[0], $rgbA[1], $rgbA[2], $opacity));
}
else {
imagesetpixel($img, $x, $y, imagecolorallocatealpha($img, $rgbB[0], $rgbB[1], $rgbB[2], $opacity));
}
}
}
}
}
Оригинальный шейдер GLSL:
uniform sampler2D tDiffuse;
uniform vec2 resolution;
varying vec2 vUv;
#define FXAA_REDUCE_MIN (1.0/128.0)
#define FXAA_REDUCE_MUL (1.0/8.0)
#define FXAA_SPAN_MAX 8.0
void main() {
vec3 rgbNW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;
vec3 rgbNE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;
vec3 rgbSW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;
vec3 rgbSE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;
vec4 rgbaM = texture2D( tDiffuse, gl_FragCoord.xy * resolution );
vec3 rgbM = rgbaM.xyz;
float opacity = rgbaM.w;
vec3 luma = vec3( 0.299, 0.587, 0.114 );
float lumaNW = dot( rgbNW, luma );
float lumaNE = dot( rgbNE, luma );
float lumaSW = dot( rgbSW, luma );
float lumaSE = dot( rgbSE, luma );
float lumaM = dot( rgbM, luma );
float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );
float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );
vec2 dir;
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );
float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );
dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),dir * rcpDirMin)) * resolution;
vec3 rgbA = 0.5 * (
texture2D( tDiffuse, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz +
texture2D( tDiffuse, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz );
vec3 rgbB = rgbA * 0.5 + 0.25 * (
texture2D( tDiffuse, gl_FragCoord.xy * resolution + dir * -0.5 ).xyz +
texture2D( tDiffuse, gl_FragCoord.xy * resolution + dir * 0.5 ).xyz );
float lumaB = dot( rgbB, luma );
if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) ) {
gl_FragColor = vec4( rgbA, opacity );
} else {
gl_FragColor = vec4( rgbB, opacity );
}
}
1 ответ
Проблема с вашим кодом в том, что он работает с одним изображением, и, таким образом, операция сглаживания читает свой собственный вывод.
Будет отображаться вывод операции сглаживания в новое изображение.