amplify-ui icon indicating copy to clipboard operation
amplify-ui copied to clipboard

Performance Warning in amplify-liveness: Canvas2D getImageData should use willReadFrequently

Open nonimusCode opened this issue 1 year ago • 4 comments

Before creating a new issue, please confirm:

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.

image

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!

  1. Set up a Next.js application: Create or use an existing Next.js project with version 13.4.10.

  2. Install the necessary libraries: Add @aws-amplify/ui-react-liveness and aws-amplify to your project.

  3. Configure Amplify: Set up AWS Amplify with the appropriate configuration in your project.

  4. Add the Amplify Liveness component: Include the AmplifyLiveness component in one of your pages.

  5. Run the application: Start the development server.

  6. Open the page with Liveness component: Navigate to the page in your browser.

  7. Enable the camera: Follow the prompts to access the camera.

  8. 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", }

image

nonimusCode avatar Jul 26 '24 12:07 nonimusCode

Hey @nonimusCode thanks for creating this issue. We'll take this into consideration for our roadmap.

esauerbo avatar Jul 29 '24 18:07 esauerbo

@esauerbo any update about this?

andresem avatar Oct 01 '24 16:10 andresem

@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!

cwomack avatar Oct 07 '24 18:10 cwomack

@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

osama-rizk avatar Jun 03 '25 09:06 osama-rizk

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!

osama-rizk avatar Aug 07 '25 09:08 osama-rizk