2012-03-17 51 views
6

我有一個像下面的一些靜態圖像:Android:如何做這個框架油漆?

enter image description here

現在,我想的是,當我在臉上或用手觸摸,那麼所選擇的顏色應該對皮膚部分填補。

請參見下面的結果圖像:

那麼如何得到的結果類似上面? 重做和撤消功能應該也在那裏。

我嘗試使用FloodFill顏色,但是這樣做只能夠對特定部分進行顏色處理。因爲FloodFill只填充顏色,直到出現相同的pixwl顏色。如果觸摸位置的像素顏色發生變化,則不會在其上填充顏色。

所以Usinf FloodFill我得到了像下面的圖像的結果,如果我按手,然後只有手部分將填充顏色,而不是它我想填補顏色的另一方面,也面對。 enter image description here

所以請幫助我在這種情況下。

EDITED

一些答覆我得到了像this one溶液之後。

但仍然存在內存問題。它消耗大量內存來繪製顏色。所以請任何人都可以幫助我呢?

回答

13

你可以有一個完整的圖像顏色的實際方式,當你填寫一個特定區域用顏色,它將替換所有由該顏色中指定要填充的區域

通俗地說:

  1. 用戶會點擊OUTLINE
  2. 那點擊的位置將用完美的色彩編碼的區域另一幅圖像進行檢查的手。讓我們稱它爲這種情況下的MASK。所有的皮膚區域將具有相同的顏色。襯衫領域將是另一種顏色。
  3. 無論用戶單擊哪個位置,用戶選擇的顏色都將應用於MASK中具有相似顏色的每個像素,但不是直接在MASK上繪製,而是繪製到OUTLINE的像素上。

我希望這會有所幫助。

隨意評論,如果你想要的例子,然後我可以更新答案,但我認爲你可以從這裏得到它。

編輯:

基本上從這樣一個簡單的圖像開始。這個我們可以稱之爲爲外形

Simple image

然後作爲開發者,你必須做一些工作。在這裏,您的顏色代碼爲,輪廓。結果我們稱爲MASK。爲了做到這一點,我們使用相同顏色的區域進行顏色編碼。這可以在油漆或其他方面完成。我用Photoshop來冷靜哈哈:D。

Mask

再有就是算法得到它的工作在手機上。在閱讀代碼之前,先看看這個變量。

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並相應地繪製OUTLINE。但是,你可以變得非常有趣和互動。

結果

當我碰到這個人的頭髮,它不僅顏色的頭髮,而且歪曲了他的襯衫和手用相同的顏色。將其與MASK進行比較,以瞭解發生了什麼。

Result

這僅僅是一個基本的想法。我創建了多個Bitmaps,但並不需要這樣做。我曾用它進行測試,並佔用不必要的內存。而你並不需要重建的每次點擊面具等

我希望這可以幫助你:d

好運

+0

請給我的例子。我得到了點,但如何用我的代碼呢?請給我舉例Android版。 – 2012-03-19 04:44:18

+0

:請爲我提供演示。我真的需要它。 – 2012-03-19 06:12:44

+0

你的邏輯是正確的,但默認情況下,我對所有部分都有白色。那麼如何得到它的工作?而且我也想要對它進行撤銷和重做。所以請幫助我,如果你有任何想法。 – 2012-03-19 12:01:27

1

一個基本的方式將是像floodfill algorythm。 維基百科的文章很好地描述了algorythm及其變體。

Here你可以在SO上找到一個實現。但取決於您的具體需求,這個必須進行修改。

3

使用FloodFill算法。 Fill the complete canvas but keep the bound fill area as it is like circle, rectangle。你也可以檢查這個鏈接。 Android: How to fill color to the specific part of the Image only?。總體思路是通過點擊獲得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(). 

檢查上面的floodfill算法android的鏈接。這應該可以幫助你實現你想要的。 Android FingerPaint Undo/Redo implementation。這應該可以幫助你根據你的需要修改撤銷和重做。

編輯:

計算器上的一個帖子促使我用洪水填充算法沒有延遲和OOM的有效方式。

從SO郵政

採摘填補了小禁區正常工作與上面的洪水填充算法。然而,對於大面積的算法,該算法工作緩慢並且消耗大量內存。最近我遇到了一個使用QueueLinear Flood Fill的帖子,它的速度更快。

來源:

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; 
     } 
    } 
}