【图像缩放】双立方(三次)卷积插值(Android版改写)

最近在做图片放大之后的画面处理,尝试了这种卷积插值法,原文如下:https://dailc.github.io/2017/11/01/imageprocess_bicubicinterpolation.html

然后我将其工程简单地改写成了Android版本的代码(只是个Demo,用来看看效果,可能含Bug):

package com.cjz.image;

/**
 * Created by cjz on 2018/12/5.
 */

import android.graphics.Bitmap;

/**
 * 采样公式的常数A取值,调整锐化与模糊
 * -0.5 三次Hermite样条
 * -0.75 常用值之一
 * -1 逼近y = sin(x*PI)/(x*PI)
 * -2 常用值之一
 *
 * todo bug 只能放大不能缩小
 */
public class ConvolutionImageProccess {
    private static final float A = -1;

    /**插值核心方程**/
    private static float interpolationCalculate(float x) {
        float absX = x >= 0 ? x : -x;
        float x2 = x * x;
        float x3 = absX * x2;

        if (absX <= 1) {
            return 1 - (A + 3) * x2 + (A + 2) * x3;
        } else if (absX <= 2) {
            return -4 * A + 8 * A * absX - 5 * A * x2 + A * x3;
        }

        return 0;
    }

    private static int getPixelValue(int pixelValue) {
        int newPixelValue = pixelValue;

        newPixelValue = Math.min(255, newPixelValue);
        newPixelValue = Math.max(0, newPixelValue);

        return newPixelValue;
    }

    /**
     * 获取某行某列的像素对于的rgba值
     * @param {Object} data 图像数据
     * @param {Number} srcWidth 宽度
     * @param {Number} srcHeight 高度
     * @param {Number} row 目标像素的行
     * @param {Number} col 目标像素的列
     */
    private static int[] getRGBAValue(int data[], int srcWidth, int srcHeight, int row, int col) {
        int newRow = row;
        int newCol = col;

        if (newRow >= srcHeight) {
            newRow = srcHeight - 1;
        } else if (newRow < 0) {
            newRow = 0;
        }

        if (newCol >= srcWidth) {
            newCol = srcWidth - 1;
        } else if (newCol < 0) {
            newCol = 0;
        }

        int newIndex = (newRow * srcWidth) + newCol;

        newIndex *= 4;

        return new int[] {
                data[newIndex + 0],
                data[newIndex + 1],
                data[newIndex + 2],
                data[newIndex + 3]
        };
    }




    private static  void  scale(int data[], int width, int height, int newData[], int newWidth, int newHeight) {
        int dstData[] = newData;

        // 计算压缩后的缩放比
        float scaleW = newWidth / width;
        float scaleH = newHeight / height;

        // 区块
        for (int col = 0; col < newWidth; col += 1) {
            for (int row = 0; row < newHeight; row += 1) {
                filter(data, dstData, col, row, width, height, scaleW, scaleH, newWidth);
            }
        }
    }


    private static  void filter(int data[], int dstData[], int dstCol, int dstRow, int width ,int height, float scaleW, float scaleH, int newWidth){
        // 源图像中的坐标(可能是一个浮点)
        int srcCol = (int) Math.min(width - 1, dstCol / scaleW);
        int srcRow = (int) Math.min(height - 1, dstRow / scaleH);
        int intCol = (int) Math.floor(srcCol);
        int intRow = (int) Math.floor(srcRow);
        // 计算u和v
        int u = srcCol - intCol;
        int v = srcRow - intRow;
        // 真实的index,因为数组是一维的
        int dstI = (dstRow * newWidth) + dstCol;
        dstI *= 4;
        // 存储灰度值的权重卷积和
        int rgbaData[] = new int[]{0, 0, 0, 0};
        // 根据数学推导,16个点的f1*f2加起来是趋近于1的(可能会有浮点误差)
        // 因此就不再单独先加权值,再除了
        // 16个邻近点
        for (int m = -1; m <= 2; m += 1) {
            for (int n = -1; n <= 2; n += 1) {
                int rgba[] = getRGBAValue(data, width, height, intRow + m, intCol + n);
                // 一定要正确区分 m,n和u,v对应的关系,否则会造成图像严重偏差(譬如出现噪点等)
                // F(row + m, col + n)S(m - v)S(n - u)
                float f1 = interpolationCalculate(m - v);
                float f2 = interpolationCalculate(n - u);
                float weight = f1 * f2;

                rgbaData[0] += rgba[0] * weight;
                rgbaData[1] += rgba[1] * weight;
                rgbaData[2] += rgba[2] * weight;
                rgbaData[3] += rgba[3] * weight;
            }
        }

        dstData[dstI + 0] = getPixelValue(rgbaData[0]);
        dstData[dstI + 1] = getPixelValue(rgbaData[1]);
        dstData[dstI + 2] = getPixelValue(rgbaData[2]);
        dstData[dstI + 3] = getPixelValue(rgbaData[3]);
    }

    public static Bitmap bicubicInterpolation(Bitmap src, int newWidth, int newHeight) {
        int pixels[] = new int[src.getWidth() * src.getHeight() * 4];
        int offset = 0;
        for(int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                int pixel = src.getPixel(x, y);
                int alpha = pixel >> 24 & 0xFF;
                int red = pixel >> 16 & 0xFF;
                int green = pixel >> 8 & 0xFF;
                int blue = pixel & 0xFF;
                pixels[offset++] = alpha;
                pixels[offset++] = red;
                pixels[offset++] = green;
                pixels[offset++] = blue;
            }
        }
        int newPixels[] = new int[newWidth * newHeight * 4];
        scale(pixels,
                src.getWidth(),
                src.getHeight(),
                newPixels,
                newWidth,
                newHeight);

        int newImgData[] = new int[newWidth * newHeight];
        for(int i = 0, j = 0; i < newPixels.length; j++){
            int alpha = newPixels[i++];
            int red = newPixels[i++];
            int green = newPixels[i++];
            int blue = newPixels[i++];
            newImgData[j] |= (alpha << 24);
            newImgData[j] |= (red << 16);
            newImgData[j] |= (green << 8);
            newImgData[j] |= (blue);
        }
        Bitmap bitmap = Bitmap.createBitmap(newImgData, newWidth, newHeight, Bitmap.Config.ARGB_8888);
        return bitmap;
    }


    private static int getPixelInPixelsArray(int array[], int width, int height, int x, int y) {
        int offset = width * y + x;
        if (offset < array.length && x > 0 && y > 0) {
            return array[offset];
        } else {
            return 0;
        }
    }
}

文字和线条栅格化之后的图片放大两倍效果:

【图像缩放】双立方(三次)卷积插值(Android版改写)_第1张图片

 

你可能感兴趣的:(JAVA,理论基础)