最近在做图片放大之后的画面处理,尝试了这种卷积插值法,原文如下: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;
}
}
}
文字和线条栅格化之后的图片放大两倍效果: