0、准备一个文件夹,文件夹要包含两个子文件夹
1)JPEGImages 尺寸一样的小猪佩奇图片
2)Annotations 这里是放置pascal-voc格式的xml文件的,该文件在第3步时会生成。
1、打开精灵标注,新建一个目标检测项目
由于这个示例只识别小猪佩奇,因此分类值只写 Peppa Pig。
2、逐个进行框选标注
要记住每次标注后,都要点击下面的蓝色对勾按钮,否则它不保存的(这里是坑)。
3、都标注完成后导出pascal-voc格式
4、在paddlex GUI中新建目标检测数据集
5、导入文件夹并切分数据集
6、在paddlex GUI中,新建一个目标检测项目。
7、输入参数启动训练
8、等训练完成后,点击模型评估
9、启动测试
测试是可以找到的
10、发布模型
也可以移动端发布
11、新建一个python脚本载入模型,进行测试
import paddlex as pdx
import cv2
model = pdx.deploy.Predictor('inference_model', use_gpu=False)
im = cv2.imread('test.jpg')
im = im.astype('float32')
result = model.predict(im)
print(result)
12、使用python开发一个web服务。
# coding: utf-8
import os
import uuid
import hashlib
from time import time
from flask import Flask, flash, request, redirect, url_for, render_template, jsonify, json
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename
from flask_cors import CORS
from paddlex.cls import transforms
import paddlex
import cv2
import warnings
train_transforms = transforms.Compose([
transforms.RandomCrop(crop_size=224),
transforms.Normalize()
])
model = paddlex.load_model('./models/peppa/inference_model')#目标检测模型
UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
app = Flask(import_name=__name__,
static_url_path='/', # 配置静态文件的访问 url 前缀
static_folder='public', # 配置静态文件的文件夹
template_folder='templates') # 配置模板文件的文件夹
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
api = Api(app)
CORS(app)
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/')
def html():
return render_template('index.html')
class Upload(Resource):
def post(self):
if 'file' not in request.files:
return jsonify(err=True, msg='No file part')
file = request.files['file']
print(file)
if file.filename == '':
return jsonify(err=True, msg='No selected file')
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
arr = filename.split('.')
extname = arr[len(arr)-1]
# pdf = file.read()
# file_md5 = hashlib.md5(file.read()).hexdigest()
id = str(uuid.uuid4())
newfilename = "{}.{}".format(id, extname)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], newfilename)
file.save(filepath)
im = cv2.imread(filepath)
result = model.predict(im)
arr = []
for item in result:
if item["score"]>0.1:
position = item["bbox"]
x = position[0]
y = position[1]
w = position[2]
h = position[3]
item["position"] = dict()
item["position"]["x"] = x
item["position"]["y"] = y
item["position"]["w"] = w
item["position"]["h"] = h
item["confidence"]=item["score"]
item["text"]=item["category"]
item.pop("bbox")
item.pop("score")
item.pop("category")
arr.append(item)
# im.close()
return jsonify(arr)
return jsonify(err=True, msg='file type Not Allowed')
api.add_resource(Upload, '/api/upload')
app.run(port=8080, debug=True, host='0.0.0.0')
13、使用React开发一个web界面,实现拖拽并生成框框。
import React, { useState } from "react";
import Dropzone from "react-dropzone";
import ReactLoading from "react-loading";
import axios, { AxiosRequestConfig, AxiosInstance } from "axios";
import Snackbar from "@material-ui/core/Snackbar";
import Alert from "@material-ui/lab/Alert"
import "./App.css";
import { stat } from "fs";
enum MsgType {
error = "error",
warning = "warning",
info = "info",
success = "success"
}
interface Imsg {
type: MsgType,
text: string
}
interface IRect {
text: string;
confidence: number;
position: {
x: number;
y: number;
w: number;
h: number;
};
}
enum Status {
ready,
uploading,
res_empty,
res_ok,
res_err,
img_err,
}
const Text = ({
text,
x,
y,
w,
h,
}: {
text: string;
x: number;
y: number;
w: number;
h: number;
}) => {
return (
<div
className="rect-text"
style={{
left: x + "px",
top: y - 20 + "px",
width: w + "px",
height: "20px",
}}
>
{text}
</div>
);
};
const Rect = ({
text,
x,
y,
w,
h,
}: {
text: string;
x: number;
y: number;
w: number;
h: number;
}) => {
return (
<div
className="rect"
style={{
left: x + "px",
top: y + "px",
width: w + "px",
height: h + "px",
}}
></div>
);
};
function App() {
const [src, setSrc] = useState<string>("");
const [msg, setMsg] = useState<Imsg>({
type: MsgType.info,
text: ""
});
const [status, setStatus] = useState<Status>(Status.ready);
const [rects, setRects] = useState<IRect[]>([]);
const onDrop = async (acceptedFiles: File[]) => {
setRects([]);
acceptedFiles.forEach(async (file: File) => {
console.log(file);
// reader.readAsArrayBuffer(file)
if (file.type.includes("image")) {
setStatus(Status.uploading);
const reader = new FileReader();
reader.onload = async () => {
const base64Str: string = reader.result + "";
setSrc(base64Str);
};
reader.readAsDataURL(file);
const data = new FormData();
data.append("file", file);
console.log(data);
try {
const res = await axios.post("/api/upload", data);
const arr: IRect[] = res.data;
console.log(arr);
if (arr.length > 0) {
setStatus(Status.res_ok);
setMsg({
type: MsgType.success,
text: `找到 ${arr.length} 个`
})
} else {
setStatus(Status.res_empty);
setMsg({
type: MsgType.error,
text: "not found"
})
}
setRects(arr);
setStatus(Status.res_empty)
} catch (e) {
setStatus(Status.res_err)
setMsg({
type: MsgType.error,
text: (e as Error).message
})
}
} else {
setStatus(Status.img_err)
setMsg({
type: MsgType.error,
text: "image type error"
})
}
});
};
return (
<div className="App">
<header className="App-header">
<Dropzone onDrop={onDrop}>
{({ getRootProps, getInputProps }: any) => (
<section>
<div {...getRootProps()}>
{/* <input {...getInputProps()} /> */}
<div className="img-pan">
<img src={src} />
{rects.map((rect: IRect, index: number) => {
return (
<div key={index}>
<Rect
x={rect.position.x}
y={rect.position.y}
w={rect.position.w}
h={rect.position.h}
text={rect.text}
/>
<Text
x={rect.position.x}
y={rect.position.y}
w={rect.position.w}
h={rect.position.h}
text={rect.text}
/>
</div>
);
})}
<p></p>
</div>
{src === "" && <p>Drag 'n' drop some files here</p>}
</div>
</section>
)}
</Dropzone>
{status === Status.uploading && (
<div className="loading">
<ReactLoading type={"bars"} color={"#fff"} />
</div>
)}
<Snackbar open={status === Status.res_err || status === Status.img_err || status === Status.res_empty} autoHideDuration={6000} >
<Alert severity={msg.type} onClose={() => {
setStatus(Status.ready)
}}>
{msg.text}
</Alert>
</Snackbar>
</header>
</div>
);
}
export default App;
相应css:
.img-pan{
position: relative;
}
div[tabindex="0"]{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
}
.loading{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100;
right: 0;
bottom: 0;
background: #000;
z-index: 999;
opacity: 0.8;
display: flex;
align-items: center;
justify-content: center;
}
最后效果:
git地址: