web-dev-qa-db-ja.com

imresizeを使用せずに双線形補間で画像のサイズを変更する

画像を拡大する方法をいくつか見つけましたが、画像を縮小する解決策はありません。現在、最近傍法を使用しています。 MATLABでimresize関数を使用せずに双線形補間でこれを行うにはどうすればよいですか?

13
teardrop

あなたのコメントでは、双線形補間を使用して画像のサイズを変更したいと述べました。バイリニア補間アルゴリズムはサイズに依存しないことに注意してください。同じアルゴリズムを使用して、画像の拡大と縮小を非常にうまく行うことができます。ピクセル位置をサンプリングするための適切なスケール係数は、指定した出力サイズに依存します。このは、コアアルゴリズムを変更しません

コードを始める前に、 Richard Alan PetersのII補間によるデジタル画像処理スライド 、具体的にはスライド#59を紹介します。これには、MATLABに適した双線形補間の実行方法に関する素晴らしいコードと疑似コードが含まれています。自己完結型であるために、私は彼のスライドをここに含めて、フォローしてコーディングできるようにします。

enter image description here

これを行う関数を作成しましょう。この関数は、イメージ( imread を介して読み込まれます)を取り込みます。これは、カラーまたはグレースケールのいずれかであり、2つの要素の配列です-サイズ変更するイメージ必要な最終サイズ変更画像の2要素配列の出力次元。この配列の最初の要素は行になり、この配列の2番目の要素は列になります。このアルゴリズムを実行して、この擬似コードを使用して出力ピクセルの色/グレースケール値を計算します。

_function [out] = bilinearInterpolation(im, out_dims)

    %// Get some necessary variables first
    in_rows = size(im,1);
    in_cols = size(im,2);
    out_rows = out_dims(1);
    out_cols = out_dims(2);

    %// Let S_R = R / R'        
    S_R = in_rows / out_rows;
    %// Let S_C = C / C'
    S_C = in_cols / out_cols;

    %// Define grid of co-ordinates in our image
    %// Generate (x,y) pairs for each point in our image
    [cf, rf] = meshgrid(1 : out_cols, 1 : out_rows);

    %// Let r_f = r'*S_R for r = 1,...,R'
    %// Let c_f = c'*S_C for c = 1,...,C'
    rf = rf * S_R;
    cf = cf * S_C;

    %// Let r = floor(rf) and c = floor(cf)
    r = floor(rf);
    c = floor(cf);

    %// Any values out of range, cap
    r(r < 1) = 1;
    c(c < 1) = 1;
    r(r > in_rows - 1) = in_rows - 1;
    c(c > in_cols - 1) = in_cols - 1;

    %// Let delta_R = rf - r and delta_C = cf - c
    delta_R = rf - r;
    delta_C = cf - c;

    %// Final line of algorithm
    %// Get column major indices for each point we wish
    %// to access
    in1_ind = sub2ind([in_rows, in_cols], r, c);
    in2_ind = sub2ind([in_rows, in_cols], r+1,c);
    in3_ind = sub2ind([in_rows, in_cols], r, c+1);
    in4_ind = sub2ind([in_rows, in_cols], r+1, c+1);       

    %// Now interpolate
    %// Go through each channel for the case of colour
    %// Create output image that is the same class as input
    out = zeros(out_rows, out_cols, size(im, 3));
    out = cast(out, class(im));

    for idx = 1 : size(im, 3)
        chan = double(im(:,:,idx)); %// Get i'th channel
        %// Interpolate the channel
        tmp = chan(in1_ind).*(1 - delta_R).*(1 - delta_C) + ...
                       chan(in2_ind).*(delta_R).*(1 - delta_C) + ...
                       chan(in3_ind).*(1 - delta_R).*(delta_C) + ...
                       chan(in4_ind).*(delta_R).*(delta_C);
        out(:,:,idx) = cast(tmp, class(im));
    end
_

上記のコードをコピーし、_bilinearInterpolation.m_というファイルにコピーして貼り付けて保存します。このファイルを保存した作業ディレクトリを必ず変更してください。


_sub2ind_およびおそらくmeshgridを除いて、すべてがアルゴリズムに従っているようです。 meshgrid の説明は非常に簡単です。あなたがしているのは、画像の各位置に_(x,y)_または列と行の座標のペアがある、_(x,y)_座標の2Dグリッドを指定することだけです。 meshgridを介してグリッドを作成すると、続行する前に必要なアルゴリズムから適切なピクセル位置をすべて生成するため、forループを回避できます。

_sub2ind_ の仕組みは、2Dマトリックスの行と列の位置を取得することです(まあ...実際にはany必要な次元の量)、およびsinglelinear indexを出力します。 MATLABがマトリックスにインデックスを付ける方法を知らない場合、マトリックス内の要素にアクセスする方法は2つあります。行と列を使用して必要なものを取得するか、column-majorインデックスを使用できます。以下にあるこのマトリックスの例を見てください。

_A = 

1  2  3  4  5
6  7  8  9  10
11 12 13 14 15
_

9番にアクセスしたい場合は、ほとんどの人がデフォルトとする傾向があるA(2,4)を実行できます。単一の番号を使用して番号9にアクセスする別の方法があります。これはA(11)...です。 MATLABは、行列のメモリをcolumn-major形式でレイアウトします。つまり、このマトリックスを使用して、そのをすべて単一の配列にスタックすると、次のようになります。

_A = 

1
6
11
2
7
12
3
8
13
4
9
14
5
10
15
_

ここで、要素番号9にアクセスする場合、この配列の11番目の要素にアクセスする必要があります。補間ビットに戻ると、forループを実行せずに補間を行うために画像内の要素にアクセスしてベクトル化する場合、_sub2ind_は非常に重要です。そのため、擬似コードの最後の行を見ると、rc、_r+1_、および_c+1_の要素にアクセスできます。これらはすべて2D配列であることに注意してください。これらの配列のすべての一致する位置の各要素は、生成するためにサンプリングする必要がある4つのピクセルを示します。最終出力ピクセル。 _sub2ind_の出力は、also出力イメージと同じサイズの2D配列にもなります。ここで重要なのは、rc、_r+1_、および_c+1_の2D配列の各要素が列を提供することです。 -majorは、アクセスしたい画像にインデックスを付けます。これをインデックスとして画像に入力することで、必要なピクセル位置を正確に取得します。


アルゴリズムを実装するときに追加したい重要な微妙な点がいくつかあります。

  1. 画像の外で補間するときに画像にアクセスするためのインデックスが1か、行または列の数に設定されていることを確認して、範囲外にならないようにする必要があります。実際、画像の右または下に拡張する場合は、これを最大値belowに設定する必要があります。補間では、1つ右のピクセルにアクセスする必要があるため以下。これにより、まだ範囲内にいることが確認されます。

  2. また、出力画像が入力画像と同じクラスにキャストされることを確認する必要があります。

  3. forループを実行して、各チャンネルを単独で補間しました。 bsxfun を使用してこれをインテリジェントに行うことができますが、簡単にするためにforループを使用することを決定しました。


これが機能することを示す例として、MATLABのシステムパスの一部である_onion.png_イメージを使用してみましょう。この画像の元の寸法は_135 x 198_です。元の画像の2倍のサイズである_270 x 396_に移動して、この画像を拡大してみましょう。

_im = imread('onion.png');
out = bilinearInterpolation(im, [270 396]);
figure;
imshow(im);
figure;
imshow(out);
_

上記のコードは、各次元を2倍に増やすことで画像を補間し、元の画像と拡大された画像で別の図を表示します。これは私が両方について得たものです:

enter image description here

enter image description here


同様に、画像を半分に縮小します。

_im = imread('onion.png');
out = bilinearInterpolation(im, [68 99]);
figure;
imshow(im);
figure;
imshow(out);
_

行の135の半分は67.5ですが、68に切り上げられていることに注意してください。

enter image description here

enter image description here


実際に気づいたことの1つは、bilinearを使用したアップサンプリングは、bicubic ...または Lanczos のような他のスキームと比較して、適切なパフォーマンスを発揮することです。ただし、詳細を削除するため、画像を縮小する場合は、最近傍で十分です。バイリニアまたはバイキュービックは過剰すぎると感じます。あなたのアプリケーションが何であるかはわかりませんが、さまざまな補間アルゴリズムを試してみて、結果から好きなものを見てください。バイキュービックは別の話であり、演習としてそれをお任せします。私が紹介したスライドには、興味があるならバイキュービック補間に関する資料があります。


幸運を!

57
rayryeng