无法从arFragment拍照-ARCore-增强脸部

问题描述

在这里遵循代码,但是无法捕获我的ArFragment。请帮我。 https://codelabs.developers.google.com/codelabs/sceneform-intro/index.html?index=..%2F..io2018#15

该项目的链接https://github.com/DSparda/Breakable-Cam

始终获取IOException:“无法将位图保存到磁盘”

我的WritingArFragment:

    class FaceArFragment : ArFragment() {

    override fun getSessionConfiguration(session: Session?): Config {
        return Config(session).apply { augmentedFaceMode = Config.AugmentedFaceMode.MESH3D }
    }

    override fun getSessionFeatures(): MutableSet<Session.Feature> {
        return EnumSet.of(Session.Feature.FRONT_CAMERA)
    }
    override fun getAdditionalPermissions(): Array<String?>? {
        val additionalPermissions = super.getAdditionalPermissions()
        val permissionLength =
            additionalPermissions?.size ?: 0
        val permissions =
            arrayOfNulls<String>(permissionLength + 1)
        permissions[0] = Manifest.permission.WRITE_EXTERNAL_STORAGE
        if (permissionLength > 0) {
            System.arraycopy(
                additionalPermissions,permissions,1,additionalPermissions!!.size
            )
        }
        return permissions
    }

    /**
     * Override to turn off planediscoveryController. Plane trackables are not supported with the
     * front camera.
     */
    override fun onCreateView(
        inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
    ): View {

        val layout = super.onCreateView(inflater,container,savedInstanceState) as FrameLayout
        planediscoveryController.apply {
            hide()
            setInstructionView(null)
        }
        return layout
    }
}

我的活动XML:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="takePictureviewmodel"
            type="com.example.breakablecam.screens.takingPicture.TakePictureviewmodel" />
    </data>

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

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/face_fragment_cointanier"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:paddingBottom="16dp"
            app:layout_constraintBottom_toTopOf="@+id/takePhotoView"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:visibility="visible">

            <fragment
                android:id="@+id/face_fragment"
                android:name="com.example.breakablecam.screens.takingPicture.FaceArFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </androidx.fragment.app.FragmentContainerView>

        <ImageView
            android:id="@+id/backArrow"
            android:layout_width="@android:dimen/app_icon_size"
            android:layout_height="@android:dimen/app_icon_size"
            android:layout_marginEnd="16dp"
            android:layout_marginBottom="16dp"
            android:visibility="gone"

            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />

        <ImageView
            android:id="@+id/stickerView"
            android:layout_width="@android:dimen/app_icon_size"
            android:layout_height="@android:dimen/app_icon_size"
            android:layout_marginBottom="16dp"
            android:onClick="@{() -> takePictureviewmodel.tapSticker()}"
            android:visibility="visible"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/takePhotoView"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent" />

        <ImageView
            android:id="@+id/stickerView1"
            android:layout_width="@android:dimen/app_icon_size"
            android:layout_height="@android:dimen/app_icon_size"
            android:layout_marginStart="16dp"
            android:layout_marginBottom="16dp"
            android:visibility="gone"
            android:onClick="@{() -> takePictureviewmodel.tapSticker1()}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

        <ImageView
            android:id="@+id/takePhotoView"
            android:layout_width="@android:dimen/app_icon_size"
            android:layout_height="@android:dimen/app_icon_size"
            android:layout_marginBottom="16dp"
            android:visibility="visible"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/makeupView"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/stickerView" />

        <ImageView
            android:id="@+id/makeupView"
            android:layout_width="@android:dimen/app_icon_size"
            android:layout_height="@android:dimen/app_icon_size"
            android:layout_marginBottom="16dp"
            android:onClick="@{() -> takePictureviewmodel.tapMakeupView()}"
            android:visibility="visible"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/takePhotoView" />

        <ImageView
            android:id="@+id/makeupView1a"
            android:layout_width="@android:dimen/app_icon_size"
            android:layout_height="@android:dimen/app_icon_size"
            android:layout_marginStart="16dp"
            android:layout_marginBottom="16dp"
            android:onClick="@{() -> takePictureviewmodel.tapMakeup1aView()}"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            tools:visibility="visible" />

        <ImageView
            android:id="@+id/makeupView1"
            android:layout_width="@android:dimen/app_icon_size"
            android:layout_height="@android:dimen/app_icon_size"
            android:layout_marginStart="16dp"
            android:visibility="gone"
            app:layout_constraintBottom_toTopOf="@id/makeupView1a"
            app:layout_constraintStart_toStartOf="parent"
            tools:visibility="visible" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

我的活动:

    class TakePictureActivity : AppCompatActivity() {

    private lateinit var viewmodel: TakePictureviewmodel
    private lateinit var arFragment: FaceArFragment
    private var modelRenderable: ModelRenderable? = null
    private var meshTexture: Texture? = null
    private val faceNodeMap = HashMap<AugmentedFace,AugmentedFaceNode?>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityTakePictureBinding =
            DataBindingUtil.setContentView(this,R.layout.activity_take_picture)

        viewmodel = viewmodelProvider(this).get(TakePictureviewmodel::class.java)


        binding.apply {
            viewmodel.apply {
                setStickerViewSource(stickerView)
                setMakeupViewSource(makeupView)
                setTakePhotoViewSource(takePhotoView)
                setMakeup1ViewSource(makeupView1)
                setMakeup1aViewSource(makeupView1a)
                setSticker1ViewSource(stickerView1)
                setBackArrowSource(backArrow)
            }
        }

        binding.takePictureviewmodel = viewmodel
        binding.lifecycleOwner = this

        viewmodel.makeupTap.observe(this,Observer { check ->
            if (check == 1) {
                binding.apply {
                    makeupView.visibility = GONE
                    takePhotoView.visibility = GONE
                    stickerView.visibility = GONE
                    makeupView1.visibility = VISIBLE
                    makeupView1a.visibility = VISIBLE
                    backArrow.visibility = VISIBLE
                    val params =
                        faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
                    params.bottomToTop = R.id.makeupView1
                }
                viewmodel.doneTapMakeup()
            }
        })

        viewmodel.stickerTap.observe(this,Observer { check ->
            if (check == 1) {
                binding.apply {
                    makeupView.visibility = GONE
                    takePhotoView.visibility = GONE
                    stickerView.visibility = GONE
                    stickerView1.visibility = VISIBLE
                    backArrow.visibility = VISIBLE
                    val params =
                        faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
                    params.bottomToTop = R.id.stickerView1
                }
                viewmodel.doneTapSticker()
            }
        })
        binding.apply {
            backArrow.setonClickListener {
                makeupView.visibility = VISIBLE
                takePhotoView.visibility = VISIBLE
                stickerView.visibility = VISIBLE
                makeupView1.visibility = GONE
                makeupView1a.visibility = GONE
                backArrow.visibility = GONE
                stickerView1.visibility = GONE
                backArrow.visibility = GONE
                val params =
                    faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
                params.bottomToTop = R.id.takePhotoView
            }
        }

        arFragment = supportFragmentManager.findFragmentById(R.id.face_fragment) as FaceArFragment
        val sceneView = arFragment.arSceneView
        sceneView.cameraStreamRenderPriority = Renderable.RENDER_PRIORITY_FirsT
        val scene = sceneView.scene
        loadTexture(R.drawable.makeup1a)
        loadModel(R.raw.fox_face)
        viewmodel.makeupTap1a.observe(this,Observer { check ->
            when (check) {
                1 -> {
                    scene.addOnUpdateListener {
                        val collection: Collection<AugmentedFace>? =
                            sceneView.session?.getAllTrackables(AugmentedFace::class.java)
                        collection?.forEach { face ->
                            if (!faceNodeMap.containsKey(face)) {
                                val faceNode = AugmentedFaceNode(face)
                                faceNode.apply {
                                    setParent(scene)
                                    faceMeshTexture = meshTexture
                                }
                                faceNodeMap[face] = faceNode
                            }
                        }

                        val iterator = faceNodeMap.entries.iterator()
                        while (iterator.hasNext()) {
                            val entry = iterator.next()
                            val face = entry.key
                            if (face.trackingState == TrackingState.STOPPED) {
                                val faceNode = entry.value
                                faceNode!!.setParent(null)
                                iterator.remove()
                            }
                        }
                    }
                }
                2 -> {
                    val children: List<Node> =
                        ArrayList(arFragment.arSceneView.scene.children)
                    for (node in children) {
                        if (node is AnchorNode) {
                            if (node.anchor != null) {
                                node.anchor?.detach()
                            }
                        }
                        if (node !is Camera && node !is Sun) {
                            node.setParent(null)
                        }
                    }
                }
            }
        })
        viewmodel.sticker1Tap.observe(this,Observer { check ->
            when (check) {
                1 -> {
                    scene.addOnUpdateListener {
                        val collection: Collection<AugmentedFace>? =
                            sceneView.session?.getAllTrackables(AugmentedFace::class.java)
                        collection?.forEach { face ->
                            if (!faceNodeMap.containsKey(face)) {
                                val faceNode = AugmentedFaceNode(face)
                                faceNode.apply {
                                    setParent(scene)
                                    faceRegionsRenderable = modelRenderable
                                }
                                faceNodeMap[face] = faceNode
                            }
                        }

                        val iterator = faceNodeMap.entries.iterator()
                        while (iterator.hasNext()) {
                            val entry = iterator.next()
                            val face = entry.key
                            if (face.trackingState == TrackingState.STOPPED) {
                                val faceNode = entry.value
                                faceNode!!.setParent(null)
                                iterator.remove()
                            }
                        }
                    }
                }
                2 -> {
                    val children: List<Node> =
                        ArrayList(arFragment.arSceneView.scene.children)
                    for (node in children) {
                        if (node is AnchorNode) {
                            if (node.anchor != null) {
                                node.anchor?.detach()
                            }
                        }
                        if (node !is Camera && node !is Sun) {
                            node.setParent(null)
                        }
                    }
                }
            }
        })
        binding.takePhotoView.setonClickListener {
            takePhoto()
        }
    }

    private fun loadTexture(tex: Int) {
        Texture.builder()
            .setSource(this,tex)
            .build()
            .thenAccept { texture -> meshTexture = texture }
    }
    private fun loadModel(mod: Int) {
        ModelRenderable.builder()
            .setSource(this,mod)
            .build()
            .thenAccept { model ->
                model.apply {
                    isShadowCaster = false // optional
                    isShadowReceiver = false
                }
                modelRenderable = model
            }
    }
    private fun generateFilename(): String? {
        val date =
            SimpleDateFormat("yyyyMMddHHmmss",Locale.getDefault())
                .format(Date())
        return Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES
        ).toString() + File.separator + "SceneForm/" + date + "_screenshot.jpg"
    }

    @Throws(IOException::class)
    private fun saveBitmapTodisk(bitmap: Bitmap,filename: String) {
        val out = File(filename)
        if (!out.parentFile.exists()) {
            out.parentFile.mkdirs()
        }
        try {
            FileOutputStream(filename).use { outputStream ->
                ByteArrayOutputStream().use { outputData ->
                    bitmap.compress(Bitmap.CompressFormat.PNG,100,outputData)
                    outputData.writeto(outputStream)
                    outputStream.flush()
                    outputStream.close()
                }
            }
        } catch (ex: IOException) {
            throw IOException("Failed to save bitmap to disk",ex)
        }
    }

    private fun takePhoto() {
        val filename = generateFilename()
        val view: ArSceneView = arFragment.getArSceneView()

        // Create a bitmap the size of the scene view.
        val bitmap = Bitmap.createBitmap(
            view.width,view.height,Bitmap.Config.ARGB_8888
        )

        // Create a handler thread to offload the processing of the image.
        val handlerThread = HandlerThread("Pixelcopier")
        handlerThread.start()
        // Make the request to copy.
        Pixelcopy.request(view,bitmap,{ copyResult ->
            if (copyResult === Pixelcopy.SUCCESS) {
                try {
                    saveBitmapTodisk(bitmap,filename!!)
                } catch (e: IOException) {
                    val toast = Toast.makeText(
                        this,e.toString(),Toast.LENGTH_LONG
                    )
                    toast.show()
                    return@request
                }
                val snackbar = Snackbar.make(
                    findViewById(android.R.id.content),"Photo saved",Snackbar.LENGTH_LONG
                )
                snackbar.setAction(
                    "Open in Photos"
                ) { v: View? ->
                    val photoFile = File(filename)
                    val photoURI = FileProvider.getUriForFile(
                        this,this.getPackageName()
                            .toString() + ".ar.codelab.name.provider",photoFile
                    )
                    val intent = Intent(Intent.ACTION_VIEW,photoURI)
                    intent.setDataAndType(photoURI,"image/*")
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                    startActivity(intent)
                }
                snackbar.show()
            } else {
                val toast = Toast.makeText(
                    this,"Failed to copyPixels: $copyResult",Toast.LENGTH_LONG
                )
                toast.show()
            }
            handlerThread.quitSafely()
        },Handler(handlerThread.looper))
    }
}

非常感谢大家。

解决方法

尝试添加

<application android:requestLegacyExternalStorage="true" ... > ... </application> 

显示在清单上。

我认为这与Android 11中即将实施的范围存储实施有关。

,

您必须在代码实验室(权限和文件提供者)中指定的AndroidManifest.xml中添加标签。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<provider
   android:name="android.support.v4.content.FileProvider"
   android:authorities="${applicationId}.ar.codelab.name.provider"
   android:exported="false"
   android:grantUriPermissions="true">
   <meta-data
       android:name="android.support.FILE_PROVIDER_PATHS"
       android:resource="@xml/paths"/>
</provider>

然后,您必须从使用File迁移到使用Uris和ContentResolvers,因为Android 10弃用了File功能,并且您必须使用MediaStore。

https://developer.android.com/training/data-storage/shared/media