Spaces:
Sleeping
Sleeping
<html> | |
<head> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> | |
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js" integrity="sha256-CMMTrj5gGwOAXBeFi7kNokqowkzbeL8ydAJy39ewjkQ=" crossorigin="anonymous"></script> | |
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.js" integrity="sha256-qwbDmNVLiCqkqRBpF46q5bjYH11j5cd+K+Y6D3/ja28=" crossorigin="anonymous"></script> | |
<script src="https://unpkg.com/split.js@1.3.5/split.js" integrity="sha384-zUUFqW6+QulITI/GAXB5UnHBryKHEZp4G+eNNC1/3Z3IJ+6CZRSFcdl4f7gCa957" crossorigin="anonymous"></script> | |
<style> | |
[v-cloak] { | |
display: none; | |
} | |
body, html { height: 100vh; } | |
#app { | |
border: 0; | |
margin: 0; | |
padding: 0; | |
height: 100vh; | |
width: 100vw; | |
} | |
table.pane { | |
float: left; | |
display: inline-table; | |
border: 0; | |
margin: 0; | |
padding: 0; | |
border-spacing: 0; | |
height: 100vh; | |
width: 50vw; | |
} | |
.gutter-horizontal { | |
float: left; | |
height: 100vh; | |
background-color: #eee; | |
cursor: ew-resize; | |
} | |
.palette, .preview { | |
height: 100%; | |
overflow-y: scroll; | |
text-align: center; | |
} | |
.unitviz, .unitviz .modal-header, .unitviz .modal-body, .unitviz .modal-footer { | |
font-family: Arial; | |
font-size: 15px; | |
} | |
.unitgrid { | |
text-align: center; | |
border-spacing: 5px; | |
border-collapse: separate; | |
max-height: 100%; | |
} | |
.unitgrid .info { | |
text-align: left; | |
display: block; | |
position: absolute; | |
top: 0; | |
color: white; | |
} | |
.unitgrid .layername { | |
} | |
.unitgrid .unitnum { | |
} | |
.unitlabel { | |
font-weight: bold; | |
line-height: 1; | |
position: absolute; | |
bottom: 0; | |
color: white; | |
white-space: nowrap; | |
} | |
.lowscore .unitlabel { | |
color: silver; | |
} | |
.thumbcrop { | |
overflow: hidden; | |
width: 72px; | |
height: 72px; | |
} | |
.thumbcrop img, .img-scroller img { | |
image-rendering: pixelated; | |
} | |
.unit { | |
display: inline-block; | |
background: white; | |
padding: 0; | |
margin: 2px; | |
box-shadow: 0 5px 12px grey; | |
overflow: hidden; | |
height: 72px; | |
width: 72px; | |
position: relative; | |
user-select: none; | |
} | |
.unit.ablated .ablationmark::after, | |
.selmodeablation .unit.dragged .ablationmark::after { | |
content: '\2716'; | |
bottom: 0; | |
} | |
.unit.inserted .insertionmark::after, | |
.selmodeinsertion .unit.dragged .insertionmark::after { | |
content: '\2713'; | |
font-weight: bold; | |
text-shadow: 0 0 #000; | |
bottom: 10px; | |
} | |
.ablationmark::after, .insertionmark::after { | |
line-height: 1; | |
position: absolute; | |
right: 0; | |
} | |
.unit.ablated .ablationmark { | |
color: red; | |
} | |
.unit.inserted .insertionmark { | |
color: lime; | |
} | |
.unit.dragged { | |
opacity: 0.8; | |
} | |
.selmodeablation .unit.dragged .ablationmark, | |
.selmodeinsertion .unit.dragged .insertionmark { | |
color: yellow; | |
} | |
.iou { | |
display: inline-block; | |
float: right; | |
} | |
.modal .big-modal { | |
width:auto; | |
max-width:90%; | |
max-height:80%; | |
} | |
.modal-title { | |
display: inline-block; | |
} | |
.footer-caption { | |
float: left; | |
width: 100%; | |
} | |
.histogram { | |
text-align: center; | |
margin-top: 3px; | |
} | |
.img-wrapper { | |
text-align: center; | |
} | |
.big-modal img { | |
max-height: 60vh; | |
} | |
.img-scroller { | |
overflow-x: scroll; | |
} | |
.img-scroller .img-fluid { | |
max-width: initial; | |
} | |
.gridheader { | |
font-size: 12px; | |
margin-bottom: 10px; | |
margin-left: 30px; | |
margin-right: 30px; | |
} | |
.gridheader:after { | |
content: ''; | |
display: table; | |
clear: both; | |
} | |
.sortheader { | |
float: right; | |
cursor: default; | |
} | |
.layerinfo { | |
float: left; | |
} | |
.sortby { | |
text-decoration: underline; | |
cursor: pointer; | |
} | |
.sortby.currentsort { | |
text-decoration: none; | |
font-weight: bold; | |
cursor: default; | |
} | |
.bg-inverse { | |
background: #021B54; | |
} | |
.layout { | |
width: 50%; | |
vertical-align: top; | |
} | |
.chooser-cell { | |
height: 100px; | |
background: #021B54; | |
color: white; | |
padding: 5px 5px 0; | |
} | |
.examplepair { | |
display: inline-block; | |
margin: 2px; | |
position: relative; | |
line-height: 0; | |
} | |
.exampleid { | |
position: absolute; | |
line-height: 1; | |
font-size: large; | |
font-weight: bold; | |
text-shadow: -1px 2px #000, 1px 2px #000; | |
bottom: 0; | |
color: white; | |
z-index: 1; | |
} | |
.querymark, .editmark { | |
position: absolute; | |
background: rgba(255,0,0,0.5); | |
width: 10px; | |
height: 10px; | |
margin: -5px; | |
border-radius: 5px; | |
top: 0; | |
left: 0; | |
pointer-events: none; | |
} | |
.editmark { | |
background: rgba(0,255,0,0.5); | |
} | |
.chooser-cell input[type=button] { | |
border-radius: 5px; padding: 0 5px; | |
} | |
</style> | |
</head> | |
<body class="unitviz"> | |
<div id="app" :class="['selmode' + selection_mode]" v-cloak> | |
<table class="pane" id="leftpane"> | |
<tr> | |
<td class="chooser-cell"> | |
<div v-if="dissect"> | |
<p> | |
<label for="selmode-ablation-radio"> | |
<input type="radio" id="selmode-ablation-radio" | |
value="ablation" v-model="selection_mode"> | |
<span style="color:red">✖</span> | |
Choose units to ablate.</label> | |
<label for="selmode-insertion-radio"> | |
<input type="radio" id="selmode-insertion-radio" | |
value="insertion" v-model="selection_mode"> | |
<span style="color:lime;font-weight:bold">✓</span> | |
Choose units to insert.</label> | |
</p> | |
<label for="ranking-radio"> | |
<input type="radio" id="ranking-radio" value="ranking" v-model="palette_mode"> | |
Show all units in | |
<select v-model="palette_layer" v-on:change="palette_mode='ranking'"> | |
<option v-for="(lrec, lind) in dissect.layers" :value="lind" | |
>{{lrec.layer}}</option> | |
</select>, | |
sorted by | |
<template v-for="lrec in [dissect.layers[palette_layer]]"> | |
<select v-model="sort_order" v-on:change="palette_mode='ranking'"> | |
<option v-for="rank in lrec['rankings']" :value="rank.name" | |
>{{rank.name}}</option> | |
</select> | |
</template> | |
</label><br> | |
<label for="ablation-radio"> | |
<input type="radio" id="ablation-radio" value="ablation" v-model="palette_mode"> | |
Show current ablation | |
({{ _.sum(_.map(selected_ablation, | |
function(x) { return _.sum(x)} )) | |
}} units ablated)</label> | |
<input type="button" value="Reset" v-on:click="resetselection('ablation')"> | |
<input type="button" :value="'Invert ' + dissect.layers[palette_layer].layer" | |
v-on:click="invertselection('ablation')"><br> | |
<label for="insertion-radio"> | |
<input type="radio" id="insertion-radio" value="insertion" | |
v-model="palette_mode"> | |
Show current insertion | |
({{ _.sum(_.map(selected_insertion, | |
function(x) { return _.sum(x)} )) | |
}} units inserted)</label> | |
<input type="button" value="Reset" v-on:click="resetselection('insertion')"> | |
<input type="button" :value="'Invert ' + dissect.layers[palette_layer].layer" | |
v-on:click="invertselection('insertion')"><br> | |
<label for="query-radio"> | |
<input type="radio" id="query-radio" value="query" v-model="palette_mode" | |
:disabled="!(dissect.layers[palette_layer].layer in query_ranking)"> | |
Units in {{ dissect.layers[palette_layer].layer }} | |
by | |
<select v-model="query_stat"> | |
<option value="mean_quantile">quantile of average</option> | |
<option value="max_quantile">quantile of maximum</option> | |
<option value="mean">average activation</option> | |
<option value="max">maximum activation</option> | |
</select> | |
activation on <span style="color:red">●</span> selected pixels of | |
{{ currentquery.map(x => 'image #' + x.id).join(', ') || 'an image' }}. | |
</label> | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td class="layout"> | |
<div class="palette" v-if="dissect"> | |
<div class="unitgrid" | |
><div :class="{unit: true, lowscore: !urec.interp, | |
ablated: (selected_ablation[urec.layer][urec.unit] > 0), | |
inserted: (selected_insertion[urec.layer][urec.unit] > 0), | |
dragged: (dragging.active && | |
((dragging.first <= ordernum && ordernum <= dragging.last) || | |
(dragging.last <= ordernum && ordernum <= dragging.first))) | |
}" | |
v-for="urec, ordernum in palette_units" | |
:data-ordernum="ordernum" | |
> | |
<template v-if="sort_order.indexOf('-') < 0" | |
><div v-if="'iou_label' in urec" class="unitlabel" | |
>{{urec.iou_label}}</div></template> | |
<div class="info" | |
><span class="layername">{{urec.layer}}</span | |
> <span class="unitnum">{{urec.unit}}</span> | |
</div> | |
<div class="thumbcrop" | |
><img | |
:src="urec.dirname + '/s-image/' + urec.unit + '-top.jpg'" | |
height="72" | |
></div> | |
<div class="ablationmark"></div> | |
<div class="insertionmark"></div> | |
</div> <!-- end unit --> | |
</div> <!-- end unitgrid --> | |
</div> <!-- end palette --> | |
</td> | |
</tr> | |
</table> <!-- end pane --> | |
<table class="pane" id="rightpane"> | |
<tr> | |
<td rowspan="2" class="layout"> | |
<div class="preview" v-if="dissect"> | |
<p>Seeds to generate <input size="30" v-model="image_numbers"></p> | |
<p style="text-align: left"> | |
To transfer activations from one pixel to another (1) click on a source pixel | |
on the left image and (2) click on a target pixel on a right image, | |
then (3) choose a set of units to insert in the palette.</p> | |
<div v-for="ex in examples" class="examplepair"> | |
<div class="exampleid">#{{ ex.id }}</div> | |
<img :src="ex.baseline" style="max-width:50%;" | |
:data-imgid="ex.id" | |
data-side="left" | |
v-on:click="clickexample" | |
><img :src="ex.modified" style="max-width:50%;" | |
:data-imgid="ex.id" | |
data-side="right" | |
v-on:click="clickexample" | |
><div class="querymark" v-if="ex.mask" :style="{ | |
top: ((ex.mask.bitbounds[0] + ex.mask.bitbounds[2]) / 0.02 | |
/ ex.mask.shape[0]) + '%', | |
left: ((ex.mask.bitbounds[1] + ex.mask.bitbounds[3]) / 0.04 | |
/ ex.mask.shape[1]) + '%'}" | |
></div><div class="editmark" v-if="ex.edit" :style="{ | |
top: ((ex.edit.bitbounds[0] + ex.edit.bitbounds[2]) / 0.02 | |
/ ex.edit.shape[0]) + '%', | |
left: ((ex.edit.bitbounds[1] + ex.edit.bitbounds[3]) / 0.04 | |
/ ex.edit.shape[1]) + 50 + '%'}" | |
></div> | |
</div> | |
</div> | |
</td> | |
</tr> | |
</table> | |
</div> <!-- end app --> | |
<div class="modal" id="lightbox"> | |
<div class="modal-dialog big-modal" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title"></h5> | |
<button type="button" class="close" | |
data-dismiss="modal" aria-label="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
<div class="img-wrapper img-scroller"> | |
<img class="fullsize img-fluid"> | |
</div> | |
</div> | |
<div class="modal-footer"> | |
<div class="footer-caption"> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
$(document).on('click', '[data-toggle=lightbox]', function(event) { | |
$('#lightbox img').attr('src', $(this).attr('href')); | |
$('#lightbox .modal-title').text($(this).data('title') || | |
$(this).closest('.unit').find('.unitlabel').text()); | |
$('#lightbox .footer-caption').text($(this).data('footer') || | |
$(this).closest('.unit').find('.info').text()); | |
event.preventDefault(); | |
$('#lightbox').modal(); | |
$('#lightbox img').closest('div').scrollLeft(0); | |
}); | |
$(document).on('mousedown', '.unit', function(event) { | |
if (!(event.buttons & 1)) return; | |
var layer = $(event.currentTarget).find('.info .layername').text(); | |
var unit = $(event.currentTarget).closest('.unit').data('ordernum'); | |
theapp.dragging.active = true; | |
theapp.dragging.first = unit; | |
theapp.dragging.last = unit; | |
event.preventDefault(); | |
}); | |
$(document).on('mouseup', function(event) { | |
if (!theapp.dragging.active) { return; } | |
theapp.dragging.active = false; | |
var first_sel = $('.unit').filter(function(i, elt) { | |
return elt.dataset.ordernum == theapp.dragging.first; | |
}).map(function(i, elt) { | |
return {layer: $(elt).find('.layername').text(), | |
unit: $(elt).find('.unitnum').text()}; })[0]; | |
var selected = $('.unit').filter(function(i, elt) { | |
return (theapp.dragging.first <= elt.dataset.ordernum && | |
elt.dataset.ordernum <= theapp.dragging.last) || | |
(theapp.dragging.last <= elt.dataset.ordernum && | |
elt.dataset.ordernum <= theapp.dragging.first); }) | |
.map(function(i, elt) { | |
return {layer: $(elt).find('.layername').text(), | |
unit: $(elt).find('.unitnum').text()}; }); | |
if (selected.length) { | |
var selection = 'selected_' + theapp.selection_mode; | |
var mode = 1 - theapp[selection][first_sel.layer][first_sel.unit]; | |
for (u of selected) { | |
theapp[selection][u.layer].splice(u.unit, 1, mode); | |
} | |
theapp.selectionchange(); | |
} | |
}); | |
$(document).on('mouseenter', '.unit', function(event) { | |
if (!(event.buttons & 1)) { theapp.dragging.active = false; } | |
if (!theapp.dragging.active) return; | |
theapp.dragging.last = | |
$(event.currentTarget).closest('.unit').data('ordernum'); | |
}); | |
$(function() { | |
window.Split(['#leftpane', '#rightpane'], { | |
sizes: [50, 50], | |
minSize: 280, | |
snapOffset: 0, | |
}); | |
}); | |
var theapp = new Vue({ | |
el: '#app', | |
data: { | |
palette_mode: 'ranking', | |
sort_order: 'unit', | |
palette_layer: null, | |
selected_ablation: null, | |
selected_insertion: null, | |
selection_mode: 'ablation', | |
dragging: { active: false, first: null, last: null }, | |
recipe: null, | |
dissect: null, | |
image_numbers: '10-19', | |
examples: _.range(10, 20).map(function(x) { | |
return {id: x, baseline: '', modified: '', mask: null, edit: null}; }), | |
query_stat: 'mean_quantile', | |
query_ranking: {}, | |
}, | |
created: function() { | |
var self = this; | |
$.getJSON('dissect.json?' + Math.random(), function(d) { | |
self.selected_ablation = {}; | |
self.selected_insertion = {}; | |
for (var layer of d.layers) { | |
// Preprocess ranking records to sort them. | |
for (var rank of layer.rankings) { | |
if (!('ranking' in rank)) { | |
rank.ranking = rank.score.map((score, index) => [score, index]) | |
.sort(([score1], [score2]) => score1 - score2) | |
.map(([, index]) => index); | |
} | |
} | |
// Note layer in each unit record to simplify mixing. | |
for (var urec of layer.units) { | |
urec.layer = layer.layer; | |
urec.dirname = layer.dirname; | |
} | |
Vue.set(self.selected_ablation, layer.layer, | |
_.fill(Array(layer.units.length), 0)); | |
Vue.set(self.selected_insertion, layer.layer, | |
_.fill(Array(layer.units.length), 0)); | |
} | |
self.dissect = d; | |
self.sort_order = d.default_ranking; | |
self.palette_layer = Math.floor((d.layers.length - 1) / 2); | |
}); | |
this.selectionchange(); | |
}, | |
computed: { | |
currentquery: function() { | |
var regions = []; | |
for (var ex of this.examples) { | |
if (ex.mask) { | |
regions.push({id: ex.id, mask: ex.mask}); | |
} | |
} | |
return regions; | |
}, | |
palette_units: function() { | |
if (this.palette_mode == 'ranking') { | |
// Order units according to an iou-matching sort order. | |
var lrec = this.dissect.layers[this.palette_layer]; | |
var ranking = _.find(lrec.rankings, x=>x.name == this.sort_order); | |
return ranking.ranking.map(x => lrec.units[x]); | |
} else if (this.palette_mode == 'ablation' || | |
this.palette_mode == 'insertion') { | |
// Show units involved in the edit | |
var result = []; | |
var selectionname = 'selected_' + this.palette_mode; | |
for (var lrec of this.dissect.layers) { | |
var sel = this[selectionname][lrec.layer]; | |
for (var u in sel) { | |
if (sel[u] > 0) { | |
result.push(lrec.units[u]); | |
} | |
} | |
} | |
return result; | |
} else if (this.palette_mode == 'query') { | |
// Order units according to query ranking | |
var lrec = this.dissect.layers[this.palette_layer]; | |
var ranking = this.query_ranking[lrec.layer]; | |
return ranking.ranking.map(x => lrec.units[x]); | |
} | |
} | |
}, | |
watch: { | |
query_stat: function() { | |
this.querychange(); | |
}, | |
palette_layer: function(val) { | |
// If sort_order is not available at this layer, reset it to default. | |
var self = this; | |
if (!_.find(self.dissect.layers[val].rankings, | |
function(x) { return x.name == self.sort_order; })) { | |
self.sort_order = self.dissect.default_ranking; | |
} | |
}, | |
image_numbers: function(val) { | |
// Parse a series of image numbers | |
var max_examples = 1000; | |
var rs = val.replace(/[^-\d]+/g, ' ').replace(/\s*-\s*/g, '-').split(' '); | |
var indexes = []; | |
for (var r of rs) { | |
if (r.match(/\d+-\d+/)) { | |
for (var i = parseInt(r.split('-')[0]); | |
i <= parseInt(r.split('-')[1]); i++) { | |
indexes.push(i); | |
} | |
} else if (r.match(/\d+/)) { | |
indexes.push(parseInt(r)); | |
} else if (indexes.length == 0) { | |
indexes.push(0); | |
} | |
if (indexes.length >= max_examples) { break; } | |
} | |
// Update examples to match. | |
var modified = false; | |
var examples = this.examples; | |
for (i = 0; i < indexes.length; i++) { | |
if (i >= examples.length) { | |
examples.push({id: indexes[i], baseline: '', modified: '', | |
mask: null }); | |
modified = true | |
} else if (examples[i].id != indexes[i]) { | |
examples[i].id = indexes[i]; | |
examples[i].baseline = ''; | |
examples[i].modified = ''; | |
examples[i].mask = null; | |
examples[i].edit = null; | |
modified = true | |
} | |
} | |
if (examples.length > indexes.length) { | |
examples.splice(indexes.length, examples.length - indexes.length); | |
modified = true | |
} | |
if (modified) { | |
this.generate_examples([], _.range(examples.length), true); | |
this.selectionchange(); | |
} | |
} | |
}, | |
methods: { | |
hashchange: function() { | |
this.palette_layer = +window.location.hash.substr(1) || 0; | |
}, | |
resetselection: function(mode) { | |
var selection = 'selected_' + (mode); | |
console.log(selection); | |
for (var layer in this[selection]) { | |
this[selection][layer] = _.fill(Array(this[selection][layer].length),0); | |
} | |
this.selectionchange(); | |
}, | |
invertselection: function(mode) { | |
var layer = this.dissect.layers[this.palette_layer].layer; | |
var selection = 'selected_' + (mode); | |
var sel = this[selection][layer]; | |
for (var u in sel) { | |
sel.splice(u, 1, 1 - sel[u]); | |
} | |
this.selectionchange(); | |
}, | |
selectionchange: function() { | |
var edited_indices = []; | |
var unedited_indices = []; | |
var ca = this.currentablations(); | |
for (var i in this.examples) { | |
var ci = this.currentinsertion(ca, i); | |
if (ci) { | |
edited_indices.push({intervention: ci, index: i}); | |
} else{ | |
unedited_indices.push(i); | |
} | |
} | |
if (!window.skipupdate && unedited_indices.length) { | |
this.generate_examples(ca, unedited_indices, false); | |
} | |
for (var r of edited_indices) { | |
this.generate_examples(r.intervention, [r.index], false); | |
} | |
}, | |
currentablations: function() { | |
var ablations = []; | |
if (this.selected_ablation) { | |
for (var layer in this.selected_ablation) { | |
for (var unit in this.selected_ablation[layer]) { | |
if (this.selected_ablation[layer][unit] > 0) { | |
ablations.push({ | |
layer: layer, | |
unit: parseInt(unit), | |
alpha: this.selected_ablation[layer][unit], | |
}); | |
} | |
} | |
} | |
} | |
return ablations.length ? [{ablations: ablations}] : []; | |
}, | |
currentinsertion: function(currentablations, exindex) { | |
if (!this.examples[exindex].edit || !this.selected_insertion) { | |
return null; | |
} | |
var insertions = []; | |
for (var layer in this.selected_insertion) { | |
for (var unit in this.selected_insertion[layer]) { | |
if (this.selected_insertion[layer][unit] > 0) { | |
insertions.push({ | |
layer: layer, | |
unit: parseInt(unit), | |
alpha: this.selected_insertion[layer][unit], | |
value: this.query_ranking && layer in this.query_ranking && | |
this.query_ranking[layer].activation[unit] || 0 | |
}); | |
} | |
} | |
} | |
var result = _.clone(currentablations); | |
result.push({ablations: insertions, mask: this.examples[exindex].edit}); | |
return result; | |
}, | |
querychange: function() { | |
this.query_regions(this.currentquery, this.currentablations()); | |
}, | |
query_regions: function(regions, intervention) { | |
if (!_.keys(regions).length) { | |
return; | |
} | |
var ids = regions.map(x => x.id); | |
var masks = regions.map(x => x.mask); | |
var self = this; | |
$.post({ | |
url: '/api/features', | |
data: JSON.stringify({ | |
project: this.currentproject(), | |
ids: ids, | |
masks: masks, | |
// layers: [this.dissect.layers[this.palette_layer].layer], | |
layers: this.dissect.layers.map(x => x.layer), | |
// interventions: intervention, | |
}), | |
headers: { | |
"Content-type": "application/json; charset=UTF-8" | |
}, | |
success: function(resp) { | |
var statname = self.query_stat; | |
var actname = statname.replace('_quantile', ''); | |
var stats = resp.res; | |
var result = {}; | |
for (var layer in stats) { | |
result[layer] = { | |
score: stats[layer][statname], | |
ranking: stats[layer][statname] | |
.map((score, index) => [score, index]) | |
.sort(([score1], [score2]) => score2 - score1) | |
.map(([, index]) => index), | |
activation: stats[layer][actname]}; | |
} | |
self.query_ranking = result; | |
self.palette_mode = 'query'; | |
// If there are any insertions, now the edit has changed | |
if (_.sum(_.map(self.selected_insertion, x => _.sum(x))) > 0) { | |
self.selectionchange(); | |
} | |
}, | |
dataType: 'json' | |
}); | |
}, | |
clickexample: function(ev) { | |
var elt = ev.currentTarget; | |
var imgid = elt.dataset.imgid * 1; | |
var side = elt.dataset.side; | |
var w = elt.naturalWidth; | |
var h = elt.naturalHeight; | |
var x = Math.round((ev.pageX - $(elt).offset().left) * (w / elt.width)); | |
var y = Math.round((ev.pageY - $(elt).offset().top) * (h / elt.height)); | |
// Clear all masks and leave one mask with one pixel chosen. | |
var field = (side == 'right') ? 'edit' : 'mask' | |
for (var ex of this.examples) { | |
if (ex.id != imgid) { | |
Vue.set(ex, field, null); | |
} else { | |
Vue.set(ex, field, { | |
shape: [h, w], | |
bitbounds: [y, x, y+1, x+1], | |
bitstring: '1' | |
}); | |
} | |
} | |
if (field == 'edit') { | |
this.selection_mode = 'insertion'; | |
this.selectionchange(); | |
} else { | |
this.querychange(); | |
} | |
}, | |
generate_examples: function(intervention, example_indexes, baseline_only) { | |
var self = this; | |
var ids = example_indexes.map(x => self.examples[x].id); | |
$.post({ | |
url: '/api/generate', | |
data: JSON.stringify({ | |
project: this.currentproject(), | |
wantz: 0, | |
ids: ids, | |
interventions: intervention | |
}), | |
headers: { | |
"Content-type": "application/json; charset=UTF-8" | |
}, | |
success: function(resp) { | |
for (var j in resp.res) { | |
var i = example_indexes[j]; | |
if (self.examples[i].id == ids[j]) { | |
if (!baseline_only) { | |
self.examples[i].modified = resp.res[j].d; | |
} | |
if (!intervention || !intervention.length) { | |
self.examples[i].baseline = resp.res[j].d; | |
} | |
} | |
} | |
}, | |
dataType: 'json' | |
}); | |
}, | |
currentproject: function() { | |
return location.pathname.match(/\/data\/([^\/]*)/)[1]; | |
} | |
}, | |
filters: { | |
fixed: function(value, digits, truncate) { | |
if (typeof value != 'number') return value; | |
var fixed = value.toFixed(digits); | |
return truncate ? +fixed : fixed; | |
} | |
} | |
}); | |
</script> | |
</body> | |
</html> | |