画像をロードする関数が何であれ、次の 2 つの情報が得られます。1 つは、幅や高さなどの画像情報です。もう 1 つは、数値の配列である画像データです。
b1
画像とを取得しb2
、GLubyte の配列であるb1
としましょう。b2
そしてb1_w
、b1_h
、 、がb2_w
およびb2_h
の幅と高さであるb1
とb2
しましょう (これは、ビットマップ ロード関数によって提供される必要があります)。
やりたいことは 2 つの部分で構成されています。まず、ストレッチが必要ですb1
。次に、 の途中に書く必要がありますb2
。実際には両方を同時に行うこともできますが、今のところは分けておきます。
ストレッチ
画像を引き伸ばすには、簡単な計算ができる必要があります。マップに入る前に、画像変換 (回転、ストレッチなど) の基本的な考え方について説明します。最も簡単にできることは、ターゲットイメージのピクセルを調べて元の画像に変換し、元の画像からどの色を取得してそのピクセルに割り当てるかを決定することです。
たとえば、画像を幅と高さから と に引き伸ばしたい場合w1
、h1
変換w2
はh2
次のようになります。
xt = w2/w1*xs
yt = h2/h1*ys
ここでxt
、 とyt
はターゲット イメージの座標であり、xs
とys
はソース イメージの座標です。逆変換は次のとおりです。
xs = w1/w2*xt
ys = h1/h2*yt
続行する前に、作業を楽にするマクロを定義しましょう。データの配列は 1 次元配列ですが、x インデックスと y インデックスを使用するため、変換を行うマクロを記述します。
#define INDEX(x, y, width) (((y)*(width)+(x))*3)
(補足:画像はRGB(またはBGR)で保存されていると仮定したことに注意してください。つまり、ピクセルごとに3つの値があります。また、画像の幅が4の倍数でない場合、パディングと各行の幅があることに注意してください。 2 番目のケースでは、次のようにマクロを記述できますwidth*3
。width*3+something_to_make_it_divisible_by_4
#define INDEX(x, y, row_bytes) ((y)*(row_bytes)+(x)*3) // note the parentheses
)
したがって、コードは次のようになります (float
特に指定がない限り、幅/高さと x/y の値はすべて同じです) 。
for (unsigned int yt = 0; yt < h2; ++yt)
for (unsigned int xt = 0; xt < w2; ++xt)
{
float xs = w1/w2*xt;
float ys = h1/h2*yt;
target[INDEX(xt, yt, w2)] = // COMPUTED IN THE NEXT STEP
target[INDEX(xt, yt, w2)+1] = // COMPUTED IN THE NEXT STEP
target[INDEX(xt, yt, w2)+2] = // COMPUTED IN THE NEXT STEP
}
3 つの色コンポーネント (R、G、B) があるため、3 つの値の割り当てがあります。アルファ 2 がある場合は、4 にします。グレースケールで作業している場合は、1 のままにしてください (お分かりいただけると思います)。
次のステップは、ピクセルの色を実際に割り当てることです。これは、さまざまなストレッチ アルゴリズムが異なる場所であり、ストレッチの品質はこのステップに依存します。
だから、ここにアイデアがあります。と が完全に丸められた整数になることを想像xs
してみてください。ys
それは簡単です。ソース イメージでそのピクセルの色を選択し、それをターゲットに配置するだけです。しかし、xs
とys
が整数でない場合はどうなるでしょうか。では、ターゲット ピクセルにはどの色を使用しますか? 2 つの一般的な方法を次に示します。
最近傍
これは簡単です。計算した位置に最も近いピクセルの色を選択してください。たとえば、だった場合(xs, ys)
は(12.78, 98.2)
、ポイントの色を選択します(13, 98)
。それはあなたが持っているあなたのコードにあります:
unsigned int xs_int = (unsigned int)round(xs);
unsigned int ys_int = (unsigned int)round(ys);
target[INDEX(xt, yt, w2)] = source[INDEX(xs_int, ys_int, w1)]
線形化
この方法には少し計算が必要です。つまり、周囲の 4 つのピクセルの間で線形化された値を取得し、(xs, ys)
それをターゲット ピクセルの色として選択します。それはどうですか?お見せします。これらの 4 つのピクセルと計算された(xs, ys)
v_tl v_tr
+------------+
| |
| .(xs, ys) |
| |
| |
| |
+------------+
v_bl v_br
周囲の4つのピクセルの値は、図に示すように v_{t,b}{l,r} (上、下、左、右の略) です。position の値を線形化するには、の線形化された(xs, ys)
値を取得して(floor(xs), ys)
再度(ceil(xs), ys)
線形化するか、同じことを最初に y で行い、次に x で行います。したがって、次のようになります。
unsigned int x_bl = (unsigned int)floor(xs);
unsigned int y_bl = (unsigned int)floor(ys);
float v_l = v_bl+(v_tl-v_bl)*(ys-y_bl); // (floor(xs), ys)
// note that in the end, there must be a (x_tl-x_bl) but this value is 1
float v_r = v_br+(v_tr-v_br)*(ys-y_bl); // (ceil(xs), ys)
float v = v_l+(v_r-v_l)*(xs-x_bl);
最後に、あなたは得る
target[INDEX(xt, yt, w2)] = v;
画像のすべてのチャンネル (R、G、B など) に対してこの線形化を行い、適切な v を適切なインデックス (INDEX(...)、INDEX(...)+1、および INDEX( ...)+2 例)。
どちらの方法でも、配列の範囲外にならないように、ソース イメージで計算されたインデックスをチェックする必要があることに注意してください。その場合、配列の境界外にあるピクセルの色を黒/白/ミッドグレーと見なすことができます。または、それらをチェックして、ピクセルが配列外にある場合は、メソッドを変更して、ソース画像内にあるピクセルのみを処理するようにします。
他の画像を上書きする
この部分は簡単です。あなたはイメージstretched_b2
を持っていて、それを書きたいと思っていますb1
。書き込みたい左下の位置が の場合は、単に次のように記述stretched_b2
します。b1
(x_start, y_start)
for (unsigned int y = 0; y < h2_stretched; ++y)
for (unsigned int x = 0; x < w2_stretched; ++x)
{
b1[INDEX(x_start+x, y_start+y, w1)] =
stretched_b2[INDEX(x, y, w2_stretched)];
b1[INDEX(x_start+x, y_start+y, w1)+1] =
stretched_b2[INDEX(x, y, w2_stretched)+1];
b1[INDEX(x_start+x, y_start+y, w1)+2] =
stretched_b2[INDEX(x, y, w2_stretched)+2];
}
ここに画像を配置します。
(b1の横中央-40、縦中央のb1-30)~(横中央のb1+40、縦中央のb1+30)
とても簡単に、あなたは持っています
w2_stretched = 80 // or +1 if you want to be really precise,
// but for now let's say 80 to keep it a multiple of 4
h2_stretched = 60
x_start = w1/2-40
y_start = h1/2-30
2 つの部分を組み合わせる
ストレッチセクションで、あなたが持っていた部分を思い出してください
for (unsigned int yt = 0; yt < h2; ++yt)
for (unsigned int xt = 0; xt < w2; ++xt)
そして、あなたが持っていた配置セクションで
for (unsigned int y = 0; y < h2_stretched; ++y)
for (unsigned int x = 0; x < w2_stretched; ++x)
ストレッチ セクションh2
でw2
は、ストレッチ ターゲット イメージの高さと幅を覚えていますか? それが私が呼んだものh2_stretched
でw2_stretched
、配置セクションにあります。ご覧のとおり、これらの for ループは同じことを繰り返します。次に、ストレッチセクションでターゲット イメージの値を計算し、配置セクションでそれをコピーしb1
ます。一時的なターゲット イメージに保持する代わりに、値を直接計算して上書きできるb1
ので、次のようになります。
for (unsigned int yt = 0; yt < h2; ++yt)
for (unsigned int xt = 0; xt < w2; ++xt)
{
float xs = w1/w2*xt;
float ys = h1/h2*yt;
b1[INDEX(x_start+xt, y_start+yt, w1)] = compute_with_mentioned_methods;
b1[INDEX(x_start+xt, y_start+yt, w1)+1] = compute_with_mentioned_methods;
b1[INDEX(x_start+xt, y_start+yt, w1)+2] = compute_with_mentioned_methods;
}
最後の注意: 画像操作は簡単なことですが、多くのインデックス チェックが必要で、最初から正しく処理するのは困難です。セグメンテーション違反に備えて、デバッグに時間がかかっても動揺しないでください。
最終的なメモ: これらを説明して学習させましたが、OpenGL を使用する場合、私であれば、両方の画像をテクスチャに変換しQUADS
、適切な位置に描画し、OpenGL で画像をストレッチします。