如何使用CameraX在Android Studio中拍摄多张照片并将它们发送到下一个Activity,并显示它们?

3

我正在使用最新的cameraX相机

def camerax_version = "1.0.0-beta11"

我可以使用以下代码拍照并保存图像到外部存储器中的文件夹:

我能够使用以下代码拍照并将图像保存到外部存储器中的文件夹中:

File photoFile = new File(outputDirectory, "Image_" + System.currentTimeMillis() + ".jpg");

            ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build();

            imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(getBaseContext()), new ImageCapture.OnImageSavedCallback() {
                @Override
                public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                    Uri.fromFile(photoFile);
                    Toast.makeText(getBaseContext(), "Image Saved" + photoFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onError(@NonNull ImageCaptureException exception) {
                    Toast.makeText(getBaseContext(), "Error Saving Image" + photoFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();
                }
            });

现在问题在于如何在保存到外部存储之前提取图像。我想要实现的是捕获多个图像并将其保存在缓冲区中,并使用列表或其他方式将这些图像发送到下一个Activity,并在imageView中显示它们。
可以使用imageCapture上的onImageCapturedCallback来实现这一点,它会给我一个ImageProxy,然后必须将其转换为字节数组。但是这个过程仅适用于小尺寸和单张图片。 如何才能实现高分辨率和多个图像呢?
以下是我用于捕获ImageProxy并将imageCapture设置为“YUV”的代码,遗憾的是它完全没有起作用。
    imageCapture.takePicture(ContextCompat.getMainExecutor(getBaseContext()), new ImageCapture.OnImageCapturedCallback() {
        @Override
        public void onCaptureSuccess(@NonNull ImageProxy image) {
            super.onCaptureSuccess(image);
            @SuppressLint("UnsafeExperimentalUsageError") Image cimage = image.getImage();
            Image.Plane[] planes = cimage.getPlanes();
            ByteBuffer yBuffer = planes[0].getBuffer();
            ByteBuffer uBuffer = planes[1].getBuffer();
            ByteBuffer vBuffer = planes[2].getBuffer();

            int ySize = yBuffer.remaining();
            int uSize = uBuffer.remaining();
            int vSize = vBuffer.remaining();

            byte[] nv21 = new byte[ySize + uSize + vSize];

            yBuffer.get(nv21,0,ySize);
            vBuffer.get(nv21,ySize,vSize);
            uBuffer.get(nv21,ySize + vSize,uSize);

            YuvImage yuvImage = new YuvImage(nv21,ImageFormat.NV21,cimage.getWidth(),cimage.getHeight(),null);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            yuvImage.compressToJpeg(new Rect(0,0,yuvImage.getWidth(),yuvImage.getHeight()),100,out);
            byte[] imageBytes = out.toByteArray();

            Intent intent = new Intent(MainActivity.this,MainActivity2.class);
            intent.putExtra("image",imageBytes);
            MainActivity.this.startActivity(intent);

        }

        @Override
        public void onError(@NonNull ImageCaptureException exception) {
            super.onError(exception);
        }
    });

我能将图片添加到 ArrayList 中并发送它们吗?

谢谢提前。


1
我建议将图像转换为位图,然后使用ImageProxy.close获取下一帧并捕获它,然后将所有位图保存到ArrayList中,然后您可以显示它们。 - mmdreza baqalpour
@mmdrezabaqalpour 我可以使用Intent.putExtra()将这些位图发送到新的Activity吗? - Ayush Naik
只要bitmap是可包裹的对象,您就可以通过意图发送它。Intent.putParcelableArrayListExtra() - mmdreza baqalpour
@mmdrezabaqalpour 在ImageCaptureCallback期间,我必须将imageCapture设置为YUV格式以将图像转换为位图。这会显著降低图像质量。有没有什么解决方法.. - Ayush Naik
如果这些方法对您有用,我会将它们作为答案发布。 - mmdreza baqalpour
@mmdrezabaqalpour 好的 - Ayush Naik
4个回答

2

我在我的项目中有两种做法。代码是用kotlin语言编写的。 你可以轻松理解它。

val image = imageProxy.image
val bitmap = Bitmap.createBitmap(image.width, image.height, 
Bitmap.Config.ARGB_8888)

如果不起作用,您可以使用YuvtoRgbConvertor。如果需要我有完整的Kotlin代码,或者您可以编写自己的代码。然后,您可以像这样转换位图。
val convertor = YuvToRgbConvertor
convertor.yuvToRgb(image , bitmap)

这就是我为我的项目所做的事情。

1
我建议您将内容存储在数组列表中,然后将数组列表传递给其他活动。
您需要创建一个数组列表,并将uri.tostring存储在数组列表中。
String newurl=uri.toString
`arraylist.add(newurl)`

这样,您可以在ArrayList中添加多个图像URL,并借助Picasso库进行显示。无需从数据库获取图像。

0

首先,在takepicture回调函数中捕获图像后,您需要关闭该图像。使用image.close()可以关闭当前图像。创建全局ArrayList并将imageURL添加到其中。然后,您可以通过意图将ArrayList发送到任何活动。

 imageCapture.takePicture(ContextCompat.getMainExecutor(getBaseContext()), new ImageCapture.OnImageCapturedCallback() {
    @Override
    public void onCaptureSuccess(@NonNull ImageProxy image) {
        super.onCaptureSuccess(image);
        @SuppressLint("UnsafeExperimentalUsageError") Image cimage = image.getImage();
        Image.Plane[] planes = cimage.getPlanes();
        ByteBuffer yBuffer = planes[0].getBuffer();
        ByteBuffer uBuffer = planes[1].getBuffer();
        ByteBuffer vBuffer = planes[2].getBuffer();

        int ySize = yBuffer.remaining();
        int uSize = uBuffer.remaining();
        int vSize = vBuffer.remaining();

        byte[] nv21 = new byte[ySize + uSize + vSize];

        yBuffer.get(nv21,0,ySize);
        vBuffer.get(nv21,ySize,vSize);
        uBuffer.get(nv21,ySize + vSize,uSize);

        YuvImage yuvImage = new YuvImage(nv21,ImageFormat.NV21,cimage.getWidth(),cimage.getHeight(),null);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        yuvImage.compressToJpeg(new Rect(0,0,yuvImage.getWidth(),yuvImage.getHeight()),100,out);
        byte[] imageBytes = out.toByteArray();

        Intent intent = new Intent(MainActivity.this,MainActivity2.class);
        intent.putExtra("image",imageBytes);
        MainActivity.this.startActivity(intent);
        image.close();
    }

    @Override
    public void onError(@NonNull ImageCaptureException exception) {
        super.onError(exception);
    }
});

我认为这会对你有所帮助,如果有任何疑问,请参考我的博客文章

这是将ImageProxy转换为位图的函数

 // output of the image capture image proxy to bitmap
public Bitmap imageProxyToBitmap(ImageProxy image) {
    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    buffer.rewind();
    byte[] bytes = new byte[buffer.capacity()];
    buffer.get(bytes);
    byte[] clonedBytes = bytes.clone();
    return BitmapFactory.decodeByteArray(clonedBytes, 0, clonedBytes.length);
}

并将位图保存到本地存储

    // used for save the files internal storage , can view in the gallery or internal storage
public String saveTOInternamMemory(Activity activity, Bitmap bitmapImage){

    File myPath = getInternalStorageDir(internalStorageDir,imageFormat,Environment.DIRECTORY_PICTURES);

    Log.d(TAG, "directory: " + myPath.getAbsolutePath());

    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(myPath);
        // Use the compress method on the BitMap object to write image to the OutputStream
        bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
        Log.d(TAG, "bit exception: Success" );
    } catch (Exception e) {
        Log.d(TAG, "bit exception: " + e.getMessage());
        e.printStackTrace();
    } finally {
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            Log.d(TAG, "io exce: " + e.getMessage());
        }
    }
    Log.d(TAG, "absolute path " + myPath.getAbsolutePath());
    return myPath.getAbsolutePath();
}

如果您要在答案中链接到自己的博客(尤其是在多个答案中这样做),则必须在答案中声明您的关联,否则您的帖子很可能会被标记为垃圾邮件。请阅读《如何避免成为垃圾邮件发送者》(https://stackoverflow.com/help/promotion)。 - David Buck
嗨,查理利奥。如果你收到像上面那样的反馈,然后在不回应它的情况下编辑你的帖子(例如提供或指出与所提到的怀疑相矛盾的信息),那么你给人的印象可能是你故意做了被指出的事情。 - Yunnosch
@DavidBuck 我错过了这些链接指向自己的网页内容的标志。你能给我解释一下吗? - Yunnosch
嗨,@DavidBuck,谢谢你的信息。我会在以后的帖子中检查并使用它。 - Charles Raj I
嗨@Yunnosch,抱歉我不明白你在说什么?我是这里回答问题的新手。 - Charles Raj I
@Yunnosch 在个人资料下的 关于 部分:"这是我的博客,点击此处" 链接到该域名。 - General Grievance

0
我发现最简单的方法是这样的:
preview.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Bitmap scaledBitmap = null;

                ContextWrapper cw = new ContextWrapper(getApplicationContext());
                String PATH = Environment.getExternalStorageDirectory() + "/download/";
                File file = new  File(PATH + "myImage.jpeg");

                if (file.exists()) {
                    myImage.setImageDrawable(Drawable.createFromPath(file.toString()));
                }
                else {
                    myImage.setImageDrawable(Drawable.createFromPath(null));
                    Toast.makeText(nextPage.this, "Not found", Toast.LENGTH_LONG).show();
                }
            }
        });

您可以根据您的代码更改路径。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接