Created
November 21, 2018 06:43
-
-
Save hexagit/1ccc50274c1ee60d70739623cc726984 to your computer and use it in GitHub Desktop.
Lanczos法
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using UnityEngine; | |
public partial class Bitmap | |
{ | |
private const int lanczosDefaultN = 3; | |
private Bitmap resizeLanczos(uint width, uint height, uint range = lanczosDefaultN) | |
{ | |
var bitmap = new Bitmap(width, height); | |
// スケール | |
var scaleX = (float)width / _width; | |
var scaleY = (float)height / _height; | |
// 単位を原画像の座標系に変換 | |
var unitX = 1.0f / scaleX; | |
var unitY = 1.0f / scaleY; | |
// 原画像から参照する範囲 | |
var n = (float)range; | |
var rangeX = n; | |
var rangeY = n; | |
// 1ピクセル幅を考慮した座標の補正 | |
var correctionX = unitX * 0.5f; | |
var correctionY = unitY * 0.5f; | |
// 縮小のときは参照範囲を拡大 | |
if(unitX > 1.0f) { | |
rangeX *= unitX; | |
} | |
if(unitY > 1.0f) { | |
rangeY *= unitY; | |
} | |
parallelFor(0, (int)bitmap._height, y => { | |
for(int x = 0; x < bitmap._width; ++x) { | |
// 原画像の座標系に変換 | |
var srcX = unitX * x + correctionX; | |
var srcY = unitY * y + correctionY; | |
bitmap[x, y] = interpolateLanczos(srcX, srcY, rangeX, rangeY, n); | |
} | |
}); | |
return bitmap; | |
} | |
private Color interpolateLanczos(float srcX, float srcY, float rangeX, float rangeY, float n) | |
{ | |
float r = 0.0f; | |
float g = 0.0f; | |
float b = 0.0f; | |
float a = 0.0f; | |
// 距離のスケール修正 | |
var scaleCorrectionX = n / rangeX; | |
var scaleCorrectionY = n / rangeY; | |
var totalWeight = 0.0f; | |
// 基準位置(srcX, srcY)から距離n未満のピクセル範囲を求める | |
int firstX = (int)Mathf.Clamp(srcX - rangeX + 0.5f, 0.0f, _width + 1); | |
int firstY = (int)Mathf.Clamp(srcY - rangeY + 0.5f, 0.0f, _height + 1); | |
int lastX = (int)Mathf.Clamp(srcX + rangeX - 0.5f, 0.0f, _width + 1); | |
int lastY = (int)Mathf.Clamp(srcY + rangeY - 0.5f, 0.0f, _height + 1); | |
// 1ピクセルずつ参照して距離パターンが限られるため重みをキャッシュ | |
var weightX = new float[lastX - firstX + 1]; | |
var weightY = new float[lastY - firstY + 1]; | |
// X方向の重みを事前計算 | |
for(int intX = firstX; intX <= lastX; ++intX) { | |
// 着目ピクセルの中心と基準位置との距離から重み算出 | |
var diff = (intX + 0.5f - srcX) * scaleCorrectionX; | |
weightX[intX - firstX] = lanczos(diff, n); | |
} | |
// Y方向の重みを事前計算 | |
for(int intY = firstY; intY <= lastY; ++intY) { | |
// 着目ピクセルの中心と基準位置との距離から重み算出 | |
var diff = (intY + 0.5f - srcY) * scaleCorrectionY; | |
weightY[intY - firstY] = lanczos(diff, n); | |
} | |
for(int intY = firstY; intY <= lastY; ++intY) { | |
for(int intX = firstX; intX <= lastX; ++intX) { | |
var weight = weightX[intX - firstX] * weightY[intY - firstY]; | |
if(weight == 0.0f) continue; | |
totalWeight += weight; | |
var color = get(intX, intY); | |
// 完全透過部分のカラー影響を無視 | |
if(color.a == 0.0f) continue; | |
r += color.r * weight; | |
g += color.g * weight; | |
b += color.b * weight; | |
a += color.a * weight; | |
} | |
} | |
var result = new Color(r, g, b, a); | |
// 積分なので全体で重み1となるよう補正 | |
if(totalWeight != 0.0f) { | |
result = result / totalWeight; | |
} | |
return result; | |
} | |
private static float lanczos(float x, float n) | |
{ | |
if(Math.Abs(x) >= n) return 0.0f; | |
return sinc(x) * sinc(x / n); | |
} | |
private static float sinc(float x) | |
{ | |
if(x == 0.0f) return 1.0f; | |
return (float)(Math.Sin(Math.PI * x) / (Math.PI * x)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment