14

カメラ経由でキャプチャしたビットマップで透視変換を実行しようとしています。ユーザーは、四角形のオブジェクトの周囲の境界クワッド (白いボックスで示されている) を調整します。次に、以下のコードを使用して、これを長方形の画像に変換しようとします。

public static Bitmap perspectiveTransformation(Bitmap bitmap,BoundingQuad boundingQuad)
{
    Matrix matrix = new Matrix();
    float[] dst = new float[] {
            0,
            0,
            bitmap.getWidth(),
            0,
            bitmap.getWidth(),
            bitmap.getHeight(),
            0,
            bitmap.getHeight()
    };
    float[] src = new float[] {
            boundingQuad.topLeft.x,
            boundingQuad.topLeft.y,
            boundingQuad.topRight.x,
            boundingQuad.topRight.y,
            boundingQuad.bottomRight.x,
            boundingQuad.bottomRight.y,
            boundingQuad.bottomLeft.x,
            boundingQuad.bottomLeft.y
    };
    matrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1);
    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}

ただし、結果の画像には、クワッドの境界外にある画像データが含まれています。これは、変換後のクワッドの座標を把握できれば許容できるので、結果を切り取ることができますが、その方法がまったくわかりません。

変換後にクワッドの座標を見つけるか、理想的にはこの状況が最初に発生するのを防ぐ方法を見つけるのに、どんな助けも大歓迎です。

入力:

http://i.stack.imgur.com/33RfN.png

出力:

http://i.stack.imgur.com/zWFvA.png

4

2 に答える 2

9

私は同じ問題を抱えていて、変換後に長方形の座標を見つけることで解決しました。

これらの座標を見つけるには、何が起こっているのかを理解する必要があります。マトリックスは、クワッドの 4 つのエッジ ポイントと対応するポイントによって与えられる透視変換を定義します。次のコードでこれを行いました。

Matrix matrix = new Matrix();
float[] dst = new float[] {
        0,
        0,
        bitmap.getWidth(),
        0,
        bitmap.getWidth(),
        bitmap.getHeight(),
        0,
        bitmap.getHeight()
};
float[] src = new float[] {
        boundingQuad.topLeft.x,
        boundingQuad.topLeft.y,
        boundingQuad.topRight.x,
        boundingQuad.topRight.y,
        boundingQuad.bottomRight.x,
        boundingQuad.bottomRight.y,
        boundingQuad.bottomLeft.x,
        boundingQuad.bottomLeft.y
}; 
matrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1);

これは、(たとえば) クワッドの左上のポイントがポイント (0,0) に変換されることを意味します。これを確認するには、行列をポイントに適用し、結果の値を確認します。マトリックスを適用するには、メソッドを使用できますmapPoints(...)。定義された変換マトリックスは正常に機能します。(一見すると) この変換の奇妙な動作は、ビットマップの作成に起因します。

    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

結果のビットマップは間違っているように見えます。これは、一部のポイント (たとえば、クワッドの左上のポイントの左側にあるすべてのポイント) が負の座標に変換され、ビットマップに何かを描画する場合は座標が正でなければならないためです。 . このため、変換されたポイントがシフトされ、ビットマップ内の変換されたポイントの座標が奇妙になります。

結論: ポイントは新しい座標に正しく変換されますが、表示できません。このため、ポイントはシフトされ、ビットマップ内の変換されたポイントのシフトされた座標は、変換マトリックスで定義されている座標ではありません。

この問題を解決し、ビットマップ内の変換されたポイントの適切な座標を取得するには、シフトの値を計算する必要があります。シフトは、x 値と y 値で構成されます。

x 値を計算するために、元の画像の左上の点の x 値と左下の点の x 値を、以前に定義した行列で変換しました。左上のポイントまたは左下のポイントのいずれかが、結果のビットマップの左境界に変換されます。このため、このポイントのビットマップ座標の x 値は 0 に等しく、否定されます (x-値は正である必要があります) 変換された座標の x 値は、シフトの x 値です。結果のビットマップの左境界に変換されるポイントは、最大の否定された x 値を持つポイントです。したがって、シフトの x 値は、変換された左上点と左下点の否定された x 値の最大値です。

y 値を計算するために、元の画像の左上の点の y 値と右上の点の y 値を変換しました。これは、結果の上部境界に変換される可能性のある点であるためです。変換されたポイントの y 値は、結果のビットマップでは 0 に等しくなります。変換された y 値の否定された値の最大値を再度取得することにより、シフトの y 値を取得します。

結果のコードは次のようになります。

    float[] mappedTL = new float[] { 0, 0 };
    matrix.mapPoints(mappedTL);
    int maptlx = Math.round(mappedTL[0]);
    int maptly = Math.round(mappedTL[1]);

    float[] mappedTR = new float[] { bitmap.getWidth(), 0 };
    matrix.mapPoints(mappedTR);
    int maptrx = Math.round(mappedTR[0]);
    int maptry = Math.round(mappedTR[1]);

    float[] mappedLL = new float[] { 0, bitmap.getHeight() };
    matrix.mapPoints(mappedLL);
    int mapllx = Math.round(mappedLL[0]);
    int maplly = Math.round(mappedLL[1]);

    int shiftX = Math.max(-maptlx, -mapllx);
    int shiftY = Math.max(-maptry, -maptly);

    Bitmap resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    return Bitmap.createBitmap(resultBitmap, shiftX, shiftY, bitmap.getWidth(), bitmap.getHeight(), null, true);
于 2014-10-30T12:30:41.430 に答える
-3

新しいBitmap画像を作成したら、「切り抜き」インテントを作成して呼び出すことで、Android の組み込みの切り抜き機能を呼び出すことができます。次に例を示します。

Intent cropIntent = new Intent("com.android.camera.action.CROP");

// Put in 'Extras' here to build the intent

// Start the activity. It will be handled by returning data to onActivityResult
startActivityForResult(cropIntent, PIC_CROP); // PIC_CROP is just an arbitrary tag name.

Android での画像トリミングの詳細については、ここから取得できます。

これを機能させるには、アプリのマニフェスト ファイルに次を追加する必要もあります。

<uses-feature android:name="android.hardware.camera" ></uses-feature>
<uses-permission android:name="android.permission.CAMERA" />
于 2013-10-25T16:36:30.777 に答える