426 lines
9.9 KiB
JavaScript
Raw Normal View History

2025-06-06 15:17:30 +08:00
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
});
}
}
});