React 實現圖片識別App
(首先宣告,我並不是這方面的學習者,我也不懂這個什麼神經網路的學習,寫這一篇和做這一個demo 完全是因為覺得好玩。所以裡面的程式碼,除了 react 我懂,其他的,我就。。。)
先把效果圖給大家放上來
個人覺得效果還行。識別不太準確是因為這個 app學習圖片的時間太短(電腦太卡)。
(筆者是 window10)
安裝執行環境:
npm install --global windows-build-tools
(這個時間很漫長。。。)npm install @tensorflow/tfjs-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, 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
安裝一些執行專案需要的外掛
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' }}
>識別可能不準確</span>
<Button
type="primary"
size="large"
style={{width: '100%'}}
onClick={() => this.upload.click()}
>
選擇圖片識別
</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}}>識別結果: </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, 224, 224, 3])
})
}
執行專案程式碼之前,我們需要先在 train 目錄下執行,node index.js,生成 model.json 以供識別系統使用。之後需要在根目錄下執行 hs outputDir --cors, 使得生成的 model.json 執行在 http 環境下,之後才可以執行 npm start ,不然專案是會報錯的。
主要的程式碼就是上面這些。前面筆者也說了。自己對這方面完全不懂,所以也無法解說其中的程式碼。各位感興趣就自己研究一下。程式碼地址奉上。
https://gitee.com/suiboyu/garbage-classify
相關文章
- 圖片文字識別怎麼實現
- AI大模型實現圖片OCR識別AI大模型
- Java 實現OCR掃描/識別圖片文字Java
- 實現圖片文字識別的方法有哪些
- 哪個圖片識別文字app能快速轉換圖片成文字?APP
- React如何實現圖片懶載入React
- Tesseract 圖片識別
- 圖片文字識別工具怎樣進行批次識別圖片?
- 基於React Hook實現圖片的裁剪ReactHook
- paddleocr圖片文字識別
- 利用百度AI OCR圖片識別,Java實現PDF中的圖片轉換成文字AIJava
- 用canvas實現一個自動識別兩張圖片差異(圖片找不同)的功能Canvas
- 如何免費識別圖片文字?圖片文字識別軟體怎麼用
- app直播原始碼,js實現上傳圖片型別+大小+尺寸驗證APP原始碼JS型別
- WebView實現長按儲存圖片 長按識別二維碼WebView
- 分享:識別圖片文字方法
- Tesseract OCR 圖片文字識別
- 【326】PIL 截圖及圖片識別
- 鴻蒙專案實戰(六):識別本地圖片鴻蒙地圖
- Java也能做OCR!SpringBoot 整合 Tess4J 實現圖片文字識別JavaSpring Boot
- PDF 轉換圖片然後識別圖片內容
- 如何將圖片識別成文字?
- 手動輸入圖片識別
- 圖片裁剪-文字識別-文字新增
- 圖片識別文字具體操作
- 圖片識別文字,分享給你!
- 採用React + Fabric + ImageMagick 實現大圖片DIY定製React
- 5步實現深度學習OpenCV物件檢測:Faster-RCNN圖片識別深度學習OpenCV物件ASTCNN
- 快速入門開發實現訂單類圖片識別結果抽象解析抽象
- Python技巧-只用一行程式碼輕鬆實現圖片文字識別Python行程
- Android實現二值點陣圖識別Android
- opencv圖片處理與OCR識別OpenCV
- Python 做圖片清晰度識別Python
- 使用Tesseract進行圖片文字識別
- 圖片識別軟體選哪個?
- 直播app開發搭建,純javascript實現圖片放大鏡效果APPJavaScript
- java指紋識別以及谷歌圖片識別技術原始碼Java谷歌原始碼
- react全家桶實現招聘app-路由實現(二)ReactAPP路由