2025-06-06 15:17:30 +08:00

426 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
});
}
}
});