How can I get pixelate the image with pixie.js?

I’m trying to build a function, so that the user when uploading their photos can have the option to pixelate their face and I’m using pixi.js with react, but it’s not working. I’ve modified the function in many ways but it always returns me

Unhandled Promise Rejection: TypeError: undefined is not an object (evaluating ‘this.renderer.canvas’)

Please, could someone help me with this?

import { Application, Assets, Sprite, BlurFilter } from 'pixi.js';
//Function to load image from file
const loadImageElement = (src) => {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.src = src;
        img.onload = () => resolve(img);
        img.onerror = (error) => reject(error);
    });
};
//Function to pixelate and blur face
export const pixelateFace = async (imageFile, detection) => {
    //Load image using created url from file
    const img = await loadImageElement(URL.createObjectURL(imageFile));

    const app = new Application({
        width: img.width,
        height: img.height,
        backgroundColor: 0x1099bb, 
        view: document.createElement('canvas'), 
    });

    document.body.appendChild(app.view);

    const sprite = Sprite.from(img);

    sprite.width = app.screen.width;
    sprite.height = app.screen.height;
    app.stage.addChild(sprite);

    const blurFilter = new BlurFilter();
    blurFilter.blur = 10;

    const { box } = detection;
    const { x, y, width, height } = box;

    const maskSprite = new Sprite();
    maskSprite.width = width;
    maskSprite.height = height;
    maskSprite.x = x;
    maskSprite.y = y;
    maskSprite.filters = [blurFilter];

    app.stage.addChild(maskSprite);
    sprite.mask = maskSprite;

    app.render();

    const canvas = app.renderer.extract.canvas(sprite);

    return new Promise((resolve) => {
        canvas.toBlob((blob) => {
            if (blob) {
                const pixelatedImage = new File([blob], imageFile.name, {
                    type: imageFile.type,
                });
                resolve(pixelatedImage);
            } else {
                reject(new Error("Error al convertir el canvas a Blob"));
            }
        }, imageFile.type);
    });
};

I hope to be able to pixelate the photo and return a file to replace the original.

This is the parent component:

import React, {useEffect, useState} from "react";
import {Button, message, Modal, Progress, Upload} from "antd";
import {PlusOutlined} from "@ant-design/icons";
import {router} from "@inertiajs/react";
import {sightEngine} from "@/Utils/constants.js";
import * as faceapi from "face-api.js";
import axios from "axios";
import {pixelateFace} from "@/Utils/pixelateFace.jsx";
import {useAppDispatch} from "@/Redux/hooks.js";
import {addNotification} from "@/Redux/Layout/notificationSlice.js";

export default function AddPhotosToGallery() {
    const dispatch = useAppDispatch();
    const [openModal, setOpenModal] = useState(false);
    const [fileList, setFileList] = useState([]);
    const [uploading, setUploading] = useState(false);
    const [modelsLoaded, setModelsLoaded] = useState(false);
    const [progress, setProgress] = useState(0);

    const MAX_FILE_SIZE_MB = 20;
    const MAX_FILE_COUNT = 10;

    useEffect(() => {
        const loadModels = async () => {
            const MODEL_URL = '/build/assets/models';
            try {
                await faceapi.loadTinyFaceDetectorModel(MODEL_URL);
                await faceapi.loadFaceLandmarkTinyModel(MODEL_URL);
                await faceapi.loadFaceRecognitionModel(MODEL_URL);
                setModelsLoaded(true);
            } catch (error) {
                dispatch(addNotification({
                    type: 'warning',
                    message: '¡Advertencia!',
                    description: 'No se han podido cargar los modelos de face-api.'
                }));
            }
        };

        loadModels();
    }, [dispatch]);

    const handleOpenModal = () => setOpenModal(!openModal);

    const beforeUpload = (file) => {
        const isLt20MB = file.size / 1024 / 1024 < MAX_FILE_SIZE_MB;
        if (!isLt20MB) {
            message.error("Cada archivo debe ser menor a 20MB.");
        }
        return isLt20MB;
    };

    const handleChange = ({ fileList }) => {
        if (fileList.length > MAX_FILE_COUNT) {
            message.error(`Solo puedes subir un máximo de ${MAX_FILE_COUNT} fotos.`);
            return;
        }
        setFileList(fileList);
    };

    const validateWithSightEngine = async (file) => {
        const formData = new FormData();
        formData.append("media", file.originFileObj);

        try {
            const response = await axios.post(
                `https://api.sightengine.com/1.0/check-workflow.json`,
                formData,
                {
                    params: {
                        api_user: sightEngine.api_user,
                        api_secret: sightEngine.api_secret,
                        workflow: sightEngine.workflow,
                    },
                }
            );
            const result = response.data;
            const isSuitable = !(result.nudity.sexual_activity > 0.1 ||
                result.nudity.sexual_display > 0.1 ||
                result.nudity.erotica > 0.4);

            return { file, isSuitable };
        } catch (error) {
            return { file, isSuitable: false };
        }
    };

    const loadImageElement = (file) => {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.src = URL.createObjectURL(file);

            img.onload = () => resolve(img);
            img.onerror = reject;
        });
    };

    const getTinyFaceDetectorOptions = () => new faceapi.TinyFaceDetectorOptions({
        inputSize: 160,
        scoreThreshold: 0.5,
    });

    const processImages = async (fileList) => {
        if (!modelsLoaded) {
            message.error("Los modelos de detección de rostros no están cargados aún.");
            return false;
        }

        for (const file of fileList) {
            const imageElement = await loadImageElement(file.originFileObj);

            console.time("Detección de rostro");
            const detection = await faceapi.detectSingleFace(imageElement, getTinyFaceDetectorOptions());
            console.timeEnd("Detección de rostro");

            if (detection) {
                const shouldPixelate = window.confirm("¿Quieres pixelar el rostro en esta foto?");

                if (shouldPixelate) {
                    file.originFileObj = await pixelateFace(file.originFileObj, detection);
                    message.success("El rostro ha sido pixelado correctamente.");
                } else {
                    message.info("El rostro no ha sido pixelado.");
                }
            } else {
                console.log("No se detectó rostro en la imagen");
            }

            const validation = await validateWithSightEngine(file);
            if (!validation.isSuitable) {
                message.error("Esta imagen contiene contenido explícito o desnudez.");
                return false;
            }
        }

        return true;
    };

    const handleUpload = async () => {
        setUploading(true);
        const isValid = await processImages(fileList);
        if (!isValid) {
            setUploading(false);
            return;
        }

        const formData = new FormData();
        fileList.forEach((file) => {
            formData.append("files[]", file.originFileObj);
        });

        router.post(route("upload.photos"), formData, {
            forceFormData: true,
            onFinish: () => setUploading(false),
            onProgress: (event) => {
                if (event.total > 0) {
                    const percent = Math.round((event.loaded / event.total) * 100);
                    setProgress(percent);
                }
            },
        });
    };

    return (
        <>
            <Button
                type="primary"
                className="bg-blue-500 hover:!bg-blue-600 font-bold"
                onClick={handleOpenModal}
                icon={<PlusOutlined />}
            >
                Añadir fotos
            </Button>
            <Modal
                open={openModal}
                onCancel={() => {
                    setUploading(false);
                    setFileList([]);
                    handleOpenModal();
                }}
                okText="Subir"
                okButtonProps={{
                    color: 'danger',
                    loading: uploading,
                    onClick: handleUpload,
                    disabled: fileList.length === 0
                }}
                maskClosable={false}
                width={1000}
                centered
            >
                <h2>Agregar fotos a la galería</h2>
                <Upload
                    customRequest={({ onSuccess }) => onSuccess()}
                    listType="picture-card"
                    fileList={fileList}
                    onChange={handleChange}
                    beforeUpload={beforeUpload}
                    multiple
                >
                    {fileList.length < MAX_FILE_COUNT && "+ Subir"}
                </Upload>
                {uploading && (
                    <Progress percent={progress} status="active" />
                )}
            </Modal>
        </>
    );
}