2019-12-16 11:19:48 +00:00
|
|
|
|
|
|
|
//////////////////////////
|
|
|
|
// use texture.js without d3 to create discerning patterns on the email blocks
|
|
|
|
/////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
// some faked dom, see https://github.com/riccardoscalco/textures/issues/17
|
|
|
|
function dom(name) {
|
|
|
|
this.name = name;
|
|
|
|
this.els = [];
|
|
|
|
this.attrs = {};
|
|
|
|
}
|
|
|
|
dom.prototype.append = function(name) {
|
|
|
|
if (name === "defs") return this;
|
|
|
|
if (this.name === undefined) {
|
|
|
|
this.name = name;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
var el = new dom(name);
|
|
|
|
this.els.push(el);
|
|
|
|
return el;
|
|
|
|
}
|
|
|
|
dom.prototype.attr = function(key, value) {
|
|
|
|
this.attrs[key] = value;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
dom.prototype.toString = function() {
|
|
|
|
var attrs = [];
|
|
|
|
var k;
|
|
|
|
for (k in this.attrs) {
|
|
|
|
attrs += " " + k + "='" + this.attrs[k] + "'";
|
|
|
|
}
|
|
|
|
if (this.els.length) {
|
|
|
|
return "<"+this.name+attrs+">"+this.els.map(function(el) { return el.toString()}).join('\n')+"</"+this.name+">";
|
|
|
|
} else {
|
|
|
|
return "<"+this.name+attrs+"/>"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//////////// end of faked dom ///////////////////////////////
|
|
|
|
|
|
|
|
let categoryMap = {};
|
|
|
|
let textureMap = {};
|
|
|
|
function getColoredTexture(i, color) {
|
|
|
|
let j = i % 11; // update when adding items to switch:
|
|
|
|
switch(j) {
|
|
|
|
case 0:
|
|
|
|
return textures.lines().size(4).strokeWidth(1).stroke(color);
|
|
|
|
case 1:
|
|
|
|
return textures.circles().radius(2).size(5).fill('none').strokeWidth(1).stroke(color).complement();
|
|
|
|
case 2:
|
|
|
|
return textures.lines().size(10).orientation("3/8").stroke(color);
|
|
|
|
case 3:
|
|
|
|
return textures.lines().heavier(4).thinner(.8).stroke(color);
|
|
|
|
case 4:
|
|
|
|
return textures.paths().d("hexagons").size(3).strokeWidth(1).stroke(color);
|
|
|
|
case 5:
|
|
|
|
return textures.lines().orientation("vertical", "horizontal").size(4).strokeWidth(1).shapeRendering("crispEdges").stroke(color);
|
|
|
|
case 6:
|
|
|
|
return textures.paths().d("hexagons").size(5).strokeWidth(2).stroke("rgba(0,0,0,0)").fill(color);
|
|
|
|
case 7:
|
2020-03-04 21:24:49 +00:00
|
|
|
return textures.circles().size(4).fill(color);
|
2019-12-16 11:19:48 +00:00
|
|
|
case 8:
|
2020-03-04 21:24:49 +00:00
|
|
|
return textures.circles().thicker().complement().fill(color);
|
2019-12-16 11:19:48 +00:00
|
|
|
case 9:
|
|
|
|
return textures.paths().d("caps").lighter().thicker().size(5).stroke(color);
|
|
|
|
case 10:
|
|
|
|
return textures.paths().d("hexagons").size(4).strokeWidth(2).stroke(color);
|
|
|
|
// textures.lines().size(4).strokeWidth(1).orientation("-3/8"),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const categoryColors = {
|
2020-03-04 21:24:49 +00:00
|
|
|
"person": "#FA0800",
|
|
|
|
"vehicle": "#80FF00",
|
|
|
|
"outdoor": "#00FFFF",
|
|
|
|
"animal": "#FF00FF",
|
|
|
|
"accessory": "#FFFF00",
|
|
|
|
"food": "#00FA85",
|
|
|
|
"furniture": "#BB00FA",
|
|
|
|
"indoor": "#8500FA",
|
|
|
|
"electronic": "#FAD900",
|
|
|
|
"kitchen": "#FA8500",
|
|
|
|
"sports": "#0080FF",
|
|
|
|
"appliance": "#FF0080",
|
2019-12-16 11:19:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function getColorForSuperCategory(name) {
|
|
|
|
return categoryColors[name];
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTextureForCategory(id) {
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
let hash = id;
|
|
|
|
if(!textureMap.hasOwnProperty(hash)) {
|
|
|
|
let color = categoryColors[categoryMap[id]['supercategory']];
|
|
|
|
textureMap[hash] = getColoredTexture(id, color);
|
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
return textureMap[hash];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CocoCanvas {
|
|
|
|
start(){
|
|
|
|
this.catNavEl = document.getElementById('catNav');
|
2020-03-04 21:24:49 +00:00
|
|
|
this.catNav2El = document.getElementById('catNav2');
|
2019-12-16 11:19:48 +00:00
|
|
|
this.canvas = document.getElementById('svgCanvas');
|
2020-03-04 21:24:49 +00:00
|
|
|
this.loadImagesBtnEl = document.getElementById("loadImages");
|
|
|
|
this.loadLabelsBtnEl = document.getElementById("loadLabels");
|
|
|
|
this.sceneSelectEl = document.getElementById('scene');
|
|
|
|
this.savedBtnEl = document.getElementById('save');
|
2019-12-16 11:19:48 +00:00
|
|
|
this.loadNav()
|
2020-03-04 21:24:49 +00:00
|
|
|
this.annotations = [];
|
|
|
|
|
|
|
|
this.loadImagesBtnEl.addEventListener('change', (e) => this.toggleImages(e));
|
|
|
|
this.loadLabelsBtnEl.addEventListener('change', (e) => this.toggleLabels(e));
|
2020-03-10 10:53:00 +00:00
|
|
|
this.sceneSelectEl.addEventListener('change', (e) => this.toggleBackground(e));
|
2020-03-04 21:24:49 +00:00
|
|
|
this.savedBtnEl.addEventListener('click', function(e){
|
|
|
|
if(this.savedBtnEl.disabled || this.annotations.length < 1) return;
|
|
|
|
this.save();
|
|
|
|
}.bind(this));
|
2020-03-10 10:53:00 +00:00
|
|
|
|
|
|
|
this.toggleBackground();
|
2019-12-16 11:19:48 +00:00
|
|
|
}
|
|
|
|
loadNav() {
|
2020-03-10 08:05:17 +00:00
|
|
|
let r = new Request('categories.json');
|
2019-12-16 11:19:48 +00:00
|
|
|
fetch(r)
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(categories => {
|
|
|
|
for(let cat of categories) {
|
|
|
|
categoryMap[cat['id']] = cat;
|
|
|
|
}
|
|
|
|
this.buildNav(categories);
|
|
|
|
}).catch(function(e){
|
|
|
|
console.error(e);
|
|
|
|
});
|
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
buildNav(categories) {
|
2020-03-04 21:24:49 +00:00
|
|
|
let lastSuperCat = null;
|
|
|
|
let supercategories = []
|
|
|
|
let supercategoriesEls = {}
|
|
|
|
// create menu's per supercategory, divide these over left & right
|
2019-12-16 11:19:48 +00:00
|
|
|
for(let cat of categories) {
|
2020-03-04 21:24:49 +00:00
|
|
|
if(supercategories.indexOf(cat['supercategory']) < 0) {
|
|
|
|
supercategories.push(cat['supercategory']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(let supercat of supercategories) {
|
|
|
|
let ulEl = crel('ul', {'data-name': supercat, 'class':'supercategory-'+supercat});
|
|
|
|
supercategoriesEls[supercat] = ulEl;
|
|
|
|
// first half left, other half right side of the page
|
|
|
|
if(supercategories.indexOf(supercat)/supercategories.length < .5) {
|
|
|
|
this.catNavEl.appendChild(ulEl);
|
|
|
|
} else {
|
|
|
|
this.catNav2El.appendChild(ulEl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// entries for the menus
|
|
|
|
for(let cat of categories) {
|
|
|
|
// let firstOfType = lastSuperCat != cat['supercategory'] ? ' first-of-super' : '';
|
|
|
|
// lastSuperCat = cat['supercategory'];
|
|
|
|
supercategoriesEls[cat['supercategory']].appendChild(crel('li', {
|
|
|
|
'id': 'category-' + cat['id'],
|
|
|
|
// 'class': 'supercategory-' + cat['supercategory'],
|
|
|
|
'on': {
|
|
|
|
'click': (e) => {
|
|
|
|
this.requestAnnotation(cat['id']);
|
2019-12-16 11:19:48 +00:00
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
}
|
|
|
|
}, cat['name']));
|
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
this.defsEl = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
|
2019-12-16 11:19:48 +00:00
|
|
|
for(let cat of categories) {
|
|
|
|
let texture = getTextureForCategory(cat['id']);
|
|
|
|
let sel = new dom();
|
|
|
|
texture(sel);
|
2020-03-04 21:24:49 +00:00
|
|
|
this.defsEl.innerHTML += sel.toString();
|
2019-12-16 11:19:48 +00:00
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
this.canvas.appendChild(this.defsEl);
|
2019-12-16 11:19:48 +00:00
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
requestAnnotation(category_id) {
|
2020-03-10 08:05:17 +00:00
|
|
|
let r = new Request(`annotation.json?category=${category_id}&normalise=100`);
|
2020-03-04 21:24:49 +00:00
|
|
|
return fetch(r)
|
2019-12-16 11:19:48 +00:00
|
|
|
.then(response => response.json())
|
|
|
|
.then(annotation => {
|
|
|
|
this.addAnnotationAsShape(annotation);
|
|
|
|
}).catch(function(e){
|
|
|
|
console.error(e);
|
2020-03-04 21:24:49 +00:00
|
|
|
});
|
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
pointsToD(points) {
|
|
|
|
let start = points.shift()
|
|
|
|
let d = `M${start[0].toPrecision(4)} ${start[1].toPrecision(4)} L `;
|
|
|
|
points = points.map((p) => `${p[0].toPrecision(4)} ${p[1].toPrecision(4)}`);
|
|
|
|
d += points.join(' ');
|
|
|
|
return d;
|
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
getMousePosition(evt) {
|
|
|
|
// from http://www.petercollingridge.co.uk/tutorials/svg/interactive/dragging/
|
|
|
|
let CTM = this.canvas.getScreenCTM();
|
|
|
|
return {
|
|
|
|
x: (evt.clientX - CTM.e) / CTM.a,
|
|
|
|
y: (evt.clientY - CTM.f) / CTM.d
|
|
|
|
};
|
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
addAnnotationAsShape(annotation) {
|
|
|
|
console.log('Add annotation', annotation);
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
let category = categoryMap[annotation['category_id']]
|
|
|
|
let texture = getTextureForCategory(category['id']);
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
let x = 500 - annotation['bbox'][2]/2;
|
|
|
|
let y = 500 - annotation['bbox'][3]/2;
|
|
|
|
let annEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'g'), {
|
|
|
|
'data-id': annotation['id'],
|
|
|
|
'transform': `translate(${x}, ${y})`,
|
|
|
|
'on': {
|
|
|
|
'mousedown': function(downE) {
|
2020-03-04 21:24:49 +00:00
|
|
|
// after clicking the element should be the top element
|
|
|
|
// and the last svg element is drawn on top.
|
|
|
|
annEl.parentNode.appendChild(annEl);
|
|
|
|
// console.log(downE);
|
|
|
|
// console.log(this)
|
2019-12-16 11:19:48 +00:00
|
|
|
let offset = this.getMousePosition(downE);
|
|
|
|
|
|
|
|
// Get initial translation amount
|
2020-03-04 21:24:49 +00:00
|
|
|
// console.log(annEl.transform.baseVal);
|
2019-12-16 11:19:48 +00:00
|
|
|
let transform = annEl.transform.baseVal.getItem(0);
|
|
|
|
offset.x -= transform.matrix.e;
|
|
|
|
offset.y -= transform.matrix.f;
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
let moveEvent = (moveE) => {
|
|
|
|
let coord = this.getMousePosition(moveE);
|
|
|
|
transform.matrix.e = coord.x - offset.x;
|
|
|
|
transform.matrix.f = coord.y - offset.y;
|
|
|
|
// annEl.setAttributeNS(null, "x", coord.x - offset.x);
|
|
|
|
// annEl.setAttributeNS(null, "y", coord.y - offset.y);
|
|
|
|
};
|
2020-03-04 21:24:49 +00:00
|
|
|
|
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
document.addEventListener('mousemove', moveEvent);
|
|
|
|
document.addEventListener('mouseup', (upE) => {
|
|
|
|
document.removeEventListener('mousemove', moveEvent);
|
|
|
|
});
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
}.bind(this)
|
|
|
|
}
|
|
|
|
});
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
for(let segment of annotation['segments']) {
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
let pathEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'path'), {
|
|
|
|
'fill': texture.url(),
|
|
|
|
'd': this.pointsToD(segment)
|
|
|
|
});
|
|
|
|
annEl.appendChild(pathEl);
|
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
2019-12-16 11:19:48 +00:00
|
|
|
this.canvas.appendChild(annEl);
|
2020-03-04 21:24:49 +00:00
|
|
|
|
|
|
|
// keep info on annotation elements in CocoCanvas object
|
|
|
|
annotation['element'] = annEl;
|
|
|
|
this.annotations.push(annotation);
|
|
|
|
this.savedBtnEl.disabled = false;
|
|
|
|
|
|
|
|
// based on status of checkboxes, add label or image (bit of a later hack)
|
|
|
|
this.toggleImages();
|
|
|
|
this.toggleLabels();
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert annotation shapes on canvas to images, which are masked by the shape
|
|
|
|
convertToImages() {
|
|
|
|
console.log('convert');
|
|
|
|
for(let annotation of this.annotations) {
|
|
|
|
console.log(annotation);
|
|
|
|
if(annotation['element'].getElementsByTagName('image').length) {
|
|
|
|
//already done
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let scale = annotation['scale'];
|
|
|
|
|
|
|
|
let bbox = annotation['is_normalised'] ? annotation['bbox_original'] : annotation['bbox'];
|
|
|
|
let imgEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'image'), {
|
|
|
|
'href': annotation['image']['coco_url'],
|
|
|
|
'width': annotation['image']['width']* scale,
|
|
|
|
'height': annotation['image']['height']* scale,
|
|
|
|
'x': bbox[0] * -1 * scale,
|
|
|
|
'y': bbox[1] * -1 * scale,
|
|
|
|
});
|
|
|
|
annotation['element'].prepend(imgEl );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addLabels() {
|
|
|
|
console.log('labels');
|
|
|
|
for(let annotation of this.annotations) {
|
|
|
|
console.log(annotation);
|
|
|
|
if(annotation['element'].getElementsByTagName('text').length) {
|
|
|
|
//already done
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let textEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'text'), {
|
|
|
|
'x': annotation['bbox'][2] + 5,
|
|
|
|
'y': annotation['bbox'][3],
|
|
|
|
}, categoryMap[annotation['category_id']]['name']);
|
|
|
|
annotation['element'].append(textEl );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleImages(e) {
|
|
|
|
if(this.loadImagesBtnEl.checked) {
|
|
|
|
this.convertToImages();
|
|
|
|
document.body.classList.remove('hideImages');
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
document.body.classList.add('hideImages');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
toggleLabels(e) {
|
|
|
|
if(this.loadLabelsBtnEl.checked) {
|
|
|
|
this.addLabels();
|
|
|
|
document.body.classList.remove('hideLabels');
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
document.body.classList.add('hideLabels');
|
|
|
|
}
|
|
|
|
}
|
2020-03-10 10:53:00 +00:00
|
|
|
|
|
|
|
toggleBackground(e) {
|
|
|
|
let url = cc.sceneSelectEl.selectedOptions[0].dataset.background;
|
|
|
|
document.body.style.backgroundImage = `url(${url})`;
|
|
|
|
}
|
2020-03-04 21:24:49 +00:00
|
|
|
|
|
|
|
save() {
|
|
|
|
let scene = this.sceneSelectEl.value;
|
|
|
|
let annotations = [];
|
|
|
|
for (let ann of this.annotations) {
|
|
|
|
annotations.push({
|
|
|
|
'id': ann['id'],
|
|
|
|
'x': ann.element.transform.baseVal.getItem(0).matrix.e,
|
|
|
|
'y': ann.element.transform.baseVal.getItem(0).matrix.f
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let data = JSON.stringify({
|
|
|
|
'scene': this.sceneSelectEl.value,
|
|
|
|
'annotations': annotations
|
|
|
|
});
|
2020-03-10 08:05:17 +00:00
|
|
|
let r = new Request('save', {'method': 'POST', 'body': data});
|
2020-03-04 21:24:49 +00:00
|
|
|
fetch(r)
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(submission => {
|
|
|
|
console.log('saved', submission);
|
|
|
|
window.location = window.location + 'saved';
|
|
|
|
}).catch(function(e){
|
|
|
|
alert("Something went wrong when saving the file");
|
|
|
|
console.error(e);
|
|
|
|
});
|
2019-12-16 11:19:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let cc = new CocoCanvas();
|
|
|
|
cc.start();
|
2020-03-04 21:24:49 +00:00
|
|
|
|
|
|
|
//for testing only:
|
|
|
|
//cc.requestAnnotation(1).then((e) => cc.convertToImages());
|