|
10 | 10 | <link rel="stylesheet" href="{{ base_path }}/assets/css/tags.css"> |
11 | 11 | <script src="https://cdn.plot.ly/plotly-3.3.0.min.js" charset="utf-8"></script> |
12 | 12 | <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.min.js"></script> |
| 13 | +<script src="{{ base_path }}/assets/js/charty.js"></script> |
13 | 14 | <script src="{{ base_path }}/assets/js/tags.js"></script> |
14 | | -这是页面的介绍内容。 |
15 | | - |
16 | | - |
17 | | - |
18 | | -<ul class="custom-tabs"> |
19 | | - <li><a href="#tab1">研究方向</a></li> |
20 | | - <li><a href="#tab2">发表论文</a></li> |
21 | | - <li><a href="#tab3">项目经历</a></li> |
22 | | -</ul> |
23 | | - |
24 | | -<div class="tab-content-wrapper"> |
25 | | - <div id="tab1" class="tab-content-item"> |
26 | | - <h3>研究方向</h3> |
27 | | - <p>这里是研究方向的内容。</p> |
28 | | - <ul> |
29 | | - <li>3D Gaussian Splatting</li> |
30 | | - <li>Point Cloud Processing</li> |
31 | | - <li>Computer Vision</li> |
32 | | - </ul> |
33 | | - </div> |
34 | | - |
35 | | - <div id="tab2" class="tab-content-item"> |
36 | | - <h3>发表论文</h3> |
37 | | - <p>这里是论文列表。</p> |
38 | | - <ol> |
39 | | - <li>Paper 1 - CVPR 2026</li> |
40 | | - <li>Paper 2 - ICCV 2025</li> |
41 | | - </ol> |
42 | | - </div> |
43 | | - |
44 | | - <div id="tab3" class="tab-content-item"> |
45 | | - <h3>项目经历</h3> |
46 | | - <p>这里是项目经历。</p> |
47 | | - <ul> |
48 | | - <li>Project A - 2024</li> |
49 | | - <li>Project B - 2023</li> |
50 | | - </ul> |
51 | | - </div> |
52 | | -</div> |
| 15 | + |
| 16 | +<ul id="scene-tabs" class="custom-tabs"></ul> |
| 17 | + |
| 18 | +<div id="scene-tabs-content" class="tab-content-wrapper"></div> |
| 19 | + |
| 20 | +<script> |
| 21 | +const DATASET_NAME = 'Mip-NeRF360'; |
| 22 | + |
| 23 | +function createSceneCharts(sceneName, containerPrefix) { |
| 24 | + if (!charty.dataManager.isInitialized()) return; |
| 25 | + |
| 26 | + var subset = charty.dataManager.subset(DATASET_NAME, sceneName); |
| 27 | + |
| 28 | + if (!subset || !subset.data || subset.data.length === 0) { |
| 29 | + console.warn(`No data for scene: ${sceneName}`); |
| 30 | + return; |
| 31 | + } |
| 32 | + |
| 33 | + var data = subset.data; |
| 34 | + var uniqueMethods = subset.uniqueMethods; |
| 35 | + var colorMap = charty.dataManager.colorMap; |
| 36 | + var colorMapLight = charty.dataManager.colorMapLight; |
| 37 | + var gpu = subset.gpu; |
| 38 | + var times = subset.times; |
| 39 | + var uniqueDownsampling = subset.uniqueDownsampling; |
| 40 | + |
| 41 | + var layout_base = charty.getThemedLayoutBase(); |
| 42 | + var barStyle = charty.getThemedBarStyle(); |
| 43 | + var plotConfig = {displayModeBar: false}; |
| 44 | + |
| 45 | + var traces1 = []; |
| 46 | + uniqueMethods.forEach((method, i) => { |
| 47 | + var row = data.find(d => d.method === method && d.downsampling == 8); |
| 48 | + if (!row) return; |
| 49 | + traces1.push({ |
| 50 | + x: [method], |
| 51 | + y: [parseFloat(row.psnr_7k_train) || 0], |
| 52 | + name: i === 0 ? '7k' : '', |
| 53 | + legendgroup: '7k', |
| 54 | + showlegend: i === 0, |
| 55 | + type: 'bar', |
| 56 | + marker: {color: colorMapLight[method], line: barStyle.line}, |
| 57 | + offsetgroup: '7k' |
| 58 | + }); |
| 59 | + traces1.push({ |
| 60 | + x: [method], |
| 61 | + y: [parseFloat(row.psnr_30k_train) || 0], |
| 62 | + name: i === 0 ? '30k' : '', |
| 63 | + legendgroup: '30k', |
| 64 | + showlegend: i === 0, |
| 65 | + type: 'bar', |
| 66 | + marker: {color: colorMap[method], line: barStyle.line}, |
| 67 | + offsetgroup: '30k' |
| 68 | + }); |
| 69 | + }); |
| 70 | + |
| 71 | + Plotly.newPlot(`${containerPrefix}-chart1`, traces1, Object.assign({}, layout_base, { |
| 72 | + title: {text: `${sceneName} - Training PSNR: 7k vs 30k (DS=8)`, font: {size: 14}, x: 0.5}, |
| 73 | + barmode: 'group', |
| 74 | + xaxis: Object.assign({}, layout_base.xaxis, {}), |
| 75 | + yaxis: Object.assign({}, layout_base.yaxis, {title: {text: 'PSNR (dB)', font: {size: 12}}}), |
| 76 | + legend: Object.assign({}, layout_base.legend, {traceorder: 'grouped'}) |
| 77 | + }), plotConfig); |
| 78 | + |
| 79 | + var traces2 = []; |
| 80 | + uniqueMethods.forEach((method, i) => { |
| 81 | + var row = data.find(d => d.method === method && d.downsampling == 8); |
| 82 | + if (!row) return; |
| 83 | + traces2.push({ |
| 84 | + x: [method], |
| 85 | + y: [parseFloat(row.psnr_30k_train) || 0], |
| 86 | + name: i === 0 ? 'Train' : '', |
| 87 | + legendgroup: 'Train', |
| 88 | + showlegend: i === 0, |
| 89 | + type: 'bar', |
| 90 | + marker: {color: colorMap[method], line: barStyle.line}, |
| 91 | + offsetgroup: 'Train' |
| 92 | + }); |
| 93 | + traces2.push({ |
| 94 | + x: [method], |
| 95 | + y: [parseFloat(row.psnr_30k_test) || 0], |
| 96 | + name: i === 0 ? 'Test' : '', |
| 97 | + legendgroup: 'Test', |
| 98 | + showlegend: i === 0, |
| 99 | + type: 'bar', |
| 100 | + marker: {color: colorMapLight[method], line: barStyle.line}, |
| 101 | + offsetgroup: 'Test' |
| 102 | + }); |
| 103 | + }); |
| 104 | + |
| 105 | + Plotly.newPlot(`${containerPrefix}-chart2`, traces2, Object.assign({}, layout_base, { |
| 106 | + title: {text: `${sceneName} - Train vs Test PSNR (30k, DS=8)`, font: {size: 14}, x: 0.5}, |
| 107 | + barmode: 'group', |
| 108 | + xaxis: Object.assign({}, layout_base.xaxis, {}), |
| 109 | + yaxis: Object.assign({}, layout_base.yaxis, {title: {text: 'PSNR (dB)', font: {size: 12}}}), |
| 110 | + legend: Object.assign({}, layout_base.legend, {traceorder: 'grouped'}) |
| 111 | + }), plotConfig); |
| 112 | + |
| 113 | + Plotly.newPlot(`${containerPrefix}-chart3`, [{ |
| 114 | + x: uniqueMethods, |
| 115 | + y: uniqueMethods.map(m => { |
| 116 | + var idx = data.findIndex(d => d.method === m && d.downsampling == 8); |
| 117 | + return idx >= 0 ? times[idx] / 60 : 0; |
| 118 | + }), |
| 119 | + type: 'bar', |
| 120 | + marker: Object.assign({color: uniqueMethods.map(m => colorMap[m])}, barStyle), |
| 121 | + text: uniqueMethods.map(m => { |
| 122 | + var idx = data.findIndex(d => d.method === m && d.downsampling == 8); |
| 123 | + return idx >= 0 ? gpu[idx] : ''; |
| 124 | + }), |
| 125 | + textposition: 'outside', |
| 126 | + textfont: {size: 9, color: charty.getCurrentTheme() === 'dark' ? '#ccc' : '#333'} |
| 127 | + }], Object.assign({}, layout_base, { |
| 128 | + title: {text: `${sceneName} - Training Time (DS=8)`, font: {size: 14}, x: 0.5}, |
| 129 | + showlegend: false, |
| 130 | + xaxis: Object.assign({}, layout_base.xaxis, {}), |
| 131 | + yaxis: Object.assign({}, layout_base.yaxis, {title: {text: 'Time (min)', font: {size: 12}}}) |
| 132 | + }), plotConfig); |
| 133 | + |
| 134 | + var traces4 = uniqueMethods.map(method => { |
| 135 | + var yVals = uniqueDownsampling.map(ds => { |
| 136 | + var row = data.find(d => d.method === method && d.downsampling == ds); |
| 137 | + return row ? parseFloat(row.psnr_30k_train) || null : null; |
| 138 | + }); |
| 139 | + return { |
| 140 | + x: uniqueDownsampling.map(d => '1/' + d), |
| 141 | + y: yVals, |
| 142 | + name: method, |
| 143 | + type: 'bar', |
| 144 | + hovertemplate: method + '<br>Factor: %{x}<br>PSNR: %{y:.2f} dB<extra></extra>', |
| 145 | + marker: Object.assign({color: colorMap[method]}, barStyle) |
| 146 | + }; |
| 147 | + }); |
| 148 | + |
| 149 | + Plotly.newPlot(`${containerPrefix}-chart4`, traces4, Object.assign({}, layout_base, { |
| 150 | + title: {text: `${sceneName} - PSNR vs Resolution (30k)`, font: {size: 14}, x: 0.5}, |
| 151 | + barmode: 'group', |
| 152 | + xaxis: Object.assign({}, layout_base.xaxis, {tickangle: 0}), |
| 153 | + yaxis: Object.assign({}, layout_base.yaxis, {title: {text: 'PSNR (dB)', font: {size: 12}}}) |
| 154 | + }), plotConfig); |
| 155 | + |
| 156 | + var traces5 = []; |
| 157 | + |
| 158 | + uniqueMethods.forEach((method, i) => { |
| 159 | + traces5.push({ |
| 160 | + x: [null], |
| 161 | + y: [null], |
| 162 | + name: method, |
| 163 | + legendgroup: method, |
| 164 | + type: 'scatter', |
| 165 | + mode: 'markers', |
| 166 | + marker: { |
| 167 | + color: colorMap[method], |
| 168 | + symbol: 'circle', |
| 169 | + size: 10, |
| 170 | + line: {color: charty.getCurrentTheme() === 'dark' ? '#666' : '#000', width: 1} |
| 171 | + } |
| 172 | + }); |
| 173 | + }); |
| 174 | + |
| 175 | + uniqueDownsampling.forEach((ds, i) => { |
| 176 | + traces5.push({ |
| 177 | + x: [null], |
| 178 | + y: [null], |
| 179 | + name: '1/' + ds, |
| 180 | + legendgroup: 'factors', |
| 181 | + type: 'scatter', |
| 182 | + mode: 'markers', |
| 183 | + marker: { |
| 184 | + color: '#666', |
| 185 | + symbol: charty.markerShapes[ds] || 'circle', |
| 186 | + size: 10, |
| 187 | + line: {color: charty.getCurrentTheme() === 'dark' ? '#666' : '#000', width: 1} |
| 188 | + } |
| 189 | + }); |
| 190 | + }); |
| 191 | + |
| 192 | + uniqueMethods.forEach(method => { |
| 193 | + uniqueDownsampling.forEach(ds => { |
| 194 | + var methodData = data.filter(d => d.method === method && d.downsampling == ds); |
| 195 | + if (methodData.length === 0) return; |
| 196 | + traces5.push({ |
| 197 | + x: methodData.map(d => parseFloat(d.gs_number) || 0), |
| 198 | + y: methodData.map(d => parseFloat(d.psnr_30k_train) || 0), |
| 199 | + legendgroup: method, |
| 200 | + showlegend: false, |
| 201 | + hovertemplate: method + ' (1/' + ds + ')<br>GS: %{x:.3s}<br>PSNR: %{y:.2f} dB<extra></extra>', |
| 202 | + type: 'scatter', |
| 203 | + mode: 'markers', |
| 204 | + marker: { |
| 205 | + color: colorMap[method], |
| 206 | + symbol: charty.markerShapes[ds] || 'circle', |
| 207 | + size: 10, |
| 208 | + line: {color: charty.getCurrentTheme() === 'dark' ? '#666' : '#000', width: 1} |
| 209 | + } |
| 210 | + }); |
| 211 | + }); |
| 212 | + }); |
| 213 | + |
| 214 | + Plotly.newPlot(`${containerPrefix}-chart5`, traces5, Object.assign({}, layout_base, { |
| 215 | + title: {text: `${sceneName} - GS Number vs PSNR (30k)`, font: {size: 14}, x: 0.5}, |
| 216 | + xaxis: Object.assign({}, layout_base.xaxis, {tickangle: 0, rangemode: 'tozero'}), |
| 217 | + yaxis: Object.assign({}, layout_base.yaxis, {title: {text: 'PSNR (dB)', font: {size: 12}}}), |
| 218 | + legend: Object.assign({}, layout_base.legend, {tracegroupgap: 0}) |
| 219 | + }), plotConfig); |
| 220 | + |
| 221 | + var traces6 = []; |
| 222 | + uniqueMethods.forEach((method, i) => { |
| 223 | + var row = data.find(d => d.method === method && d.downsampling == 8); |
| 224 | + if (!row) return; |
| 225 | + traces6.push({ |
| 226 | + x: [method], |
| 227 | + y: [parseFloat(row.train_render_times) / 60 || 0], |
| 228 | + name: i === 0 ? 'Render' : '', |
| 229 | + legendgroup: 'Render', |
| 230 | + showlegend: i === 0, |
| 231 | + type: 'bar', |
| 232 | + marker: {color: colorMapLight[method], line: barStyle.line}, |
| 233 | + offsetgroup: 'Render' |
| 234 | + }); |
| 235 | + traces6.push({ |
| 236 | + x: [method], |
| 237 | + y: [parseFloat(row.train_optimal_times) / 60 || 0], |
| 238 | + name: i === 0 ? 'Optimize' : '', |
| 239 | + legendgroup: 'Optimize', |
| 240 | + showlegend: i === 0, |
| 241 | + type: 'bar', |
| 242 | + marker: {color: colorMap[method], line: barStyle.line}, |
| 243 | + offsetgroup: 'Optimize' |
| 244 | + }); |
| 245 | + }); |
| 246 | + |
| 247 | + Plotly.newPlot(`${containerPrefix}-chart6`, traces6, Object.assign({}, layout_base, { |
| 248 | + title: {text: `${sceneName} - Training Time Breakdown (DS=8)`, font: {size: 14}, x: 0.5}, |
| 249 | + barmode: 'group', |
| 250 | + xaxis: Object.assign({}, layout_base.xaxis, {}), |
| 251 | + yaxis: Object.assign({}, layout_base.yaxis, {title: {text: 'Time (min)', font: {size: 12}}}), |
| 252 | + legend: Object.assign({}, layout_base.legend, {traceorder: 'grouped'}) |
| 253 | + }), plotConfig); |
| 254 | +} |
| 255 | + |
| 256 | +function createAllSceneTabs() { |
| 257 | + if (!charty.dataManager.isInitialized()) return; |
| 258 | + |
| 259 | + var scenes = charty.dataManager.getScenes(); |
| 260 | + |
| 261 | + if (!scenes || scenes.length === 0) { |
| 262 | + console.warn('No scenes found'); |
| 263 | + return; |
| 264 | + } |
| 265 | + |
| 266 | + var tabsContainer = document.getElementById('scene-tabs'); |
| 267 | + tabsContainer.innerHTML = ''; |
| 268 | + |
| 269 | + scenes.forEach((scene, index) => { |
| 270 | + var li = document.createElement('li'); |
| 271 | + var a = document.createElement('a'); |
| 272 | + a.href = `#scene-${scene}`; |
| 273 | + a.textContent = scene; |
| 274 | + if (index === 0) a.classList.add('active'); |
| 275 | + li.appendChild(a); |
| 276 | + tabsContainer.appendChild(li); |
| 277 | + }); |
| 278 | + |
| 279 | + var contentContainer = document.getElementById('scene-tabs-content'); |
| 280 | + contentContainer.innerHTML = ''; |
| 281 | + |
| 282 | + scenes.forEach((scene, index) => { |
| 283 | + var tabDiv = document.createElement('div'); |
| 284 | + tabDiv.id = `scene-${scene}`; |
| 285 | + tabDiv.className = 'tab-content-item'; |
| 286 | + if (index === 0) tabDiv.classList.add('active'); |
| 287 | + |
| 288 | + tabDiv.innerHTML = ` |
| 289 | + <h3>${scene}</h3> |
| 290 | + <div id="scene-${scene}-chart1" class="chart"></div> |
| 291 | + <div id="scene-${scene}-chart2" class="chart"></div> |
| 292 | + <div id="scene-${scene}-chart3" class="chart"></div> |
| 293 | + <div id="scene-${scene}-chart4" class="chart"></div> |
| 294 | + <div id="scene-${scene}-chart5" class="chart"></div> |
| 295 | + <div id="scene-${scene}-chart6" class="chart"></div> |
| 296 | + `; |
| 297 | + |
| 298 | + contentContainer.appendChild(tabDiv); |
| 299 | + }); |
| 300 | + |
| 301 | + scenes.forEach(scene => { |
| 302 | + createSceneCharts(scene, `scene-${scene}`); |
| 303 | + }); |
| 304 | +} |
| 305 | + |
| 306 | +Papa.parse(charty.sheetsUrls.MipNerf360, { |
| 307 | + download: true, |
| 308 | + header: true, |
| 309 | + complete: function(results) { |
| 310 | + charty.dataManager.init(results.data); |
| 311 | + createAllSceneTabs(); |
| 312 | + |
| 313 | + const observer = new MutationObserver(() => { |
| 314 | + createAllSceneTabs(); |
| 315 | + }); |
| 316 | + |
| 317 | + observer.observe(document.documentElement, { |
| 318 | + attributes: true, |
| 319 | + attributeFilter: ['class', 'data-theme'] |
| 320 | + }); |
| 321 | + |
| 322 | + observer.observe(document.body, { |
| 323 | + attributes: true, |
| 324 | + attributeFilter: ['class', 'data-theme'] |
| 325 | + }); |
| 326 | + |
| 327 | + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { |
| 328 | + createAllSceneTabs(); |
| 329 | + }); |
| 330 | + } |
| 331 | +}); |
| 332 | +</script> |
0 commit comments