📷
画像処理100本ノックに挑戦|Bi-cubic補間(027/100)
これはなに?
画像処理100本ノックを、TypeScriptとlibvipsで挑戦してみる記事の27本目です。
前回
実装
お題
Bi-cubic補間により画像を1.5倍に拡大せよ。
Bi-cubic補間とはBi-linear補間の拡張であり、周辺の16画素から補間を行う。
Coding
import sharp from 'sharp';
function calculateWeight(t: number): number {
const a = -1.0;
const absT = Math.abs(t);
if (absT <= 1) {
return (a + 2) * Math.pow(absT, 3) - (a + 3) * Math.pow(absT, 2) + 1;
} else if (absT <= 2) {
return a * Math.pow(absT, 3) - 5 * a * Math.pow(absT, 2) + 8 * a * absT - 4 * a;
}
return 0;
}
export async function bicubicInterpolation(
inputPath: string,
outputPath: string,
scaleX: number = 1.5,
scaleY: number = 1.5
): Promise<void> {
try {
const image = await sharp(inputPath)
.raw()
.toBuffer({ resolveWithObject: true });
const { data, info } = image;
const { width, height, channels } = info;
// 新しいサイズを計算
const newWidth = Math.round(width * scaleX);
const newHeight = Math.round(height * scaleY);
const newData = Buffer.alloc(newWidth * newHeight * channels);
// 各ピクセルについて補間を計算
for (let y = 0; y < newHeight; y++) {
for (let x = 0; x < newWidth; x++) {
// 元の座標を計算(浮動小数点)
const srcX = x / scaleX;
const srcY = y / scaleY;
// 基準となるピクセルの座標
const ix = Math.floor(srcX);
const iy = Math.floor(srcY);
// 距離の計算
const dx = srcX - ix;
const dy = srcY - iy;
// 各チャネルについて16点の補間を実行
for (let c = 0; c < channels; c++) {
let sum = 0;
let weightSum = 0;
// 16点のピクセルを使用
for (let jy = -1; jy <= 2; jy++) {
for (let jx = -1; jx <= 2; jx++) {
// 参照ピクセルの座標
const px = Math.min(Math.max(ix + jx, 0), width - 1);
const py = Math.min(Math.max(iy + jy, 0), height - 1);
// 重みの計算
const wx = calculateWeight(jx - dx);
const wy = calculateWeight(jy - dy);
const weight = wx * wy;
// ピクセル値の取得と重み付け
const pos = (py * width + px) * channels + c;
sum += weight * data[pos];
weightSum += weight;
}
}
// 結果の正規化と保存
const newPos = (y * newWidth + x) * channels + c;
newData[newPos] = Math.min(255, Math.max(0, Math.round(sum / weightSum)));
}
}
}
await sharp(newData, {
raw: {
width: newWidth,
height: newHeight,
channels
}
})
.toFile(outputPath);
console.log('バイキュービック補間による拡大が完了しました');
console.log(`元のサイズ: ${width}x${height}`);
console.log(`拡大後のサイズ: ${newWidth}x${newHeight}`);
} catch (error) {
console.error('画像処理中にエラーが発生しました:', error);
throw error;
}
}
結果
入力 | 出力 |
---|---|
![]() |
![]() |
Discussion