如何根据预览显示上的矩形裁剪 CameraX 捕获的图像

2023-12-23

你好,我是android开发的新手,谁能帮我根据PreviewView上的矩形裁剪CameraX捕获的图像?

这是activity_main.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/frameRoot"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.camera.view.PreviewView
            android:id="@+id/viewFinder"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <View
            android:id="@+id/viewOverlay_center"
            android:background="@drawable/card_rectangle"
            android:layout_width="270dp"
            android:layout_height="400dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@id/ll_bottom"/>

        <LinearLayout
            android:id="@+id/ll_bottom"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="@color/black"
            android:gravity="center"
            android:orientation="horizontal"
            android:padding="16dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent">

            <ImageView
                android:id="@+id/image_capture_button"
                android:layout_width="60dp"
                android:layout_height="60dp"
                android:background="?android:selectableItemBackground"
                android:src="@drawable/shutter_camera" />
        </LinearLayout>
        
    </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>

这是我的MainActivity.java:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(viewBinding.getRoot());

        viewBinding.imageCaptureButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                capturePhoto();
            }
        });
    }

    private void capturePhoto(){
        if (imageCapture == null) {
            return;
        }

        String filename = "Card-" + mDateFormat.format(System.currentTimeMillis());


        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "Card-" + mDateFormat.format(System.currentTimeMillis()));
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");


        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

        imageCapture.takePicture(ContextCompat.getMainExecutor(this), new ImageCapture.OnImageCapturedCallback() {
            @Override
            public void onCaptureSuccess(@NonNull ImageProxy imageProxy) {
                super.onCaptureSuccess(imageProxy);

                ImageInfo imageInfo = imageProxy.getImageInfo();

                Bitmap image = rotateBitmapIfNeeded(imageProxyToBitmap(imageProxy),imageInfo);
                saveBitmap(cropBitmapToCard(image,viewBinding.frameRoot,viewBinding.viewOverlayCenter),uri);

                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setClass(MainActivity.this,  DecoderActivity.class);
                intent.putExtra("KEY", uri.toString());
                startActivity(intent);

                imageProxy.close();
            }

            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                super.onError(exception);
                Log.e("MainActivity","Error on takePicture",exception);
            }
        });

    }

    private void startCamera() {
        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        cameraProviderFuture.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                    startCameraX(cameraProvider);
                } catch (ExecutionException | InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, ContextCompat.getMainExecutor(this));
    }

    void startCameraX(@NonNull ProcessCameraProvider cameraProvider) {
        cameraProvider.unbindAll();

        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();

        // preview use case
        Preview preview = new Preview.Builder().build();
        preview.setSurfaceProvider(viewBinding.viewFinder.getSurfaceProvider());

        // image capture use case
        imageCapture = new ImageCapture.Builder()
                .setTargetRotation(this.getWindowManager().getDefaultDisplay().getRotation())
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build();

        // bind to lifecycle
        cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, preview, imageCapture);
    }

    void saveBitmap(Bitmap target, Uri uri){
        try {
            OutputStream output = getContentResolver().openOutputStream(uri);
            target.compress(Bitmap.CompressFormat.JPEG, 100, output);
        }
        catch (Exception e) {
            Log.d("onBtnSavePng", e.toString()); // java.io.IOException: Operation not permitted
        }
    }

    Bitmap cropBitmapToCard(Bitmap source,View frame, View cardPlaceHolder){
        float scaleX = source.getWidth() / (float)frame.getWidth();
        float scaleY = source.getHeight() / (float) frame.getHeight();

        int x =(int)((cardPlaceHolder.getLeft()) * scaleX);
        int y = (int)((cardPlaceHolder.getTop()) * scaleY);

        Log.v("MainActivity-Crop","leftPos: " + cardPlaceHolder.getLeft() + " width: " + cardPlaceHolder.getWidth());

        int width = (int)(cardPlaceHolder.getWidth() * scaleX);
        int height = (int)(cardPlaceHolder.getHeight() * scaleY);

        return Bitmap.createBitmap(source,x,y,width,height);
    }

    private Bitmap imageProxyToBitmap(ImageProxy image)
    {
        ImageProxy.PlaneProxy planeProxy = image.getPlanes()[0];
        ByteBuffer buffer = planeProxy.getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);

        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    }

    private Bitmap rotateBitmapIfNeeded(Bitmap source, ImageInfo info){
        int angle = info.getRotationDegrees();
        Matrix mat = new Matrix();
        mat.postRotate(angle);
        return Bitmap.createBitmap(source,0,0,source.getWidth(),source.getHeight(),mat,true);
    }
}

这就是我的应用程序的样子(预览模式),我想提取矩形内位图的 ROI

我得到了这个,你可以看到我的裁剪仍然不正确。

y 轴似乎存在映射错误。 有人可以帮助我如何正确裁剪图像吗?因为我已经搜索和研究如何做到这一点,但仍然感到困惑并且不适合我


请查看下面的 APIandroidx.camera.view.transform https://developer.android.com/reference/androidx/camera/view/transform/package-summary。这是一些未经测试的示例代码:

OutputTransform source = previewView.getOutputTransform()
// imageProxy is the output of the ImageCapture.
OutputTransform target = ImageProxyTransformFactory().getOutputTransform(imageProxy);;

// Build the transform from ImageAnalysis to PreviewView
CoordinateTransform coordinateTransform = new CoordinateTransform(source, target);

// The variable box here is your bounding box in PreviewView
coordinateTransform.mapRect(box);

// At this point, the variable box will contain the value you need.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何根据预览显示上的矩形裁剪 CameraX 捕获的图像 的相关文章

  • Android - 如何一次只允许播放一个 MediaPlayer 实例?

    我正在尝试创建一个简单的 Sound board Android 应用程序 使用 ListView 项目作为按钮 顺便说一句 我是一个新手程序员 我的想法是 我按下一个按钮 就会播放一个特定的声音文件 如果我在播放声音时按下任何按钮 它应该
  • 如何将安卓手机从睡眠状态唤醒?

    如何以编程方式将 Android 手机从睡眠状态唤醒 挂起至内存 我不想获取任何唤醒锁 这意味着手机在禁用 CPU 的情况下进入 真正的 睡眠状态 我想我可以使用某种RTC 实时时钟 机制 有人有例子吗 Thanks 为了让Activity
  • Android:“dp”到“px”转换?

    我正在读这篇文章 http developer android com guide practices screens support html http developer android com guide practices scre
  • Delphi XE7 Android 全屏(隐藏软键)

    如何在XE7中全屏显示 隐藏顶部 标题 和底部 软键 工具栏 在 XE6 中 我可以通过在应用程序部分写入来调整 AndroidManifest 以使我的应用程序全屏显示并且没有操作栏 android theme android style
  • KitKat(及更低版本)设备上的 Android Material Design

    我将在我们学校开发一个 Android 应用程序作为一个项目 我想使用 Google 的新 Material Design 但我知道它仅适用于 Android L 设备 Jack Underwood 最近发布了名为 Today Calend
  • 无法在自定义 AOSP 上安装 Google Play 中的某些应用程序:项目不可用。理由:9

    我在尝试从 Google Play 安装某些应用程序时收到以下错误 LibraryUtils isAvailable not available restriction 9 DocUtils getAvailabilityRestricti
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • 在 Android 中使用 DataOutputStream 在 POST 正文中发送特殊字符 (ë ä ï)

    我目前正在开发一个具有大量服务器端通信的 Android 应用程序 昨天 我收到一份错误报告 称用户无法发送 简单 特殊字符 例如 我搜索过但没有找到任何有用的东西 可能重复 没有答案 https stackoverflow com que
  • 应用程序未安装在 Android 模拟器上

    我正在 android Geocoder 中开发一个应用程序 当我运行该应用程序时 它会显示 2011 01 11 11 08 13 GeoTourProject 自动目标模式 使用现有模拟器 emulator 5554 运行兼容的 AVD
  • minHeight 有什么作用吗?

    在附图中 我希望按钮列与图像的高度相匹配 但我也希望按钮列有一个最小高度 它正确匹配图像的高度 但不遵守 minHeight 并且会使按钮向下滑动 我正在为按钮列设置这些属性
  • Flutter 深度链接

    据Flutter官方介绍深层链接页面 https flutter dev docs development ui navigation deep linking 我们不需要任何插件或本机 Android iOS 代码来处理深层链接 但它并没
  • Android 启动器快捷方式

    我制作了一个简单的打卡 打卡时钟应用程序 我想向用户添加在主屏幕上创建快捷方式的选项 该快捷方式将切换应用程序的状态 超时 超时 但我根本不希望此快捷方式在屏幕上打开应用程序 这是我的 setupShortcut private void
  • 调节麦克风录音音量

    我们正在尝试调整录音时的音量级别 麦克风似乎非常敏感 会接收到很多静电 我们查看了 setVolumeControlStream 但找不到传入其中来控制麦克风的流 将您的音频源设置为 MIC using MediaRecorder Audi
  • 如何创建像谷歌位置历史记录一样的Android时间轴视图?

    我想设计像谷歌位置历史这样的用户界面 我必须为我正在使用的应用程序复制此 UIRecyclerView 每行都是水平的LinearLayout其中包含右侧的图标 线条和视图 该线是一个FrameLayout具有圆形背景和半透明圆圈Views
  • 通过 ADB 拔出设备:“找不到服务”

    我必须测试我的应用程序在打瞌睡模式下的行为 根据文档 https developer android com training monitoring device state doze standby html testing doze 我
  • 当手机旋转(方向改变)时如何最好地重新创建标记/折线

    背景 开发一个使用 Android Google Map v2 的本机 Android 应用程序 使用android support v4 app FragmentActivity 在 Android v2 2 上运行 客观的 在更改手机方
  • Android 如何聚焦当前位置

    您好 我有一个 Android 应用程序 可以在谷歌地图上找到您的位置 但是当我启动该应用程序时 它从非洲开始 而不是在我当前的城市 国家 位置等 我已经在developer android com上检查了信息与位置问题有关 但问题仍然存在
  • 在webview android中加载本地html文件

    我正在尝试在 android 的 webview 中加载 html 文件的内容 但是 它给了我 网页不可用错误 如果我尝试使用谷歌或雅虎等网站 它们就会起作用 html文件位于src gt main gt assests gt index
  • 在 Android 中,如何将字符串从 Activity 传递到 Service?

    任何人都可以告诉如何将字符串或整数从活动传递到服务 我试图传递一个整数 setpossition 4 但它不需要 启动时总是需要 0 Service 我不知道为什么我不能通过使用 Service 实例从 Activity 进行操作 publ
  • 在 Google 地图上绘制线条/路径

    我很长一段时间都在忙于寻找如何在 HelloMapView 中的地图上的两个 GPS 点之间画一条线 但没有运气 谁能告诉我该怎么做 假设我使用扩展 MapView 的 HelloMapView 我需要使用叠加层吗 如果是这样 我是否必须重

随机推荐

  • 使用java将字符串内容传输到远程机器中的文件

    我需要将字符串内容放入远程文件中 理想情况下 我曾经在本地创建一个文件 然后将该文件传输到远程计算机 下面是我用来将文件复制到远程的代码片段 ChannelSftp sftpChannel ChannelSftp channel File
  • Spring Integration:消除设置 bean 的重复代码

    对于我的 SFTP 客户端项目 我使用 spring 集成 我们有不同的客户端 必须连接到不同的 SFTP 服务器 但是 所有逻辑都是相同的 所以我将它们抽象为 AbstractSFTPEndPoint 每个特定于客户端的类都实现 getC
  • 带有建议下拉菜单的 NSTextField

    我没有实现我自己的 而是考虑重用现有的自定义 NSTextField 它支持建议下拉菜单 与浏览器中的下拉菜单相同 当您键入时 您会在下面看到建议列表 你知道有什么好的吗 谢谢你 它们被称为完成 您仍然使用常规文本字段 但添加以下内容 co
  • 删除彩色图像的晕影滤镜

    我是 Python OpenCV 图像处理新手 我想删除图像的边框 轮廓阴影 如下所示 我检查了 如何去除扫描图像中的阴影 https stackoverflow com questions 44752240 how to remove s
  • 我是否在登录系统中使用 cookie 或会话登录?

    我是否在登录系统中使用 cookie 或会话登录 我看过使用会话和 cookie 的示例 所以我很困惑 有人可以解释一下吗 大多数网站使用什么 很想知道 提前致谢 会话 在大多数情况下 使用 cookie 来存储其会话 ID 因此几乎总是您
  • 一旦舞台设置可见,就无法设置样式

    我有一个舞台并将其风格设置为 stage initStyle StageStyle TRANSPARENT 几秒钟后我需要将 initStyle 更改为 Decorate 但当我使用 stage initStyle StageStyle D
  • 在 LINUX 中确定 .a 库/存档是 32 位还是 64 位?

    我们在 Linux 中分发了 64 位和 32 位版本的静态库 在为客户排除故障时 我希望我的诊断 shell 脚本能够通过检查 a 存档文件以确定它是 32 位还是 64 位来快速消除问题 我想到的方法不太优雅 提取 o 成员并询问 文件
  • 在 cxf_home/lib 中找不到 cxf-manifest.jar

    我已经从 tomcat 生成了 wsdl 文件 当我尝试使用 apache cxf 2 5 2 从 wsdl 生成客户端存根时 我无法在 cxf home lib 中找到 cxf manifest jar 我已经为 java home to
  • 在 Google Appengine 上使用 Spring Data JPA

    我正在尝试在 GAE 上使用 Spring 3 2 和 Spring Data 但在正确配置时遇到了一些问题 它很小 但从 Spring Data 文档来看 您似乎不需要比实体管理器工厂 bean 多得多的东西 这是我的配置和启动时遇到的异
  • 通过 Python 使用 .pem 证书进行 SSL 连接

    我正在尝试使用身份验证通过 HTTPS 连接建立成功的通信 我在 Ubuntu 12 04 上使用 Python 2 7 和 Django 1 4 我关注的 API 文档对身份验证有特定要求 包括Authentication您将在下面找到标
  • 使用成员函数启动线程

    我正在尝试构建一个std thread使用不带参数并返回的成员函数void 我无法找出任何有效的语法 编译器无论如何都会抱怨 正确的实施方法是什么spawn 这样它就会返回一个std thread执行的test include
  • Entity Framework Core 添加唯一约束代码优先

    我找不到使用 using 属性向我的字段添加唯一约束的方法 public class User Required public int Id get set Required Index IX FirstAndSecond 2 IsUniq
  • 如何使用 Google 路线服务获取具有不同交通方式的多个路径点的路线?

    因此 我了解如何使用 Google Directions 服务添加航路点并更改交通模式 但是是否可以使用相同的 API 调用来完成这两项操作 我正在尝试获取步行 骑自行车 然后再步行的路线 但我不确定如何使用一个 API 调用来完成此操作
  • TextMate 中的⌃⇧H 到“Tidy”HTML 会导致 NoMethodError

    昨天我第一次尝试在 HTML 文档中使用 Tidy 结果 tmp temp textmate Z2P0KX 30 in
  • 在Linux上编译MonoDevelop 5.3时出错

    我已经在我的笔记本电脑上安装了 Ubuntu 14 04 我正在尝试编译从 GitHub 下载的 MonoDevelop 5 3 的代码 我已经安装了所有依赖项 我已经安装了Mono 3 2 8 raven raven laptop Dow
  • 如何通过右键单击事件(动词)将多个文件/文件夹路径传递给可执行文件?

    Related 如何在 Windows 中的文件夹和文件的右键单击事件中添加新项目 https stackoverflow com questions 1821662 how to add new items to right click
  • TStringList 与 TList

    使用标准有什么区别 type sl TStringList 与使用通用 TList 相比 type sl TList
  • 要求未定义

    我正在构建一个新的 React 应用程序 但出现以下错误 未定义需求 你好世界 html
  • Keras 对层数的困惑

    我对 Keras 模型中使用的层数有点困惑 文档在这个问题上相当不透明 根据 Jason Brownlee 的说法 第一层在技术上由两层组成 即输入层 指定为input dim和一个隐藏层 请参阅第一个问题his blog http mac
  • 如何根据预览显示上的矩形裁剪 CameraX 捕获的图像

    你好 我是android开发的新手 谁能帮我根据PreviewView上的矩形裁剪CameraX捕获的图像 这是activity main xml