图像叠加在 TextureView 上 android

Image overlay over the TextureView android

我想在 textureView(相机预览)上叠加一些图像,并且有一个限制,即图像高度应等于图像宽度。这意味着图像的宽度高度将根据保持纵横比不变的屏幕尺寸而变化。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <TextureView
            android:id="@+id/view_finder"
            app:layout_constraintLeft_toLeftOf="@id/stencil"
            android:layout_width="0dp"
            app:layout_constraintRight_toRightOf="@id/stencil"
            app:layout_constraintTop_toTopOf="@id/stencil"
            app:layout_constraintBottom_toBottomOf="@id/stencil"
            android:layout_height="0dp"/>

    <ImageView android:layout_width="0dp"
               android:id="@+id/stencil"
               android:adjustViewBounds="true"
               android:src="@drawable/group"
               app:layout_constraintLeft_toLeftOf="parent"
               app:layout_constraintRight_toRightOf="parent"
               app:layout_constraintTop_toTopOf="parent"
               android:layout_height="wrap_content" 
               tools:ignore="ContentDescription,MissingConstraints"/>

    <ImageButton
            android:id="@+id/capture_button"
            android:layout_width="72dp"
            android:layout_height="72dp"
            android:layout_margin="24dp"
            app:srcCompat="@android:drawable/ic_menu_camera"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

并且纹理视图取决于图像视图的大小。由于 imageview 变化得很好,所以 textureview。我正在以编程方式计算纹理视图大小。

    val metrics = DisplayMetrics().also 
 {view_finder.display.getMetrics(it)}
        val aspectRatio = Rational(viewFinder.width, viewFinder.height)
        val rotation = view_finder.display.rotation
        val resolution = Size(viewFinder.width, viewFinder.height)

        val previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatio(aspectRatio)
            setTargetResolution(resolution)
        }.build()

但出现此错误 - CorrectedAspectRatio- 某些宽高比问题

    java.lang.IllegalArgumentException: Unable to get camera ID for use 
    case androidx.camera.core.Preview-c05d3e16-8833-42f4-ac22-4783402891dc
            at 
 androidx.camera.camera2.impl.Camera2DeviceSurfaceManager.getCameraIdFromConfig(Camera2DeviceSurfaceManager.java:310)
            at androidx.camera.camera2.impl.Camera2DeviceSurfaceManager.requiresCorrectedAspectRatio(Camera2DeviceSurfaceManager.java:268)

我能够通过稍微不同的方法来做同样的事情,而且效果很好。叠加图像仅供参考。 此 activity returns 没有叠加图像的单击照片的路径。 activity.

前后摄像头都在工作
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.widget.ImageView;
import android.widget.TextView;

import com.aranoah.healthkart.plus.R;

import com.bumptech.glide.Glide;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

import static android.hardware.camera2.CameraMetadata.LENS_FACING_BACK;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CameraStencilActivity extends AppCompatActivity
{
    @BindView(R.id.textture)
    TextureView textture;
    @BindView(R.id.stencil)
    ImageView stencil;
    @BindView(R.id.stencil_description)
    TextView stencilDescription;

    protected CameraDevice cameraDevice;
    protected CameraCaptureSession cameraCaptureSessions;
    protected CaptureRequest.Builder captureRequestBuilder;
    private Size imageDimension;
    private ImageReader imageReader;
    private StencilItem stencilItem;
    private static final int REQUEST_CAMERA_PERMISSION = 200;
    public static final int STENCIL_CAMERA_REQUEST_CODE = 10;
    public static final String KEY_CAPTURED_STENCIL_DATA = "KEY_CAPTURED_STENCIL_DATA";
    private Handler mBackgroundHandler;
    private HandlerThread mBackgroundThread;
    private Integer mCameraLensFacingDirection;

    public static void startForResult(Fragment fragment, StencilItem stencilItem)
    {
        Intent intent = new Intent(fragment.getContext(), CameraStencilActivity.class);
        intent.putExtra("stencil", stencilItem);
        fragment.startActivityForResult(intent, STENCIL_CAMERA_REQUEST_CODE);
    }

    @OnClick(R.id.btn_rotate)
    void onRotateClick()
    {
        switchCamera();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_stencil);
        ButterKnife.bind(this);
        stencilItem = getIntent().getParcelableExtra("stencil");
        Glide.with(this).load(stencilItem.getStencil().getUrl()).into(stencil);
        textture.setSurfaceTextureListener(textureListener);
        stencilDescription.setText(stencilItem.getStencil().getUnderlineText());
    }

    TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener()
    {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
        {
            openCamera();
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)
        {
            // Transform you image captured size according to the surface width and height
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface)
        {
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface)
        {
            // do nothing
        }
    };

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void switchCamera()
    {
        if (mCameraLensFacingDirection == LENS_FACING_BACK)
        {
            mCameraLensFacingDirection = CameraMetadata.LENS_FACING_FRONT;
            closeCamera();
            reopenCamera();

        } else if (mCameraLensFacingDirection == CameraMetadata.LENS_FACING_FRONT)
        {
            mCameraLensFacingDirection = LENS_FACING_BACK;
            closeCamera();
            reopenCamera();
        }
    }

    private void reopenCamera()
    {
        if (textture.isAvailable())
        {
            openCamera();
        } else
        {
            textture.setSurfaceTextureListener(textureListener);
        }
    }

    private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback()
    {
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void onOpened(CameraDevice camera)
        {
            cameraDevice = camera;
            createCameraPreview();
        }

        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void onDisconnected(CameraDevice camera)
        {
            cameraDevice.close();
        }

        @Override
        public void onError(CameraDevice camera, int error)
        {
            cameraDevice.close();
            cameraDevice = null;
        }
    };

    protected void startBackgroundThread()
    {
        mBackgroundThread = new HandlerThread("Camera Background");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }

    protected void stopBackgroundThread()
    {
        mBackgroundThread.quitSafely();
        try
        {
            mBackgroundThread.join();
            mBackgroundThread = null;
            mBackgroundHandler = null;
        } catch (InterruptedException e)
        {
            // do nothing
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @OnClick(R.id.btn_takepicture)
    void takePicture()
    {
        if (null == cameraDevice)
        {
            return;
        }
        try
        {
            int width = 640;
            int height = 480;
            ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
            List<Surface> outputSurfaces = new ArrayList<>(2);
            outputSurfaces.add(reader.getSurface());
            outputSurfaces.add(new Surface(textture.getSurfaceTexture()));
            final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(reader.getSurface());
            captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);

            CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
            CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(cameraDevice.getId());
            int rotation = cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, rotation);

            final File file = new File(Environment.getExternalStorageDirectory() + "/" + System.currentTimeMillis() + ".jpeg");
            if (file.exists())
            {
                file.delete();
            }
            ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener()
            {
                @Override
                public void onImageAvailable(ImageReader reader)
                {
                    try(Image image = reader.acquireLatestImage())
                    {
                        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                        byte[] bytes = new byte[buffer.capacity()];
                        buffer.get(bytes);
                        save(bytes);
                    } catch (IOException e)
                    {
                        // do nothing
                    }
                }

                private void save(byte[] bytes) throws IOException
                {
                    OutputStream output = null;
                    try
                    {
                        if (!file.exists())
                        {
                            output = new FileOutputStream(file);
                            output.write(bytes);
                        }
                    } finally
                    {
                        if (null != output)
                        {
                            output.close();
                        }
                    }
                }
            };
            reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
            final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback()
            {
                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result)
                {
                    super.onCaptureCompleted(session, request, result);

                    Intent intent = new Intent();
                    intent.putExtra(KEY_CAPTURED_STENCIL_DATA, new CapturedStencilData(file.getAbsolutePath(), stencilItem));
                    setResult(RESULT_OK, intent);
                    finish();
                }
            };
            cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback()
            {
                @Override
                public void onConfigured(CameraCaptureSession session)
                {
                    try
                    {
                        session.capture(captureBuilder.build(), captureListener, mBackgroundHandler);
                    } catch (CameraAccessException e)
                    {
                        // do nothing
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session)
                {

                }
            }, mBackgroundHandler);
        } catch (CameraAccessException e)
        {
            // do nothing
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    protected void createCameraPreview()
    {
        try
        {
            SurfaceTexture texture = textture.getSurfaceTexture();
            assert texture != null;
            texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
            Surface surface = new Surface(texture);
            captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            captureRequestBuilder.addTarget(surface);
            cameraDevice.createCaptureSession(Collections.singletonList(surface), new CameraCaptureSession.StateCallback()
            {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession)
                {
                    if (null == cameraDevice)
                    {
                        return;
                    }
                    cameraCaptureSessions = cameraCaptureSession;
                    updatePreview();
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession)
                {
                    // do nothing
                }
            }, null);
        } catch (CameraAccessException e)
        {
            // do nothing
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    void openCamera()
    {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try
        {
            String cameraId = null;
            if (mCameraLensFacingDirection != null)
            {
                int size = manager.getCameraIdList().length;
                for (int i = 0; i < size; i++)
                {
                    if (mCameraLensFacingDirection == manager.getCameraCharacteristics(manager.getCameraIdList()[i]).get(CameraCharacteristics.LENS_FACING))
                    {
                        cameraId = manager.getCameraIdList()[i];
                    }
                }
            } else
            {
                cameraId = manager.getCameraIdList()[0];
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
                mCameraLensFacingDirection = Objects.requireNonNull(characteristics.get(CameraCharacteristics.LENS_FACING));
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                assert map != null;
                imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];
            }

            if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
            {
                ActivityCompat.requestPermissions(CameraStencilActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
                return;
            }
            assert cameraId != null;
            manager.openCamera(cameraId, stateCallback, null);
        } catch (CameraAccessException e)
        {
            // do nothing
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    protected void updatePreview()
    {
        captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        try
        {
            cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
        } catch (CameraAccessException e)
        {
            // do nothing
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void closeCamera()
    {
        if (null != cameraDevice)
        {
            cameraDevice.close();
            cameraDevice = null;
        }
        if (null != imageReader)
        {
            imageReader.close();
            imageReader = null;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
    {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CAMERA_PERMISSION)
        {
            if (grantResults[0] == PackageManager.PERMISSION_DENIED)
            {
                finish();
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onResume()
    {
        super.onResume();
        startBackgroundThread();
        if (textture.isAvailable())
        {
            openCamera();
        } else
        {
            textture.setSurfaceTextureListener(textureListener);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onPause()
    {
        closeCamera();
        stopBackgroundThread();
        super.onPause();
    }
}