385 lines
15 KiB
Vue
385 lines
15 KiB
Vue
|
<template>
|
|||
|
<Card style="width: 100%;">
|
|||
|
<p slot="title" style="font-size: 18px; font-weight: bold; color: #464c5b;">
|
|||
|
<Icon type="md-stats" size="20" style="color: #464c5b;" />
|
|||
|
Inference Result: <span style="color: green;">{{ name }}</span>
|
|||
|
</p>
|
|||
|
<Alert show-icon><span style="font-weight: bold;">Success Rate(SR)</span> = Predicted Final Coverage Rate / Max
|
|||
|
GT Final Coverage Rate</Alert>
|
|||
|
<Row>
|
|||
|
<!-- 左侧展示条形图 -->
|
|||
|
<Col span="12">
|
|||
|
<Card>
|
|||
|
<p slot="title" style="font-size: 16px; font-weight: bold;">Success Rate Histogram</p>
|
|||
|
<div ref="histogramChart" style="height: 600px; width: 100%;"></div>
|
|||
|
</Card>
|
|||
|
</Col>
|
|||
|
<!-- 右侧展示饼状图 -->
|
|||
|
<Col span="12">
|
|||
|
<Card>
|
|||
|
<p slot="title" style="font-size: 16px; font-weight: bold;">Success Rate Pie Chart</p>
|
|||
|
<div ref="pieChart" style="height: 600px; width: 100%;"></div>
|
|||
|
</Card>
|
|||
|
</Col>
|
|||
|
</Row>
|
|||
|
<Row style="margin-top: 10px;">
|
|||
|
<Card style="width: 100%; height: 600px;">
|
|||
|
<p slot="title" style="font-size: 16px; font-weight: bold;">Single Object Analysis</p>
|
|||
|
<Row>
|
|||
|
<Col span="8">
|
|||
|
<Row style="margin-bottom: 10px; margin-left: 10px; margin-right: 10px;">
|
|||
|
<Col span="8">
|
|||
|
<p style="font-size: 16px; color: #464c5b; font-weight: bold;">Select Scene:</p>
|
|||
|
</Col>
|
|||
|
<Col span="14">
|
|||
|
<Select v-model="sceneName" style="width: 100%;" @on-change="handleSceneChange"
|
|||
|
placeholder="please select..." filterable>
|
|||
|
<Option v-for="(value, key) in result" :value="key" :key="key" :label="key"><span>{{ key
|
|||
|
}}</span>
|
|||
|
<span style="float:right;color:#ccc">(SR=<span
|
|||
|
:style="{ color: getSuccessRateColor(result[key]['success_rate']) }">{{
|
|||
|
(result[key]["success_rate"] * 100).toFixed(2) }}%</span>)</span>
|
|||
|
</Option>
|
|||
|
</Select>
|
|||
|
</Col>
|
|||
|
</Row>
|
|||
|
</Col>
|
|||
|
<Col span="1">
|
|||
|
<Divider type="vertical" style="height: 100%;" />
|
|||
|
</Col>
|
|||
|
<Col span="14">
|
|||
|
|
|||
|
<span style="font-weight: bold;">Max Coverage Rate: <span style="color: green;">{{ result[sceneName]
|
|||
|
? (result[sceneName]["max_coverage_rate"] * 100).toFixed(2) : 0 }}%</span></span>
|
|||
|
<Divider type="vertical" style="height: 100%;" />
|
|||
|
<span style="font-weight: bold;">Pred Coverage Rate: <span style="color: green;">{{
|
|||
|
result[sceneName]
|
|||
|
? (result[sceneName]["pred_max_coverage_rate"]*100).toFixed(2) : 0 }}%</span></span>
|
|||
|
<Divider type="vertical" style="height: 100%;" />
|
|||
|
<span style="font-weight: bold;">Success Rate: <span style="color: green;">{{ result[sceneName] ?
|
|||
|
(result[sceneName]["success_rate"] * 100).toFixed(2) : 0 }}%</span></span>
|
|||
|
<Divider type="vertical" style="height: 100%;" />
|
|||
|
<span style="font-weight: bold;">Pred Length/GT Length:
|
|||
|
<span style="color: red;">{{ result[sceneName] ? (result[sceneName]["pred_seq_len"]) : 0
|
|||
|
}}</span>/<span style="color: green;">?</span>
|
|||
|
</span>
|
|||
|
|
|||
|
</Col>
|
|||
|
</Row>
|
|||
|
<Divider />
|
|||
|
<Row>
|
|||
|
<Card style="height: 400px; width: 100%;">
|
|||
|
<p slot="title" style="font-size: 16px; font-weight: bold;">Prediction Curve</p>
|
|||
|
<div ref="predictionCurve" style="height: 350px; width: 100%;"></div>
|
|||
|
</Card>
|
|||
|
</Row>
|
|||
|
</Card>
|
|||
|
</Row>
|
|||
|
</Card>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
import * as echarts from 'echarts';
|
|||
|
import { Divider } from 'view-design';
|
|||
|
|
|||
|
export default {
|
|||
|
name: "StatResultAnalysis",
|
|||
|
props: {
|
|||
|
result: Object,
|
|||
|
name: String,
|
|||
|
},
|
|||
|
mounted() {
|
|||
|
console.log(this.result);
|
|||
|
this.drawCharts();
|
|||
|
},
|
|||
|
data() {
|
|||
|
return {
|
|||
|
sceneName: null,
|
|||
|
|
|||
|
};
|
|||
|
},
|
|||
|
methods: {
|
|||
|
getSuccessRateColor(rate) {
|
|||
|
if (rate >= 0.7) {
|
|||
|
return 'green';
|
|||
|
} else if (rate >= 0.4) {
|
|||
|
return 'orange';
|
|||
|
} else {
|
|||
|
return 'red';
|
|||
|
}
|
|||
|
},
|
|||
|
handleSceneChange(val) {
|
|||
|
if (val) {
|
|||
|
this.sceneName = val;
|
|||
|
console.log("Selected scene name:", this.sceneName);
|
|||
|
this.drawLines(); // 选择场景后绘制折线图
|
|||
|
}
|
|||
|
},
|
|||
|
drawLines() {
|
|||
|
if (!this.sceneName || !this.result[this.sceneName]) return;
|
|||
|
|
|||
|
const coverageRateSeq = this.result[this.sceneName]["coverage_rate_seq"];
|
|||
|
const maxCoverageRate = this.result[this.sceneName]["max_coverage_rate"];
|
|||
|
const successRateSeq = coverageRateSeq.map(rate => (rate * 100 / maxCoverageRate).toFixed(2)); // 计算成功率序列
|
|||
|
|
|||
|
// 初始化折线图
|
|||
|
const predictionCurve = echarts.init(this.$refs.predictionCurve);
|
|||
|
const lineOption = {
|
|||
|
tooltip: {
|
|||
|
trigger: 'axis',
|
|||
|
formatter: (params) => {
|
|||
|
let tooltip = params[0].name + '<br>';
|
|||
|
params.forEach(item => {
|
|||
|
tooltip += `${item.seriesName}: ${item.data}%<br>`;
|
|||
|
});
|
|||
|
return tooltip;
|
|||
|
}
|
|||
|
},
|
|||
|
xAxis: {
|
|||
|
type: 'category',
|
|||
|
data: coverageRateSeq.map((_, index) => `${index + 1}`), // x轴标签
|
|||
|
},
|
|||
|
yAxis: {
|
|||
|
type: 'value',
|
|||
|
name: 'Coverage Rate',
|
|||
|
axisLabel: {
|
|||
|
formatter: '{value}%'
|
|||
|
}
|
|||
|
},
|
|||
|
series: [
|
|||
|
{
|
|||
|
name: 'Coverage Rate',
|
|||
|
type: 'line',
|
|||
|
data: coverageRateSeq.map(rate => (rate * 100).toFixed(2)),
|
|||
|
itemStyle: {
|
|||
|
color: '#3399ff'
|
|||
|
},
|
|||
|
smooth: false // 曲线平滑
|
|||
|
},
|
|||
|
{
|
|||
|
name: 'Success Rate',
|
|||
|
type: 'line',
|
|||
|
data: successRateSeq,
|
|||
|
itemStyle: {
|
|||
|
color: '#ff5733' // 另一种颜色
|
|||
|
},
|
|||
|
smooth: false // 曲线平滑
|
|||
|
},
|
|||
|
{
|
|||
|
name: 'Max Coverage Rate',
|
|||
|
type: 'line',
|
|||
|
|
|||
|
lineStyle: {
|
|||
|
type: 'dashed' // 设置为虚线
|
|||
|
},
|
|||
|
markLine: {
|
|||
|
data: [
|
|||
|
{
|
|||
|
name: 'GT Max Coverage Rate',
|
|||
|
yAxis: (maxCoverageRate * 100).toFixed(2),
|
|||
|
label: {
|
|||
|
formatter: '{c}%' // 在标记线上显示百分号
|
|||
|
}
|
|||
|
}
|
|||
|
],
|
|||
|
|
|||
|
},
|
|||
|
}
|
|||
|
|
|||
|
]
|
|||
|
};
|
|||
|
predictionCurve.setOption(lineOption);
|
|||
|
},
|
|||
|
drawCharts() {
|
|||
|
// 提取成功率数据
|
|||
|
const successRates = Object.values(this.result).map(item => item.success_rate);
|
|||
|
|
|||
|
// 计算平均数
|
|||
|
const avgSuccessRate = parseFloat((successRates.reduce((sum, rate) => sum + rate, 0) / successRates.length).toFixed(4));
|
|||
|
console.log("avg", avgSuccessRate);
|
|||
|
|
|||
|
// 计算中位数
|
|||
|
const sortedRates = [...successRates].sort((a, b) => a - b);
|
|||
|
const mid = Math.floor(sortedRates.length / 2);
|
|||
|
const medianSuccessRate = parseFloat((sortedRates.length % 2 !== 0 ? sortedRates[mid] : ((sortedRates[mid - 1] + sortedRates[mid]) / 2)).toFixed(4));
|
|||
|
|
|||
|
// 定义区间
|
|||
|
const intervals = ['0-10%', '10-20%', '20-30%', '30-40%', '40-50%', '50-60%', '60-70%', '70-80%', '80-90%', '90-100%', '>=100%'];
|
|||
|
const intervalCounts = new Array(intervals.length).fill(0);
|
|||
|
|
|||
|
// 将成功率分类到对应的区间
|
|||
|
successRates.forEach(rate => {
|
|||
|
let index = Math.min(Math.floor(rate * 10), 10); // 大于100%的情况归类为最后一项
|
|||
|
intervalCounts[index]++;
|
|||
|
});
|
|||
|
|
|||
|
// 生成条形图数据
|
|||
|
const histogramData = intervalCounts.map((count, index) => ({
|
|||
|
name: intervals[index],
|
|||
|
value: count
|
|||
|
}));
|
|||
|
|
|||
|
const histogramChart = echarts.init(this.$refs.histogramChart);
|
|||
|
const seriesData = [
|
|||
|
{
|
|||
|
name: 'Number of Objects',
|
|||
|
type: 'bar',
|
|||
|
data: histogramData.map(item => item.value),
|
|||
|
itemStyle: {
|
|||
|
color: '#3399ff'
|
|||
|
},
|
|||
|
markPoint: {
|
|||
|
data: [
|
|||
|
{
|
|||
|
name: 'Median',
|
|||
|
coord: [medianSuccessRate * 10 - 1, histogramData[Math.floor(medianSuccessRate * 10)].value],
|
|||
|
label: {
|
|||
|
formatter: `Median: \n${(medianSuccessRate * 100).toFixed(2)}%`,
|
|||
|
position: 'top'
|
|||
|
},
|
|||
|
itemStyle: {
|
|||
|
color: 'green'
|
|||
|
}
|
|||
|
},
|
|||
|
{
|
|||
|
name: 'Avg',
|
|||
|
coord: [avgSuccessRate * 10 - 1, histogramData[Math.floor(avgSuccessRate * 10)].value],
|
|||
|
label: {
|
|||
|
formatter: `Average: \n${(avgSuccessRate * 100).toFixed(2)}%`,
|
|||
|
position: 'top'
|
|||
|
},
|
|||
|
itemStyle: {
|
|||
|
color: 'orange'
|
|||
|
}
|
|||
|
},
|
|||
|
]
|
|||
|
}
|
|||
|
}
|
|||
|
];
|
|||
|
|
|||
|
const histogramOption = {
|
|||
|
tooltip: {
|
|||
|
trigger: 'axis',
|
|||
|
axisPointer: {
|
|||
|
type: 'shadow'
|
|||
|
}
|
|||
|
},
|
|||
|
xAxis: {
|
|||
|
type: 'category',
|
|||
|
data: intervals,
|
|||
|
name: 'SR (%)'
|
|||
|
},
|
|||
|
yAxis: {
|
|||
|
type: 'value',
|
|||
|
name: 'Number of Objects'
|
|||
|
},
|
|||
|
series: seriesData
|
|||
|
};
|
|||
|
histogramChart.setOption(histogramOption);
|
|||
|
|
|||
|
const pieIntervals = ['0-40%', '40-70%', '70-100%', '>=100%'];
|
|||
|
const pieIntervalCounts = new Array(intervals.length).fill(0);
|
|||
|
|
|||
|
successRates.forEach(rate => {
|
|||
|
if (rate <= 0.4) {
|
|||
|
pieIntervalCounts[0]++;
|
|||
|
} else if (rate <= 0.7) {
|
|||
|
pieIntervalCounts[1]++;
|
|||
|
} else if (rate < 1.0) {
|
|||
|
pieIntervalCounts[2]++;
|
|||
|
} else {
|
|||
|
pieIntervalCounts[3]++;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 生成内环饼图数据
|
|||
|
const innerPieData = pieIntervals.map((interval, index) => ({
|
|||
|
name: interval,
|
|||
|
value: pieIntervalCounts[index]
|
|||
|
}));
|
|||
|
|
|||
|
// 生成外环饼图数据
|
|||
|
const outerPieData = histogramData.map(item => ({
|
|||
|
name: item.name,
|
|||
|
value: item.value
|
|||
|
}));
|
|||
|
|
|||
|
// 初始化饼状图
|
|||
|
const pieChart = echarts.init(this.$refs.pieChart);
|
|||
|
const pieOption = {
|
|||
|
tooltip: {
|
|||
|
trigger: 'item',
|
|||
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|||
|
},
|
|||
|
legend: {
|
|||
|
data: outerPieData.map(item => item.name) // 使用内环数据的名称
|
|||
|
},
|
|||
|
series: [
|
|||
|
{
|
|||
|
name: 'Success Rate',
|
|||
|
type: 'pie',
|
|||
|
selectedMode: 'single',
|
|||
|
radius: [0, '30%'],
|
|||
|
label: {
|
|||
|
position: 'inner',
|
|||
|
fontSize: 14
|
|||
|
},
|
|||
|
labelLine: {
|
|||
|
show: false
|
|||
|
},
|
|||
|
data: innerPieData // 内环数据
|
|||
|
},
|
|||
|
{
|
|||
|
name: 'Success Rate',
|
|||
|
type: 'pie',
|
|||
|
radius: ['45%', '60%'],
|
|||
|
labelLine: {
|
|||
|
length: 30
|
|||
|
},
|
|||
|
label: {
|
|||
|
formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
|
|||
|
backgroundColor: '#F6F8FC',
|
|||
|
borderColor: '#8C8D8E',
|
|||
|
borderWidth: 1,
|
|||
|
borderRadius: 4,
|
|||
|
rich: {
|
|||
|
a: {
|
|||
|
color: '#6E7079',
|
|||
|
lineHeight: 22,
|
|||
|
align: 'center'
|
|||
|
},
|
|||
|
hr: {
|
|||
|
borderColor: '#8C8D8E',
|
|||
|
width: '100%',
|
|||
|
borderWidth: 1,
|
|||
|
height: 0
|
|||
|
},
|
|||
|
b: {
|
|||
|
color: '#4C5058',
|
|||
|
fontSize: 14,
|
|||
|
fontWeight: 'bold',
|
|||
|
lineHeight: 33
|
|||
|
},
|
|||
|
per: {
|
|||
|
color: '#fff',
|
|||
|
backgroundColor: '#4C5058',
|
|||
|
padding: [3, 4],
|
|||
|
borderRadius: 4
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
data: outerPieData // 外环数据
|
|||
|
}
|
|||
|
]
|
|||
|
};
|
|||
|
|
|||
|
pieChart.setOption(pieOption);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
</script>
|
|||
|
|
|||
|
<style scoped>
|
|||
|
/* 添加必要的样式 */
|
|||
|
</style>
|