2011-06-25 84 views
32

有幾個教程,解釋如何獲得一個簡單的相機預覽和運行在Android設備上。但是我找不到任何解釋如何在渲染圖像之前處理圖像的例子。
我想要做的是實現自定義濾鏡來模擬例如紅色和/或綠色缺陷。如何操作相機預覽?

+1

,如果ü可以用代碼解釋,什麼烏爾試圖做,它會很容易幫助ü – the100rabh

+0

https://github.com/whiskeysierra/impaired-vision/blob/master/src/org/whiskeysierra/impairedvision/ImpairedVision.java這是我目前擁有的。我想要做的是註冊某種回調(Camera.PreviewCallback?),然後使用它來操縱當前幀。 – whiskeysierra

+0

鏈接不再工作,但回購仍然存在,如果有人仍然感興趣:https://github.com/whiskeysierra/impaired-vision – whiskeysierra

回答

49

我在這方面做了一些研究,並把一個工作(ish)的例子。這是我發現的。從相機中取出原始數據非常容易。它作爲YUV字節數組返回。您需要將其手動繪製到曲面上以便修改它。要做到這一點,你需要有一個SurfaceView,你可以手動運行繪製調用。有幾個標誌可以設置完成。

爲了手動執行繪製調用,您需要將字節數組轉換爲某種類型的位圖。 Bitmaps和BitmapDecoder似乎並沒有很好地處理YUV字節數組。有一個錯誤提出這個問題,但不是沒有這個地位。所以人們一直試圖將字節數組本身解碼爲RGB格式。

似乎手動進行解碼一直有點慢,人們有不同程度的成功。像這樣的東西應該可以真正用NDK級別的本地代碼來完成。

儘管如此,仍有可能使其工作。另外,我的小演示僅僅是花費了幾個小時的時間在一起(我想這樣做會讓我的想象力有點過分;))。所以很可能有一些調整你可以大大改善我設法工作。

這個小小的代碼片段包含了我發現的其他幾個寶石。如果您只需要在表面上繪製圖形,就可以覆蓋曲面的onDraw函數 - 您可以分析返回的相機圖像並繪製覆蓋圖 - 這比嘗試處理每個幀要快得多。另外,如果您想要顯示相機預覽,我會更改SurfaceHolder.SURFACE_TYPE_NORMAL。因此,一對夫婦更改代碼 - 註釋掉的代碼:

//try { mCamera.setPreviewDisplay(holder); } catch (IOException e) 
// { Log.e("Camera", "mCamera.setPreviewDisplay(holder);"); } 

而且:

SurfaceHolder.SURFACE_TYPE_NORMAL //SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS - for preview to work 

應該讓你覆蓋基於對現實預覽頂部的攝像頭預覽畫面。

無論如何,這是一段代碼 - 應該給你一些啓動。

只要把一行代碼在這樣的你的意見之一:

<pathtocustomview.MySurfaceView android:id="@+id/surface_camera" 
    android:layout_width="fill_parent" android:layout_height="10dip" 
    android:layout_weight="1"> 
</pathtocustomview.MySurfaceView> 

而且在源的地方包括這個類:

package pathtocustomview; 

import java.io.IOException; 
import java.nio.Buffer; 

import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Canvas; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.hardware.Camera; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.SurfaceHolder; 
import android.view.SurfaceHolder.Callback; 
import android.view.SurfaceView; 

public class MySurfaceView extends SurfaceView implements Callback, 
    Camera.PreviewCallback { 

    private SurfaceHolder mHolder; 

    private Camera mCamera; 
    private boolean isPreviewRunning = false; 
    private byte [] rgbbuffer = new byte[256 * 256]; 
    private int [] rgbints = new int[256 * 256]; 

    protected final Paint rectanglePaint = new Paint(); 

    public MySurfaceView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
     rectanglePaint.setARGB(100, 200, 0, 0); 
     rectanglePaint.setStyle(Paint.Style.FILL); 
     rectanglePaint.setStrokeWidth(2); 

     mHolder = getHolder(); 
     mHolder.addCallback(this); 
     mHolder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL); 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     canvas.drawRect(new Rect((int) Math.random() * 100, 
      (int) Math.random() * 100, 200, 200), rectanglePaint); 
     Log.w(this.getClass().getName(), "On Draw Called"); 
    } 

    public void surfaceChanged(SurfaceHolder holder, int format, int width, 
      int height) { 
    } 

    public void surfaceCreated(SurfaceHolder holder) { 
     synchronized (this) { 
      this.setWillNotDraw(false); // This allows us to make our own draw 
            // calls to this canvas 

      mCamera = Camera.open(); 

      Camera.Parameters p = mCamera.getParameters(); 
      p.setPreviewSize(240, 160); 
      mCamera.setParameters(p); 


      //try { mCamera.setPreviewDisplay(holder); } catch (IOException e) 
      // { Log.e("Camera", "mCamera.setPreviewDisplay(holder);"); } 

      mCamera.startPreview(); 
      mCamera.setPreviewCallback(this); 

     } 
    } 

    public void surfaceDestroyed(SurfaceHolder holder) { 
     synchronized (this) { 
      try { 
       if (mCamera != null) { 
        mCamera.stopPreview(); 
        isPreviewRunning = false; 
        mCamera.release(); 
       } 
      } catch (Exception e) { 
       Log.e("Camera", e.getMessage()); 
      } 
     } 
    } 

    public void onPreviewFrame(byte[] data, Camera camera) { 
     Log.d("Camera", "Got a camera frame"); 

     Canvas c = null; 

     if(mHolder == null){ 
      return; 
     } 

     try { 
      synchronized (mHolder) { 
       c = mHolder.lockCanvas(null); 

       // Do your drawing here 
       // So this data value you're getting back is formatted in YUV format and you can't do much 
       // with it until you convert it to rgb 
       int bwCounter=0; 
       int yuvsCounter=0; 
       for (int y=0;y<160;y++) { 
        System.arraycopy(data, yuvsCounter, rgbbuffer, bwCounter, 240); 
        yuvsCounter=yuvsCounter+240; 
        bwCounter=bwCounter+256; 
       } 

       for(int i = 0; i < rgbints.length; i++){ 
        rgbints[i] = (int)rgbbuffer[i]; 
       } 

       //decodeYUV(rgbbuffer, data, 100, 100); 
       c.drawBitmap(rgbints, 0, 256, 0, 0, 256, 256, false, new Paint()); 

       Log.d("SOMETHING", "Got Bitmap"); 

      } 
     } finally { 
      // do this in a finally so that if an exception is thrown 
      // during the above, we don't leave the Surface in an 
      // inconsistent state 
      if (c != null) { 
       mHolder.unlockCanvasAndPost(c); 
      } 
     } 
    } 
} 
+0

當我嘗試你的方法時,只有錯誤,你有沒有完整的工作例子?謝謝 – goodm

+0

c = mHolder.lockCanvas(null); c爲null 08-05 18:49:23.419:E/SurfaceHolder(13927):異常鎖定表面 08-05 18:49:23.419:E/SurfaceHolder(13927):java.lang.IllegalArgumentException異常 08- 05 18:49:23.419:E/SurfaceHolder(13927):\t at android.view.Surface.nativeLockCanvas(Native Method) 08-05 18:49:23.419:E/SurfaceHolder(13927):\t at android.view。 Surface.lockCanvas(Surface.java:243) 08-05 18:49:23.419:E/SurfaceHolder(13927):\t在android.view.SurfaceView $ 4.internalLockCanvas(SurfaceView.java:814) 請幫助 –

+0

哪裏你打電話onPreviewFrame?什麼是「數據」? –

9

我用walta的解決方案,但我有YUV轉換的某些問題,相機鏡頭輸出尺寸和相機釋放時的崩潰。

最後下面的代碼爲我工作:

public class MySurfaceView extends SurfaceView implements Callback, Camera.PreviewCallback { 

private static final String TAG = "MySurfaceView"; 

private int width; 
private int height; 

private SurfaceHolder mHolder; 

private Camera mCamera; 
private int[] rgbints; 

private boolean isPreviewRunning = false; 

private int mMultiplyColor; 

public MySurfaceView(Context context, AttributeSet attrs) { 
    super(context, attrs); 

    mHolder = getHolder(); 
    mHolder.addCallback(this); 
    mMultiplyColor = getResources().getColor(R.color.multiply_color); 
} 

// @Override 
// protected void onDraw(Canvas canvas) { 
// Log.w(this.getClass().getName(), "On Draw Called"); 
// } 

@Override 
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 

} 

@Override 
public void surfaceCreated(SurfaceHolder holder) { 
    synchronized (this) { 
     if (isPreviewRunning) 
      return; 

     this.setWillNotDraw(false); // This allows us to make our own draw calls to this canvas 


     mCamera = Camera.open(); 
     isPreviewRunning = true; 
     Camera.Parameters p = mCamera.getParameters(); 
     Size size = p.getPreviewSize(); 
     width = size.width; 
     height = size.height; 
     p.setPreviewFormat(ImageFormat.NV21); 
     showSupportedCameraFormats(p); 
     mCamera.setParameters(p); 

     rgbints = new int[width * height]; 

     // try { mCamera.setPreviewDisplay(holder); } catch (IOException e) 
     // { Log.e("Camera", "mCamera.setPreviewDisplay(holder);"); } 

     mCamera.startPreview(); 
     mCamera.setPreviewCallback(this); 

    } 
} 


@Override 
public void surfaceDestroyed(SurfaceHolder holder) { 
    synchronized (this) { 
     try { 
      if (mCamera != null) { 
       //mHolder.removeCallback(this); 
       mCamera.setPreviewCallback(null); 
       mCamera.stopPreview(); 
       isPreviewRunning = false; 
       mCamera.release(); 
      } 
     } catch (Exception e) { 
      Log.e("Camera", e.getMessage()); 
     } 
    } 
} 

@Override 
public void onPreviewFrame(byte[] data, Camera camera) { 
    // Log.d("Camera", "Got a camera frame"); 
    if (!isPreviewRunning) 
     return; 

    Canvas canvas = null; 

    if (mHolder == null) { 
     return; 
    } 

    try { 
     synchronized (mHolder) { 
      canvas = mHolder.lockCanvas(null); 
      int canvasWidth = canvas.getWidth(); 
      int canvasHeight = canvas.getHeight(); 

      decodeYUV(rgbints, data, width, height); 

      // draw the decoded image, centered on canvas 
      canvas.drawBitmap(rgbints, 0, width, canvasWidth-((width+canvasWidth)>>1), canvasHeight-((height+canvasHeight)>>1), width, height, false, null); 

      // use some color filter 
      canvas.drawColor(mMultiplyColor, Mode.MULTIPLY); 

     } 
    } catch (Exception e){ 
     e.printStackTrace(); 
    } finally { 
     // do this in a finally so that if an exception is thrown 
     // during the above, we don't leave the Surface in an 
     // inconsistent state 
     if (canvas != null) { 
      mHolder.unlockCanvasAndPost(canvas); 
     } 
    } 
} 



/** 
* Decodes YUV frame to a buffer which can be use to create a bitmap. use 
* this for OS < FROYO which has a native YUV decoder decode Y, U, and V 
* values on the YUV 420 buffer described as YCbCr_422_SP by Android 
* 
* @param rgb 
*   the outgoing array of RGB bytes 
* @param fg 
*   the incoming frame bytes 
* @param width 
*   of source frame 
* @param height 
*   of source frame 
* @throws NullPointerException 
* @throws IllegalArgumentException 
*/ 
public void decodeYUV(int[] out, byte[] fg, int width, int height) throws NullPointerException, IllegalArgumentException { 
    int sz = width * height; 
    if (out == null) 
     throw new NullPointerException("buffer out is null"); 
    if (out.length < sz) 
     throw new IllegalArgumentException("buffer out size " + out.length + " < minimum " + sz); 
    if (fg == null) 
     throw new NullPointerException("buffer 'fg' is null"); 
    if (fg.length < sz) 
     throw new IllegalArgumentException("buffer fg size " + fg.length + " < minimum " + sz * 3/2); 
    int i, j; 
    int Y, Cr = 0, Cb = 0; 
    for (j = 0; j < height; j++) { 
     int pixPtr = j * width; 
     final int jDiv2 = j >> 1; 
    for (i = 0; i < width; i++) { 
     Y = fg[pixPtr]; 
     if (Y < 0) 
      Y += 255; 
     if ((i & 0x1) != 1) { 
      final int cOff = sz + jDiv2 * width + (i >> 1) * 2; 
      Cb = fg[cOff]; 
      if (Cb < 0) 
       Cb += 127; 
      else 
       Cb -= 128; 
      Cr = fg[cOff + 1]; 
      if (Cr < 0) 
       Cr += 127; 
      else 
       Cr -= 128; 
     } 
     int R = Y + Cr + (Cr >> 2) + (Cr >> 3) + (Cr >> 5); 
     if (R < 0) 
      R = 0; 
     else if (R > 255) 
      R = 255; 
     int G = Y - (Cb >> 2) + (Cb >> 4) + (Cb >> 5) - (Cr >> 1) + (Cr >> 3) + (Cr >> 4) + (Cr >> 5); 
     if (G < 0) 
      G = 0; 
     else if (G > 255) 
      G = 255; 
     int B = Y + Cb + (Cb >> 1) + (Cb >> 2) + (Cb >> 6); 
     if (B < 0) 
      B = 0; 
     else if (B > 255) 
      B = 255; 
     out[pixPtr++] = 0xff000000 + (B << 16) + (G << 8) + R; 
    } 
    } 

} 

private void showSupportedCameraFormats(Parameters p) { 
    List<Integer> supportedPictureFormats = p.getSupportedPreviewFormats(); 
    Log.d(TAG, "preview format:" + cameraFormatIntToString(p.getPreviewFormat())); 
    for (Integer x : supportedPictureFormats) { 
     Log.d(TAG, "suppoterd format: " + cameraFormatIntToString(x.intValue())); 
    } 

} 

private String cameraFormatIntToString(int format) { 
    switch (format) { 
    case PixelFormat.JPEG: 
     return "JPEG"; 
    case PixelFormat.YCbCr_420_SP: 
     return "NV21"; 
    case PixelFormat.YCbCr_422_I: 
     return "YUY2"; 
    case PixelFormat.YCbCr_422_SP: 
     return "NV16"; 
    case PixelFormat.RGB_565: 
     return "RGB_565"; 
    default: 
     return "Unknown:" + format; 

     } 
    } 
} 

要使用它,從你們的活動的onCreate運行下面的代碼:

  SurfaceView surfaceView = new MySurfaceView(this, null); 
     RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); 
     surfaceView.setLayoutParams(layoutParams); 
     mRelativeLayout.addView(surfaceView); 
+3

我嘗試了不同的解決方案,並在我自己的主題中介紹。我得到了下面的異常。我不認爲可以鎖定從Android 4.4以上的onPreviewFrame方法的畫布E/SurfaceHolder(2872):java.lang.IllegalArgumentException E/SurfaceHolder(2872):\t at android.view.Surface。 nativeLockCanvas(本地方法) E/SurfaceHolder(2872):\t at android.view.Surface.lockCanvas(Surface.java:243) –

+1

我實現了相同的解決方案,但在我的情況下,我得到一個沒有任何例外的黑色曲面。 –

3

你看着GPUImage?

它最初是由Brad Larson製作的OSX/iOS庫,作爲圍繞OpenGL/ES的Objective-C包裝存在。

https://github.com/BradLarson/GPUImage

在Cyber​​Agent的人民作出了一個Android端口(不具有完整的功能奇偶校驗),這是對的OpenGLES東西頂了一組Java包裝的。這是比較高的水平,並且很容易實現,有很多的上述相同的功能...

https://github.com/CyberAgent/android-gpuimage