Version with save option
This commit is contained in:
parent
1702cce9d1
commit
7d11274be8
13 changed files with 1150 additions and 132 deletions
30
README.md
30
README.md
|
@ -1,3 +1,20 @@
|
|||
server.py
|
||||
: Server for the web interface
|
||||
|
||||
create_shapes.py
|
||||
: Create an svg file per image, with classes on the shapes according to their classes
|
||||
|
||||
zoom_animation.py
|
||||
: Create svg frames for a category. Ordered by the area of the shapes.
|
||||
|
||||
generate_lonely_segments.py
|
||||
: Find and download the images with only one object in them.
|
||||
|
||||
tools.py
|
||||
: Turn a COCO json file (eg `instances_val2017.json`) into a database format (eg `coco_train.db`)
|
||||
|
||||
|
||||
---
|
||||
|
||||
Build array of images sorted by size:
|
||||
|
||||
|
@ -20,3 +37,16 @@ cd ../dog_png
|
|||
ffmpeg -f image2 -pattern_type glob -i '*.png' ../dog.mp4
|
||||
|
||||
```
|
||||
|
||||
# To run as server:
|
||||
```bash
|
||||
cp plottingdata_coco.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable plottingdata_coco.service
|
||||
systemctl start plottingdata_coco.service
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
rsync . --exclude zoom --exclude venv --exclude archive -av here.rubenvandeven.com:/home/ruben/coco/ --exclude shapes --exclude lonely --exclude .git --exclude __pycache__ --info progress2
|
||||
```
|
||||
|
|
1
coco.sql
1
coco.sql
|
@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS "annotations" (
|
|||
"bbox_left" FLOAT,
|
||||
"bbox_width" FLOAT,
|
||||
"bbox_height" FLOAT,
|
||||
"zerkine_moments" TEXT DEFAULT NULL,
|
||||
PRIMARY KEY("id")
|
||||
) WITHOUT ROWID;
|
||||
CREATE INDEX IF NOT EXISTS "segments_annotation" ON "segments" (
|
||||
|
|
9
plottingdata_coco.service
Normal file
9
plottingdata_coco.service
Normal file
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description=PlottingData COCO interface
|
||||
[Service]
|
||||
ExecStart=/home/ruben/coco/venv/bin/python /home/ruben/coco/server.py --db /home/ruben/coco/coco_train.db --port 8080
|
||||
WorkingDirectory=/home/ruben/coco
|
||||
User=www-data
|
||||
Restart=on-failure
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
tornado
|
||||
coloredlogs
|
||||
pycocotools
|
||||
numpy
|
||||
mahotas
|
110
server.py
110
server.py
|
@ -7,6 +7,9 @@ import coloredlogs
|
|||
from coco.storage import COCOStorage
|
||||
import json
|
||||
from urllib.parse import urlparse
|
||||
import uuid
|
||||
import os
|
||||
import glob
|
||||
|
||||
logger = logging.getLogger('coco.server')
|
||||
|
||||
|
@ -25,6 +28,9 @@ class RestHandler(tornado.web.RequestHandler):
|
|||
|
||||
def get(self, *params):
|
||||
self.write(json.dumps(self.getData(*params), cls=JsonEncoder))
|
||||
|
||||
def post(self, *params):
|
||||
self.write(json.dumps(self.getData(*params), cls=JsonEncoder))
|
||||
|
||||
class CategoryHandler(RestHandler):
|
||||
def getData(self):
|
||||
|
@ -51,28 +57,93 @@ class AnnotationHandler(RestHandler):
|
|||
if normalise:
|
||||
return annotation.getNormalised(normalise, normalise)
|
||||
return annotation
|
||||
|
||||
|
||||
class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
||||
CORS_ORIGINS = ['localhost', 'coco.local', 'r3.local']
|
||||
|
||||
def check_origin(self, origin):
|
||||
parsed_origin = urlparse(origin)
|
||||
# parsed_origin.netloc.lower() gives localhost:3333
|
||||
valid = parsed_origin.hostname in self.CORS_ORIGINS
|
||||
return valid
|
||||
|
||||
# the client connected
|
||||
def open(self, p = None):
|
||||
WebSocketHandler.connections.add(self)
|
||||
logger.info("New client connected")
|
||||
self.write_message("hello!")
|
||||
|
||||
# the client sent the message
|
||||
def on_message(self, message):
|
||||
logger.debug(f"recieve: {message}")
|
||||
class SaveHandler(RestHandler):
|
||||
def getData(self):
|
||||
"""
|
||||
Save an SVG. Regenerate it on the server to prevent any maliscious input
|
||||
"""
|
||||
req = tornado.escape.json_decode(self.request.body)
|
||||
scene = int(req['scene'])
|
||||
annotations = []
|
||||
|
||||
with open('www/canvas_patterns.json') as fp:
|
||||
patterns = json.load(fp)
|
||||
svgGs = []
|
||||
|
||||
for annotation in req['annotations'][:100]: # max 200 annotations
|
||||
annId = int(annotation['id'])
|
||||
ann = self.storage.getAnnotationById(annId)
|
||||
normalisedAnn = ann.getNormalised(100,100)
|
||||
x = float(annotation['x'])
|
||||
y = float(annotation['y'])
|
||||
fill = patterns[str(ann.category_id)]
|
||||
segments = []
|
||||
|
||||
textX = normalisedAnn.bbox[2]+5
|
||||
textY = normalisedAnn.bbox[3]
|
||||
|
||||
cat = self.storage.getCategory(ann.category_id)
|
||||
image = self.storage.getImage(ann.image_id)
|
||||
|
||||
for segment in normalisedAnn.segments:
|
||||
d = segment.getD()
|
||||
segments.append(f"""<path fill="{fill}" d="{d}"></path>""")
|
||||
svgGs.append(f"""
|
||||
<g data-id="{ann.id}" transform="translate({x},{y})">
|
||||
<image href="{image ['coco_url']}"
|
||||
width="{image['width']*normalisedAnn.scale}"
|
||||
height="{image['height']*normalisedAnn.scale}"
|
||||
x="{ann.bbox[0] * -1 * normalisedAnn.scale}"
|
||||
y="{ann.bbox[1] * -1 * normalisedAnn.scale}"></image>
|
||||
{"".join(segments)}
|
||||
<text fill="white" font-size="40pt" font-family="sans-serif" x="{textX}" y="{textY}">{cat['name']}</text>
|
||||
</g>
|
||||
""")
|
||||
annotations.append({'id': annId, 'x': x, 'y': y})
|
||||
|
||||
source = json.dumps({
|
||||
'scene': scene,
|
||||
'annotations': annotations
|
||||
})
|
||||
|
||||
with open('www/canvas.svg') as fp:
|
||||
svgContent = fp.read()
|
||||
svgContent = svgContent.replace('{source}', json.dumps(source))\
|
||||
.replace('</svg>', "".join(svgGs)+"</svg>")
|
||||
|
||||
|
||||
saveId = uuid.uuid4().hex + '.svg'
|
||||
filename = os.path.join('www/saved', saveId)
|
||||
with open(filename, 'w') as fp:
|
||||
fp.write(svgContent)
|
||||
return {'submission':'/saved/'+saveId}
|
||||
|
||||
|
||||
|
||||
class SavedHandler(tornado.web.RequestHandler):
|
||||
def initialize(self, storage: COCOStorage):
|
||||
self.storage = storage
|
||||
|
||||
def get(self):
|
||||
images = []
|
||||
files = glob.glob("www/saved/*.svg")
|
||||
files.sort(key=lambda f: -1 * os.path.getmtime(f))
|
||||
|
||||
for filename in files[:100]:
|
||||
with open(filename, 'r') as fp:
|
||||
# remove first XML line:
|
||||
contents = '\n'.join(fp.read().split('\n')[1:])
|
||||
images.append(contents)
|
||||
|
||||
with open("www/saved.html") as fp:
|
||||
template = fp.read()
|
||||
|
||||
template = template.replace("{images}", ''.join(images))
|
||||
self.write(template)
|
||||
|
||||
|
||||
class StaticFileWithHeaderHandler(tornado.web.StaticFileHandler):
|
||||
def set_extra_headers(self, path):
|
||||
"""For subclass to add extra headers to the response"""
|
||||
|
@ -83,9 +154,10 @@ def make_app(db_filename, debug):
|
|||
storage = COCOStorage(db_filename)
|
||||
|
||||
return tornado.web.Application([
|
||||
(r"/ws(.*)", WebSocketHandler),
|
||||
(r"/categories.json", CategoryHandler, {'storage': storage}),
|
||||
(r"/annotation.json", AnnotationHandler, {'storage': storage}),
|
||||
(r"/save", SaveHandler, {'storage': storage}),
|
||||
(r"/saved", SavedHandler, {'storage': storage}),
|
||||
(r"/(.*)", StaticFileWithHeaderHandler,
|
||||
{"path": 'www', "default_filename": 'index.html'}),
|
||||
], debug=debug)
|
||||
|
|
243
tools.py
243
tools.py
|
@ -4,6 +4,16 @@ import logging
|
|||
import os
|
||||
import pprint
|
||||
import sqlite3
|
||||
from coco.storage import COCOStorage, Annotation, Segment
|
||||
import cv2
|
||||
import mahotas
|
||||
import subprocess
|
||||
import tqdm
|
||||
import numpy as np
|
||||
import ast
|
||||
import svgwrite
|
||||
from svgwrite.extensions import Inkscape
|
||||
from xml.etree import ElementTree
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger("coco")
|
||||
|
@ -20,10 +30,34 @@ argParser.add_argument(
|
|||
help='Show categories'
|
||||
)
|
||||
argParser.add_argument(
|
||||
'--propagate',
|
||||
'--db',
|
||||
type=str,
|
||||
metavar='DATABASE',
|
||||
help='Store data in sqlite db'
|
||||
help='SQLite db filename, will be created if not existing'
|
||||
)
|
||||
argParser.add_argument(
|
||||
'--propagate',
|
||||
action='store_true',
|
||||
help='Store annotation data in sqlite db'
|
||||
)
|
||||
argParser.add_argument(
|
||||
'--zerkine',
|
||||
action='store_true',
|
||||
help='Find and store annotation Zerkine moments for those that do not have it yet'
|
||||
)
|
||||
argParser.add_argument(
|
||||
'--similar',
|
||||
type=int,
|
||||
metavar="ANNOTATION_ID",
|
||||
help='Find similar shapes for annotation'
|
||||
)
|
||||
argParser.add_argument(
|
||||
'--stickers',
|
||||
type=str,
|
||||
metavar="SVG_FILENAME",
|
||||
help="""
|
||||
Create an SVG with sticker pages (afterwards convert to EPS: \"for f in *; do echo $f; inkscape -f $f --export-eps $f.eps; done\")
|
||||
"""
|
||||
)
|
||||
args = argParser.parse_args()
|
||||
|
||||
|
@ -40,31 +74,28 @@ if args.categories:
|
|||
cats[cat['supercategory']].append(cat)
|
||||
# pp = pprint.PrettyPrinter(indent=4)
|
||||
pprint.pprint(cats, sort_dicts=False)
|
||||
|
||||
|
||||
storage = None
|
||||
if args.db:
|
||||
storage = COCOStorage(args.db)
|
||||
con = storage.con
|
||||
|
||||
if args.propagate:
|
||||
if not os.path.exists(args.propagate):
|
||||
con = sqlite3.connect(args.propagate)
|
||||
cur = con.cursor()
|
||||
with open('coco.sql', 'r') as fp:
|
||||
cur.executescript(fp.read())
|
||||
con.close()
|
||||
|
||||
con = sqlite3.connect(args.propagate)
|
||||
logger.info("Create categories")
|
||||
cur = con.cursor()
|
||||
cur.executemany('INSERT OR IGNORE INTO categories(id, supercategory, name) VALUES (:id, :supercategory, :name)', coco.cats.values())
|
||||
con.commit()
|
||||
|
||||
|
||||
logger.info("Images...")
|
||||
cur.executemany('''
|
||||
INSERT OR IGNORE INTO images(id, flickr_url, coco_url, width, height, date_captured)
|
||||
VALUES (:id, :flickr_url, :coco_url, :width, :height, :date_captured)
|
||||
''', coco.imgs.values())
|
||||
con.commit()
|
||||
|
||||
|
||||
logger.info("Annotations...")
|
||||
|
||||
|
||||
|
||||
|
||||
def annotation_generator():
|
||||
for c in coco.anns.values():
|
||||
ann = c.copy()
|
||||
|
@ -73,16 +104,16 @@ if args.propagate:
|
|||
ann['bbox_width'] = ann['bbox'][2]
|
||||
ann['bbox_height'] = ann['bbox'][3]
|
||||
yield ann
|
||||
|
||||
|
||||
cur.executemany('''
|
||||
INSERT OR IGNORE INTO annotations(id, image_id, category_id, iscrowd, area, bbox_top, bbox_left, bbox_width, bbox_height)
|
||||
VALUES (:id, :image_id, :category_id, :iscrowd, :area, :bbox_top, :bbox_left, :bbox_width, :bbox_height)
|
||||
''', annotation_generator())
|
||||
con.commit()
|
||||
|
||||
|
||||
|
||||
|
||||
logger.info("Segments...")
|
||||
|
||||
|
||||
def segment_generator():
|
||||
for ann in coco.anns.values():
|
||||
for i, seg in enumerate(ann['segmentation']):
|
||||
|
@ -91,16 +122,178 @@ if args.propagate:
|
|||
'annotation_id': ann['id'],
|
||||
'points': str(seg)[1:-1],
|
||||
}
|
||||
|
||||
|
||||
cur.executemany('''
|
||||
INSERT OR IGNORE INTO segments(id, annotation_id, points)
|
||||
VALUES (:id, :annotation_id, :points)
|
||||
''', segment_generator())
|
||||
con.commit()
|
||||
|
||||
|
||||
|
||||
|
||||
logger.info("Done...")
|
||||
|
||||
if args.zerkine:
|
||||
nr = storage.countAnnotationsWithoutZerkine()
|
||||
for i in tqdm.tqdm(range(nr)):
|
||||
annotation = storage.getAnnotationWithoutZerkine()
|
||||
normAnn = annotation.getNormalised(100, 100)
|
||||
filenameRoot = '/tmp/tmp_ann_to_convert'
|
||||
dwg = normAnn.asSvg(filenameRoot + '.svg', square=True, bg='black')
|
||||
dwg.save()
|
||||
# convert to rasterised
|
||||
subprocess.call([
|
||||
'inkscape',
|
||||
'-f', filenameRoot + '.svg',
|
||||
'-e', filenameRoot + '.png',
|
||||
],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
# read with opencv
|
||||
image = cv2.imread(filenameRoot + '.png', cv2.IMREAD_GRAYSCALE)
|
||||
moments = mahotas.features.zernike_moments(image, 21)
|
||||
storage.storeZerkineForAnnotation(annotation, moments, delayCommit = True)
|
||||
if not i % 100:
|
||||
storage.con.commit()
|
||||
storage.con.commit()
|
||||
|
||||
if args.similar:
|
||||
# todo find similar
|
||||
annotation = storage.getAnnotationById(args.similar, withZerkine=True)
|
||||
dwg = annotation.asSvg(f'tmp/source.svg', square=True)
|
||||
dwg.save()
|
||||
|
||||
# for id, cat in coco.cats.items():
|
||||
# cur = con.cursor()
|
||||
# cur.execute
|
||||
shapeA = np.array(annotation.segments[0].points)
|
||||
|
||||
annMoments = np.array(annotation.zerkine_moment)
|
||||
distances = []
|
||||
print(annotation)
|
||||
# Stupid, this seems to have been superfluous
|
||||
# zerkines = storage.getZerkines()
|
||||
# for zerkine in tqdm.tqdm(zerkines):
|
||||
# if annotation.id == zerkine['id']:
|
||||
# continue
|
||||
#
|
||||
# diff = annMoments - np.array(Annotation.parseZerkineFromDB(zerkine['zerkine_moment']))
|
||||
# distance = np.linalg.norm(diff)
|
||||
# distances.append((zerkine['id'], distance))
|
||||
anns = storage.getAllAnnotationPoints()
|
||||
for ann in tqdm.tqdm(anns):
|
||||
try:
|
||||
shapeB = np.array(Segment.asCoordinates(ast.literal_eval('['+ann['points']+']')))
|
||||
# fourth param is require, but according to docs does nothing
|
||||
distance= cv2.matchShapes(shapeA, shapeB, cv2.CONTOURS_MATCH_I2, 1)
|
||||
distances.append((ann['id'], distance))
|
||||
except Exception as e:
|
||||
logger.critical(f"Exception comparing {annotation.id} to {ann['id']}, points: {ann['points']}")
|
||||
logger.exception(e)
|
||||
|
||||
distances = sorted(distances, key=lambda d: d[1])
|
||||
|
||||
for i in range(10):
|
||||
similarAnnotation = storage.getAnnotationById(distances[i][0])
|
||||
print(coco.cats[similarAnnotation.category_id])
|
||||
dwg = similarAnnotation.asSvg(f'tmp/result_{i}.svg', square=True)
|
||||
dwg.save()
|
||||
|
||||
if args.stickers:
|
||||
grid = (3, 4) # items in the grid x,y
|
||||
size = (105, 148) # in mm
|
||||
sizeFactor = 5 # influences the size of the patterns
|
||||
viewBoxSize = (size[0] * sizeFactor, size[1] * sizeFactor)
|
||||
margin = 5
|
||||
gridSize = (
|
||||
int((viewBoxSize[0]-((grid[0]+1)*margin))/grid[0]),
|
||||
int((viewBoxSize[1]-((grid[1]+1)*margin))/grid[1])
|
||||
)
|
||||
|
||||
# see also textures.xml
|
||||
textureIds = ["#siqwx","#wjnbs","#pnfez","#ejtxy","#obabs","#hehoj","#mrwjs","#ryjbw","#rkkau","#vbjcl","#zzehx","#mumke","#brhhk","#gujvh","#hfgqa","#lrbsh","#bndby","#bfnxk","#ydler","#pnxdr","#htqlj","#nunnt","#tidaw","#tcdum","#kwwja","#hgdkl","#nvkwz","#uzdqb","#fgshk","#vknil","#yeenr","#mslkw","#eibaw","#meama","#akuvz","#khkpp","#ibnow","#wivvx","#svksy","#xhmew","#jmiqu","#gfcer","#iueil","#iufvt","#ugkud","#dchzd","#nejks","#dqseb","#yhrwm","#bmiet","#qovkk","#hxoiq","#jfguh","#kbpkl","#ikarj","#nucap","#qfsqn","#bboqt","#pxkjn","#lbnx","#nxkmp","#snojb","#oioil","#hvldz","#qpscp","#oborh","#crobu","#ydhwn","#geanf","#sdfeo","#cgtma","#rjfrc","#uhcys","#lrgem","#osiho","#etssd","#esxcs","#hczhr","#nnhxw","#wrlbu"]
|
||||
|
||||
nr = 0
|
||||
total_nr = len(coco.cats)
|
||||
for category_id, cat in coco.cats.items():
|
||||
nr+=1
|
||||
filename = os.path.join(
|
||||
args.stickers,
|
||||
f"{category_id}_{cat['supercategory']}_{cat['name']}.svg")
|
||||
dwg = svgwrite.Drawing(
|
||||
filename,
|
||||
size=(f'{size[0]}mm', f'{size[1]}mm'),
|
||||
viewBox=f"0 0 {viewBoxSize[0]} {viewBoxSize[1]}"
|
||||
)
|
||||
|
||||
annotations = storage.getRandomAnnotations(
|
||||
limit = grid[0]*grid[1],
|
||||
category_id = category_id
|
||||
)
|
||||
|
||||
inkscape = Inkscape(dwg)
|
||||
contourG = inkscape.layer(label='Snijlijnen')
|
||||
drawingG = inkscape.layer(label='Shapes')
|
||||
|
||||
# dwg.add(svgwrite.container.Defs())
|
||||
dwg.add(drawingG)
|
||||
dwg.add(contourG)
|
||||
|
||||
|
||||
font_size = 10
|
||||
text = dwg.text(
|
||||
f"{nr:02d}/{total_nr}",
|
||||
insert=(margin, margin+font_size), font_size=font_size, fill='black'
|
||||
)
|
||||
drawingG.add(text)
|
||||
|
||||
text = dwg.text(
|
||||
f"{category_id}. {cat['supercategory']} - {cat['name']}",
|
||||
insert=(viewBoxSize[0]-margin, margin+font_size), font_size=font_size, fill='black',
|
||||
style='text-anchor:end;')
|
||||
drawingG.add(text)
|
||||
|
||||
text = dwg.text(
|
||||
f"Common Objects In Context",
|
||||
insert=(margin, viewBoxSize[1]-margin), font_size=font_size, fill='black',
|
||||
)
|
||||
drawingG.add(text)
|
||||
|
||||
text = dwg.text(
|
||||
f"Plotting Data",
|
||||
insert=(viewBoxSize[0]-margin, viewBoxSize[1]-margin), font_size=font_size, fill='black',
|
||||
style='text-anchor:end;')
|
||||
drawingG.add(text)
|
||||
|
||||
for i, annotation in enumerate(annotations):
|
||||
normAnn = annotation.getNormalised(gridSize[0], gridSize[1])
|
||||
translation = normAnn.getTranslationToCenter()
|
||||
# print(translation)
|
||||
|
||||
pX = i%grid[0]
|
||||
pY = int(i/grid[0])
|
||||
posX = pX*gridSize[0] + (pX+1)*margin - translation[0]
|
||||
posY = pY*gridSize[1] + (pY+1)*margin - translation[1]
|
||||
# print(i, posX, posY, gridSize)
|
||||
|
||||
|
||||
positionG = svgwrite.container.Group(transform=f'translate({posX}, {posY})')
|
||||
normAnn.writeToDrawing(positionG, stroke='#2FEE2F', stroke_width='1pt', fill_opacity="0")
|
||||
contourG.add(positionG)
|
||||
|
||||
position2G = svgwrite.container.Group(transform=f'translate({posX}, {posY})')
|
||||
pattern_id = textureIds[category_id % len(textureIds)]
|
||||
normAnn.writeToDrawing(position2G, fill=f'url({pattern_id})', stroke='blue', stroke_width='0')
|
||||
drawingG.add(position2G)
|
||||
|
||||
|
||||
|
||||
xml = dwg.get_xml()
|
||||
with open('textures.xml', 'r') as fp:
|
||||
textureTree = ElementTree.fromstring(fp.read())
|
||||
defsTree = xml.find('defs')
|
||||
for pattern in textureTree:
|
||||
defsTree.append(pattern)
|
||||
xmlString = ElementTree.tostring(xml)
|
||||
with open(filename, 'wb') as fp:
|
||||
# print(xmlString)
|
||||
fp.write(xmlString)
|
||||
logger.info(f"Wrote to {filename}")
|
||||
|
||||
|
309
www/canvas.svg
Normal file
309
www/canvas.svg
Normal file
|
@ -0,0 +1,309 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" id="svgCanvas" viewBox="0 0 1000 1000">
|
||||
<metadata><rdf:RDF><dc:source>{source}</dc:source></rdf:RDF></metadata>
|
||||
<defs>
|
||||
<pattern id="tprke" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||
<circle cx="0" cy="5" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||
<circle cx="5" cy="0" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||
<circle cx="5" cy="5" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||
</pattern>
|
||||
<pattern id="szylj" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#80FF00" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="ooumd" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#80FF00" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="fheyp" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#80FF00" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="ssscs" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#80FF00" stroke-linecap="square" />
|
||||
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#80FF00" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="htafw" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#80FF00" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="mwmpe" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<circle cx="2" cy="2" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="ipzlm" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<circle cx="5" cy="5" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="0" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="10" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="0" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="10" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="ryfgx" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#80FF00" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="gktcz" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#00FFFF" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="nkuai" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#00FFFF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="tnder" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#00FFFF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="hfgks" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#00FFFF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="mrlhp" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#00FFFF" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="cmsda" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#FF00FF" stroke-linecap="square" />
|
||||
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#FF00FF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="wsewh" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#FF00FF" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="htoli" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<circle cx="2" cy="2" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="aoggk" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<circle cx="5" cy="5" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="0" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="10" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="0" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="10" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="mfcrf" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#FF00FF" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="igtrq" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#FF00FF" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="wgyom" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#FF00FF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="qxsnm" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||
<circle cx="0" cy="5" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||
<circle cx="5" cy="0" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||
<circle cx="5" cy="5" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||
</pattern>
|
||||
<pattern id="gmgmx" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#FF00FF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="qubbv" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#FF00FF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="ttene" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#FFFF00" stroke-linecap="square" />
|
||||
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#FFFF00" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="fazqm" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#FFFF00" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="nfcwz" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#FFFF00" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="rwmct" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#FFFF00" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="iiitd" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#FFFF00" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="eajyr" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||
<circle cx="0" cy="5" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||
<circle cx="5" cy="0" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||
<circle cx="5" cy="5" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||
</pattern>
|
||||
<pattern id="aknuz" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#0080FF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="vhjmw" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#0080FF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="mrist" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#0080FF" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="mbbhg" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#0080FF" stroke-linecap="square" />
|
||||
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#0080FF" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="hfuqj" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#0080FF" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="jrwrh" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<circle cx="2" cy="2" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="bncvb" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<circle cx="5" cy="5" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="0" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="10" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="0" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="10" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="upgob" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#0080FF" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="krnlo" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#0080FF" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="sgkgd" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#FA8500" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="mfhbm" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#FA8500" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="skgis" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#FA8500" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="oswbs" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#FA8500" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="lsouj" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#FA8500" stroke-linecap="square" />
|
||||
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#FA8500" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="bvkgt" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#FA8500" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="fgivr" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<circle cx="2" cy="2" r="2" fill="#FA8500" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="yfcyp" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<circle cx="5" cy="5" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="0" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="10" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="0" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="10" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="gmbhz" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#00FA85" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="yfydn" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#00FA85" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="gxqls" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#00FA85" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="cfbwj" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||
<circle cx="0" cy="5" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||
<circle cx="5" cy="0" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||
<circle cx="5" cy="5" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||
</pattern>
|
||||
<pattern id="fsgnv" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#00FA85" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="vakgp" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#00FA85" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="oaprx" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#00FA85" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="fxicb" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#00FA85" stroke-linecap="square" />
|
||||
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#00FA85" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="hjala" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#00FA85" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="xjxrr" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<circle cx="2" cy="2" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="yzmaq" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<circle cx="5" cy="5" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="0" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="10" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="0" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="10" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="dguls" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#BB00FA" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="hltax" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#BB00FA" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="czaje" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||
<circle cx="0" cy="5" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||
<circle cx="5" cy="0" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||
<circle cx="5" cy="5" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||
</pattern>
|
||||
<pattern id="julqj" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#BB00FA" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="gic" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#FAD900" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="ohqxb" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<circle cx="2" cy="2" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="bvwec" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<circle cx="5" cy="5" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="0" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="10" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="0" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="10" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="jdlwh" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#FAD900" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="wwcdm" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#FAD900" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="wcyvp" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#FAD900" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="ljnqw" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||
<circle cx="0" cy="5" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||
<circle cx="5" cy="0" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||
<circle cx="5" cy="5" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||
</pattern>
|
||||
<pattern id="xcdur" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#FF0080" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="nxkhz" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#FF0080" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="cpnap" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#FF0080" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="lhupq" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#FF0080" stroke-linecap="square" />
|
||||
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#FF0080" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="aaqtb" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<circle cx="2" cy="2" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="lufrm" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<circle cx="5" cy="5" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="0" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="0" cy="10" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="0" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||
<circle cx="10" cy="10" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||
</pattern>
|
||||
<pattern id="azwdr" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#8500FA" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="yffat" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#8500FA" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||
</pattern>
|
||||
<pattern id="hslpy" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#8500FA" stroke-linecap="square" />
|
||||
</pattern>
|
||||
<pattern id="zsvlo" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||
<circle cx="0" cy="0" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||
<circle cx="0" cy="5" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||
<circle cx="5" cy="0" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||
<circle cx="5" cy="5" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||
</pattern>
|
||||
<pattern id="muhub" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#8500FA" stroke-linecap="square" />
|
||||
</pattern>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 27 KiB |
82
www/canvas_patterns.json
Normal file
82
www/canvas_patterns.json
Normal file
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"1": "url(#tprke)",
|
||||
"2": "url(#szylj)",
|
||||
"3": "url(#ooumd)",
|
||||
"4": "url(#fheyp)",
|
||||
"5": "url(#ssscs)",
|
||||
"6": "url(#htafw)",
|
||||
"7": "url(#mwmpe)",
|
||||
"8": "url(#ipzlm)",
|
||||
"9": "url(#ryfgx)",
|
||||
"10": "url(#gktcz)",
|
||||
"11": "url(#nkuai)",
|
||||
"13": "url(#tnder)",
|
||||
"14": "url(#hfgks)",
|
||||
"15": "url(#mrlhp)",
|
||||
"16": "url(#cmsda)",
|
||||
"17": "url(#wsewh)",
|
||||
"18": "url(#htoli)",
|
||||
"19": "url(#aoggk)",
|
||||
"20": "url(#mfcrf)",
|
||||
"21": "url(#igtrq)",
|
||||
"22": "url(#wgyom)",
|
||||
"23": "url(#qxsnm)",
|
||||
"24": "url(#gmgmx)",
|
||||
"25": "url(#qubbv)",
|
||||
"27": "url(#ttene)",
|
||||
"28": "url(#fazqm)",
|
||||
"31": "url(#nfcwz)",
|
||||
"32": "url(#rwmct)",
|
||||
"33": "url(#iiitd)",
|
||||
"34": "url(#eajyr)",
|
||||
"35": "url(#aknuz)",
|
||||
"36": "url(#vhjmw)",
|
||||
"37": "url(#mrist)",
|
||||
"38": "url(#mbbhg)",
|
||||
"39": "url(#hfuqj)",
|
||||
"40": "url(#jrwrh)",
|
||||
"41": "url(#bncvb)",
|
||||
"42": "url(#upgob)",
|
||||
"43": "url(#krnlo)",
|
||||
"44": "url(#sgkgd)",
|
||||
"46": "url(#mfhbm)",
|
||||
"47": "url(#skgis)",
|
||||
"48": "url(#oswbs)",
|
||||
"49": "url(#lsouj)",
|
||||
"50": "url(#bvkgt)",
|
||||
"51": "url(#fgivr)",
|
||||
"52": "url(#yfcyp)",
|
||||
"53": "url(#gmbhz)",
|
||||
"54": "url(#yfydn)",
|
||||
"55": "url(#gxqls)",
|
||||
"56": "url(#cfbwj)",
|
||||
"57": "url(#fsgnv)",
|
||||
"58": "url(#vakgp)",
|
||||
"59": "url(#oaprx)",
|
||||
"60": "url(#fxicb)",
|
||||
"61": "url(#hjala)",
|
||||
"62": "url(#xjxrr)",
|
||||
"63": "url(#yzmaq)",
|
||||
"64": "url(#dguls)",
|
||||
"65": "url(#hltax)",
|
||||
"67": "url(#czaje)",
|
||||
"70": "url(#julqj)",
|
||||
"72": "url(#gic)",
|
||||
"73": "url(#ohqxb)",
|
||||
"74": "url(#bvwec)",
|
||||
"75": "url(#jdlwh)",
|
||||
"76": "url(#wwcdm)",
|
||||
"77": "url(#wcyvp)",
|
||||
"78": "url(#ljnqw)",
|
||||
"79": "url(#xcdur)",
|
||||
"80": "url(#nxkhz)",
|
||||
"81": "url(#cpnap)",
|
||||
"82": "url(#lhupq)",
|
||||
"84": "url(#aaqtb)",
|
||||
"85": "url(#lufrm)",
|
||||
"86": "url(#azwdr)",
|
||||
"87": "url(#yffat)",
|
||||
"88": "url(#hslpy)",
|
||||
"89": "url(#zsvlo)",
|
||||
"90": "url(#muhub)"
|
||||
}
|
155
www/coco.css
Normal file
155
www/coco.css
Normal file
|
@ -0,0 +1,155 @@
|
|||
body{
|
||||
font-family:sans-serif;
|
||||
background: darkblue;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
#svgCanvas{
|
||||
width:100vw;
|
||||
height:100vh;
|
||||
}
|
||||
.catNav{
|
||||
position:absolute;
|
||||
top:0;
|
||||
color:white;
|
||||
bottom:0;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
#catNav {
|
||||
left: 0;
|
||||
}
|
||||
#catNav2 {
|
||||
right:0;
|
||||
}
|
||||
|
||||
.catNav ul{
|
||||
list-style:none;
|
||||
padding:0px;
|
||||
margin: 30px 0px;
|
||||
border-width: 3px;
|
||||
}
|
||||
#catNav ul {
|
||||
border-left-style: solid;
|
||||
}
|
||||
#catNav2 ul {
|
||||
border-right-style: solid;
|
||||
}
|
||||
|
||||
.catNav li{
|
||||
padding:2px;
|
||||
}
|
||||
.catNav li:hover{
|
||||
cursor:pointer;
|
||||
text-decoration:underline;
|
||||
}
|
||||
#catNav li{
|
||||
transform: rotate(-30deg);
|
||||
transform-origin: top left;
|
||||
padding-left: 5px;
|
||||
}
|
||||
#catNav2 li{
|
||||
transform: rotate(30deg);
|
||||
text-align:right;
|
||||
transform-origin: top right;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
g{
|
||||
cursor:grab;
|
||||
}
|
||||
|
||||
g:active{
|
||||
cursor:grabbing;
|
||||
}
|
||||
|
||||
svg text{
|
||||
fill:white;
|
||||
font-size: 40pt;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.buttons{
|
||||
position:absolute;
|
||||
top: 10px;
|
||||
text-align:center;
|
||||
width: 300px;
|
||||
left: calc(50% - 150px);
|
||||
}
|
||||
.buttons.buttons-inline{
|
||||
position:static;
|
||||
margin: 10px;
|
||||
width:100%;
|
||||
}
|
||||
.buttons span, .buttons label{
|
||||
color:white;
|
||||
cursor:pointer;
|
||||
}
|
||||
.buttons span:hover{
|
||||
text-decoration:underline;
|
||||
}
|
||||
.buttons #sceneLabel{
|
||||
display:block;
|
||||
}
|
||||
|
||||
body.hideImages svg image{
|
||||
display:none;
|
||||
}
|
||||
body.hideLabels svg text{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.supercategory-person{
|
||||
border-color:rgb(250,8,0);
|
||||
}
|
||||
.supercategory-vehicle{
|
||||
border-color:rgb(128,255,0);
|
||||
}
|
||||
.supercategory-outdoor{
|
||||
border-color:rgb(0,255,255);
|
||||
}
|
||||
.supercategory-animal{
|
||||
border-color:rgb(255,0,255);
|
||||
}
|
||||
.supercategory-accessory{
|
||||
border-color:rgb(255,255,0);
|
||||
}
|
||||
.supercategory-sports{
|
||||
border-color:rgb(0,128,255);
|
||||
}
|
||||
.supercategory-kitchen{
|
||||
border-color:rgb(250,133,0);
|
||||
}
|
||||
.supercategory-furniture{
|
||||
border-color:rgb(187,0,250);
|
||||
}
|
||||
.supercategory-food{
|
||||
border-color:rgb(0,250,133);
|
||||
}
|
||||
.supercategory-electronic{
|
||||
border-color:rgb(250,217,0);
|
||||
}
|
||||
.supercategory-appliance{
|
||||
border-color:rgb(255,0,128);
|
||||
}
|
||||
.supercategory-indoor{
|
||||
border-color:rgb(133,0,250);
|
||||
}
|
||||
|
||||
#save{margin: 5px;}
|
||||
|
||||
#saved{
|
||||
text-align: center;
|
||||
}
|
||||
#saved svg{
|
||||
width: 30vw;
|
||||
height: 20vw;
|
||||
margin: 10px;
|
||||
border:solid 1px white;
|
||||
}
|
244
www/coco.js
244
www/coco.js
|
@ -58,9 +58,9 @@ function getColoredTexture(i, color) {
|
|||
case 6:
|
||||
return textures.paths().d("hexagons").size(5).strokeWidth(2).stroke("rgba(0,0,0,0)").fill(color);
|
||||
case 7:
|
||||
return textures.circles().size(4).stroke(color);
|
||||
return textures.circles().size(4).fill(color);
|
||||
case 8:
|
||||
return textures.circles().thicker().complement().stroke(color);
|
||||
return textures.circles().thicker().complement().fill(color);
|
||||
case 9:
|
||||
return textures.paths().d("caps").lighter().thicker().size(5).stroke(color);
|
||||
case 10:
|
||||
|
@ -70,17 +70,18 @@ function getColoredTexture(i, color) {
|
|||
};
|
||||
|
||||
const categoryColors = {
|
||||
"person": "#f00",
|
||||
"vehicle": "#0f0",
|
||||
"outdoor": "#006",
|
||||
"animal": "#ff0",
|
||||
"food": "#0ff",
|
||||
"furniture": "#f0f",
|
||||
"indoor": "#fff",
|
||||
"electronic": "#390",
|
||||
"kitchen": "#930",
|
||||
"accessory": "#f90",
|
||||
"sports": "#f09",
|
||||
"person": "#FA0800",
|
||||
"vehicle": "#80FF00",
|
||||
"outdoor": "#00FFFF",
|
||||
"animal": "#FF00FF",
|
||||
"accessory": "#FFFF00",
|
||||
"food": "#00FA85",
|
||||
"furniture": "#BB00FA",
|
||||
"indoor": "#8500FA",
|
||||
"electronic": "#FAD900",
|
||||
"kitchen": "#FA8500",
|
||||
"sports": "#0080FF",
|
||||
"appliance": "#FF0080",
|
||||
}
|
||||
|
||||
function getColorForSuperCategory(name) {
|
||||
|
@ -88,13 +89,13 @@ function getColorForSuperCategory(name) {
|
|||
}
|
||||
|
||||
function getTextureForCategory(id) {
|
||||
|
||||
|
||||
let hash = id;
|
||||
if(!textureMap.hasOwnProperty(hash)) {
|
||||
let color = categoryColors[categoryMap[id]['supercategory']];
|
||||
textureMap[hash] = getColoredTexture(id, color);
|
||||
}
|
||||
|
||||
|
||||
return textureMap[hash];
|
||||
}
|
||||
|
||||
|
@ -103,8 +104,21 @@ function getTextureForCategory(id) {
|
|||
class CocoCanvas {
|
||||
start(){
|
||||
this.catNavEl = document.getElementById('catNav');
|
||||
this.catNav2El = document.getElementById('catNav2');
|
||||
this.canvas = document.getElementById('svgCanvas');
|
||||
this.loadImagesBtnEl = document.getElementById("loadImages");
|
||||
this.loadLabelsBtnEl = document.getElementById("loadLabels");
|
||||
this.sceneSelectEl = document.getElementById('scene');
|
||||
this.savedBtnEl = document.getElementById('save');
|
||||
this.loadNav()
|
||||
this.annotations = [];
|
||||
|
||||
this.loadImagesBtnEl.addEventListener('change', (e) => this.toggleImages(e));
|
||||
this.loadLabelsBtnEl.addEventListener('change', (e) => this.toggleLabels(e));
|
||||
this.savedBtnEl.addEventListener('click', function(e){
|
||||
if(this.savedBtnEl.disabled || this.annotations.length < 1) return;
|
||||
this.save();
|
||||
}.bind(this));
|
||||
}
|
||||
loadNav() {
|
||||
let r = new Request('/categories.json');
|
||||
|
@ -119,44 +133,67 @@ class CocoCanvas {
|
|||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
buildNav(categories) {
|
||||
let ulEl = crel('ul');
|
||||
let lastSuperCat = null;
|
||||
let supercategories = []
|
||||
let supercategoriesEls = {}
|
||||
// create menu's per supercategory, divide these over left & right
|
||||
for(let cat of categories) {
|
||||
ulEl.appendChild(
|
||||
crel('li', {
|
||||
'id': 'category-' + cat['id'],
|
||||
'on': {
|
||||
'click': (e) => {
|
||||
this.requestAnnotation(cat['id']);
|
||||
}
|
||||
}
|
||||
}, cat['name'])
|
||||
);
|
||||
if(supercategories.indexOf(cat['supercategory']) < 0) {
|
||||
supercategories.push(cat['supercategory']);
|
||||
}
|
||||
}
|
||||
this.catNavEl.appendChild(ulEl);
|
||||
|
||||
let defsEl = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
|
||||
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']);
|
||||
}
|
||||
}
|
||||
}, cat['name']));
|
||||
|
||||
}
|
||||
|
||||
|
||||
this.defsEl = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
|
||||
for(let cat of categories) {
|
||||
let texture = getTextureForCategory(cat['id']);
|
||||
let sel = new dom();
|
||||
texture(sel);
|
||||
defsEl.innerHTML += sel.toString();
|
||||
this.defsEl.innerHTML += sel.toString();
|
||||
}
|
||||
this.canvas.appendChild(defsEl);
|
||||
this.canvas.appendChild(this.defsEl);
|
||||
}
|
||||
|
||||
|
||||
requestAnnotation(category_id) {
|
||||
let r = new Request(`/annotation.json?category=${category_id}&normalise=100`);
|
||||
fetch(r)
|
||||
return fetch(r)
|
||||
.then(response => response.json())
|
||||
.then(annotation => {
|
||||
this.addAnnotationAsShape(annotation);
|
||||
}).catch(function(e){
|
||||
console.error(e);
|
||||
});;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
pointsToD(points) {
|
||||
let start = points.shift()
|
||||
let d = `M${start[0].toPrecision(4)} ${start[1].toPrecision(4)} L `;
|
||||
|
@ -164,7 +201,7 @@ class CocoCanvas {
|
|||
d += points.join(' ');
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
getMousePosition(evt) {
|
||||
// from http://www.petercollingridge.co.uk/tutorials/svg/interactive/dragging/
|
||||
let CTM = this.canvas.getScreenCTM();
|
||||
|
@ -173,13 +210,13 @@ class CocoCanvas {
|
|||
y: (evt.clientY - CTM.f) / CTM.d
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
addAnnotationAsShape(annotation) {
|
||||
console.log('Add annotation', annotation);
|
||||
|
||||
|
||||
let category = categoryMap[annotation['category_id']]
|
||||
let texture = getTextureForCategory(category['id']);
|
||||
|
||||
|
||||
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'), {
|
||||
|
@ -187,18 +224,19 @@ class CocoCanvas {
|
|||
'transform': `translate(${x}, ${y})`,
|
||||
'on': {
|
||||
'mousedown': function(downE) {
|
||||
console.log(downE);
|
||||
console.log(this)
|
||||
// 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)
|
||||
let offset = this.getMousePosition(downE);
|
||||
// offset.x -= parseFloat(downE.target.getAttributeNS(null, "x"));
|
||||
// offset.y -= parseFloat(downE.target.getAttributeNS(null, "y"));
|
||||
|
||||
// Get initial translation amount
|
||||
console.log(annEl.transform.baseVal);
|
||||
// console.log(annEl.transform.baseVal);
|
||||
let transform = annEl.transform.baseVal.getItem(0);
|
||||
offset.x -= transform.matrix.e;
|
||||
offset.y -= transform.matrix.f;
|
||||
|
||||
|
||||
let moveEvent = (moveE) => {
|
||||
let coord = this.getMousePosition(moveE);
|
||||
transform.matrix.e = coord.x - offset.x;
|
||||
|
@ -206,29 +244,131 @@ class CocoCanvas {
|
|||
// annEl.setAttributeNS(null, "x", coord.x - offset.x);
|
||||
// annEl.setAttributeNS(null, "y", coord.y - offset.y);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
document.addEventListener('mousemove', moveEvent);
|
||||
document.addEventListener('mouseup', (upE) => {
|
||||
document.removeEventListener('mousemove', moveEvent);
|
||||
});
|
||||
|
||||
|
||||
}.bind(this)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
for(let segment of annotation['segments']) {
|
||||
|
||||
|
||||
let pathEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'path'), {
|
||||
'fill': texture.url(),
|
||||
'd': this.pointsToD(segment)
|
||||
});
|
||||
annEl.appendChild(pathEl);
|
||||
}
|
||||
console.log(annEl);
|
||||
|
||||
this.canvas.appendChild(annEl);
|
||||
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
let r = new Request('/save', {'method': 'POST', 'body': data});
|
||||
fetch(r)
|
||||
.then(response => response.json())
|
||||
.then(submission => {
|
||||
// alert("Something went wrong when saving the file");
|
||||
// todo redirect to submission
|
||||
console.log('saved', submission);
|
||||
window.location = window.location + 'saved';
|
||||
}).catch(function(e){
|
||||
alert("Something went wrong when saving the file");
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let cc = new CocoCanvas();
|
||||
cc.start();
|
||||
|
||||
//for testing only:
|
||||
//cc.requestAnnotation(1).then((e) => cc.convertToImages());
|
||||
|
|
|
@ -1,43 +1,12 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<style type='text/css'>
|
||||
body{
|
||||
font-family:sans-serif;
|
||||
background: darkblue;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
#svgCanvas{
|
||||
width:100vw;
|
||||
height:100vh;
|
||||
}
|
||||
#catNav{
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
color:white;
|
||||
}
|
||||
#catNav ul{
|
||||
list-style:none;
|
||||
padding:10px;
|
||||
}
|
||||
#catNav li:hover{
|
||||
cursor:pointer;
|
||||
text-decoration:underline;
|
||||
}
|
||||
|
||||
g{
|
||||
cursor:grab;
|
||||
}
|
||||
|
||||
g:active{
|
||||
cursor:grabbing;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="coco.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav id='catNav'>
|
||||
<nav id='catNav' class='catNav'>
|
||||
</nav>
|
||||
<nav id='catNav2' class='catNav'>
|
||||
</nav>
|
||||
<svg
|
||||
id='svgCanvas'
|
||||
|
@ -47,10 +16,21 @@ cursor:grabbing;
|
|||
viewBox="0 0 1000 1000">
|
||||
</svg>
|
||||
|
||||
<div class='buttons'>
|
||||
<label id='scenelabel'>Scene <select id='scene'>
|
||||
<!-- <option>-</option> -->
|
||||
<option value="1">View from a drone</option>
|
||||
<option value="2">Self driving car</option>
|
||||
<option value="3">Product placement detector</option>
|
||||
</select></label>
|
||||
<label id='convert'><input type='checkbox' id='loadImages'> images</label>
|
||||
<label id='labelcheck'><input type='checkbox' id='loadLabels'> labels</label>
|
||||
<label id='savewrap'><input type='submit' id='save' disabled='disabled' value='save'></label>
|
||||
</div>
|
||||
|
||||
<!-- <script src="svg-inject.min.js"></script> -->
|
||||
<script src="textures.js"></script>
|
||||
<script src="crel.min.js"></script>
|
||||
<script src="coco.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
40
www/saved.html
Normal file
40
www/saved.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Plotting Data: COCO drawings</title>
|
||||
<link rel="stylesheet" type="text/css" href="coco.css">
|
||||
</head>
|
||||
<body class='hideImages'>
|
||||
<div class='buttons buttons-inline'>
|
||||
<label id='convert'><input type='checkbox' id='loadImages'> images</label>
|
||||
<label id='labelcheck'><input type='checkbox' id='loadLabels'> labels</label>
|
||||
</div>
|
||||
<div id='saved'>
|
||||
{images}
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
let loadImagesEl = document.getElementById('loadImages');
|
||||
let loadLabelsEl = document.getElementById('loadLabels');
|
||||
|
||||
let setImageClass = function() {
|
||||
if(loadImagesEl.checked) {
|
||||
document.body.classList.remove('hideImages');
|
||||
} else {
|
||||
document.body.classList.add('hideImages');
|
||||
}
|
||||
}
|
||||
let setLabelClass = function() {
|
||||
if(loadLabelsEl.checked) {
|
||||
document.body.classList.remove('hideLabels');
|
||||
} else {
|
||||
document.body.classList.add('hideLabels');
|
||||
}
|
||||
}
|
||||
|
||||
loadImagesEl.addEventListener('change', (e) => setImageClass());
|
||||
loadLabelsEl.addEventListener('change', (e) => setLabelClass());
|
||||
setImageClass();
|
||||
setLabelClass();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
2
www/saved/.gitignore
vendored
Normal file
2
www/saved/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
Loading…
Reference in a new issue