java - 在 Java Camera2 API 中使用 OpenCV

标签 java android opencv image-processing opencv4android

我正在使用 Android OpenCV 构建实时对象检测应用程序。我正在使用带有 TextureView 的 Android Camera2 API 来捕获图像。我想添加 OpenCV 代码来做一些实时图像处理并预览结果。

这是我的拍照代码

 protected void takePicture() {
    if(null == cameraDevice) {
        Log.e(TAG, "cameraDevice is null");
        return;
    }
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId());
        Size[] jpegSizes = null;
        if (characteristics != null) {
            jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
        }
        int width = 640;
        int height = 480;
        if (jpegSizes != null && 0 < jpegSizes.length) {
            width = jpegSizes[0].getWidth();
            height = jpegSizes[0].getHeight();
        }
        ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
        List<Surface> outputSurfaces = new ArrayList<Surface>(2);
        outputSurfaces.add(reader.getSurface());
        outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));
        final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureBuilder.addTarget(reader.getSurface());
        captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        // Orientation
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
        String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmmss").format(new Date());
        final File file = new File(Environment.getExternalStorageDirectory()+"/Billboard_" + timeStamp + ".jpg");

        // get the location from the NetworkProvider
        LocationManager lm = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        LocationListener locationListener = new LocationListener() {

            @Override
            public void onLocationChanged(Location location) {
                longitude = location.getLongitude();
                latitude = location.getLatitude();

                storeGeoCoordsToImage(file, location);

                Log.e(TAG, "Latitude = " + latitude);
                Log.e(TAG, "Longitude = " + longitude);
            }

            @Override
            public void onProviderDisabled(String provider) {}

            @Override
            public void onProviderEnabled(String provider) {}

            @Override
            public void onStatusChanged(String provider, int status,Bundle extras) {}

        };
        // update location listener
        lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

        ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = null;
                try {
                    image = reader.acquireLatestImage();

                    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                    byte[] bytes = new byte[buffer.capacity()];
                    buffer.get(bytes);

                    save(bytes);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (image != null) {
                        image.close();
                    }
                }
            }
            private void save(byte[] bytes) throws IOException {
                OutputStream output = null;
                try {
                    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);
                Toast.makeText(MainActivity.this, "Saved:" + file, Toast.LENGTH_SHORT).show();
            createCameraPreview();
        }
    };
    cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(CameraCaptureSession session) {
            try {
                session.capture(captureBuilder.build(), captureListener, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onConfigureFailed(CameraCaptureSession session) {
        }
    }, mBackgroundHandler);
} catch (CameraAccessException e) {
    e.printStackTrace();
}

}

我想添加一些像这样的 Java OpenCV 代码并在屏幕上预览结果。
Mat destination = new Mat();
Imgproc.cvtColor(source, destination, Imgproc.COLOR_RGB2GRAY);
Imgproc.equalizeHist(destination, destination);
Imgproc.canny(destination, destination, 50, 150);

我很困惑如何从相机预览中获取图像并对其进行一些图像处理,然后显示结果。

任何有关 OpenCV 和 Camera2 API 代码集成的帮助都会有所帮助。
谢谢你。

最佳答案

因此,如果您想处理来自相机的照片,您应该:

  • 将图像从 JPEG 转换为 ARGB8888 格式,然后获取
    从中得到图像垫;
  • 使用 OpenCV
  • 处理您的图像
  • 使用 listener 将处理后的图像发送到 UI

  • 例如,您可以这样做:
        // you need to create some interface in your activity to update image on the screen
        // and initialize it before you will use it
        OnImageReadyListener onImageReadyListener = null; 
        //...
    
    private final ImageReader.OnImageAvailableListener onImageAvailableListener = (ImageReader imReader) -> {
    
        final Image image = imReader.acquireLatestImage();
    
        // 1st step: convert the image to Mat
        Mat source = ImageConverter.ImageToMat(image);
    
        // 2nd step: process it with OpenCV
        Mat destination = new Mat();
        Imgproc.cvtColor(source, destination, Imgproc.COLOR_RGB2GRAY);
        Imgproc.equalizeHist(destination, destination);
        Imgproc.canny(destination, destination, 50, 150);
    
        // 3rd step: publish your result
        if(onImageReadyListener != null)
            onImageReadyListener.getImage(ImageConverter.MatToBitmap(destination));
    
        image.close();
    };
    

    因此,您的监听器可能如下所示:
    // setup this interface in your activity where you will update your image
    public interface OnImageReadyListener {
    public getImage(Bitmap image); // then you should override this method
    }
    

    ImageConverter 类应该是这样的:
    public class ImageConverter {
    
    private static final String TAG = ImageConverter.class.getSimpleName();
    
    // Convert bitmap from JPEG to ARGB8888 format
    private static Bitmap JPEGtoARGB8888(Bitmap input ){
        Bitmap output = null;
        int size = input.getWidth() * input.getHeight();
        int[] pixels = new int[size];
        input.getPixels(pixels,0,input.getWidth(),0,0,input.getWidth(),input.getHeight());
        output = Bitmap.createBitmap(input.getWidth(),input.getHeight(), Bitmap.Config.ARGB_8888);
        output.setPixels(pixels, 0, output.getWidth(), 0, 0, output.getWidth(), output.getHeight());
        return output; // ARGB_8888 formated bitmap
    }
    
    // Get image Mat from Bitmap
    private static Mat BitmapToMat(Bitmap bitmap){
        Bitmap bitmapARGB8888 = JPEGtoARGB8888(bitmap);
        Mat imageMat = new Mat();
        Utils.bitmapToMat(bitmapARGB8888, imageMat);
        return imageMat;
    }
    
    // Convert camera Image data to OpenCV image Mat(rix)
    public static Mat ImageToMat(Image image){
    
        // check image
        if(image == null)
            return null;
    
        // store image to bytes array
        final ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        final byte[] bytes = new byte[buffer.capacity()];
        buffer.get(bytes);
    
        // get bitmap from bytes and convert it to Mat
        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, picture.getImageData().length);
        return BitmapToMat(bitmap);
    }
    
    // Inverse conversion after image processing to show it on device screen
    public static Bitmap MatToBitmap(Mat image){
        Bitmap bitmap = null;
        Mat convertedMat = new Mat (image.height(), image.width(), CvType.CV_8U, new Scalar(4));
        try {
            Imgproc.cvtColor(image, convertedMat, Imgproc.COLOR_GRAY2RGBA, 4);
            bitmap = Bitmap.createBitmap(convertedMat.cols(), convertedMat.rows(), Bitmap.Config.ARGB_8888);
            Utils.matToBitmap(convertedMat, bitmap);
        }
        catch (CvException e){
            Log.d(TAG, e.getMessage());
        }
        return bitmap;
    }
    }
    

    附言我也推荐阅读this article了解如何将 OpenCV 连接到 Android。

    关于java - 在 Java Camera2 API 中使用 OpenCV,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39268912/

    相关文章:

    java - 无法访问 Google App Engine 上的 Html 文件

    php - 要插入的PHP脚本只是返回<br/>而没有将数据插入数据库中

    c++ - 如何定义和平滑 3D cv::Mat

    java - 使用 Mediacodec 解码并使用 OpenCV

    java - 在循环内动态创建 ArrayList

    java - 从具有千位分隔符的字符串中解析数字

    java - 尝试编译 jomp hello world 时出错

    android - 属性窗口布局编辑器中android studio复选框的含义

    java - 每次应用程序启动时 AlarmManager 都会触发

    c++ - 使用基线和单相机校准从 2 个图像进行 3D 重建