如何利用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í)間很漫長(zhǎng)。。。)npm install @tensorflow/tfjs-node
(這個(gè)時(shí)間很漫長(zhǎng)。。。)
項(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 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: '是指在日常生活中或者為日常生活提供服務(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: '是指生活垃圾中對(duì)人體健康或者自然環(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ò)的。
主要的代碼就是上面這些。前面筆者也說了。自己對(duì)這方面完全不懂,所以也無法解說其中的代碼。各位感興趣就自己研究一下。代碼地址奉上。
gitee.com/suiboyu/gar…
總結(jié)
到此這篇關(guān)于如何利用React實(shí)現(xiàn)圖片識(shí)別App的文章就介紹到這了,更多相關(guān)React圖片識(shí)別App內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(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ò)請(qǐng)求的方法
axios是一個(gè)基于Promise的Http網(wǎng)絡(luò)庫,可運(yùn)行在瀏覽器端和Node.js中,Vue應(yīng)用的網(wǎng)絡(luò)請(qǐng)求基本都是使用它完成的。這篇文章主要介紹了React Native使用axios進(jìn)行網(wǎng)絡(luò)請(qǐng)求,需要的朋友可以參考下2021-08-08使用React?SSR寫Demo一學(xué)就會(huì)
這篇文章主要為大家介紹了使用React?SSR寫Demo實(shí)現(xiàn)教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06詳解如何使用React Hooks請(qǐng)求數(shù)據(jù)并渲染
這篇文章主要介紹了如何使用React Hooks請(qǐng)求數(shù)據(jù)并渲染,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10react使用axios實(shí)現(xiàn)上傳下載功能
這篇文章主要為大家詳細(xì)介紹了react使用axios實(shí)現(xiàn)上傳下載功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08