有幾個教程,解釋如何獲得一個簡單的相機預覽和運行在Android設備上。但是我找不到任何解釋如何在渲染圖像之前處理圖像的例子。
我想要做的是實現自定義濾鏡來模擬例如紅色和/或綠色缺陷。如何操作相機預覽?
回答
我在這方面做了一些研究,並把一個工作(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);
}
}
}
}
當我嘗試你的方法時,只有錯誤,你有沒有完整的工作例子?謝謝 – goodm
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) 請幫助 –
哪裏你打電話onPreviewFrame?什麼是「數據」? –
我用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);
我嘗試了不同的解決方案,並在我自己的主題中介紹。我得到了下面的異常。我不認爲可以鎖定從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) –
我實現了相同的解決方案,但在我的情況下,我得到一個沒有任何例外的黑色曲面。 –
你看着GPUImage?
它最初是由Brad Larson製作的OSX/iOS庫,作爲圍繞OpenGL/ES的Objective-C包裝存在。
https://github.com/BradLarson/GPUImage
在CyberAgent的人民作出了一個Android端口(不具有完整的功能奇偶校驗),這是對的OpenGLES東西頂了一組Java包裝的。這是比較高的水平,並且很容易實現,有很多的上述相同的功能...
- 1. 從相機操作預覽位圖
- 2. 作物相機預覽
- 3. 相機預覽
- 4. 相機預覽Android
- 5. Android - 相機預覽
- 6. Monodroid相機+預覽
- 7. 用相機預覽的Pi相機預覽 - 樹莓派
- 8. 的Android如何設置相機預覽
- 9. 如何繪製相機預覽?
- 10. 如何增強相機預覽?
- 11. 如何將相機預覽放入UIImageView?
- 12. 相機預覽不起作用
- 13. Android - 相機預覽不起作用
- 14. Android相機預覽 - 如何「凍結」相機?
- 15. 過濾相機預覽
- 16. 定製相機預覽Android
- 17. Android相機預覽很暗
- 18. 相機預覽 - 安卓
- 19. Android相機預覽錯誤
- 20. 安卓相機預覽
- 21. Android背景相機預覽
- 22. Android:相機預覽傾斜
- 23. 相機預覽質量差
- 24. AVCaptureSession VS UIImagePickerController相機預覽
- 25. iPhone:相機預覽覆蓋
- 26. Android相機預覽問題
- 27. Android上的相機預覽
- 28. Android相機預覽錯誤
- 29. Android相機預覽教程
- 30. Android中的相機預覽
,如果ü可以用代碼解釋,什麼烏爾試圖做,它會很容易幫助ü – the100rabh
https://github.com/whiskeysierra/impaired-vision/blob/master/src/org/whiskeysierra/impairedvision/ImpairedVision.java這是我目前擁有的。我想要做的是註冊某種回調(Camera.PreviewCallback?),然後使用它來操縱當前幀。 – whiskeysierra
鏈接不再工作,但回購仍然存在,如果有人仍然感興趣:https://github.com/whiskeysierra/impaired-vision – whiskeysierra