幅と高さが固定されたTextureViewがあり、その中にカメラのプレビューを表示したいと思います。 TextureView内で引き伸ばされて表示されないように、カメラプレビューをトリミングする必要があります。トリミングを行う方法は? OpenGLを使用する必要がある場合、表面テクスチャを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
}
}
また、プレビューを正しく行った後、トリミングされた画像の中央にあるピクセルをリアルタイムで読み取ることができる必要があります。
@Romanskiによる以前のソリューションの提供は問題なく機能しますが、トリミングに合わせてスケーリングされます。フィットするようにスケーリングする必要がある場合は、次のソリューションを使用してください。サーフェスビューが変更されるたびに、つまりonSurfaceTextureAvailableおよびonSurfaceTextureSizeChangedメソッドでupdateTextureMatrixを呼び出します。このソリューションは、アクティビティが構成の変更を無視することにも依存していることにも注意してください(つまり、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<Integer, Integer> size = getMaxSize(parameters.getSupportedPreviewSizes());
parameters.setPreviewSize(size.first, size.second);
orgPreviewWidth = size.first;
orgPreviewHeight = size.second;
camera.setParameters(parameters);
getMaxSizeメソッド:
private static Pair<Integer, Integer> getMaxSize(List<Camera.Size> 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<Integer, Integer>(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に適用するだけです。表面のアスペクト比とプレビュー画像のアスペクト比に基づいて、プレビュー画像は上下または左右にトリミングされます。私が見つけた別の解決策は、カメラを開くとbefore 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);
}
ピクセル化された外観を得ることなく実際の入力のx2でプレビューを表示する必要がある作業用アプリを作成しました。つまり。 640x360 TextureViewで1280x720ライブプレビューの中央部分を表示する必要がありました。
これが私がしたことです。
カメラプレビューを必要なx2解像度に設定します。
params.setPreviewSize(1280, 720);
そして、それに応じてテクスチャビューをスケーリングします。
this.captureView.setScaleX(2f);
this.captureView.setScaleY(2f);
これは、小さなデバイスで問題なく実行されます。
onPreview()
からbyte[] data
を操作できます。
私はあなたがしなければならないと思う:
Bitmap
でトリミングを行うBitmap
をSurfaceView
に渡しますこれはあまり効果的な方法ではありません。 byte[]
を直接操作できるかもしれませんが、NV21のような画像形式を処理する必要があります。
@SatteliteSDによる回答が最も適切です。すべてのカメラは、HALで設定されている特定のプレビューサイズのみをサポートしています。したがって、利用可能なプレビューサイズが要件を満たしていない場合は、onPreviewからデータを抽出する必要があります
カメラのプレビュー画像をリアルタイムで操作するには、OpenCV for Androidが完全に適応されています。必要なサンプルはすべてここにあります: http://opencv.org/platforms /Android/opencv4Android-samples.html 、そしてご覧のとおり、リアルタイムでうまく動作します。
免責事項:Androidでのopencv libの設定は、c ++/opencv/the ndkをどのように試してみるかに応じて、かなり頭痛の種になる可能性があります。すべての場合において、opencvコードの記述は決して簡単ではありませんが、一方、非常に強力なライブラリです。