如何利用React實(shí)現(xiàn)圖片識(shí)別App
先把效果圖給大家放上來



個(gè)人覺得效果還行。識(shí)別不太準(zhǔn)確是因?yàn)檫@個(gè) app學(xué)習(xí)圖片的時(shí)間太短(電腦太卡)。
(筆者是 window10) 安裝運(yùn)行環(huán)境:
npm install --global windows-build-tools(這個(gè)時(shí)間很漫長。。。)npm install @tensorflow/tfjs-node(這個(gè)時(shí)間很漫長。。。)
項(xiàng)目目錄如下 
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 () => {
// 加載數(shù)據(jù)
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'
}))
// 訓(xùn)練模型
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(處理數(shù)據(jù))
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, 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
}
}
module.exports = getData安裝一些運(yùn)行項(xiàng)目需要的插件 
app 文件夾
import React, { PureComponent } from 'react'
import { Button, Progress, Spin, Empty } from 'antd'
import 'antd/dist/antd.css'
import * as tf from '@tensorflow/tfjs'
import { file2img, img2x } from './utils'
import intro 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) => ({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' }}
>識(shí)別可能不準(zhǔn)確</span>
<Button
type="primary"
size="large"
style={{width: '100%'}}
onClick={() => this.upload.click()}
>
選擇圖片識(shí)別
</Button>
<input
type="file"
onChange={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}}>識(shí)別結(jié)果: </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 Appindex.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: '是指在日常生活中或者為日常生活提供服務(wù)的活動(dòng)中產(chǎn)生的,已經(jīng)失去原有全部或者部分使用價(jià)值,回收后經(jīng)過再加工可以成為生產(chǎn)原料或者經(jīng)過整理可以再利用的物品,包括廢紙類、塑料類、玻璃類、金屬類、織物類等。'
},
'有害垃圾': {
icon: 'https://lajifenleiapp.com/static/svg/2v_B43953.svg',
color: '#b43953',
intro: '是指生活垃圾中對人體健康或者自然環(huán)境造成直接或者潛在危害的物質(zhì),包括廢充電電池、廢扣式電池、廢燈管、棄置藥品、廢殺蟲劑(容器)、廢油漆(容器)、廢日用化學(xué)品、廢水銀產(chǎn)品、廢舊電器以及電子產(chǎn)品等。'
},
'廚余垃圾': {
icon: 'https://lajifenleiapp.com/static/svg/3v_48925B.svg',
color: '#48925b',
intro: '是指居民日常生活中產(chǎn)生的有機(jī)易腐垃圾,包括菜葉、剩菜、剩飯、果皮、蛋殼、茶渣、骨頭等。'
},
'其他垃圾': {
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, 224, 224, 3])
})
}運(yùn)行項(xiàng)目代碼之前,我們需要先在 train 目錄下運(yùn)行,node index.js,生成 model.json 以供識(shí)別系統(tǒng)使用。之后需要在根目錄下運(yùn)行 hs outputDir --cors, 使得生成的 model.json 運(yùn)行在 http 環(huán)境下,之后才可以運(yùn)行 npm start ,不然項(xiàng)目是會(huì)報(bào)錯(cuò)的。
主要的代碼就是上面這些。前面筆者也說了。自己對這方面完全不懂,所以也無法解說其中的代碼。各位感興趣就自己研究一下。代碼地址奉上。
gitee.com/suiboyu/gar…
總結(jié)
到此這篇關(guān)于如何利用React實(shí)現(xiàn)圖片識(shí)別App的文章就介紹到這了,更多相關(guān)React圖片識(shí)別App內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React路由跳轉(zhuǎn)的實(shí)現(xiàn)示例
在React中,可以使用多種方法進(jìn)行路由跳轉(zhuǎn),本文主要介紹了React路由跳轉(zhuǎn)的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
關(guān)于React Native使用axios進(jìn)行網(wǎng)絡(luò)請求的方法
axios是一個(gè)基于Promise的Http網(wǎng)絡(luò)庫,可運(yùn)行在瀏覽器端和Node.js中,Vue應(yīng)用的網(wǎng)絡(luò)請求基本都是使用它完成的。這篇文章主要介紹了React Native使用axios進(jìn)行網(wǎng)絡(luò)請求,需要的朋友可以參考下2021-08-08
使用React?SSR寫Demo一學(xué)就會(huì)
這篇文章主要為大家介紹了使用React?SSR寫Demo實(shí)現(xiàn)教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
詳解如何使用React Hooks請求數(shù)據(jù)并渲染
這篇文章主要介紹了如何使用React Hooks請求數(shù)據(jù)并渲染,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
react使用axios實(shí)現(xiàn)上傳下載功能
這篇文章主要為大家詳細(xì)介紹了react使用axios實(shí)現(xiàn)上傳下載功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08

