amplify-ui
amplify-ui copied to clipboard
Performance Warning in amplify-liveness: Canvas2D getImageData should use willReadFrequently
Before creating a new issue, please confirm:
- [X] I have searched for duplicate or closed issues and discussions.
- [X] I have tried disabling all browser extensions or using a different browser
- [X] I have tried deleting the node_modules folder and reinstalling my dependencies
- [X] I have read the guide for submitting bug reports.
On which framework/platform are you having an issue?
React, Other
Which UI component?
Liveness
How is your app built?
My app is built using the following system: Framework: Next.js 13.4.10 Build System: Webpack 5
What browsers are you seeing the problem on?
Chrome, Firefox, Microsoft Edge
Which region are you seeing the problem in?
us-east-1
Please describe your bug.
When I enable the camera in the Amplify Liveness component in my Next.js application, I receive a warning related to Canvas2D performance. The warning message is as follows:
Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true. See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
This warning indicates that multiple readback operations using getImageData could be optimized by setting the willReadFrequently attribute to true.
What's the expected behaviour?
I expected the Amplify Liveness component to run without any performance-related warnings in the console when the camera is enabled.
Help us reproduce the bug!
-
Set up a Next.js application: Create or use an existing Next.js project with version 13.4.10.
-
Install the necessary libraries: Add @aws-amplify/ui-react-liveness and aws-amplify to your project.
-
Configure Amplify: Set up AWS Amplify with the appropriate configuration in your project.
-
Add the Amplify Liveness component: Include the AmplifyLiveness component in one of your pages.
-
Run the application: Start the development server.
-
Open the page with Liveness component: Navigate to the page in your browser.
-
Enable the camera: Follow the prompts to access the camera.
-
Check the console: Look for a performance warning related to Canvas2D.
Code Snippet
import React, { useState, useContext, useEffect } from 'react' import { FaceLivenessDetector } from '@aws-amplify/ui-react-liveness' import { useServiceAwsRecognition } from '../../hooks/fecth/rekogniton' import { View, Flex, ThemeProvider } from '@aws-amplify/ui-react' import '@aws-amplify/ui-react/styles.css' import { AuthEmailContext } from '../../context/AuthContext' import { useRouter } from 'next/router' import { Button } from 'react-daisyui' import { useTranslation } from 'react-i18next' import Image from 'next/image' import { Cookies } from 'react-cookie' import { ErrorsLivenessInterface } from '../../interfaces/liveness.interfaces' import { OverlayModal } from '@components/modal/OverlayModal' import { LoadingDinamic } from '@components/loading/loadingDinamic' import { LivenessError } from '@aws-amplify/ui-react-liveness/dist/types/components/FaceLivenessDetector/service' import { useLocation } from '../../hooks/location/useGetLocation' import { useDeviceDetected } from '../../hooks/deviceDetect/useDeviceDetected' import { useIPAddress } from '../../hooks/fecth/ipAddress'
const cookies = new Cookies()
interface Prop { setNumberFails: (param: number | ((prevFails: number) => number)) => void numberFails: number }
export function LivenessQuickStartReact({ setNumberFails, numberFails }: Prop) { const router = useRouter() const { connectWithFace } = useContext(AuthEmailContext) const [loading, setLoading] = useState(true) const [modalHandleErrors, setModalHandleErrors] = useState(false) const [manageErrors, setManageErrors] = useState<string | null>(null) const { t } = useTranslation() const [createLivenessApiData, setCreateLivenessApiData] = useState<{ SessionId: string }>({ SessionId: '' }) const [loadingFaceId, setLoadingFaceId] = useState(false) const { positionLocation } = useLocation() const { InformationUser } = useDeviceDetected() const { Ip } = useIPAddress() const dictionary = { hintTooFarText: t('hintTooFarText'), hintTooCloseText: t('hintTooCloseText'), hintHoldFaceForFreshnessText: t('hintHoldFaceForFreshnessText'), hintConnectingText: t('hintConnectingText'), hintVerifyingText: t('hintVerifyingText'), tryAgainText: t('tryAgainText'), landscapeMessageText: t('landscapeMessageText'), landscapeHeaderText: t('landscapeHeaderText'), cameraNotFoundHeadingText: t('cameraNotFoundHeadingText'), cameraNotFoundMessageText: t('cameraNotFoundMessageText'), retryCameraPermissionsText: t('retryCameraPermissionsText') }
const ErrorsLiveness: ErrorsLivenessInterface = { TIMEOUT: { title: t('liveness_TIMEOUT.title'), description: t('liveness_TIMEOUT.description') }, MOBILE_LANDSCAPE_ERROR: { title: t('liveness_MOBILE_LANDSCAPE_ERROR.title'), description: t('liveness_MOBILE_LANDSCAPE_ERROR.description') }, FACE_DISTANCE_ERROR: { title: t('liveness_FACE_DISTANCE_ERROR.title'), description: t('liveness_FACE_DISTANCE_ERROR.description') }, MULTIPLE_FACES_ERROR: { title: 'Multiple faces detected', description: 'More than one face cannot be detected' } }
const handleCloseModal = () => { setLoadingFaceId(!loadingFaceId) } const { createFaceLiveness, getFaceLiveness, loginFaceID } = useServiceAwsRecognition()
useEffect(() => { getSessionId() }, []) const getSessionId = async () => { const SessionId = await createFaceLiveness() if (typeof SessionId === 'string') { return SessionId } setCreateLivenessApiData(SessionId) setLoading(false) } const handleAnalysisComplete = async () => { const responseResultsFaces = await getFaceLiveness( createLivenessApiData.SessionId ) const dataMachine = InformationUser() const ip: any = await Ip() const data: any = { imageName: responseResultsFaces.data.responselivvenesseccion.ReferenceImage .S3Object.Name, bucketName: responseResultsFaces.data.responselivvenesseccion.ReferenceImage .S3Object.Bucket, session: 'FLAS', fingerprint: cookies.get('deviceFingerprint') } const percentage: number = responseResultsFaces.data.responselivvenesseccion.Confidence if (percentage <= 65) { setNumberFails((prevFails) => prevFails + 1) setLoadingFaceId(true) return } data.dataMachine = { ...dataMachine } data.ip = ip if (positionLocation) { data.latitude = positionLocation.latitude data.longitude = positionLocation.longitude data.acurrency = positionLocation.accuracy data.type = 'login' } handleLoginVerify(data) }
const handleError = async (error: LivenessError) => { if (error.state === 'FACE_DISTANCE_ERROR') { createSessionId() return } if (ErrorsLiveness[error.state]) { await setManageErrors(error.state) setModalHandleErrors(true) } else { console.error(JSON.stringify(error)) } } const createSessionId = () => { setLoading(true) getSessionId().finally(() => { setLoading(false) }) }
const handleLoginVerify = async (data: any) => { try { const response: any = await loginFaceID(data) await connectWithFace(response)
// if success redirect to dashboard wih next router
if (response.data.user.roleId === 8) {
return router.push('/generation')
}
return router.push('/dashboard')
} catch (err) {
console.log(err)
setNumberFails((prevFails) => prevFails + 1)
setLoadingFaceId(true)
return
}
}
return ( <ThemeProvider> {loading ? ( <LoadingDinamic height={100} width={100} text={t('establishing_connection')} /> ) : ( <Flex justifyContent="center" alignItems="center" width={{ base: '80vh', small: '70vh', medium: '50vh', large: '50vh', xl: '400px', xxl: '700px' }}> <View width={{ base: '100%', small: '100%', medium: '100%', large: '100%', xl: '100%', xxl: '100%' }} position={'flex justify-center'} backgroundColor="#00d40f"> <FaceLivenessDetector sessionId={createLivenessApiData.SessionId} region="us-east-1" onUserCancel={createSessionId} onAnalysisComplete={handleAnalysisComplete} onError={handleError} disableInstructionScreen={true} displayText={dictionary} components={{ ErrorView: () => { return <div className="hidden"> } }} /> </View> </Flex> )}
<OverlayModal
className="z-999"
activeModal={modalHandleErrors}
closeModal={() => {
setModalHandleErrors(!modalHandleErrors)
createSessionId()
}}>
<OverlayModal.Header>
{manageErrors && ErrorsLiveness[manageErrors]['title']}
</OverlayModal.Header>
<OverlayModal.Body>
{manageErrors && ErrorsLiveness[manageErrors]['description']}
</OverlayModal.Body>
</OverlayModal>
<OverlayModal
className="z-999"
activeModal={loadingFaceId}
closeModal={() => {
handleCloseModal()
createSessionId()
}}>
<OverlayModal.Header>No se reconoce el rostro</OverlayModal.Header>
<OverlayModal.Body>
<div className="mt-2 gap-4">
<div className="flex flex-row justify-center item text-6xl text-red-600 mb-5">
<Image src={'/red.svg'} alt="error" width={100} height={100} />
</div>
<span className="text-xl text-primary">Asegurate de tener:</span>
<div className="flex justify-start flex-col">
<p>• Buena iluminación</p>
<p>• Cámara Limpia</p>
</div>
<p className="text-primary">
Te quedan {`(${3 - numberFails}) `}intentos
</p>
</div>
</OverlayModal.Body>
<OverlayModal.Actions className="flex justify-center">
<Button
className="rounded-full text-xl"
color="primary"
id="button-cookies"
onClick={() => {
handleCloseModal()
createSessionId()
}}>
Volver a Intentar
</Button>
</OverlayModal.Actions>
</OverlayModal>
</ThemeProvider>
) }
Console log output
No response
Additional information and screenshots
"dependencies": { "@aws-amplify/ui-react-liveness": "^2.0.4", "aws-amplify": "^5.3.8", }
Hey @nonimusCode thanks for creating this issue. We'll take this into consideration for our roadmap.
@esauerbo any update about this?
@andresem, we don't have any updates at this point or an ETA on when this will be implemented. We'll update this issue as progress is made however. Thanks!
@nonimusCode @andresem This appears to have been addressed in recent updates. I'd recommend trying the same operation with the latest version of Amplify and @aws-amplify/ui-react-liveness, as this might resolve the problem you're experiencing
Hey,
We are closing this issue due to inactivity. If you are still experiencing this problem, please feel free to reopen the ticket.
Thank you!