1''*_*1'' 9 android android-camera
我对OpenCV的Android相机示例代码感到困惑.他们创建了一个自定义类,它SurfaceHolder.Callback在方法中实现并放入以下行surfaceChanged:
mCamera.setPreviewDisplay(null);
Run Code Online (Sandbox Code Playgroud)
Android文档setPreviewDisplay解释如下:
必须在startPreview()之前调用此方法.一个例外是,如果在调用startPreview()之前未设置预览表面(或设置为null),则可以使用非null参数调用此方法一次以设置预览表面.(这允许相机设置和曲面创建并行发生,节省时间.)预览曲面在预览运行时可能不会改变.
不同寻常的是,OpenCV的代码永远不会setPreviewDisplay使用非null的SurfaceHolder调用.它工作正常,但改变图像的旋转使用setDisplayOrientation不起作用.这条线似乎也没有做任何事情,因为没有它我会得到相同的结果.
如果我setPreviewDisplay使用提供的SurfaceHolder来surfaceChanged代替null,则图像会旋转,但不包括图像处理的结果.我以后也会IllegalArgumentException打电话给我lockCanvas.
这是怎么回事?
以下是其代码中(可能)最相关的部分,略微简化并使用内联方法.这是完整版.
类定义
public abstract class SampleViewBase extends SurfaceView
implements SurfaceHolder.Callback, Runnable {
Run Code Online (Sandbox Code Playgroud)
打开相机时
mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
synchronized (SampleViewBase.this) {
System.arraycopy(data, 0, mFrame, 0, data.length);
SampleViewBase.this.notify();
}
camera.addCallbackBuffer(mBuffer);
}
});
Run Code Online (Sandbox Code Playgroud)
当表面发生变化时
/* Now allocate the buffer */
mBuffer = new byte[size];
/* The buffer where the current frame will be copied */
mFrame = new byte [size];
mCamera.addCallbackBuffer(mBuffer);
try {
mCamera.setPreviewDisplay(null);
} catch (IOException e) {
Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e);
}
[...]
/* Now we can start a preview */
mCamera.startPreview();
Run Code Online (Sandbox Code Playgroud)
运行方法
public void run() {
mThreadRun = true;
Log.i(TAG, "Starting processing thread");
while (mThreadRun) {
Bitmap bmp = null;
synchronized (this) {
try {
this.wait();
bmp = processFrame(mFrame);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bmp != null) {
Canvas canvas = mHolder.lockCanvas();
if (canvas != null) {
canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2,
(canvas.getHeight() - getFrameHeight()) / 2, null);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
Log.i(TAG, "Finishing processing thread");
}
Run Code Online (Sandbox Code Playgroud)
我遇到了同样的问题.而不是使用a SurfaceView.Callback,我将他们的类子类化JavaCameraView.在这里查看我的实时面部检测和绘图示例.在处理之前,根据设备的方向旋转从相机出来的矩阵是微不足道的.链接代码的相关摘录:
@Override
public Mat onCameraFrame(Mat inputFrame) {
int flipFlags = 1;
if(display.getRotation() == Surface.ROTATION_270) {
flipFlags = -1;
Log.i(VIEW_LOG_TAG, "Orientation is" + getRotation());
}
Core.flip(inputFrame, mRgba, flipFlags);
inputFrame.release();
Imgproc.cvtColor(mRgba, mGray, Imgproc.COLOR_RGBA2GRAY);
if (mAbsoluteFaceSize == 0) {
int height = mGray.rows();
if (Math.round(height * mRelativeFaceSize) > 0) {
mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我使用 OpenCV 本身解决了旋转问题:在使用此代码找出需要校正多少屏幕旋转后,我将旋转矩阵应用于原始相机图像(从 YUV 转换为 RGB 后):
Point center = new Point(mFrameWidth/2, mFrameHeight/2);
Mat rotationMatrix = Imgproc.getRotationMatrix2D(center, totalRotation, 1);
Run Code Online (Sandbox Code Playgroud)
[...]
Imgproc.cvtColor(mYuv, mIntermediate, Imgproc.COLOR_YUV420sp2RGBA, 4);
Imgproc.warpAffine(mIntermediate, mRgba, rotationMatrix,
new Size(mFrameHeight, mFrameWidth));
Run Code Online (Sandbox Code Playgroud)
另一个问题是setPreviewDisplay(null)某些手机上会出现黑屏。我从这里得到的解决方案并借鉴了这个错误报告和这个SO问题,将一个隐藏的“假”SurfaceView传递到预览显示以使其启动,但实际上在覆盖的自定义视图上显示输出,我称之为相机视图。因此,在调用setContentView()Activity后onCreate(),请输入以下代码:
if (VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) {
final SurfaceView fakeView = new SurfaceView(this);
fakeView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
fakeView.setZOrderMediaOverlay(false);
final CameraView cameraView = (CameraView) this.findViewById(R.id.cameraview);
cameraView.setZOrderMediaOverlay(true);
cameraView.fakeView = fakeView;
}
Run Code Online (Sandbox Code Playgroud)
然后,在设置预览显示时,使用以下代码:
try {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB)
mCamera.setPreviewTexture(new SurfaceTexture(10));
else
mCamera.setPreviewDisplay(fakeView.getHolder());
} catch (IOException e) {
Log.e(TAG, "mCamera.setPreviewDisplay fails: "+ e);
}
Run Code Online (Sandbox Code Playgroud)
如果您仅针对 Honeycomb 及以上版本进行开发,只需将 setPreviewDisplay(null) 替换为mCamera.setPreviewTexture(new SurfaceTexture(10));即可。 setDisplayOrientation()但是,如果这样做仍然不起作用,因此您仍然必须使用旋转矩阵解决方案。