2

私はVisで働いています。Std 2010 Cpp と私は、2 つの既存のビットマップからビットマップを作成する方法を見つけようとしています。

2 つの gl_drawbitmap 関数をそれぞれに 1 つずつ使用するのではなく、3 番目のビットマップと gl_drawbitmap をウィンドウに新しいものとして作成します。また、2 番目のビットマップ (b2 と呼びましょう) を最初のビットマップ (b1) の中央に配置しようとしているので、疑似コードは次のようになります (b2 が 80 x 60 であると仮定すると)

place b2 on b1 stretched from 
(width-wise middle of b1 -40, heightwise middle of b1-30) 
to (widthwise middle of b1+40, heightwise middle of b1+30)

現在のWINDOWの特定の測定値を見つけるためにglutGet(GLUT_WINDOW_WIDTH / HEIGHT)を知っていますが、ビットマップでそれを見つける方法がわかりません。それらが実行可能であるという証拠を見つけることができませんでしたが、b1.getHeight() と b1.getWidth() はトリックを行いますか?

私は全体的に、2つの実際の組み合わせを行う方法を理解するのに多くの問題を抱えています.

void drawbitmap(bitmap* b1, bitmap* b2)
{
...
}

関数関連、それは私がコードを入れようとしているものです.

4

1 に答える 1

4

画像をロードする関数が何であれ、次の 2 つの情報が得られます。1 つは、幅や高さなどの画像情報です。もう 1 つは、数値の配列である画像データです。

b1画像とを取得しb2、GLubyte の配列であるb1としましょう。b2そしてb1_wb1_h、 、がb2_wおよびb2_hの幅と高さであるb1b2しましょう (これは、ビットマップ ロード関数によって提供される必要があります)。

やりたいことは 2 つの部分で構成されています。まず、ストレッチが必要ですb1。次に、 の途中に書く必要がありますb2。実際には両方を同時に行うこともできますが、今のところは分けておきます。

ストレッチ

画像を引き伸ばすには、簡単な計算ができる必要があります。マップに入る前に、画像変換 (回転、ストレッチなど) の基本的な考え方について説明します。最も簡単にできることは、ターゲットイメージのピクセルを調べて元の画像に変換し、元の画像からどの色を取得してそのピクセルに割り当てるかを決定することです。

たとえば、画像を幅と高さから と に引き伸ばしたい場合w1h1変換w2h2次のようになります。

xt = w2/w1*xs
yt = h2/h1*ys

ここでxt、 とytはターゲット イメージの座標であり、xsysはソース イメージの座標です。逆変換は次のとおりです。

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*3width*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それは簡単です。ソース イメージでそのピクセルの色を選択し、それをターゲットに配置するだけです。しかし、xsysが整数でない場合はどうなるでしょうか。では、ターゲット ピクセルにはどの色を使用しますか? 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)

ストレッチ セクションh2w2は、ストレッチ ターゲット イメージの高さと幅を覚えていますか? それが私が呼んだものh2_stretchedw2_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 で画像をストレッチします。

于 2011-09-19T22:11:01.940 に答える