蚂蚁金服的 G2 这个库有提供写入文本的功能. 可惜没提供比较好的 示例.想动态更新文本还是要研究一番的. 我就研究了一上午. 终于研究通了, 把重点说一下. 如果要想在界面上做到下面的这种效果.
需要在代码中新建一个View专门渲染文本 , 到时候更新的时候专门更新某个View. 这样可以保证其他的数据不会被更新掉. 并且content 要写成函数的形式. 代码如下.
<script lang="ts"> import Vue from 'vue'; import { Chart } from '@antv/g2'; export default Vue.extend({ data() { return { current_epoch: 100, epochsbili: 0.5, // 进度比例 50% visiblCreatNewTrainTaskModal: false, DataSets: [], // 数据集 LossArray: [], // 损失函数指标数据. ImageFileCount: 0, // 需要训练的样本图片个数 TaskState: '已停止', currentEpochLossData: {}, // 当前的损失函数指标数据 epoch: 0, epochs: 0, // 训练参数 trainParam: { weights: './yolov5/weights/yolov5s.pt', // default='./runs/exp11/weights/best.pt', help='initial weights path') cfg: './yolov5/models/yolov5m.yaml', // help='model.yaml path') data: './yolov5/data/mydataset.yaml', // help='data.yaml path')# hyp: './yolov5/data/hyp.scratch.yaml', // help='hyperparameters path') epochs: 300, AutoSaveEpochs: 1, // 模型保存间隔 自动保存轮数 batch_size: 10, // help='total batch size for all GPUs')# img_size: [640, 640], // help='[train, test] image sizes') rect: '', // help='rectangular training') resume: false, // help='resume most recent training') resume 应该是恢复训练,加上的话是用最近一次的权重开始训练 nosave: true, // help='only save final checkpoint') notest: true, // help='only test final epoch') noautoanchor: true, // help='disable autoanchor check') evolve: true, // help='evolve hyperparameters') 进化超参数(hyp),可以试试 bucket: '', // help='gsutil bucket') cache_images: true, // help='cache images for faster training') image_weights: true, // help='use weighted image selection for training') name: '', // help='renames results.txt to results_name.txt if supplied') device: 'cpu', // help='cuda device, i.e. 0 or 0,1,2,3 or cpu') multi_scale: true, // help='vary img_size +/- 50%%') single_cls: true, // help='train as single_class dataset') adam: true, // help='use torch.optim.Adam() optimizer') sync_bn: true, // help='use SyncBatchNorm, only available in DDP mode') local_rank: -1, // help='DDP parameter, do not modify') logdir: 'runs/', // help='logging directory') workers: 8 // help='maximum number of dataloader workers') }, hyp: { lr0: 0.01, // initial learning rate (SGD=1E-2, Adam=1E-3) ######lr0:0.01#初始学习率(SGD=1E-2,Adam=1E-3) lrf: 0.2, // final OneCycleLR learning rate (lr0 * lrf) ######lrf:0.2#最终一个周期ELR学习率(lr0*lrf) momentum: 0.937, // SGD momentum/Adam beta1 ######动量:0.937新加坡元/亚当贝塔1 weight_decay: 0.0005, // optimizer weight decay 5e-4 ######重量衰减:0.0005优化器重量衰减5e-4 warmup_epochs: 3.0, // warmup epochs (fractions ok) warmup_momentum: 0.8, // warmup initial momentum warmup_bias_lr: 0.1, // warmup initial bias lr giou: 0.05, // box loss gain ######giou:0.05#盒损增益 cls: 0.5, // cls loss gain ######cls:0.5#cls损益 cls_pw: 1.0, // cls BCELoss positive_weight ######cls_pw:1.0#cls B无正重量 obj: 1.0, // obj loss gain (scale with pixels) ######obj:1.0#obj损失增益(按像素缩放) obj_pw: 1.0, // obj BCELoss positive_weight ######目标重量:1.0 iou_t: 0.20, // IoU training threshold ######iou t:0.20#iou培训阈值 anchor_t: 4.0, // anchor-multiple threshold ######锚定值:4.0#锚定多重阈值 anchors: 0, // anchors per output grid (0 to ignore) #######定位点:每个输出网格0个定位点(0忽略) fl_gamma: 0.0, // focal loss gamma (efficientDet default gamma=1.5) ######flťgamma:0.0#焦点损失gamma(efficientDet default gamma=1.5) hsv_h: 0.015, // image HSV-Hue augmentation (fraction) ######hsv_h:0.015#图像hsv色彩空间 色调H增强(分数) hsv_s: 0.7, // image HSV-Saturation augmentation (fraction) ######hsv s:0.7#图像hsv色彩空间 饱和S增强(分数) hsv_v: 0.4, // image HSV-Value augmentation (fraction) ######hsv#v:0.4#图像hsv色彩空间 亮度V增大(分数) degrees: 0.0, // image rotation (+/- deg) ######度数:0.0#图像旋转(+/-度) translate: 0.1, // image translation (+/- fraction) ######翻译:0.1#图像翻译(+/-分数) scale: 0.5, // image scale (+/- gain) ######比例:0.5#图像比例(+/-增益) shear: 0.0, // image shear (+/- deg) ######剪切:0.0#图像剪切(+/-度) perspective: 0.0, // image perspective (+/- fraction), range 0-0.001 ######透视:0.0#图像透视(+/-分数),范围0-0.001 flipud: 0.0, // image flip up-down (probability) ######flipud:0.0#图像上下翻转(概率) fliplr: 0.5, // image flip left-right (probability) ######fliplr:0.5#图像左右翻转(概率) mosaic: 1.0, // image mosaic (probability) mixup: 0.0 // image mixup (probability) ######混音:0.0#图像混音(概率) }, result: { GIoU: 0, // GIoU:推测为GIoU损失函数均值,越小方框越准; Objectness: 0, // Objectness:推测为目标检测loss均值,越小目标检测越准; Classification: 0, // Classification:推测为分类loss均值,越小分类越准; Precision: 0, // Precision:准确率(找对的/找到的); Recall: 0, // Recall:召回率(找对的/该找对的); mAP: 0, // mAP@0.5 & mAP@0.5:0.95:这里说的挺好,总之就是AP是用Precision和Recall作为两轴作图后围成的面积,m表示平均,@后面的数表示判定iou为正负样本的阈值,@0.5:0.95表示阈值取0.5:0.05:0.95后取均值。 } }; }, mounted() { // console.log(45252); // 数量÷总数×100= 百分比 this.epochsbili = 0; // Math.round(this.current_epoch / this.trainParam.epochs * 100); this.JinDuViewChart = this.JinDuView(); this.LossViewChart = this.LossView(); this.LoadDatasets(); setInterval(this.GetLossArray.bind(this), 5000); }, methods: { LoadDatasets() { this.$post({ url: '/DatasetManage/LoadDatasets', method: 'post', headers: { 'Content-Type': 'application/json;charset=UTF-8' } }) .then((res: any) => { if (res.success) { this.DataSets = res.data; } else { this.$message.info(res.message); } }); }, GetLossArray() { console.log('正在加载训练指标数据..'); this.$post({ url: '/Train/GetLossArray', method: 'post', headers: { 'Content-Type': 'application/json;charset=UTF-8' } }) .then((res: any) => { if (res.success) { this.ImageFileCount = res.data.ImageFileCount; this.LossArray = res.data.LossLogArray; this.TaskState = res.data.TaskState; this.RefreshLossView(); } else { this.$message.info(res.message); } }); }, // 刷新界面 RefreshLossView() { console.log('正在刷新训练指标数据..'); if (this.LossArray.length === 0) { return; } const lastindex = this.LossArray.length - 1; const lastloss = this.LossArray[lastindex] as any; this.epoch = lastloss.epoch; this.epochs = lastloss.epochs; this.currentEpochLossData = lastloss; this.epochsbili = Math.round(this.epoch / this.epochs * 100); const data2 = []; for (let i = 0; i < 100; i++) { const item: any = {}; item.type = i + ''; item.value = 4; if (i === this.epochsbili) { item.value = 8; } if (i > this.epochsbili) { item.value = 0; } data2.push(item); } // this.JinDuViewChart.views[2] 就是view3 单独更新数据,就可以刷新 this.JinDuViewChart.views[2].changeData(data2); // this.JinDuViewChart.paint(true); this.LossViewChart.changeData(this.LossArray); }, CreatNewTrainTask() { const par = { option: JSON.stringify(this.trainParam), hyp: JSON.stringify(this.hyp), }; this.$post.post('/Train/CreatNewTrainTask', this.ToFormData(par)) .then((res: any) => { if (res.success) { this.$message.info(res.message); // this.GetLabels(); } else { this.$message.info(res.message); } }); }, LossView() { const data = [ { year: '1991', value: 3 }, { year: '1992', value: 4 }, { year: '1993', value: 3.5 }, { year: '1994', value: 5 }, { year: '1995', value: 4.9 }, { year: '1996', value: 6 }, { year: '1997', value: 7 }, { year: '1998', value: 9 }, { year: '1999', value: 13 }, ]; const chart = new Chart({ container: 'LossViewEL', autoFit: true, height: 400, }); chart.data(data); chart.scale({ year: { range: [0, 1], }, value: { min: 0, nice: true, }, }); chart.tooltip({ showCrosshairs: true, // 展示 Tooltip 辅助线 shared: true, }); chart.line().position('year*value').label('value'); chart.point().position('year*value'); const titleView3 = chart.createView(); titleView3.annotation().text({ position: ['50%', '5%'], content: '误差(loss)', style: { fill: '#CBCBCB', fontSize: 25, textAlign: 'center', textBaseline: 'middle', }, }); chart.render(); return chart; }, JinDuView() { // 极坐标下的柱状图 // 构造数据 // 灰色线, 总进度 // 蓝色线线, 进度线 const data2 = []; for (let i = 0; i < 100; i++) { const item: any = {}; item.type = i + ''; item.value = 4; if (i === this.epochsbili) { item.value = 8; } if (i > this.epochsbili) { item.value = 0; } data2.push(item); } const chart = new Chart({ container: 'jindu', autoFit: true, height: 400, padding: 0, }); chart.scale({ type: { range: [0, 1], }, value: { sync: true, }, }); chart.legend(false); chart.tooltip(false); const view1 = chart.createView(); const data1 = []; for (let i = 0; i < 100; i++) { data1.push({ type: i + '', value: 4, // 线的长短 }); } view1.data(data1); view1.axis(false); view1.coordinate('polar', { startAngle: (-9 / 8) * Math.PI, endAngle: (1 / 8) * Math.PI, innerRadius: 0.75, radius: 0.8, }); view1 .interval() .position('type*value') .color('#CBCBCB') .size(6); const view2 = chart.createView(); view2.data(data1); view2.axis('value', false); view2.axis('type', { grid: null, line: null, tickLine: null, label: { offset: -25, style: { textAlign: 'center', fill: '#CBCBCB', fontSize: 18, }, formatter: (val) => { if (+val % 7 !== 0) { return ''; } return val; }, }, }); // 内圈小虚线 view2.coordinate('polar', { startAngle: (-9 / 8) * Math.PI, endAngle: (1 / 8) * Math.PI, innerRadius: 0.95, radius: 0.55, }); view2 .interval() .position('type*value') .color('#A987FB') .size(2); // chart.point() // .position('x*y') // .label('value', (xValue) => { // return { // content: xValue + '%', // }; // }); const view3 = chart.createView(); view3.data(data2); view3.axis(false); view3.coordinate('polar', { startAngle: (-9 / 8) * Math.PI, endAngle: (1 / 8) * Math.PI, innerRadius: 0.75, radius: 0.8, }); view3 .interval() .position('type*value') .color('value', '#3023AE-#188cfa') .size(6); view3.annotation().text({ position: ['50%', '60%'], content: '轮次', style: { fill: '#CBCBCB', fontSize: 12, textAlign: 'center', textBaseline: 'middle', }, }); view3.annotation().text({ position: ['50%', '75%'], content: '样本数', style: { fill: '#CBCBCB', fontSize: 12, textAlign: 'center', textBaseline: 'middle', }, }); view3.annotation().text({ position: ['50%', '95%'], content: () => { return this.TaskState; }, style: { fill: '#1f65a4', fontSize: 20, textAlign: 'center', textBaseline: 'middle', }, }); view3.annotation().text({ position: ['50%', '5%'], content: '进度(progress)%', style: { fill: '#CBCBCB', fontSize: 25, textAlign: 'center', textBaseline: 'middle', }, }); view3.annotation().text({ position: ['50%', '55%'], content: () => { // 注意这里是函数的形式 return this.epoch + '/' + this.epochs; }, top: true, style: { fill: '#CBCBCB', fontSize: 24, textAlign: 'center', textBaseline: 'middle', }, }); view3.annotation().text({ position: ['50%', '70%'], content: () => { // 注意这里是函数的形式 return this.ImageFileCount; }, style: { fill: '#CBCBCB', fontSize: 24, textAlign: 'center', textBaseline: 'middle', }, }); chart.render(); return chart; } } }); </script>this.JinDuViewChart.views[2].changeData(data2);