React + ML5로 브라우저에서 머신러닝 체험하기

Manngold·2020년 4월 30일
3

안녕하세요 Manngold입니다.

React 프레임워크를 공부하면서

'To Do App 말고 특정 라이브러리를 하나 선택해서 간단한 앱을 만들자'

라는 취지로 구경을 하던 도중

https://medium.com/@julienrioux/in-browser-ml-with-react-js-and-ml5-js-f3eeb5149404

이 포스트를 보고 약간의 수정을 거쳐서 함수형 컴포넌트로 만들어 보았습니다.

참고한 글은 이미지를 미리 넣어 놓았기 때문에 미리 설정을 해놓은 이미지의 결과만 나오지만

저는 살짝 바꿔서 이미지의 url을 입력 받도록 해서 다른 이미지도 분석 할 수 있도록 만들어 봤습니다.

기초 환경 세팅

npx create-react-app guess-what

create-react-app 명령어를 통해서 react app 개발을 위한 기초 환경을 세팅 해줍니다.

이후 프로젝트 파일에 들어가서 css파일과 test파일들을 삭제해줍니다.

ML5와 기타 라이브러리 설치

yarn add ml5 styled-components

yarn 명령어를 통해서 ml5와 스타일링을 위한 styled components를 설치합니다.

컴포넌트 구성

개발하기 앞서 컴포넌트 구조에 대해서 살펴봅시다.

빨간색은 전체 앱 컴포넌트
주황색은 분석할 이미지의 url을 입력받는 컴포넌트 (Uploader 컴포넌트)
노란색은 입력받은 이미지를 바탕으로 분석한 결과를 출력해줄 컴포넌트 (Classifier 컴포넌트)

총 세 개의 컴포넌트가 필요합니다.

따라서 추가적으로 components 폴더를 만들어서 Upload.js와 Classifier.js 파일을 생성 합시다.
(기본적인 디렉토리 구조)

App 컴포넌트

App 컴포넌트 안에는 두 개의 컴포넌트가 존재합니다

Uploader 컴포넌트와 Classifier 컴포넌트

그리고 url의 값은 app에서 useState로 관리가 될 것입니다.

import React, { useState } from "react";
import styled from "styled-components";
import Classifier from "./components/Classifier";
import Uploader from "./components/Uploader";

const Container = styled.div`
    height: 100vh;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    background-color: #ecf0f1;
`;

function App() {
    const [imageSrc, setImageSrc] = useState();

    return (
        <Container>
            <Uploader setImageSrc={setImageSrc} />
            <Classifier imageSrc={imageSrc} />
        </Container>
    );
}

export default App;
  1. Uploader 컴포넌트는 imageSrc의 값을 변경 할 것이기 때문에 props로 setImageSrc를 전달해줍시다.

  2. Classifier 컴포넌트는 imageSrc를 활용해서 이미지 분석을 하기 때문에 imageSrc를 전달해줍시다.

Uploader 컴포넌트

import React from "react";
import styled from "styled-components";

const Container = styled.div``;

const Upload = styled.input`
    border: none;
    width: 16rem;
    height: 2rem;
`;

function Uploader({ setImageSrc }) {
    const changeImage = (e) => {
        const imgSrc = e.target.value;
        setImageSrc(imgSrc);
    };
    return (
        <Container>
            <Upload
                type="text"
                onChange={changeImage}
                placeholder="Insert Image Url"
            />
        </Container>
    );
}

export default Uploader;

input 태그가 url을 입력 받았을 때 onChange를 통해서 input 태그 내에 있는 값을 뽑아서 setImageSrcimageSrc state 값을 설정합니다.

Classifier 컴포넌트

이미지를 받아서 분석을 하고 결과를 출력 해주는 컴포넌트입니다. 천천히 뜯어보면서 알아봅시다.

state

    const [results, setResults] = useState([]);
    const [isLoading, setLoading] = useState(false);

Classifier 컴포넌트에서는 두 개의 state를 사용합니다.

분석 결과를 담는 results, loading을 구현하기 위한 isLoading state

classifyImg

const classifyImg = () => {
        setLoading(true);
        const classifier = ml5.imageClassifier("MobileNet", () =>
            console.log("Module loaded")
        );

        const image = document.querySelector("#image");

        classifier
            .predict(image, 5, (err, classifiedResults) => {
                return classifiedResults;
            })
            .then((classifiedResults) => {
                setLoading(false);
                setResults(classifiedResults);
            });
    };

이미지 분석을 위해서 classifyImg 함수를 생성합니다.

분석을 진행 할 동안 isLoading state의 값을 true로 만들어주고,
classifier 변수에 ml5의 imageClassifier를 담아줍니다.

이후 image를 담고 predict메소드로 분석을 하고 분석이 끝나면 isLoading state를 false로 바꿔주고 분석 결과를 results state에 담아줍니다.

render

 return (
        <Container>
            <Image
                src={imageSrc}
                id="image"
                onLoad={classifyImg}
                crossOrigin="anonymous"
            />
            {isLoading ? (
                <ClipLoader />
            ) : (
                <List>
                    {results.map((result, index) => {
                        const { label, confidence } = result;
                        return (
                            <Item key={index}>{`${
                                index + 1
                            }. Predictation : ${label} , ${Math.floor(
                                confidence * 100
                            )}%`}</Item>
                        );
                    })}
                </List>
            )}
        </Container>
    );

image 태그에 imageSrc state를 담아서 이미지를 로드해줍니다.
그리고 onLoad를 통해서 이미지가 로드되면 이미지 분석을 위한 classifyImg 함수를 실행시켜줍니다.

이후 나오는 코드는 loading과 결과값을 렌더링하는 코드입니다.

Classifier 컴포넌트 전체 코드

import React, { useState } from "react";
import styled from "styled-components";
import ml5 from "ml5";
import ClipLoader from "react-spinners/ClipLoader";

const Container = styled.div`
    margin-top: 30px;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
`;

const Image = styled.img`
    height: 20rem;
    width: 30rem;
`;

const List = styled.ul`
    margin-top: 20px;
`;

const Item = styled.li`
    list-style-type: none;
`;
function Classifier({ imageSrc }) {
    const [results, setResults] = useState([]);
    const [isLoading, setLoading] = useState(false);

    const classifyImg = () => {
        setLoading(true);
        const classifier = ml5.imageClassifier("MobileNet", () =>
            console.log("Module loaded")
        );

        const image = document.querySelector("#image");

        classifier
            .predict(image, 5, (err, classifiedResults) => {
                return classifiedResults;
            })
            .then((classifiedResults) => {
                setLoading(false);
                setResults(classifiedResults);
            });
    };
    return (
        <Container>
            <Image
                src={imageSrc}
                id="image"
                onLoad={classifyImg}
                crossOrigin="anonymous"
            />
            {isLoading ? (
                <ClipLoader />
            ) : (
                <List>
                    {results.map((result, index) => {
                        const { label, confidence } = result;
                        return (
                            <Item key={index}>{`${
                                index + 1
                            }. Predictation : ${label} , ${Math.floor(
                                confidence * 100
                            )}%`}</Item>
                        );
                    })}
                </List>
            )}
        </Container>
    );
}

export default Classifier;

로딩 UI를 위해서 react-spinners를 사용했습니다.

개선사항

  • 에러 핸들링 (이미지를 불러올 수 없을 경우 등...)
  • 분석 결과를 더 보기 좋은 UI로 개선
  • 처음에 아무런 이미지를 넣지 않았을 때

Demo : https://manngold.github.io/guess-what/
github : https://github.com/Manngold/guess-what

0개의 댓글