¿Cómo crear la base gráfica para personalizar una cámara?
¡Prepárate para llevar tu aplicación de cámara al siguiente nivel! En esta guía, vamos a aprender cómo crear una capa gráfica personalizada usando clases abstractas y funciones clave. Nuestro objetivo es desarrollar una cámara que detecte y encuadre nuestro rostro para selfies perfectas, y utilizaremos herramientas de machine learning como Lens Engine para lograrlo.
¿Qué es una clase abstracta y cómo puede ayudarnos en nuestra cámara?
Una clase abstracta es una plantilla para otras clases que nos permite definir métodos que deben implementarse en las clases hijas. Aquí, vamos a crear una clase abstracta llamada BaseGraphic, que será la base para personalizar nuestra cámara usando un objeto llamado GraphicOverlay que creamos en clases anteriores.
El método draw es abstracto, lo que significa que cada clase que herede de BaseGraphic debe implementarlo.
¿Cómo escalamos y trasladamos gráficos en un lienzo?
Para escalar y trasladar nuestros gráficos en el lienzo de Android, crearemos funciones para cada uno de los ejes:
¿Cómo realizar el escalamiento en eje x e y?
El escalamiento nos permite ajustar el tamaño de nuestros gráficos en relación con el tamaño del lienzo. Definimos funciones para escalar en los ejes x e y:
funscaleX(value: Float): Float {return value * overlay.widthScaleFactor // Escala en x}funscaleY(value: Float): Float {return value * overlay.heightScaleFactor // Escala en y}
¿Cómo se realiza la traslación en eje x e y?
La traslación es necesaria para centrar los gráficos, especialmente al usar la cámara frontal. Implementamos esta lógica considerando las propiedades específicas del LensEngine.
funtranslateX(value: Float): Float {returnif(overlay.cameraFacing == LensEngine.FRONT_LENS){ overlay.width -scaleX(value)// Ajusta el eje x para cámara frontal}else{scaleX(value)// Escala x sin traslación}}funtranslateY(value: Float): Float {returnscaleY(value)// Escala en y}
¿Cómo manejamos los gráficos en el overlay?
Para gestionar los gráficos que dibujaremos, creamos una lista en la clase GraphicOverlay y definimos funciones para limpiar y añadir gráficos. Esto asegura que la cámara pueda actualizarse correctamente.
privateval graphics = synchronizedList<Graphic>()funclear(){ graphics.clear()postInvalidate()// Actualiza el overlay}funaddGraphic(graphic: Graphic){ graphics.add(graphic)}
¿Cómo dibujamos el cuadro en la cámara?
Finalmente, para dibujar un cuadro alrededor del rostro detectado, implementamos la función draw en GraphicOverlay, asegurándonos de que los gráficos se dibujen solo si el tamaño del preview es válido.
Con esto, creamos un sistema que nos permite personalizar la forma en que nuestra cámara interactúa con el mejorado overlay. La implementación del LensEngine permite focalizar el rostro en la cámara frontal para capturar selfies perfectamente centradas. No te detengas aquí; sigue aprender sobre machine learning y otras herramientas que pueden potenciar tu aplicación de cámara. ¡El futuro de la tecnología de imagen está en tus manos!
Fantástico ver como es el código de personalización de la camara 👍
BaseGraphic
package com.sandoval.hselfiecamera.overlayimport android.graphics.Canvasimport com.huawei.hms.mlsdk.common.LensEngineabstract classBaseGraphic(private val graphicOverlay:GraphicOverlay){ abstract fun draw(canvas:Canvas?) fun scaleX(x:Float):Float{return x * graphicOverlay.widtScaleValue} fun scaleY(y:Float):Float{return y * graphicOverlay.heightScaleValue} fun translateX(x:Float):Float{returnif(graphicOverlay.cameraFacing==LensEngine.FRONT_LENS){ graphicOverlay.width-scaleX(x)}else{scaleX(x)}} fun translateY(y:Float):Float{returnscaleY(y)}}
GraphicOverlay
package com.sandoval.hselfiecamera.overlayimport android.content.Contextimport android.graphics.Canvasimport android.util.AttributeSetimport android.view.Viewimport com.sandoval.hselfiecamera.camera.CameraConfigurationclassGraphicOverlay(context:Context,atts:AttributeSet?):View(context, atts){private val lock =Any()privatevar previewWidth =0privatevar previewHeight =0var widtScaleValue =1.0f
privatesetvar heightScaleValue =1.0f
privatesetvar cameraFacing =CameraConfiguration.CAMERA_FACING_FRONTprivatesetprivate val graphics:MutableList<BaseGraphic>=ArrayList() fun addGraphic(graphic:BaseGraphic){synchronized(lock){ graphics.add(graphic)}} fun clear(){synchronized(lock){ graphics.clear()}this.postInvalidate()} fun setCameraInfo(width:Int,height:Int,facing:Int){synchronized(lock){ previewWidth = width
previewHeight = height
cameraFacing = facing
}this.postInvalidate()} override fun onDraw(canvas:Canvas?){super.onDraw(canvas)synchronized(lock){if(previewWidth !=0&& previewHeight !=0){ widtScaleValue = width.toFloat()/ previewWidth.toFloat() heightScaleValue = height.toFloat()/ previewHeight.toFloat()}for(graphic in graphics){ graphic.draw(canvas)}}}}
Hola
Quisiera entender un poco más acerca de la parametrización de clases abstractas.. Honestamente he buscado en muchos sitios web y hasta este momento tenía entendido que no era posible instanciar o declarar parámetros en clases abstractas.
Agradezco de antemano al que me pueda sacar de este bug mental jaja.
Saludos
Hola Luis, Sí en clases abstractas puedes declarar parámetros, que luego serán en implementados o utilizados en la clase que herede de esta. Lo que no puedes hacer es instanciar una clase abstracta