我已经蹲了好几天试图找出在将图像缩放一半大小时我的最近邻插值有什么问题。我使用 Rust,但我认为它不如算法重要:
/// Interpolate using nearest neighbor.
pub fn nearest(mut src: &mut Image, w2: i32, h2: i32) -> RasterResult<()> {
// Compute scale
let x_scale: f64 = src.width as f64 / w2 as f64;
let y_scale: f64 = src.height as f64 / h2 as f64;
// Create destination image
let mut dest = Image::blank(w2, h2);
// Loop thru destination image's pixels
for y2 in 0..h2 {
for x2 in 0..w2 {
// Get location from src using nearest neighbor
let x1: i32 = ( x2 as f64 * x_scale ).floor() as i32;
let y1: i32 = ( y2 as f64 * y_scale ).floor() as i32;
let pixel = try!(src.get_pixel(x1, y1));
// Apply the pixels
try!(dest.set_pixel(x2, y2, pixel));
}
}
// Update the source image
src.width = dest.width;
src.height = dest.height;
src.bytes = dest.bytes;
Ok(())
}
我的调用代码:
// Open
let mut image = raster::open(r"test.png").unwrap();
// Resize to half
raster::interpolate::nearest(&mut image, 256, 128).unwrap();
// Save
raster::save(&image, r"out.png").unwrap();
用我的代码缩放到一半:
这里是 Photoshop 的比较:
我用 GD 进行了测试,它产生了与 Photoshop 相似的结果。
我的调查使我认为我对源像素(红色方 block )的计算与其他像素(绿色方 block )相比有偏差:
我也尝试寻找 into GD's source code获得线索,但我不完全理解它的作用。
澄清一下,我不是在谈论使用不同的插值方法,例如双线性和双三次。我正在将我的最近邻算法及其结果与 Photoshop 和 GD 的最近邻算法的结果进行比较。我不是在问为什么我在 Photoshop 中的结果很丑,因为我使用了最近邻算法。
(测试图片由 Jonas Berlin 通过 Eric Brasseur 提供)
最佳答案
进行最近邻插值的一种稍微好一点的方法是使插值经过目标像素的中心而不是它们的左上角。在您的代码中,这意味着添加 0.5
至 x2
和 y2
在计算 x1
时和 y1
.
let x1: i32 = ( (x2 as f64 + 0.5) * x_scale ).floor() as i32;
let y1: i32 = ( (y2 as f64 + 0.5) * y_scale ).floor() as i32;
当您按 1/2 缩放时,这不一定会产生更好的结果:每个目标像素都是从 2x2 源区域中选择的,因此您选择哪个源像素并不重要。但是当按 1/3 缩放时,这意味着您选择 3x3 区域的中心而不是左上角,这样更好。
Photoshop 可能使用这样的方法,这就是为什么您会看到不同的结果。
请注意,即使这使测试图像看起来缩放得更好,也可以制作另一张使用新算法看起来很糟糕的测试图像。将您的测试图像偏移 (1,1) 可能会做到这一点。
关于image-processing - 这种用于图像缩放的最近邻算法有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45249860/