图像伸缩与图像量化算法

图像伸缩

Write a function that takes a gray image and a target size as input, and generates the scaled image as output. Please use bi-linear for interpolation. The function prototype is “scale(input img, size) → output img”, where “input img” and “output img” are two-dimensional matrices storing images, and “size” is a tuple of (width, height) defining the spatial resolution of output. You can modify the prototype if necessary.

算法中使用了双线性插值的方法。首先考虑一个 1 x 1 的方格(见下图),已知四个顶点的值 c00,c01,c10 和 c11,现在需要估算出该区间内某一点 c 的值。第一步是做横向的线性插值:估算 a 点的值为 c01 * (1 - tx) + c11 * tx,并估算 b 点的值为 c00 * (1 - tx) + c10 * tx。第二步是在垂直方向再做一次线性插值,估算 c 点的值为 a * ty + b * (1 - ty),代入 a 和 b 的估算值可得 (1 - tx) * (1 - ty) * c00 + (1 - tx) * ty * c01 + tx * (1 - ty) * c10 + tx * ty * c11

现在来看图像的处理方法。对于目标图像上的某个点来说,它的灰度值需要依靠原图像求得。于是可以将该像素的位置按照两张图像的比例缩放,得到该像素在原图上的取值坐标。但是得到的坐标值可能是浮点数,这时就可利用双线性插值法,取出该坐标周围的四个像素点,用这四个像素点的灰度值估算目标像素点的灰度值(见上面的公式)。对目标图像的每个像素点执行该操作就能得到相对应的灰度值,最终得到目标图像。

  • 使用 Processing 实现该算法:
// bilinear interpolation
float bilinear(float tx, float ty, int c00, int c01, int c10, int c11) {
    return (1 - tx) * (1 - ty) * c00
            + (1 - tx) * ty * c01
            + tx * (1 - ty) * c10
            + tx * ty * c11;
}

// color -> gray value
int toGray(color c) {
    return int((red(c) + green(c) + blue(c)) / 3);
}

PImage scale(PImage source_img, int target_width, int target_height) {
    PImage target_img = createImage(target_width, target_height, RGB);
    source_img.loadPixels();
    target_img.loadPixels();

    for (int x = 0; x < target_width; x++) {
        for (int y = 0; y < target_height; y++) {
            float gx = (float(x) / target_width) * (source_img.width - 1);
            float gy = (float(y) / target_height) * (source_img.height - 1);
            int gxi = int(gx);
            int gyi = int(gy);
            int gxi_plus = min(gxi + 1, source_img.width - 1);
            int gyi_plus = min(gyi + 1, source_img.height - 1);
            int c00 = toGray(source_img.pixels[gxi + gyi * source_img.width]);
            int c01 = toGray(source_img.pixels[gxi + gyi_plus * source_img.width]);
            int c10 = toGray(source_img.pixels[gxi_plus + gyi * source_img.width]);
            int c11 = toGray(source_img.pixels[gxi_plus + gyi_plus * source_img.width]);
            // set pixel (x, y) in target image
            int gray = int(bilinear(gx - gxi, gy - gyi, c00, c01, c10, c11));
            target_img.pixels[x + target_img.width * y] = color(gray);
        }
    }

    target_img.updatePixels();
    return target_img;
}

图像量化

Write a function that takes a gray image and a target number of gray levels as input, and generates the quantized image as output. The function prototype is “quantize(input img, level) → “output img”, where “level” is an integer in [1, 256] defining the number of gray levels of output. You can modify the prototype if necessary.

为了让转换后的灰度值保持在 [0, 255] 的区间内,可以设置一个转换因子(conversion factor),其值为 255 / (numberOfLevels - 1)(其中 numberOfLevels 表示灰度等级数,此处暂不考虑等级数为 1 的情况),表示 255 相对于灰度等级数的大小。

在设置新的灰度等级后,首先需要知道每个灰度值属于哪一个灰度等级,计算方法为 gray-value / conversion-factor(需要取整),其值的范围是 [0, numberOfLevels - 1]。之后,再将该区间映射到 [0, 255],方法是在之前求得的值的基础上乘以转换因子。

总的来说,灰度变换的公式为:new-gray-value = Round(old-gray-value / conversion-factor) * conversion-factor。对原图像的每个像素点执行该变换即可得到目标图像。

另外,对于灰度等级为 1 的特殊情况,只需返回大小相同的全黑图像即可。

  • 使用 Processing 实现该算法:
// color -> gray value
int toGray(color c) {
    return int((red(c) + green(c) + blue(c)) / 3);
}

PImage quantize(PImage source_img, int level) {
    PImage target_img = createImage(source_img.width, source_img.height, RGB);
    source_img.loadPixels();
    target_img.loadPixels();

    if (level == 1) {
        for (int x = 0; x < source_img.width; x++) {
            for (int y = 0; y < source_img.height; y++) {
                target_img.pixels[x + target_img.width * y] = color(0);
            }
        }
    }
    else {
        int conversion_factor = 255 / (level - 1);

        for (int x = 0; x < source_img.width; x++) {
            for (int y = 0; y < source_img.height; y++) {
                int old_gray = toGray(source_img.pixels[x + y * source_img.width]);
                int new_gray = round(float(old_gray) / conversion_factor) * conversion_factor;
                target_img.pixels[x + target_img.width * y] = color(new_gray);
            }
        }
    }

    target_img.updatePixels();
    return target_img;
}

参考链接

Image Processing – Bilinear Interpolation

Bilinear Filtering

Bilinear Interpolation - Rosetta Code

Updated: