Reactを使用して画像認識アプリを実装する方法

Reactを使用して画像認識アプリを実装する方法

まずは効果の写真をお見せしましょう。

ここに画像の説明を挿入

ここに画像の説明を挿入

ここに画像の説明を挿入

個人的には効果は問題ないと思います。アプリが写真を学習する時間が短すぎるため(コンピューターが遅すぎるため)、認識はあまり正確ではありません。

(筆者はWindows 10を使用しています)インストール・動作環境:

  • npm install --global windows-build-tools (これには長い時間がかかります...)
  • npm install @tensorflow/tfjs-node (これにはかなり時間がかかります...)

プロジェクトディレクトリは次のとおりですここに画像の説明を挿入

トレインフォルダindex.js(エントリファイル)

 tf は '@tensorflow/tfjs-node' を必要とします。
const getData = require('./data')

const TRAIN_DIR = '../ガベージ分類/トレイン'
定数 OUTPUT_DIR = '../outputDir'
定数 MOBILENET_URL = 'http://ai-sample.oss-cn-hangzhou.aliyuncs.com/pipcook/models/mobilenet/web_model/model.json'

定数main = 非同期() => {
  // データをロード const { ds, classes} = await getData(TRAIN_DIR, OUTPUT_DIR)
  // モデルを定義する const mobilenet = await tf.loadLayersModel(MOBILENET_URL)
  モバイルネット.サマリー()
  // console.log(mobilenet.layers.map((l, i) => [l.name, i]))
  定数モデル = tf.sequential()
  (i = 0、i <= 86、i += 1 とします) {
    定数レイヤー = mobilenet.layers[i]
    レイヤー.trainable = false
    モデル.追加(レイヤー)
  }
  モデルを追加します(tf.layers.flatten())
  モデル.add(tf.layers.dense({
    ユニット: 10,
    アクティベーション: 'relu'
  }))
  モデル.add(tf.layers.dense({
    単位: クラス.長さ、
    アクティベーション: 'softmax'
  }))
  // トレーニングモデル model.compile({
    損失: 'sparseCategoricalCrossentropy',
    オプティマイザー: tf.train.adam(),
    メトリクス: ['acc']
  })
  model.fitDataset(ds, { エポック: 20 }) を待機します。
  model.save(`file://${process.cwd()}/${OUTPUT_DIR}`) を待機します。
}
主要()

data.js (データ処理)

定数 fs = require('fs')
tf は '@tensorflow/tfjs-node' を必要とします。

定数img2x = (画像パス) => {
  定数バッファ = fs.readFileSync(imgPath)
  tf.tidy(() => { を返す
    const imgTs = tf.node.decodeImage(新しいUint8Array(バッファ))
    定数 imgTsResized = tf.image.resizeBilinear(imgTs, [224, 224])
    imgTsResized.toFloat().sub(255/2).div(255/2).reshape([1, 224, 224, 3]) を返します
  })
}

const getData = async (trainDir, outputDir) => {
  定数クラス = fs.readdirSync(trainDir)
  fs.writeFileSync(`${outputDir}/classes.json`, JSON.stringify(classes))

  定数データ = []
  クラス.forEach((dir, dirIndex) => {
    fs.readdirSync(`${trainDir}/${dir}`)
      .filter(n => n.match(/jpg$/))
      .スライス(0, 10)
      .forEach(ファイル名 => {
        console.log('read', ディレクトリ, ファイル名)
        const imgPath = `${trainDir}/${dir}/${filename}`
        data.push({imgPath, dirIndex}) を実行します。
      })
  })

  tf.util.shuffle(データ)

  const ds = tf.data.generator(関数* () {
    定数カウント = データ.長さ
    定数バッチサイズ = 32
    (開始 = 0、開始 < カウント、開始 += バッチサイズ) {
      const end = Math.min(開始 + バッチサイズ、カウント)
      tf.tidy(() => {を生成します
        定数入力 = []
        定数ラベル = []
        (j = 開始; j < 終了; j += 1 とする) {
          const { imgPath, dirIndex } = データ[j]
          定数 x = img2x(imgPath)
          入力.push(x)
          ラベルをプッシュ(dirIndex)
        }
        const xs = tf.concat(入力)
        const ys = tf.tensor(ラベル)
        {xs,ys}を返す
      })
    }
  })

  戻る {
    ds、
    クラス
  }
}

モジュール.エクスポート = getData

プロジェクトの実行に必要なプラグインをインストールするここに画像の説明を挿入

アプリフォルダ

React をインポートします。{ PureComponent } から 'react' をインポートします。
'antd' から { Button, Progress, Spin, Empty } をインポートします。
'antd/dist/antd.css' をインポートします
'@tensorflow/tfjs' から * を tf としてインポートします。
'./utils' から { file2img, img2x } をインポートします。
'./intro' から intro をインポートします

定数DATA_URL = 'http://127.0.0.1:8080/'
クラスAppはPureComponentを拡張します{
  状態 = {}
  非同期コンポーネントDidMount() {
    this.model = tf.loadLayersModel(DATA_URL + '/model.json') を待機します。
    // this.model.summary()
    this.CLASSES = フェッチ(DATA_URL + '/classes.json').then(res => res.json()) を待機します。
  }
  予測 = 非同期 (ファイル) => {
    const img = file2img(ファイル)を待つ

    this.setState({
      画像ソース: 画像.src,
      読み込み中: true
    })
    タイムアウトを設定する(() => {
      定数 pred = tf.tidy(() => {
        定数 x = img2x(画像)
        this.model.predict(x) を返す
      })

      定数結果 = pred.arraySync()[0]
        .map((スコア, i) => ({スコア, ラベル: this.CLASSES[i]}))
        .sort((a, b) => b.スコア - a.スコア)
      this.setState({
        結果、
        読み込み中: false
      })
    }, 0)
  }

  レンダリング結果 = (アイテム) => {
    定数 finalScore = Math.round(item.score * 100)
    戻る (
      <tr キー = {item.label}>
        <td style={{ width: 80, padding: '5px 0' }}>{item.label}</td>
        <td>
          <進捗率={finalScore} ステータス={finalScore === 100 ? '成功' : '正常'} />
        </td>
      </tr>
    )
  }

  与える() {
    const { imgSrc, 結果, isLoading } = this.state
    const finalItem = 結果 && {...結果[0]、...イントロ[結果[0].ラベル]}

    戻る (
      <div スタイル={{パディング: 20}}>
        <span
          スタイル={{ 色: '#cccccc', テキスト配置: 'center', フォントサイズ: 12, 表示: 'block' }}
        >認識が正確でない可能性があります</span>
        <ボタン
          タイプ="プライマリ"
          サイズ="大"
          スタイル={{幅: '100%'}}
          onClick={() => this.upload.click()}
        >
          画像認識を選択</Button>
        <入力
          タイプ="ファイル"
          onChange={e => this.predict(e.target.files[0])}
          ref={el => {this.upload = el}}
          スタイル={{ 表示: 'なし' }}
        />
        {
          !results && !imgSrc && <空のスタイル={{ marginTop: 40 }} />
        }
        {imgSrc && <div style={{ marginTop: 20, textAlign: 'center' }}>
          <img src={imgSrc} スタイル={{ maxWidth: '100%' }} />
        </div>}
        {finalItem && <div style={{marginTop: 20}}>認識結果: </div>}
        {finalItem && <div style={{display: 'flex', alignItems: 'flex-start', marginTop: 20}}>
          <画像
            src={finalItem.icon}
            幅={120}
          />
          <div>
            <h2 スタイル = {{color: finalItem.color}}>
              {最終アイテム.ラベル}
            </h2>
            <div スタイル = {{color: finalItem.color}}>
              {最終項目.intro}
            </div>
          </div>
        </div>}
        {
          isLoading && <Spin size="large" style={{display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: 40 }} />
        }
        {結果 && <div style={{ marginTop: 20 }}>
          <テーブルスタイル={{幅: '100%'}}>
            <t本文>
              <tr>
                <td>カテゴリー</td>
                <td>マッチング度</td>
              </tr>
              {results.map(this.renderResult)}
            </tbody>
          </テーブル>
        </div>}
      </div>
    )
  }
}

デフォルトアプリをエクスポート

インデックス.html

 <!DOCTYPE html>
<html>
  <ヘッド>
    <title>ゴミの分別</title>
    <meta name="viewport" content="width=デバイス幅、初期スケール=1">
  </head>
  <本文>
    <div id="アプリ"></div>
    <script src="./index.js"></script>
  </本文>
</html>

インデックス

'react' から React をインポートします
'react-dom' から ReactDOM をインポートします。
'./App' から App をインポートします

ReactDOM.render(<App />, document.querySelector('#app'))

イントロ

エクスポートデフォルト{
  「リサイクル可能品」: {
    アイコン: 'https://lajifenleiapp.com/static/svg/1_3F6BA8.svg',
    色: '#3f6ba8',
    はじめに:「日常生活や日常生活に資するサービスを提供する活動で発生し、本来の使用価値の全部または一部を失い、リサイクルして生産原材料に加工したり、分別して再利用したりできるもの(廃紙、プラスチック、ガラス、金属、布地など)を指します。」 '
  },
  「有害廃棄物」: {
    アイコン: 'https://lajifenleiapp.com/static/svg/2v_B43953.svg',
    色: '#b43953',
    はじめに: 家庭ごみに含まれる物質のうち、人の健康や自然環境に直接的または潜在的に危害を及ぼすものを指します。これには、廃棄充電式電池、廃棄ボタン電池、廃棄電球、廃棄医薬品、廃棄農薬(容器)、廃棄塗料(容器)、廃棄日用化学薬品、廃棄水銀製品、廃棄電気機器および電子製品などが含まれます。 '
  },
  「キッチン廃棄物」: {
    アイコン: 'https://lajifenleiapp.com/static/svg/3v_48925B.svg',
    色: '#48925b',
    はじめに:「野菜の葉、残り物、果物の皮、卵の殻、お茶のかす、骨など、住民の日常生活で発生する有機廃棄物や腐敗しやすい廃棄物を指します。」 '
  },
  「その他のゴミ」: {
    アイコン: 'https://lajifenleiapp.com/static/svg/4_89918B.svg',
    色: '#89918b',
    はじめに: リサイクル可能物、有害廃棄物、厨房廃棄物を除く、混合され、汚染され、分類が困難なその他の家庭廃棄物を指します。 '
  }
}

ユーティリティ

'@tensorflow/tfjs' から * を tf としてインポートします。

エクスポートconst file2img = async(f) => {
  新しい Promise(reslove => { を返します。
    const リーダー = 新しい FileReader()
    リーダー.readAsDataURL(f)
    リーダー.onload = (e) => {
      定数img = document.createElement('img')
      img.src = e.target.result
      画像の幅 = 224
      画像の高さ = 224
      img.onload = () => { reslove(img) }
    }
  })
}

エクスポート関数img2x(imgEl) {
  tf.tidy(() => { を返す
    tf.browser.fromPixels(imgEl) を返します
        .toFloat().sub(255/2).div(255/2)
        .reshape([1, 224, 224, 3])
  })
}

プロジェクト コードを実行する前に、train ディレクトリで node index.js を実行して、認識システムが使用する model.json を生成する必要があります。その後、ルート ディレクトリで hs outputDir --cors を実行して、生成された model.json を http 環境で実行する必要があります。その後でのみ npm start を実行できます。そうしないと、プロジェクトでエラーが報告されます。

主なコードは上記です。著者も以前にそう言っていました。これについては何も知らないので、コードを説明することはできません。興味があれば、ぜひ自分で調べてみてください。コードアドレスが提供されます。

gitee.com/suiboyu/gar…

要約する

React を使って画像認識アプリを実装する方法についての記事はこれで終わりです。React 画像認識アプリに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • React でページ上のコードをハイライト表示するために highlight.js を使用する方法
  • Reactの再レンダリング問題を解決する

<<:  相対幅と絶対幅が競合する場合の HTML+CSS div ソリューション

>>:  LinuxとGNUシステムの関係の詳細な説明

推薦する

HTML 選択ボックスのプレースホルダーの作成に関する問題

テキスト入力でプレースホルダーを使用していますが、問題なく動作します。しかし、選択ボックスにはプレー...

ソフトウェア テスト - MySQL (VI: データベース関数)

1.MySQL関数1. 数学関数PI() # 円周率 (pi) の値を返します。デフォルトの小数点...

怖いハロウィーン Linux コマンド

ハロウィーンではありませんが、Linux の不気味な側面に注目する価値はあります。幽霊、魔女、ゾンビ...

mysqlサーバーは--skip-grant-tablesオプションで実行されています

MySQLサーバーは--skip-grant-tablesオプションで実行されているため、このステー...

Docker JVM メモリ使用量の表示

1. Docker コンテナのホスト マシンに入り、指定されたイメージを実行しているコンテナ ID ...

Linux でのマルチスレッドおよびマルチプロセス クラッシュのシミュレーションに関する簡単な説明

結論:マルチスレッド環境では、スレッドの 1 つがクラッシュすると、他のスレッド (プロセス全体) ...

Webフロントエンド開発経験の概要

XMLファイルは、可能な限りutf-8でエンコードする必要があります。gb2312には、?など、保存...

div+css3 を使用して背景グラデーション ボタンを実装するためのサンプル コード

フロントエンド ページの需要が増加し続けるにつれて、一部のシーンではグラデーションの背景要素が必要に...

ラベルとスパンの幅設定が無効である問題の解決

デフォルトでは、ラベルとスパンの幅の設定は無効です。一般的に、表示属性は必須ですコードをコピーコード...

ネイティブJSで様々なモーションの複合モーションを実現

この記事では、ネイティブ JS で実装された複合モーションを紹介します。複合モーションとは、異なる属...

Nodeはリクエスト追跡にasync_hooksモジュールを使用します

async_hooks モジュールは、Node.js バージョン 8.0.0 に正式に追加された実験...

JSはjQueryのappend関数を実装します

目次コードを見せてください効果をテストする効果追伸別のアプローチコードを見せてください HTMLEl...

MySQL マスタースレーブレプリケーションの実装手順

目次mysql マスタースレーブレプリケーションMySQL マスタースレーブレプリケーション方式My...

Nginx で Brotli 圧縮アルゴリズムを有効にする方法の例

Brotli は、Zopfli よりも 20 ~ 26% 高い圧縮率を実現できる新しいデータ形式です...

実行中の時計を実装するための純粋な CSS3 コード

操作効果コードの実装html <div id="ウォッチ"> <...