1. 程式人生 > 程式設計 >如何利用React實現圖片識別App

如何利用React實現圖片識別App

先把效果圖給大家放上來

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

個人覺得效果還行。識別不太準確是因為這個 app學習圖片的時間太短(電腦太卡)。

(筆者是 window10) 安裝執行環境:

  • npm install --global windows-build-tools(這個時間很漫長。。。)
  • npm install @tensorflow/tf-node(這個時間很漫長。。。)

專案目錄如下 在這裡插入圖片描述

train資料夾 index.js(入口檔案)

const tf = require('@tensorflow/tfjs-node')
const getData = require('./data')

const TRAIN_DIR = '../垃圾分類/train'
const OUTPUT_DIR = '../outputDir'
const MOBILENET_URL = 'http://ai-sample.oss-cn-hangzhou.aliyuncs.com/pipcook/models/mobilenet/web_model/model.json'

const main = async () => {
  // 載入資料
  const { ds,classes} = await getData(TRAIN_DIR,OUTPUT_DIR)
  // 定義模型
  const mobilenet = await tf.loadLayersModel(MOBILENET_URL)
  mobilenet.summary()
  // console.log(mobilenet.layers.map((l,i) => [l.name,i]))
  const model = tf.sequential()
  for (let i = 0; i <= 86; i += 1) {
    const layer = mobilenet.layers[i]
    layer.trainable = false
    model.add(layer)
  }
  model.add(tf.layers.flatten())
  model.add(tf.layers.dense({
    units: 10,activation: 'relu'
  }))
  model.add(tf.layers.dense({
    units: classes.length,activation: 'softmax'
  }))
  // 訓練模型
  model.compile({
    loss: 'sparseCategoricalCrossentropy',optimizer: tf.train.adam(),metrics: ['acc']
  })
  await model.fitDataset(ds,{ epochs: 20 })
  await model.save(`file://${process.cwd()}/${OUTPUT_DIR}`)
}
main()

data.js(處理資料)

const fs = require('fs')
const tf = require('@tensorflow/tfjs-node')

const img2x = (imgPath) => {
  const buffer = fs.readFileSync(imgPath)
  return tf.tidy(() => {
    const imgTs = tf.node.decodeImage(new Uint8Array(buffer))
    const imgTsResized = tf.image.resizeBilinear(imgTs,[224,224])
    return imgTsResized.toFloat().sub(255/2).div(255/2).reshape([1,224,3])
  })
}

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

  const data = []
  classes.forEach((dir,dirIndex) => {
    fs.readdirSync(`${trainDir}/${dir}`)
      .filter(n => n.match(/jpg$/))
      .slice(0,10)
      .forEach(filename => {
        console.log('讀取',dir,filename)
        const imgPath = `${trainDir}/${dir}/${filename}`
        data.push({ imgPath,dirIndex })
      })
  })

  tf.util.shuffle(data)

  const ds = tf.data.generator(function* () {
    const count = data.length
    const batchSize = 32
    for (let start = 0; start < count; start += batchSize) {
      const end = Math.min(start + batchSize,count)
      yield tf.tidy(() => {
        const inputs = []
        const labels = []
        for (let j = start; j < end; j += 1) {
          const { imgPath,dirIndex } = data[j]
          const x = img2x(imgPath)
          inputs.push(x)
          labels.push(dirIndex)
        }
        const xs = tf.concat(inputs)
        const ys = tf.tensor(labels)
        return { xs,ys }
      })
    }
  })

  return {
    ds,classes
  }
}

mo
dule.exports = getData

安裝一些執行專案需要的外掛 在這裡插入圖片描述

app 資料夾

import React,{ PureComponent } from 'react'
import { Button,Progress,Spin,Empty } from 'antd'
import 'antd/dist/antd.'
import * as tf from '@tensorflow/tfjs'
import { file2img,img2x } from './utils'
import introUpIfkHep from './intro'

const DATA_URL = 'http://127.0.0.1:8080/'
class App extends PureComponent {
  state = {}
  async componentDidMount() {
    this.model = await tf.loadLayersModel(DATA_URL + '/model.json')
    // this.model.summary()
    this.CLASSES = await fetch(DATA_URL + '/classes.json').then(res => res.json())
  }
  predict = async (file) => {
    const img = await file2img(file)

    this.setState({
      imgSrc: img.src,isLoading: true
    })
    setTimeout(() => {
      const pred = tf.tidy(() => {
        const x = img2x(img)
        return this.model.predict(x)
      })

      const results = pred.arraySync()[0]
        .map((score,i) =>UpIfkHep
; ({score,label: this.CLASSES[i]})) .sort((a,b) => b.score - a.score) this.setState({ results,isLoading: false }) },0) } renderResult = (item) => { const finalScore = Math.round(item.score * 100) return ( <tr key={item.label}> <td style={{ width: 80,padding: '5px 0' }}>{item.label}</td> <td> <Progress percent={finalScore} status={finalScore === 100 ? 'success' : 'normal'} /> </td> </tr> ) } render() { const { imgSrc,results,isLoading } = this.state const finalItem = results && {...results[0],...intro[results[0].label]} return ( <div style={{padding: 20}}> <span style={{ color: '#cccccc',textAlign: 'center',fontSize: 12,display: 'block' }} >識別可能不準確</span> <Button type="primary" size="large" style={{width: '100%'}} onClick={() => this.upload.click()} > 選擇圖片識別 </Button> <input type="file" UpIfkHeponChange={e => this.predict(e.target.files[0])} ref={el => {this.upload = el}} style={{ display: 'none' }} /> { !results && !imgSrc && <Empty style={{ marginTop: 40 }} /> } {imgSrc && <div style={{ marginTop: 20,textAlign: 'center' }}> <img src={imgSrc} style={{ maxWidth: '100%' }} /> </div>} {finalItem && <div style={{marginTop: 20}}>識別結果: </div>} {finalItem && <div style={{display: 'flex',alignItems: 'flex-start',marginTop: 20}}> <img src={finalItem.icon} width={120} /> <div> <h2 style={{color: finalItem.color}}> {finalItem.label} </h2> <div style={{color: finalItem.color}}> {finalItem.intro} </div> </div> </div>} { isLoading && <Spin size="large" style={{display: 'flex',justifyContent: 'center',alignItems: 'center',marginTop: 40 }} /> } {results && <div style={{ marginTop: 20 }}> <table style={{width: '100%'}}> <tbody> <tr> <td>類別</td> <td>匹配度</td> </tr> {results.map(this.renderResult)} </tbody> </table> </div>} </div> ) } } export default App

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>垃圾分類</title>
    <meta name="viewport" content="width=device-width,inital-scale=1">
  </head>
  <body>
    <div id="app"></div>
    <script src="./index.js"></script>
  </body>
</html>

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

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

intro.js

export default {
  '可回收物': {
    icon: 'https://lajifenleiapp.com/static/svg/1_3F6BA8.svg',color: '#3f6ba8',intro: '是指在日常生活中或者為日常生活提供服務的活動中產生的,已經失去原有全部或者部分使用價值,回收後經過再加工可以成為生產原料或者經過整理可以再利用的物品,包括廢紙類、塑料類、玻璃類、金屬類、織物類等。'
  },'有害垃圾': {
    icon: 'https://lajifenleiapp.com/static/svg/2v_B43953.svg',color: '#b43953',intro: '是指生活垃圾中對人體健康或者自然環境造成直接或者潛在危害的物質,包括廢充電電池、廢扣式電池、廢燈管、棄置藥品、廢殺蟲劑(容器)、廢油漆(容器)、廢日用化學品、廢水銀產品、廢舊電器以及電子產品等。'
  },'廚餘垃圾': {
    icon: 'https://lajifenleiapp.com/static/svg/3v_48925B.svg',color: '#48925b',intro: '是指居民日常生活中產生的有機易腐垃圾,包括菜葉、剩菜、剩飯、果皮、蛋殼、茶渣、骨頭等。'
  },'其他垃圾': {
    icon: 'https://lajifenleiapp.com/static/svg/4_89918B.svg',color: '#89918b',intro: '是指除可回收物、有害垃圾和廚餘垃圾之外的,混雜、汙染、難分類的其他生活垃圾。'
  }
}

utils.js

import * as tf from '@tensorflow/tfjs'

export const file2img = async (f) => {
  return new Promise(reslove => {
    const reader = new FileReader()
    reader.readAsDataURL(f)
    reader.onload = (e) => {
      const img = document.createElement('img')
      img.src = e.target.result
      img.width = 224
      img.height = 224
      img.onload = () => { reslove(img) }
    }
  })
}

export function img2x(imgEl) {
  return tf.tidy(() => {
    return tf.browser.fromPixels(imgEl)
        .toFloat().sub(255/2).div(255/2)
        .reshape([1,3])
  })
}

執行專案程式碼之前,我們需要先在 train 目錄下執行,node indUpIfkHepex.js,生成 model.json 以供識別系統使用。之後需要在根目錄下執行 hs outputDir --cors,使得生成的 model.json 執行在 http 環境下,之後才可以執行 npm start,不然專案是會報錯的。

主要的程式碼就是上面這些。前面筆者也說了。自己對這方面完全不懂,所以也無法解說其中的程式碼。各位感興趣就自己研究一下。程式碼地址奉上。

gitee.com/suiboyu/gar…

總結

到此這篇關於如何利用React實現圖片識別App的文章就介紹到這了,更多相關React圖片識別App內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!