画像のn倍拡大
遠隔地に画像をネットワーク配信するときに、遅延を少なくフレームレートを上げたい場合、とりあえず画像サイズを小さくして送っておいて、受信側で高解像度化できたらいいな、ということがあります。ざっと画像拡大縮小アルゴリズムを調べてみましょう。
一般的な画像によく使われる拡大縮小アルゴリズム
- Nearest neighbor(ニアレストネイバー)
- Bilinear(バイリニア)
- Bicubic(バイキュービック)
- Lanczos-3(ランチョス3)
任意の浮動小数点のスケール倍に拡大縮小するアルゴリズムです。これらはOpenCVのresize関数の補間アルゴリズムとしても実装されています。opencvではINTER_AREAという指定もできます。またLanczos-3ではなく4が実装されています。ただし、Lanczosアルゴリズムの結果の割には、ものすごい変な結果が返ってきますので、品質重視の場合は自分で実装しなおした方が無難です。
[1]では、このほかに、
等も紹介されていますが、オープンソースでは無いようなので使い道は限定的です。半導体組み込みIP向けにはDir8や他の物も知られているようです。
Lanczos-3が最もポピュラーなようですね。とくに縮小アルゴリズムとしては、品質が高いという評価のようです。
アイコンの拡大に使われるn倍アルゴリズム
- Nearest neighbor
- Microsoft Method
- Hq4x (hqx)
- Bicubic
- EPX
- SuperEagle
- Super2xSaI
- PhotoZoom Pro 4
- Vector Magic
- Live Trace
整数倍に拡大するアルゴリズムです。定数倍であることを利用して、テンプレートやルックアップテーブルを用意して高速化と品質向上する工夫がされています。古いゲームの高解像度化に使われているようです。Microsoftのアルゴリズム[2]の結果が素晴らしいので試してみたいのですが、実装は公開されてなさそうですね。
2015年5月 DeepLearningを使ったこれが話題
これもすごい
hq4xを試してみる
[4]で配布されているhqx-1.1.tar.gzを持ってきます。
2倍、3倍、4倍アルゴリズムが提供されています。
srcの中のcommon.h,init.c, hq2x.c, hq3x.c, hq4x.c, hqx.hを自分のプロジェクトにコピーして持ってきます。
hqx.hのdllimport宣言がコンパイルの邪魔になることがありますので、その場合は
#define HQX_API
// __declspec(dllimport)
無理やり空にしちゃいます。
使用時は最初に、ルックアップテーブルを作るためにinit.c内のhqxInit()関数を読んでおく必要があります。
入力画像フォーマットは、1ピクセルがBGRAの8bit4chで構成されたwidth×heightの画像です。オーダーはlittleendianです(Windowsはふつうlittleendian)。1ピクセルを32bitのuint32_tにcastして関数に渡します。
出力は、BGRAフォーマットのn(2~4)倍解像度の画像です。
OpenCVやPDFの色フォーマットはBGRの並びですが、多くの画像コンテナではRGBの順に並んでいることが多いので、関数を呼び出す前と後に多少整形してやる必要があります。
openframeworksのofImageの場合、ダミーコードはこんな感じ。
hqxInit();
ofImage img = ofxCv::toOf( mat ); // 適当な絵をopencvで読み込む
img.setImageType(OF_IMAGE_COLOR_ALPHA);
ofImage dst;
dst.allocate(img.getWidth()*2, img.getHeight()*2, OF_IMAGE_COLOR_ALPHA);
hq2x_32( (uint32_t*)img.getPixels(), (uint32_t *)dst.getPixels(), img.getWidth(), img.getHeight());
// BGRAをRGBAにひっくり返す
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
int b = img.getPixels()[(i*width+j)*4+0];
int g = img.getPixels()[(i*width+j)*4+1];
int r = img.getPixels()[(i*width+j)*4+2];
int a = img.getPixels()[(i*width+j)*4+3];
img.getPixels()[(i*width+j)*4+0] = b;
img.getPixels()[(i*width+j)*4+0] = g;
img.getPixels()[(i*width+j)*4+0] = r;
img.getPixels()[(i*width+j)*4+0] = a;
}
}
dst.saveImage("file.png", 100);
lanczosを試してみる
[6]のページのjavaをc++, openFrameworks向けにそのまま書き直してみます。
#define _USE_MATH_DEFINES
#include <math.h>
void lanczos(ofImage& image, ofImage& image2, int n, float sc)
{
//int n = 2; // N値
int nx = n-1;#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < image2.height; y++) {
for (int x = 0; x < image2.width; x++) {
double x0 = x/sc;
double y0 = y/sc;int xBase = (int)x0;
int yBase = (int)y0;int color = 0;
// ランツォシュの処理範囲
if (xBase >= nx && xBase < image.width - n && yBase >= nx && yBase < image.height - n) {
double *color_element = new double[3];
memset(color_element, 0, sizeof(double)*3);double w_total = 0.0;
// 周辺(a*2)^2画素を取得して処理
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = -nx; i <= n; i++) {
for (int j = -nx; j <= n; j++) {
int xCurrent = xBase + i;
int yCurrent = yBase + j;// 距離決定
double distX = abs( (double)xCurrent - x0);
double distY = abs( (double)yCurrent - y0);// 重み付け
double weight = 0.0;if (distX == 0.0) {
weight = 1.0;
} else if (distX < n) {
double dPIx = M_PI*distX;
weight = (sin(dPIx)*sin(dPIx/(double)n))/(dPIx*(dPIx/(double)n));
} else {
continue;
}if (distY == 0.0) {
;
} else if (distY < n) {
double dPIy = M_PI*distY;
weight *= (sin(dPIy)*sin(dPIy/(double)n))/(dPIy*(dPIy/(double)n));
} else {
continue;
}// 画素取得
ofColor color_process = image.getColor(xCurrent, yCurrent);
color_element[0] += color_process.r * weight;
color_element[1] += color_process.g * weight;
color_element[2] += color_process.b * weight;
w_total += weight;
}
}#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < 3; i++) {
if (w_total != 0) color_element[i] /= w_total;
color_element[i] = (color_element[i] > 255) ? 255
: (color_element[i] < 0) ? 0
: color_element[i];
color += (int)color_element[i] << i*8;
}
delete [] color_element;
}ofColor c( (color>>0*8) & 0xff, (color>>1*8) & 0xff, (color>>2*8) & 0xff);
image2.setColor(x, y, c);
}
}
}
[1] Loggia Logic: PSNRによる画像拡大アルゴリズム7種の画質評価結果
[3] Scale2x
[5] hqx - Wikipedia, the free encyclopedia
[6] 画像処理 - HexeRein