426 lines
9.9 KiB
JavaScript
426 lines
9.9 KiB
JavaScript
![]() |
const utils = require("../../utils/utils");
|
|||
|
const CameraDevice = require("../utils/camera-device");
|
|||
|
const module = require("../index.js");
|
|||
|
|
|||
|
const AiSports = requirePlugin("aiSport");
|
|||
|
const PoseGraphs = AiSports.PoseGraphs;
|
|||
|
const humanDetection = AiSports.humanDetection;
|
|||
|
|
|||
|
Component({
|
|||
|
options: {
|
|||
|
styleIsolation: 'shared'
|
|||
|
},
|
|||
|
properties: {
|
|||
|
/**
|
|||
|
* 是否启用全屏模式,true-是,全屏模式;false-否,自适应模式;
|
|||
|
* 【全屏模式】: 检测组件大小为100vw 100vh;同时建议开启页面pageOrientation为自适应,navigationStyle为custom
|
|||
|
*/
|
|||
|
fullScreen: {
|
|||
|
type: Boolean,
|
|||
|
value: false
|
|||
|
},
|
|||
|
topp: {
|
|||
|
type: Number,
|
|||
|
value: 100
|
|||
|
},
|
|||
|
/**
|
|||
|
* 是否开启骨骼图绘制,
|
|||
|
* 此值最好不要动态改变
|
|||
|
*/
|
|||
|
poseDrawEnabled: {
|
|||
|
type: Boolean,
|
|||
|
value: false
|
|||
|
},
|
|||
|
/**
|
|||
|
* 识别引擎选择
|
|||
|
* 1-ve1识别引擎
|
|||
|
* 2-ve2识别引擎
|
|||
|
*/
|
|||
|
ve: {
|
|||
|
type: Number,
|
|||
|
value: null
|
|||
|
},
|
|||
|
/**
|
|||
|
* 是否启用VE1增强模式
|
|||
|
* true-是; false-否 null-自动
|
|||
|
*/
|
|||
|
enhanced: {
|
|||
|
type: Boolean,
|
|||
|
value: null
|
|||
|
},
|
|||
|
/**
|
|||
|
* 是否显示扩展信息
|
|||
|
*/
|
|||
|
extendInfo: {
|
|||
|
type: Boolean,
|
|||
|
value: true
|
|||
|
}
|
|||
|
},
|
|||
|
data: {
|
|||
|
zoom: 1,
|
|||
|
deviceKey: "back",
|
|||
|
previewWidth: 480,
|
|||
|
previewHeight: 640,
|
|||
|
status: 'unknown',
|
|||
|
fps: 0,
|
|||
|
poseFps: 0,
|
|||
|
isHumanBody: false,
|
|||
|
previewRate: 1,
|
|||
|
previewOffsetX: 0,
|
|||
|
previewOffsetY: 0,
|
|||
|
frameWidth: 480,
|
|||
|
frameHeight: 640,
|
|||
|
videoStyle: 'width:100vw;height:100vh'
|
|||
|
},
|
|||
|
lifetimes: {
|
|||
|
attached() {
|
|||
|
this.autoFitPreview(480, 640);
|
|||
|
this.initializeHumanDetection();
|
|||
|
},
|
|||
|
detached() {
|
|||
|
if (!this.camera)
|
|||
|
return;
|
|||
|
|
|||
|
this.camera.stop();
|
|||
|
this.camera.dispose();
|
|||
|
}
|
|||
|
},
|
|||
|
observers: {
|
|||
|
'fullScreen, previewWidth, previewHeight': function () {
|
|||
|
this.updateVideoStyle();
|
|||
|
},
|
|||
|
'poseDrawEnabled': function (val) {
|
|||
|
this.initCanvas();
|
|||
|
},
|
|||
|
've, enhanced': function (ve, enhanced) {
|
|||
|
this.stopCapture();
|
|||
|
this.initializeHumanDetection();
|
|||
|
}
|
|||
|
},
|
|||
|
methods: {
|
|||
|
updateVideoStyle() {
|
|||
|
const style = this.data.fullScreen ?
|
|||
|
'width:100vw;height:100vh' :
|
|||
|
`width:${this.data.previewWidth}px;height:${this.data.previewHeight}px;`;
|
|||
|
this.setData({
|
|||
|
videoStyle: style
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
openCameraAuth() {
|
|||
|
const that = this;
|
|||
|
wx.showModal({
|
|||
|
content: '您未打开摄像头权限,无法使用本功能。',
|
|||
|
confirmText: '去打开',
|
|||
|
success(res) {
|
|||
|
if (!res.confirm) {
|
|||
|
wx.showToast({
|
|||
|
icon: 'none',
|
|||
|
title: '无法使用本功能。'
|
|||
|
});
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
wx.openSetting({
|
|||
|
success(res) {
|
|||
|
if (res.authSetting['scope.camera'] != true) {
|
|||
|
that.openCameraAuth();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
wx.showToast({
|
|||
|
icon: 'none',
|
|||
|
title: '摄像头权限已打开,稍后请重新进入本页。'
|
|||
|
});
|
|||
|
setTimeout(() => wx.navigateBack(), 1500);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
initializeHumanDetection() {
|
|||
|
wx.showLoading({
|
|||
|
title: '加载中...'
|
|||
|
});
|
|||
|
|
|||
|
const ehd = module.judgeEnhanced(this.data.enhanced);
|
|||
|
const that = this;
|
|||
|
humanDetection.initialize({
|
|||
|
ve: that.data.ve,
|
|||
|
enhanced: ehd,
|
|||
|
callback: (err) => {
|
|||
|
wx.hideLoading();
|
|||
|
if (!err)
|
|||
|
return;
|
|||
|
|
|||
|
wx.showModal({
|
|||
|
content: `初始化失败,详细信息:${err.message}`,
|
|||
|
showCancel: false
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
fullScreenFit(width, height) {
|
|||
|
const winfo = wx.getWindowInfo();
|
|||
|
|
|||
|
this.setData({
|
|||
|
previewWidth: winfo.windowWidth,
|
|||
|
previewHeight: winfo.windowHeight
|
|||
|
});
|
|||
|
|
|||
|
if (winfo.windowHeight > winfo.windowWidth) {
|
|||
|
// 竖屏模式
|
|||
|
console.log('竖屏');
|
|||
|
const previewRate = winfo.windowHeight / height;
|
|||
|
const previewOffsetX = (winfo.windowWidth - width * previewRate) / 2;
|
|||
|
|
|||
|
this.setData({
|
|||
|
previewRate: previewRate,
|
|||
|
previewOffsetX: previewOffsetX,
|
|||
|
previewOffsetY: 0
|
|||
|
});
|
|||
|
} else {
|
|||
|
// 横屏模式
|
|||
|
console.log('横屏');
|
|||
|
const previewRate = winfo.windowWidth / width;
|
|||
|
const previewOffsetY = (winfo.windowHeight - height * previewRate) / 2;
|
|||
|
|
|||
|
this.setData({
|
|||
|
previewRate: previewRate,
|
|||
|
previewOffsetX: 0,
|
|||
|
previewOffsetY: previewOffsetY
|
|||
|
});
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
autoFitPreview(width, height) {
|
|||
|
this.setData({
|
|||
|
frameWidth: width,
|
|||
|
frameHeight: height
|
|||
|
});
|
|||
|
|
|||
|
if (this.data.fullScreen) {
|
|||
|
this.fullScreenFit(width, height);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
const winfo = wx.getWindowInfo();
|
|||
|
const rate = winfo.windowWidth / width;
|
|||
|
this.setData({
|
|||
|
previewWidth: width * rate,
|
|||
|
previewHeight: height * rate,
|
|||
|
previewRate: rate,
|
|||
|
previewOffsetX: 0,
|
|||
|
previewOffsetY: 0
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
initCanvas() {
|
|||
|
if (!this.data.poseDrawEnabled)
|
|||
|
return;
|
|||
|
|
|||
|
const that = this;
|
|||
|
const query = wx.createSelectorQuery().in(this);
|
|||
|
|
|||
|
query.select('#graphics')
|
|||
|
.fields({
|
|||
|
node: true,
|
|||
|
size: true
|
|||
|
})
|
|||
|
.exec(res => {
|
|||
|
if (utils.isEmptyArray(res))
|
|||
|
return;
|
|||
|
|
|||
|
const canvas = res[0].node;
|
|||
|
const dpr = wx.getWindowInfo().pixelRatio;
|
|||
|
//res[0].width * dpr; //由于iPhone改变canvas样式后渲染有滞后性,取出的宽、高可能与样式不一致
|
|||
|
canvas.width = that.data.previewWidth * dpr;
|
|||
|
//res[0].height * dpr;
|
|||
|
canvas.height = that.data.previewHeight * dpr;
|
|||
|
|
|||
|
const ctx = canvas.getContext('2d');
|
|||
|
ctx.scale(dpr, dpr);
|
|||
|
|
|||
|
that.canvas = canvas;
|
|||
|
that.ctx = ctx;
|
|||
|
|
|||
|
that.poseGraphs = new PoseGraphs(
|
|||
|
ctx,
|
|||
|
canvas.width,
|
|||
|
canvas.height,
|
|||
|
that.data.previewRate
|
|||
|
);
|
|||
|
|
|||
|
that.poseGraphs.offsetX = that.data.previewOffsetX;
|
|||
|
that.poseGraphs.offsetY = that.data.previewOffsetY;
|
|||
|
|
|||
|
that.poseGraphs.lineColor = "#FF8E148C";
|
|||
|
that.poseGraphs.pointColor = '#00a500';
|
|||
|
that.poseGraphs.leftColor = '#007fd3';
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
async detection(frame) {
|
|||
|
|
|||
|
let ts = (new Date()).getTime();
|
|||
|
const human = await humanDetection.detectionAsync(frame);
|
|||
|
ts = (new Date()).getTime() - ts;
|
|||
|
|
|||
|
this.setData({
|
|||
|
poseFps: Math.floor(1000 / ts)
|
|||
|
});
|
|||
|
|
|||
|
if (human) {
|
|||
|
this.setData({
|
|||
|
isHumanBody: true
|
|||
|
});
|
|||
|
this.triggerEvent('on-human-detecting', {
|
|||
|
human: human,
|
|||
|
frame: frame
|
|||
|
});
|
|||
|
} else {
|
|||
|
this.setData({
|
|||
|
isHumanBody: false
|
|||
|
});
|
|||
|
this.triggerEvent('on-human-detecting', {
|
|||
|
human: null
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
if (!this.data.poseDrawEnabled)
|
|||
|
return;
|
|||
|
|
|||
|
//无结果
|
|||
|
if (!human)
|
|||
|
this.poseGraphs.clear();
|
|||
|
else
|
|||
|
this.poseGraphs.drawing(human.keypoints);
|
|||
|
},
|
|||
|
|
|||
|
initVideo() {
|
|||
|
if (this.camera)
|
|||
|
return;
|
|||
|
|
|||
|
const that = this;
|
|||
|
this.camera = new CameraDevice();
|
|||
|
this.camera.onFrame = frame => {
|
|||
|
that.setData({
|
|||
|
fps: that.camera.fps
|
|||
|
});
|
|||
|
|
|||
|
//重新适应
|
|||
|
if (frame.width != that.data.frameWidth || frame.height != that.data.frameHeight) {
|
|||
|
that.camera.stop(true);
|
|||
|
that.autoFitPreview(frame.width, frame.height);
|
|||
|
that.initCanvas();
|
|||
|
that.camera.start();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
that.detection(frame);
|
|||
|
} catch (err) {
|
|||
|
console.error(err);
|
|||
|
}
|
|||
|
};
|
|||
|
this.camera.onStatusChange = (val, text) => {
|
|||
|
if (val === -1) {
|
|||
|
wx.hideLoading();
|
|||
|
that.setData({
|
|||
|
isHumanBody: false
|
|||
|
});
|
|||
|
}
|
|||
|
that.setData({
|
|||
|
status: text
|
|||
|
})
|
|||
|
that.triggerEvent('on-camera-change', {
|
|||
|
val: val,
|
|||
|
text: text
|
|||
|
})
|
|||
|
};
|
|||
|
},
|
|||
|
startCapture() {
|
|||
|
this.initVideo();
|
|||
|
this.camera.start();
|
|||
|
wx.setKeepScreenOn({
|
|||
|
keepScreenOn: true
|
|||
|
});
|
|||
|
},
|
|||
|
delayStart() {
|
|||
|
if (this.data.camera) {
|
|||
|
const status = this.camera.getStatus();
|
|||
|
if (status != "unknown" && status != "stopped")
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//延时5s启动
|
|||
|
const that = this;
|
|||
|
setTimeout(() => that.startCapture(), 5000);
|
|||
|
wx.showToast({
|
|||
|
title: '5S后开始',
|
|||
|
duration: 4900
|
|||
|
});
|
|||
|
},
|
|||
|
stopCapture() {
|
|||
|
if (!this.camera || this.camera.getStatus() != 'running')
|
|||
|
return;
|
|||
|
|
|||
|
this.setData({
|
|||
|
isHumanBody: false
|
|||
|
});
|
|||
|
|
|||
|
if (this.camera)
|
|||
|
this.camera.stop();
|
|||
|
|
|||
|
wx.setKeepScreenOn({
|
|||
|
keepScreenOn: false
|
|||
|
});
|
|||
|
|
|||
|
this.setData({
|
|||
|
fps: this.camera.fps,
|
|||
|
status: this.camera.getStatus()
|
|||
|
});
|
|||
|
|
|||
|
wx.showLoading({
|
|||
|
title: '正在停止...'
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
toggleCamera() {
|
|||
|
this.setData({
|
|||
|
deviceKey: this.data.deviceKey === 'back' ? 'front' : 'back'
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
onCameraReady(e) {
|
|||
|
console.log('相机初始化完成。');
|
|||
|
this.setData({
|
|||
|
zoom: e.detail.maxZoom
|
|||
|
});
|
|||
|
this.initCanvas();
|
|||
|
if (!this.camera)
|
|||
|
return;
|
|||
|
|
|||
|
console.log('重新初始化CameraDevice');
|
|||
|
const status = this.camera.getStatus();
|
|||
|
this.camera.dispose();
|
|||
|
this.camera = null;
|
|||
|
this.initVideo();
|
|||
|
if (status === 'running')
|
|||
|
this.camera.start();
|
|||
|
},
|
|||
|
onCameraError(e) {
|
|||
|
const err = e.detail.errMsg;
|
|||
|
if (err.includes('auth')) {
|
|||
|
this.openCameraAuth();
|
|||
|
return;
|
|||
|
}
|
|||
|
wx.showModal({
|
|||
|
content: err,
|
|||
|
showCancel: false
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
});
|