CropView的裁剪相机预览

我有一个固定宽度和高度的TextureView,我想在其中显示相机预览。 我需要裁剪相机预览,以便它看起来不会在我的TextureView中拉伸。 怎么做种植? 如果我需要使用OpenGL,如何将Surface Texture绑定到OpenGL以及如何使用OpenGL进行裁剪?

public class MyActivity extends Activity implements TextureView.SurfaceTextureListener { private Camera mCamera; private TextureView mTextureView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_options); mTextureView = (TextureView) findViewById(R.id.camera_preview); mTextureView.setSurfaceTextureListener(this); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mCamera = Camera.open(); try { mCamera.setPreviewTexture(surface); mCamera.startPreview(); } catch (IOException ioe) { // Something bad happened } } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { mCamera.stopPreview(); mCamera.release(); return true; } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { // Invoked every time there's a new Camera preview frame } } 

此外,在正确预览后,我需要能够实时读取裁剪图像中心的像素。

Solutions Collecting From Web of "CropView的裁剪相机预览"

提供@Romanski的早期解决方案工作正常但它随着裁剪而扩展。 如果需要缩放以适合,请使用以下解决方案。 每次更改曲面视图时调用updateTextureMatrix:即onSurfaceTextureAvailable和onSurfaceTextureSizeChanged方法。 另请注意,此解决方案依赖于该活动忽略配置更改(即android:configChanges =“orientation | screenSize | keyboardHidden”或类似的东西):

 private void updateTextureMatrix(int width, int height) { boolean isPortrait = false; Display display = getWindowManager().getDefaultDisplay(); if (display.getRotation() == Surface.ROTATION_0 || display.getRotation() == Surface.ROTATION_180) isPortrait = true; else if (display.getRotation() == Surface.ROTATION_90 || display.getRotation() == Surface.ROTATION_270) isPortrait = false; int previewWidth = orgPreviewWidth; int previewHeight = orgPreviewHeight; if (isPortrait) { previewWidth = orgPreviewHeight; previewHeight = orgPreviewWidth; } float ratioSurface = (float) width / height; float ratioPreview = (float) previewWidth / previewHeight; float scaleX; float scaleY; if (ratioSurface > ratioPreview) { scaleX = (float) height / previewHeight; scaleY = 1; } else { scaleX = 1; scaleY = (float) width / previewWidth; } Matrix matrix = new Matrix(); matrix.setScale(scaleX, scaleY); textureView.setTransform(matrix); float scaledWidth = width * scaleX; float scaledHeight = height * scaleY; float dx = (width - scaledWidth) / 2; float dy = (height - scaledHeight) / 2; textureView.setTranslationX(dx); textureView.setTranslationY(dy); } 

您还需要以下字段:

 private int orgPreviewWidth; private int orgPreviewHeight; 

在调用updateTextureMatrix之前在onSurfaceTextureAvailable mathod中初始化它:

 Camera.Parameters parameters = camera.getParameters(); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); Pair size = getMaxSize(parameters.getSupportedPreviewSizes()); parameters.setPreviewSize(size.first, size.second); orgPreviewWidth = size.first; orgPreviewHeight = size.second; camera.setParameters(parameters); 

getMaxSize方法:

 private static Pair getMaxSize(List list) { int width = 0; int height = 0; for (Camera.Size size : list) { if (size.width * size.height > width * height) { width = size.width; height = size.height; } } return new Pair(width, height); } 

最后一件事 – 你需要纠正相机旋转。 因此,在Activity onConfigurationChanged方法中调用setCameraDisplayOrientation方法(并在onSurfaceTextureAvailable方法中进行初始调用):

 public static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; } camera.setDisplayOrientation(result); Camera.Parameters params = camera.getParameters(); params.setRotation(result); camera.setParameters(params); } 

只需计算宽高比,生成缩放矩阵并将其应用于TextureView。 基于表面的纵横比和预览图像的纵横比,预览图像在顶部和底部或左右裁剪。 我发现的另一个解决方案是,如果在SurfaceTexture可用之前打开相机,预览已经自动缩放。 试着移动mCamera = Camera.open(); 设置SurfaceTextureListener后,在onCreate函数中。 这在N4上对我有用。 使用此解决方案,当您从纵向旋转到横向时,您可能会遇到问题。 如果您需要纵向和横向支持,请使用比例矩阵获取解决方案!

 private void initPreview(SurfaceTexture surface, int width, int height) { try { camera.setPreviewTexture(surface); } catch (Throwable t) { Log.e("CameraManager", "Exception in setPreviewTexture()", t); } Camera.Parameters parameters = camera.getParameters(); previewSize = parameters.getSupportedPreviewSizes().get(0); float ratioSurface = width > height ? (float) width / height : (float) height / width; float ratioPreview = (float) previewSize.width / previewSize.height; int scaledHeight = 0; int scaledWidth = 0; float scaleX = 1f; float scaleY = 1f; boolean isPortrait = false; if (previewSize != null) { parameters.setPreviewSize(previewSize.width, previewSize.height); if (display.getRotation() == Surface.ROTATION_0 || display.getRotation() == Surface.ROTATION_180) { camera.setDisplayOrientation(display.getRotation() == Surface.ROTATION_0 ? 90 : 270); isPortrait = true; } else if (display.getRotation() == Surface.ROTATION_90 || display.getRotation() == Surface.ROTATION_270) { camera.setDisplayOrientation(display.getRotation() == Surface.ROTATION_90 ? 0 : 180); isPortrait = false; } if (isPortrait && ratioPreview > ratioSurface) { scaledWidth = width; scaledHeight = (int) (((float) previewSize.width / previewSize.height) * width); scaleX = 1f; scaleY = (float) scaledHeight / height; } else if (isPortrait && ratioPreview < ratioSurface) { scaledWidth = (int) (height / ((float) previewSize.width / previewSize.height)); scaledHeight = height; scaleX = (float) scaledWidth / width; scaleY = 1f; } else if (!isPortrait && ratioPreview < ratioSurface) { scaledWidth = width; scaledHeight = (int) (width / ((float) previewSize.width / previewSize.height)); scaleX = 1f; scaleY = (float) scaledHeight / height; } else if (!isPortrait && ratioPreview > ratioSurface) { scaledWidth = (int) (((float) previewSize.width / previewSize.height) * width); scaledHeight = height; scaleX = (float) scaledWidth / width; scaleY = 1f; } camera.setParameters(parameters); } // calculate transformation matrix Matrix matrix = new Matrix(); matrix.setScale(scaleX, scaleY); textureView.setTransform(matrix); } 

你可以操作onPreview()byte[] data

我想你必须:

  • 把它放在一个位图中
  • Bitmap进行裁剪
  • 做一点拉伸/resize
  • 并将Bitmap传递给SurfaceView

这不是一种非常高效的方式。 也许你可以直接操作byte[] ,但你必须处理像NV21这样的图片格式。

@SatteliteSD给出的答案是最合适的。 每台相机仅支持某些预设尺寸,这些尺寸设置为HAL。 因此,如果可用的预览大小不足以满足要求,则需要从onPreview中提取数据

我刚刚创建了一个工作应用程序,我需要显示实际输入的x2预览而不会出现像素化的外观。 IE浏览器。 我需要在640×360 TextureView中显示1280×720实时预览的中心部分。

这就是我做的。

将相机预览设置为我需要的x2分辨率:

 params.setPreviewSize(1280, 720); 

然后相应地缩放纹理视图:

 this.captureView.setScaleX(2f); this.captureView.setScaleY(2f); 

这在小型设备上没有任何打嗝。

对于相机预览图像的实时操作,OpenCV for android完全适应。 你会在这里find所需的每一个样本: http : //opencv.org/platforms/android/opencv4android-samples.html ,你会发现它实时工作得很好。

免责声明:在Android上设置opencv lib可能会非常令人头疼,具体取决于您如何使用c ++ / opencv / ndk进行实验。 在所有情况下,编写opencv代码绝不简单,但另一方面它是一个非常强大的库。

嘿,您可以简单地定义相机预览所需的高度和宽度,以使其适合屏幕内部。 而对于裁剪视图,只需在xml布局文件中使用android:scaleX和android:scaleY属性。 只记得android:scaleX =“1”:>意味着它是原始的100%宽,但在你的情况下你需要裁剪,所以让它像… android:scaleX =“1.5”,因为它会扩大图像到原始的150%,你只能看到图像的裁剪部分适合你的layour_height和layout_width参数。 如果要垂直裁剪,请在代码中进行相同的更改。 〜萨赫勒