Передача данных из одной функции в другую в React - PullRequest
3 голосов
/ 13 апреля 2020

Так что я использую P5 и ML5, и я работаю с ними в React, так что это заставляет меня перепрыгивать через некоторые препятствия, чтобы заставить вещи работать.

Я пытаюсь получить название ярлыка, чтобы нарисовать на веб-камере. Я могу получить console.log для регистрации имени метки в функции gotResults, но я не уверен, как бы я go использовал это, чтобы заставить его работать за пределами gotResults ..

Еще новичок в кодирование, так что прости меня :)

    import React, { useRef, useEffect, useState } from 'react';
import p5, { VIDEO } from 'p5'
import ml5 from 'ml5'


const Webcam = props => {
    const myRef = useRef();
    const [label, setLabel] = useState([
        { label: 'Starting Label', confidence: 0 },
    ]);

    useEffect(() => {
        let myp5 = new p5(sketch, myRef.current)
    });


    const sketch = (p) => {


        let mobilenet;
        let cam;
        let classifier;
        let thumbUpRight;
        let thumbDownRight;
        let thumbUpLeft;
        let thumbDownLeft;
        let trainButton;
        // let label = '';


        const modelReady = () => {
            console.log('Model is ready!');
        }

        const videoReady = () => {
            console.log('Video is ready!');
        }

        const whileTraining = (loss) => {
            if (loss == null) {
                console.log('Training Complete')
                classifier.classify(gotResults)
            } else {
                console.log(loss)
            }
        }



        const gotResults = (error, result) => {
            if (error) {
                console.error(error);
            } else {
                // console.log(result)
                setLabel(result);
                classifier.classify(gotResults);
            }

        }



        p.setup = () => {
            p.createCanvas(640, 550);
            cam = p.createCapture(VIDEO);
            cam.hide();
            p.background(0)
            mobilenet = ml5.featureExtractor('MobileNet', modelReady)
            classifier = mobilenet.classification(cam, videoReady)

            thumbUpRight = p.createButton('Up Right');
            thumbUpRight.mousePressed(function () {
                classifier.addImage('thumbUpRight');
            })

            thumbDownRight = p.createButton('Down Right');
            thumbDownRight.mousePressed(function () {
                classifier.addImage('thumbDownRight');
            })

            thumbUpLeft = p.createButton('Up Left');
            thumbUpLeft.mousePressed(function () {
                classifier.addImage('thumbUpLeft');
            })


            thumbDownLeft = p.createButton('Down Left');
            thumbDownLeft.mousePressed(function () {
                classifier.addImage('thumbDownLeft');
            })


            trainButton = p.createButton('Train');
            trainButton.mousePressed(function () {
                classifier.train(whileTraining);
            })
        };


        p.draw = () => {

            p.background(0);
            p.image(cam, 0, 0)
            p.fill(255);
            p.textSize(32);
            p.text(label[0].label, 10, 530);
        }

    };

    return (
        <div ref={myRef}>

        </div>
    )
}

export default Webcam;

РЕДАКТИРОВАТЬ: Таким образом, ошибка связана с сбросом состояния, повторным рендерингом веб-камеры и всем, что происходит при каждом вызове ML5? Я предполагаю, это как перерисовка каждый раз, когда он попадает в API ML5 ..?

Ответы [ 2 ]

2 голосов
/ 16 апреля 2020

Кажется, что эти повторные визуализации происходят, потому что функция classifier.classify(gotResults) будет выполняться много раз, и там вы обновляете состояние, используя setLabel(result), что вызовет повторную визуализацию каждый раз, когда она вызывается. Вы можете прокомментировать это и проверить, является ли это преступником. Если это так, то вам следует поверхностно сравнить results с label и обновлять состояние setLabel(result) только в том случае, если эти объекты различаются:

Незначительно сравнивать result с label перед обновлением состояния :

const gotResults = (error, result) => {
  if (error) {
    console.error(error);
  } else {
    if (!areEqualShallow(label, result) {
      setLabel(result);
    }
    classifier.classify(gotResults);
  }

}

function areEqualShallow(a, b) {
  for (var key in a) {
    if (a[key] !== b[key]) {
      return false;
    }
  }
  return true;
}

Если это не является причиной этих повторных рендеров, то вам стоит проверить свой хук useEffect. Я не знаю, намеренно ли вы делаете это, но имея хук useEffect без каких-либо аргументов, он будет выполнять этот хук при каждом рендере.

Использование эффекта Крюк

Запускается ли useEffect после каждого рендера? Да! По умолчанию он запускается как после первого рендера, так и после каждого обновления. (Позже мы поговорим о , как настроить это .) Вместо того, чтобы думать в терминах «монтажа» и «обновления», вам может показаться, что вам будет легче думать, что эффекты происходят «после рендеринга». React гарантирует, что DOM был обновлен к моменту запуска эффектов.

Этот хук будет выполняться при каждом рендере:

useEffect(() => {
  let myp5 = new p5(sketch, myRef.current)
});

Добавить пустой массив в конец useEffect перехватить, чтобы запустить его только один раз:

useEffect(() => {
  let myp5 = new p5(sketch, myRef.current)
}, []);

ОБНОВЛЕНИЕ

Чтобы обновить метку на вашем существующем объекте P5, вам придется использовать redraw. Вы должны создать ловушку эффекта для переменной состояния label, которая будет вызываться при изменении label:

// Set a P5 ref so you can use it again
const p5Ref = useRef();

// Change the initial hook to set the p5Ref
useEffect(() => {
  p5Ref.current = new p5(sketch, myRef.current);
}, []);

// This effect will run only when the whole label object is updated,
// not just when a label.<prop> is updated
useEffect(() => {
  // I'm not sure if that's going to work,
  // you'll have to test and see how to force redraw using the P5 returned object properly

  if (p5Ref && p5Ref.current) {
    p5Ref.current.redraw();
  }
}, [label]);

Конечно, эта ловушка не сработает, если вы просто измените label значение из-за ссылки на объект, поэтому вам придется создавать новый объект при обновлении label, используя setLabel, например:

const gotResults = (error, result) => {
  if (error) {
    console.error(error);
  } else {
    // Create a new object so we'll get the update effect hook to trigger on label change
    setLabel({ ...result });
    classifier.classify(gotResults);
  }
}
2 голосов
/ 13 апреля 2020

Можете ли вы иметь label в состоянии?

const [label, setLabel] = useState('')

и

const gotResults = (error, results) => {
    if (error) {
        console.error(error);
    } else {
        // console.log(results)
        setLabel(results[0].label)
        mobilenet.predict(gotResults)
    }
}

Затем вы можете использовать label в любом месте в функции Webcam.

...