7

私は以下のようないくつかの静止画像を持っています:

ここに画像の説明を入力してください

さて、顔や手に触れると、選択した色がその肌の部分に塗りつぶされます。

結果の以下の画像を参照してください。

ここに画像の説明を入力してください

では、上記のような結果を得るにはどうすればよいですか?やり直しと元に戻す機能もそこにあるはずです。

FloodFillカラーを試してみましたが、それを行うと、特定の部分にしかカラーを入れることができません。FloodFillは、同じpixwlカラーが来るまでカラーを塗りつぶすだけです。タッチ場所のピクセルの色が変更された場合、その色は塗りつぶされません。

だからUsinfFloodFillは下の画像のような結果を得ました。手を押すと、手の部分だけが色で塗りつぶされます。代わりに、もう一方の手と顔にも色を塗りつぶしたいと思います。 ここに画像の説明を入力してください

だからこの場合私を助けてください。

編集済み

いくつかの返信の後、私はこのような解決策を得まし

しかし、それでもメモリの問題があります。色を描くのに多くのメモリを消費します。だから誰かが私を助けてくれますか?

4

3 に答える 3

14

完全な画像に実際の色を付けることができます。特定の領域を色で塗りつぶすと、その色で指定されているすべての領域が置き換えられます。

レイマンの用語:

  1. ユーザーはアウトラインの手をクリックします
  2. そのクリック位置は、完全に色分けされた領域を持つ別の画像で確認されます。この場合、それをマスクと呼びましょう。すべての肌の領域は同じ色になります。シャツの部分は別の色になります。
  3. ユーザーがクリックするたびに、ユーザーが選択した色がMASKで同じ色のすべてのピクセルに適用されますが、MASKに直接ペイントする代わりに、OUTLINEのピクセルにペイントします。

これがお役に立てば幸いです。

例が必要な場合はコメントしてください。それで回答を更新できますが、ここから入手できると思います。

編集:

基本的には、このような単純な画像から始めます。これをOUTLINEと呼ぶことができます

シンプルな画像

次に、開発者として、いくつかの作業を行う必要があります。ここでは、 OUTLINEを色分けします。結果をMASKと呼びます。これを作成するには、必要な同じ色で領域を色分けします。これは、ペイントなどで実行できます。私はPhotoshopを使ってかっこいいです笑:D。

マスク

次に、電話で動作させるためのアルゴリズムがあります。コードを読む前に、この変数を確認してください。

int ANTILAISING_TOLERANCE = 70; //Larger better coloring, reduced sensing

境界線の黒い領域に特に注目して画像を拡大すると、実際には、コンピューターが色を少しブレンドしていることがわかります。その変化を説明するために、この許容値を使用します。

COLORINGANDROIDACTIVITY.JAVA

package mk.coloring;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.view.View.OnTouchListener;

public class ColoringAndroidActivity extends Activity implements OnTouchListener{
    /** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    findViewById(R.id.imageView1).setOnTouchListener(this);
}

int ANTILAISING_TOLERANCE = 70;
public boolean onTouch(View arg0, MotionEvent arg1) {
    Bitmap mask = BitmapFactory.decodeResource(getResources(), R.drawable.mask);
    int selectedColor = mask.getPixel((int)arg1.getX(),(int)arg1.getY());           
    int sG = (selectedColor & 0x0000FF00) >> 8;
    int sR = (selectedColor & 0x00FF0000) >> 16;
    int sB = (selectedColor & 0x000000FF);

    Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.empty);       
    Bitmap colored = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Config.ARGB_8888);
    Canvas cv = new Canvas(colored);
    cv.drawBitmap(original, 0,0, null);

    for(int x = 0; x<mask.getWidth();x++){
        for(int y = 0; y<mask.getHeight();y++){
            int g = (mask.getPixel(x,y) & 0x0000FF00) >> 8;
            int r = (mask.getPixel(x,y) & 0x00FF0000) >> 16;
            int b = (mask.getPixel(x,y) & 0x000000FF);
            if(Math.abs(sR - r) < ANTILAISING_TOLERANCE && Math.abs(sG - g) < ANTILAISING_TOLERANCE && Math.abs(sB - b) < ANTILAISING_TOLERANCE)
                colored.setPixel(x, y, (colored.getPixel(x, y) & 0xFF000000) | 0x00458414);
        }
    }
    ((ImageView)findViewById(R.id.imageView1)).setImageBitmap(colored);

    return true;
}

}

このコードは、ユーザーに多くの色の選択肢を提供しません。代わりに、ユーザーが領域に触れると、マスクが表示され、それに応じてアウトラインがペイントされます。しかし、あなたは本当に面白くてインタラクティブにすることができます。

結果

私が男の髪に触れたとき、それは髪を着色しただけでなく、彼のシャツと手を同じ色で着色しました。それをMASKと比較して、何が起こったのかをよく理解してください。

結果

これは単なる基本的な考え方です。複数のビットマップを作成しましたが、実際にはその必要はありません。私はそれをテスト目的で使用し、不要なメモリを消費しました。また、クリックするたびにマスクを再作成する必要はありません。

これがお役に立てば幸いです:D

幸運を

于 2012-03-17T08:23:57.933 に答える
4

FloodFillアルゴリズムを使用します。キャンバス全体を塗りつぶしますが、円、長方形のように、バインドされた塗りつぶし領域を保持します。このリンクを確認することもできます。Android:画像の特定の部分だけに色を塗りつぶす方法は?。一般的な考え方は、クリックするとx座標とy座標を取得します。

 final Point p1 = new Point(); 
 p1.x=(int) x; p1.y=(int) y; X and y are co-ordinates when user clicks on the screen
 final int sourceColor= mBitmap.getPixel((int)x,(int) y);
 final int targetColor =mPaint.getColor();
 new TheTask(mDrawingManager.mDrawingUtilities.mBitmap, p1, sourceColor, targetColor).execute(); //Use AsyncTask and do floodfillin the doinBackground().

Androidのフラッドフィルアルゴリズムについては、上記のリンクを確認してください。これはあなたが望むものを達成するのに役立つはずです。Android FingerPaint Undo/Redoの実装。これは、元に戻すとやり直しに関するニーズに応じて変更するのに役立ちます。

編集:

stackoverflowに関する投稿により、遅延やOOMなしでフラッドフィルアルゴリズムを効率的に使用する方法がわかりました。

SOポストからのピッキング

小さな閉じた領域の塗りつぶしは、上記の塗りつぶしアルゴリズムで正常に機能します。ただし、大面積の場合、アルゴリズムの動作は遅く、大量のメモリを消費します。最近、上記よりもはるかに高速なQueueLinearFloodFillを使用する投稿に出くわしました。

ソース :

http://www.codeproject.com/Articles/16405/Queue-Linear-Flood-Fill-A-Fast-Flood-Fill-Algorith

コード:

public class QueueLinearFloodFiller {

    protected Bitmap image = null;
    protected int[] tolerance = new int[] { 0, 0, 0 };
    protected int width = 0;
    protected int height = 0;
    protected int[] pixels = null;
    protected int fillColor = 0;
    protected int[] startColor = new int[] { 0, 0, 0 };
    protected boolean[] pixelsChecked;
    protected Queue<FloodFillRange> ranges;

    // Construct using an image and a copy will be made to fill into,
    // Construct with BufferedImage and flood fill will write directly to
    // provided BufferedImage
    public QueueLinearFloodFiller(Bitmap img) {
        copyImage(img);
    }

    public QueueLinearFloodFiller(Bitmap img, int targetColor, int newColor) {
        useImage(img);

        setFillColor(newColor);
        setTargetColor(targetColor);
    }

    public void setTargetColor(int targetColor) {
        startColor[0] = Color.red(targetColor);
        startColor[1] = Color.green(targetColor);
        startColor[2] = Color.blue(targetColor);
    }

    public int getFillColor() {
        return fillColor;
    }

    public void setFillColor(int value) {
        fillColor = value;
    }

    public int[] getTolerance() {
        return tolerance;
    }

    public void setTolerance(int[] value) {
        tolerance = value;
    }

    public void setTolerance(int value) {
        tolerance = new int[] { value, value, value };
    }

    public Bitmap getImage() {
        return image;
    }

    public void copyImage(Bitmap img) {
        // Copy data from provided Image to a BufferedImage to write flood fill
        // to, use getImage to retrieve
        // cache data in member variables to decrease overhead of property calls
        width = img.getWidth();
        height = img.getHeight();

        image = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(image);
        canvas.drawBitmap(img, 0, 0, null);

        pixels = new int[width * height];

        image.getPixels(pixels, 0, width, 1, 1, width - 1, height - 1);
    }

    public void useImage(Bitmap img) {
        // Use a pre-existing provided BufferedImage and write directly to it
        // cache data in member variables to decrease overhead of property calls
        width = img.getWidth();
        height = img.getHeight();
        image = img;

        pixels = new int[width * height];

        image.getPixels(pixels, 0, width, 1, 1, width - 1, height - 1);
    }

    protected void prepare() {
        // Called before starting flood-fill
        pixelsChecked = new boolean[pixels.length];
        ranges = new LinkedList<FloodFillRange>();
    }

    // Fills the specified point on the bitmap with the currently selected fill
    // color.
    // int x, int y: The starting coords for the fill
    public void floodFill(int x, int y) {
        // Setup
        prepare();

        if (startColor[0] == 0) {
            // ***Get starting color.
            int startPixel = pixels[(width * y) + x];
            startColor[0] = (startPixel >> 16) & 0xff;
            startColor[1] = (startPixel >> 8) & 0xff;
            startColor[2] = startPixel & 0xff;
        }

        // ***Do first call to floodfill.
        LinearFill(x, y);

        // ***Call floodfill routine while floodfill ranges still exist on the
        // queue
        FloodFillRange range;

        while (ranges.size() > 0) {
            // **Get Next Range Off the Queue
            range = ranges.remove();

            // **Check Above and Below Each Pixel in the Floodfill Range
            int downPxIdx = (width * (range.Y + 1)) + range.startX;
            int upPxIdx = (width * (range.Y - 1)) + range.startX;
            int upY = range.Y - 1;// so we can pass the y coord by ref
            int downY = range.Y + 1;

            for (int i = range.startX; i <= range.endX; i++) {
                // *Start Fill Upwards
                // if we're not above the top of the bitmap and the pixel above
                // this one is within the color tolerance
                if (range.Y > 0 && (!pixelsChecked[upPxIdx])
                        && CheckPixel(upPxIdx))
                    LinearFill(i, upY);

                // *Start Fill Downwards
                // if we're not below the bottom of the bitmap and the pixel
                // below this one is within the color tolerance
                if (range.Y < (height - 1) && (!pixelsChecked[downPxIdx])
                        && CheckPixel(downPxIdx))
                    LinearFill(i, downY);

                downPxIdx++;
                upPxIdx++;
            }
        }

        image.setPixels(pixels, 0, width, 1, 1, width - 1, height - 1);
    }

    // Finds the furthermost left and right boundaries of the fill area
    // on a given y coordinate, starting from a given x coordinate, filling as
    // it goes.
    // Adds the resulting horizontal range to the queue of floodfill ranges,
    // to be processed in the main loop.

    // int x, int y: The starting coords
    protected void LinearFill(int x, int y) {
        // ***Find Left Edge of Color Area
        int lFillLoc = x; // the location to check/fill on the left
        int pxIdx = (width * y) + x;

        while (true) {
            // **fill with the color
            pixels[pxIdx] = fillColor;

            // **indicate that this pixel has already been checked and filled
            pixelsChecked[pxIdx] = true;

            // **de-increment
            lFillLoc--; // de-increment counter
            pxIdx--; // de-increment pixel index

            // **exit loop if we're at edge of bitmap or color area
            if (lFillLoc < 0 || (pixelsChecked[pxIdx]) || !CheckPixel(pxIdx)) {
                break;
            }
        }

        lFillLoc++;

        // ***Find Right Edge of Color Area
        int rFillLoc = x; // the location to check/fill on the left

        pxIdx = (width * y) + x;

        while (true) {
            // **fill with the color
            pixels[pxIdx] = fillColor;

            // **indicate that this pixel has already been checked and filled
            pixelsChecked[pxIdx] = true;

            // **increment
            rFillLoc++; // increment counter
            pxIdx++; // increment pixel index

            // **exit loop if we're at edge of bitmap or color area
            if (rFillLoc >= width || pixelsChecked[pxIdx] || !CheckPixel(pxIdx)) {
                break;
            }
        }

        rFillLoc--;

        // add range to queue
        FloodFillRange r = new FloodFillRange(lFillLoc, rFillLoc, y);

        ranges.offer(r);
    }

    // Sees if a pixel is within the color tolerance range.
    protected boolean CheckPixel(int px) {
        int red = (pixels[px] >>> 16) & 0xff;
        int green = (pixels[px] >>> 8) & 0xff;
        int blue = pixels[px] & 0xff;

        return (red >= (startColor[0] - tolerance[0])
                && red <= (startColor[0] + tolerance[0])
                && green >= (startColor[1] - tolerance[1])
                && green <= (startColor[1] + tolerance[1])
                && blue >= (startColor[2] - tolerance[2]) && blue <= (startColor[2] + tolerance[2]));
    }

    // Represents a linear range to be filled and branched from.
    protected class FloodFillRange {
        public int startX;
        public int endX;
        public int Y;

        public FloodFillRange(int startX, int endX, int y) {
            this.startX = startX;
            this.endX = endX;
            this.Y = y;
        }
    }
}
于 2012-10-16T10:52:46.890 に答える
1

基本的な方法の1つは、塗りつぶしアルゴリズムのようなものです。ウィキペディアの記事では、アルゴリズムとそのバリエーションについて詳しく説明しています。

ここでは、SOの実装を見つけることができます。ただし、特定のニーズに応じて、これを変更する必要があります。

于 2012-03-17T08:20:20.987 に答える