【影象縮放】雙立方(三次)卷積插值(Android版改寫)
阿新 • • 發佈:2018-12-16
最近在做圖片放大之後的畫面處理,嘗試了這種卷積插值法,原文如下: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; } } }
文字和線條柵格化之後的圖片放大兩倍效果: