Add subtractions and auto updating

This commit is contained in:
Ruben van de Ven 2019-02-04 18:37:55 +01:00
parent 74470e61b3
commit ce7dfc28ea
5 changed files with 486 additions and 319 deletions

View file

@ -4,6 +4,7 @@ import tornado.ioloop
import os
web_dir = os.path.join(os.path.split(__file__)[0], 'www')
output_dir = os.path.join(os.path.split(__file__)[0], 'output')
# This is our WebSocketHandler - it handles the messages
# from the tornado server
@ -27,6 +28,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
application = tornado.web.Application([
(r"/ws", WebSocketHandler),
(r"/output/(.*)", tornado.web.StaticFileHandler, {"path": output_dir}),
(r"/(.*)", tornado.web.StaticFileHandler, {"path": web_dir, "default_filename": 'index.html'}),
],debug=True)

View file

@ -13,6 +13,9 @@ from websocket import create_connection
import logging
import json
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('portraits')
camera = picamera.PiCamera()
camera.rotation = 180
camera.resolution = (1920, 1080)
@ -24,6 +27,8 @@ outputResolution = (1000, 1000)
genders = ['male', 'female', 'unknown']
perspectives = ['side', 'front']
gender_perspectives = [g+"_"+p for p in perspectives for g in genders]
# we also want dedicated composites for the perspectives
gender_perspectives.extend(perspectives)
curdir = os.path.dirname(os.path.abspath(__file__))
tmpimage = '/tmp/piimage.jpg'
@ -42,6 +47,8 @@ environment = {
'LD_PRELOAD': '/usr/lib/arm-linux-gnueabihf/libopencv_core.so.2.4',
}
updateGoesOk = True;
def updateStats(type, name, count, image_filename):
params = {
'type': type,
@ -49,35 +56,41 @@ def updateStats(type, name, count, image_filename):
'time': int(time.time()),
'case_count': int(count),
}
try:
ws = create_connection("ws://localhost:8888/ws")
js = json.dumps({
'type': type,
'name': name,
'img_src': os.path.basename(image_filename),
'case_count': int(count),
})
ws.send(js)
except Exception as e:
raise
# try:
# ws = create_connection("ws://localhost:8888/ws")
# js = json.dumps({
# 'type': type,
# 'name': name,
# 'img_src': os.path.basename(image_filename),
# 'case_count': int(count),
# })
# ws.send(js)
# except Exception as e:
# raise
url = 'https://artstats.rubenvandeven.com/composites/views.php'
try:
if count % 10 == 0:
# only send every one in x image, so that the server never can
# retrace _exact_ faces by comparing the sent frames.
with open(image_filename) as fp:
print('send request including image')
logger.info('send request including image')
r = requests.post(
url ,
files={'image': fp},
params=params
)
else:
print('send request')
logger.info('send request')
r = requests.post(
url,
params=params
)
updateGoesOk = True
except Exception as e:
updateGoesOk = False
logger.critical("Error when updating statistics")
logger.exception(e)
class CompositeImage:
def __init__(self, name, resolution):
@ -102,7 +115,7 @@ class CompositeImage:
self.count = i
name = self.get_frame_filename(self.count)
img_file = os.path.join(dir, name)
print("\trestore {}".format(img_file))
logger.info("\trestore {}".format(img_file))
self.image = np.array(Image.open(img_file)).astype('float64')
self.state_dirty = False
@ -112,12 +125,15 @@ class CompositeImage:
def get_frame_filename(self, i):
return "{}-{}x{}-{}.png".format(self.name, self.resolution[0], self.resolution[1], i)
def get_current_filename(self):
return self.get_frame_filename(self.count)
def save_image(self, dir):
if self.state_dirty is False:
# don't save if no changes are made since last save
return
name = self.get_frame_filename(self.count)
name = self.get_current_filename()
filename = os.path.join(dir, name)
self.get_image().save(filename)
@ -140,6 +156,9 @@ class CompositeCollection:
def get_pickle_filename(self):
return os.path.join(self.target_dir, self.id + ".p")
def get_json_filename(self):
return os.path.join(self.target_dir, "composites.json")
def load(self):
pickle_file_name = self.get_pickle_filename()
# if os.path.exists(pickle_file_name):
@ -151,7 +170,7 @@ class CompositeCollection:
composites[name] = CompositeImage(name, self.size)
composites[name].restore( data['c'][name], self.target_dir)
except Exception as e:
print("Create new composite", e)
logger.info("Create new composite", e)
for name in self.names:
composites[name] = CompositeImage(name, self.size)
@ -163,9 +182,31 @@ class CompositeCollection:
data['c'][name] = self.composites[name].count
with open( self.get_pickle_filename(), "wb" ) as fp:
print("Save", data)
logger.info("Save", data)
pickle.dump( data, fp )
self.save_json()
def save_json(self):
"""
Save statistics as json
"""
data = {}
for name in self.composites:
data[name] = {
'count': self.composites[name].count,
'img': self.composites[name].get_current_filename()
}
with open( self.get_json_filename(), "w" ) as fp:
logger.debug("Json to {}".format(self.get_json_filename()))
json.dump(data, fp)
ws = create_connection("ws://localhost:8888/ws")
ws.send("update")
def save_img(self, name):
self.get(name).save_image(self.target_dir)
@ -192,19 +233,22 @@ class CompositeCollection:
for i in range(start, end):
filename = os.path.join(self.target_dir, c.get_frame_filename(i))
if os.path.exists(filename):
print("Clean {}".format(filename))
logger.info("Clean {}".format(filename))
os.unlink(filename)
def append_face(row, image, composites):
# degrees to distinguish side (as we will never be able to use 90 :-( )
suffix = 'side' if abs(float(row['yaw'])) > 20 else 'front'
# print('yaw:', float(row['yaw']))
name = "{}_{}".format(row['gender'], suffix)
# Plus, we now have a wide angle lens.
suffix = 'side' if abs(float(row['yaw'])) > 12 else 'front'
names = [suffix, "{}_{}".format(row['gender'], suffix)]
compositesToUse = []
for name in names:
if name not in composites.names:
return
composite = composites.get(name)
compositesToUse.append(composites.get(name))
# TODO: matrix transform the image, to skew the face into being a flat-ish surface
# This might yield less blurry composites
@ -228,16 +272,17 @@ def append_face(row, image, composites):
# PIL.Image handles cropping outside the canvas by filling with black/transparent
x = face_x + dx
y = face_y + dy
print('crop')
logger.debug('crop')
i = image.crop((x,y, x + size_x, y + size_y))
if suffix == 'side' and float(row['yaw']) < 0:
print('\tflip')
logger.debug('\tflip')
i = i.transpose(Image.FLIP_LEFT_RIGHT)
print('add')
for composite in compositesToUse:
logger.debug('add')
composite.addFace(i)
print('added')
logger.debug('added')
composites = CompositeCollection(gender_perspectives, outputResolution, os.path.join(curdir, 'output'))
@ -250,7 +295,7 @@ while True:
img = Image.open(tmpimage)
os.unlink(tmpimage)
with open(tmpimageResults) as csvfile:
print("open csv")
logger.debug("open csv")
data = csv.DictReader(csvfile)
faces = 0
for row in data:
@ -258,15 +303,14 @@ while True:
# not a valid face
continue
faces += 1
print("append face")
logger.info("append face")
append_face(row, img, composites)
if faces > 0:
print("save :-)")
logger.info("save :-)")
for name in composites.names:
print("\tsave img '{}'".format(name))
logger.info("\tsave img '{}'".format(name))
c = composites.save_img(name)
# save pickle after images, so they can be restored
composites.save()
composites.clean()
# TODO: trigger output update

100
www/composite_loader.js Normal file
View file

@ -0,0 +1,100 @@
class Face {
constructor(id) {
this.id = id;
this.value = 0;
// this.name = name;
this.imgEls = [];
this.valueEls = [];
let els = document.querySelectorAll(`[data-face='${this.id}']`);
for(let el of els) {
if(el.tagName == 'IMG') {
this.imgEls.push(el);
} else {
this.valueEls.push(el);
}
}
}
addImgEl(el) {
this.imgEls.push(el)
}
addValueEl(el) {
this.valueEls.push(el)
}
update(imgUrl, value) {
for(let el of this.valueEls) {
el.textContent = new Intl.NumberFormat("nl").format(value);
}
for(let el of this.imgEls) {
el.src = imgUrl;
}
}
}
class Bar {
constructor(el) {
this.el = el;
this.values = {};
}
setValue(id, value) {
this.values[id] = value;
this.update();
}
update() {
for(let id in this.values) {
let el = document.getElementById('graph--' + id);
if(!el) continue;
let nrEls = el.getElementsByClassName('nr');
for(let nrEl of nrEls) {
nrEl.textContent = new Intl.NumberFormat("nl").format(this.values[id]);
}
el.style.flex = parseInt(this.values[id]);
}
}
}
var bar = new Bar(document.getElementById('graph'));
var faces = {
'front': new Face('front'),
'side': new Face('side'),
'male_front': new Face('male_front'),
'female_front': new Face('female_front'),
'unknown_front': new Face('unknown_front'),
'male_side': new Face('male_side'),
'female_side': new Face('female_side'),
'unknown_side': new Face('unknown_side')
}
var update = function() {
let req = new XMLHttpRequest();
req.addEventListener("load", function(e){
let data = JSON.parse(this.responseText);
console.log(data);
for(let name in data){
faces[name].update('/output/'+data[name]['img'], data[name]['count']);
}
bar.setValue('male', data['male_front']['count'] + data['male_side']['count'])
bar.setValue('female', data['female_front']['count'] + data['female_side']['count'])
bar.setValue('unknown', data['unknown_front']['count'] + data['unknown_side']['count'])
});
req.open("GET", "/output/composites.json?v=" + Date.now());
req.send();
}
var socket = new ReconnectingWebSocket("ws://"+window.location.hostname+":"+window.location.port+"/ws")
socket.addEventListener('message', function (event) {
//trigger update
update();
});
update();

View file

@ -9,291 +9,164 @@
<h1>Specimens <span class='of'>of</span> Discriminatory Composite Portraiture</h1>
<section id='gender'>
<h2>Gender Differentiation<span class='star'>*</span></h2>
<div class='gender-wrap'>
<div class='genders'>
<div id="front">
<div class='composite' id="front--f">
<img data-face="female_front" src="unknown_side-1000x1000-3.png">
<div class='cases'><span class='type'>female</span> <span class='cases--nr' data-face="female_front">12.320</span> cases</div>
</div>
<div class='composite' id="front--u">
<img data-face="unknown_front" src="unknown_side-1000x1000-3.png">
<div class='cases'><span class='type'>unknown</span> <span class='cases--nr' data-face="unknown_front">100</span> cases</div>
</div>
<div class='composite' id="front--m">
<img data-face="male_front" src="unknown_side-1000x1000-3.png">
<div class='cases'><span class='type'>male</span> <span class='cases--nr' data-face="male_front">120</span> cases</div>
</div>
</div>
<div id="general">
<div class='composite' id="side--composite">
<img data-face="side" src="unknown_side-1000x1000-3.png">
<div class='cases'><span class='cases--nr' data-face="side">100</span> side faces</div>
</div>
<div class='composite' id="front--composite">
<img data-face="front" src="unknown_side-1000x1000-3.png">
<div class='cases'><span class='cases--nr' data-face="front">100</span> front faces</div>
</div>
</div>
<div id="side">
<div class='composite' id="side--m">
<img data-face="male_side" src="unknown_side-1000x1000-3.png">
<div class='cases'><span class='type'>male</span> <span class='cases--nr' data-face="male_side">120</span> cases</div>
</div>
<div class='composite' id="side--u">
<img data-face="unknown_side" src="unknown_side-1000x1000-3.png">
<div class='cases'><span class='type'>unknown</span> <span class='cases--nr' data-face="unknown_side">100</span> cases</div>
</div>
<div class='composite' id="side--f">
<img data-face="female_side" src="unknown_side-1000x1000-3.png">
<div class='cases'><span class='type'>female</span> <span class='cases--nr' data-face="female_side">12.320</span> cases</div>
</div>
</div>
</div>
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
id="svg954"
id="lines--front"
version="1.1"
viewBox="0 0 269.68921 171.03902"
height="171.03902mm"
width="249.68921mm">
<defs
id="defs948" />
viewBox="0 0 59.548019 116.38292"
height="116.38292mm"
width="59.548019mm">
<g
transform="translate(-479.74467,99.971886)"
transform="translate(679.89306,124.0486)"
id="layer1">
<path
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 707.56274,47.386401 c -39.08004,0 -26.35353,-98.175942 -59.54802,-98.175942"
id="path919" />
<path
id="path921"
d="m 707.56274,-67.970339 c -39.08004,0 -26.35353,17.180798 -59.54802,17.180798"
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 707.56274,-8.6440273 c -39.08004,0 -26.35353,-42.1455137 -59.54802,-42.1455137"
id="path923" />
<path
id="path836"
d="m 540.57238,-79.730199 c 70.83004,0 26.35353,98.17594 59.54801,98.17594"
d="m -679.89306,-123.53551 c 70.83005,0 26.35353,98.175937 59.54802,98.175937"
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 540.57238,35.626541 c 39.08004,0 26.35352,-17.1808 59.54801,-17.1808"
d="m -679.89306,-8.1787728 c 39.08005,0 26.35353,-17.1808002 59.54802,-17.1808002"
id="path838" />
<path
id="path840"
d="m 540.57238,-23.699793 c 39.08004,0 26.35352,42.145534 59.54801,42.145534"
d="m -679.89306,-67.505107 c 39.08005,0 26.35353,42.145534 59.54802,42.145534"
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<image
y="-98.78492"
x="488.05942"
id="image826"
xlink:href="unknown_side-1000x1000-3.png"
preserveAspectRatio="none"
height="51.730377"
width="51.730377" />
<use
height="100%"
width="100%"
transform="translate(-4.5528998e-7,57.022051)"
id="use828"
xlink:href="#image826"
y="0"
x="0" />
<use
height="100%"
width="100%"
transform="translate(-4.5528998e-7,58.005791)"
id="use830"
xlink:href="#use828"
y="0"
x="0" />
<image
width="49.769428"
height="49.769428"
preserveAspectRatio="none"
xlink:href="unknown_side-1000x1000-3.png"
id="use834"
x="597.66241"
y="-20.339062" />
<image
width="53.440414"
height="53.440414"
preserveAspectRatio="none"
xlink:href="unknown_side-1000x1000-3.png"
id="image910"
x="687.51868"
y="-99.971886" />
<image
y="-41.172611"
x="687.51868"
id="image913"
xlink:href="unknown_side-1000x1000-3.png"
preserveAspectRatio="none"
height="53.440414"
width="53.440414" />
<image
width="53.440414"
height="53.440414"
preserveAspectRatio="none"
xlink:href="unknown_side-1000x1000-3.png"
id="image915"
x="687.51868"
y="17.626713" />
<image
width="49.769428"
height="51.32375"
preserveAspectRatio="none"
xlink:href="unknown_side-1000x1000-3.png"
id="image917"
x="597.66241"
y="-82.246162" />
<text
id="text927"
y="63.765812"
x="542.91345"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:5.96850729px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.14921269"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:5.96850729px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.14921269"
y="63.765812"
x="542.91345"
id="tspan925">20 cases</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:0.80000001;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
x="541.38544"
y="1.2943537"
id="text931"><tspan
id="tspan929"
x="541.38544"
y="1.2943537"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:0.80000001;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924">1.200</tspan><tspan
id="tspan939"
x="541.38544"
y="7.1525693"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:0.80000001;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924">cases</tspan></text>
<text
id="text935"
y="-48.44733"
x="540.32709"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924"
y="-48.44733"
x="540.32709"
id="tspan933">120 cases</tspan></text>
<path
id="path937"
d="M 540.57238,-79.730199 Z"
style="fill:none;stroke:var(--primary-color);stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
x="603.58911"
y="37.606449"
id="text972"><tspan
id="tspan970"
x="603.58911"
y="37.606449"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924">1.340 cases</tspan></text>
<text
id="text976"
y="-24.546719"
x="608.34381"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924"
y="-24.546719"
x="608.34381"
id="tspan974">800 cases</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
x="479.47293"
y="-66.438972"
id="text1006"><tspan
id="tspan1004"
x="479.47293"
y="-66.438972"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924">M</tspan></text>
<text
id="text1010"
y="-13.522312"
x="480.53125"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924"
y="-13.522312"
x="480.53125"
id="tspan1008">F</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
x="481.06042"
y="44.686024"
id="text1014"><tspan
id="tspan1012"
x="481.06042"
y="44.686024"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924">?</tspan></text>
<text
id="text1018"
y="-66.438972"
x="742.99786"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924"
y="-66.438972"
x="742.99786"
id="tspan1016">M</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
x="744.05621"
y="-13.522312"
id="text1022"><tspan
id="tspan1020"
x="744.05621"
y="-13.522312"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924">F</tspan></text>
<text
id="text1026"
y="44.686024"
x="744.58539"
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;line-height:1.25;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:var(--primary-color);fill-opacity:1;stroke:none;stroke-width:0.18306924"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:7.32276964px;font-family:'CMU Serif';-inkscape-font-specification:'CMU Serif, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.18306924"
y="44.686024"
x="744.58539"
id="tspan1024">?</tspan></text>
</g>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
id="lines--side"
version="1.1"
viewBox="0 0 59.548019 116.38292"
height="116.38292mm"
width="59.548019mm">
<g
transform="translate(679.89306,124.0486)"
id="layer1">
<path
id="path836"
d="m -679.89306,-123.53551 c 70.83005,0 26.35353,98.175937 59.54802,98.175937"
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -679.89306,-8.1787728 c 39.08005,0 26.35353,-17.1808002 59.54802,-17.1808002"
id="path838" />
<path
id="path840"
d="m -679.89306,-67.505107 c 39.08005,0 26.35353,42.145534 59.54802,42.145534"
style="fill:none;stroke:var(--primary-color);stroke-width:1.02617979;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>
</div>
</section>
<section id='differences'>
<div class='gender'>
<h2>Distinctive traits</h2>
<div>
<div class='gender explanation'>
<!-- <p>Distinctive traits</p> -->
<h3>composite subtraction</h3>
<p>An examination of discriminatory traits in gender differentation.</p>
</div>
<div class='gender'>
<img src="unknown_side-1000x1000-3.png"><img src="unknown_side-1000x1000-3.png">
<div class='diff-layer'>
<img data-face="male_front" src="unknown_side-1000x1000-3.png"><img data-face="male_side" src="unknown_side-1000x1000-3.png">
</div>
<div class='original-layer'>
<img data-face="front" src="unknown_side-1000x1000-3.png"><img data-face="side" src="unknown_side-1000x1000-3.png">
</div>
<div class='label'>Male</div>
</div>
</div>
<div>
<div class='gender'>
<div class='diff-layer'>
<img data-face="unknown_front" src="unknown_side-1000x1000-3.png"><img data-face="unknown_side" src="unknown_side-1000x1000-3.png">
</div>
<div class='original-layer'>
<img data-face="front" src="unknown_side-1000x1000-3.png"><img data-face="side" src="unknown_side-1000x1000-3.png">
</div>
<div class='label'>Unknown</div>
</div>
<div class='gender'>
<img src="unknown_side-1000x1000-3.png"><img src="unknown_side-1000x1000-3.png">
<div class='diff-layer'>
<img data-face="female_front" src="unknown_side-1000x1000-3.png"><img data-face="female_side" src="unknown_side-1000x1000-3.png">
</div>
<div class='gender'>
<img src="unknown_side-1000x1000-3.png"><img src="unknown_side-1000x1000-3.png">
<div class='original-layer'>
<img data-face="front" src="unknown_side-1000x1000-3.png"><img data-face="side" src="unknown_side-1000x1000-3.png">
</div>
<div class='label'>Female</div>
</div>
</div>
</section>
<div id='bar-wrap'>
<section id='legend'>
<div class='legend legend--female'>Female</div>
<div class='legend legend--unknown'>Unknown</div>
<div class='legend legend--male'>Male</div>
</section>
<section id='graph'>
<div id='graph--male' data-width="30%">
<div id='graph--female'>
<div class='wrap'><div class='background'></div></div>
<div class='cases'><span class='nr'>0</span> <span class='txt'>cases</span></div>
</div>
<div id='graph--female' data-width="30%">
<div id='graph--unknown'>
<div class='wrap'><div class='background'></div></div>
<div class='cases'><span class='nr'>0</span> <span class='txt'>cases</span></div>
</div>
<div id='graph--unknown' data-width="30%">
<div id='graph--male'>
<div class='wrap'><div class='background'></div></div>
<div class='cases'><span class='nr'>0</span> <span class='txt'>cases</span></div>
</div>
</section>
<!-- <section id='ethnicity'>
<h2>Reinforced Ethnical Stereotypes</h2>
<div id='graph'>
<div id='graph--black-african'>
<div class='background'></div>
<div class='cases'><span class='nr'>0</span> <span class='txt'>cases</span></div>
</div>
<div id='graph--caucasian'>
<div class='background'></div>
<div class='cases'><span class='nr'>0</span> <span class='txt'>cases</span></div>
</div>
<div id='graph--east-asian'>
<div class='background'></div>
<div class='cases'><span class='nr'>0</span> <span class='txt'>cases</span></div>
</div>
<div id='graph--hispanic'>
<div class='background'></div>
<div class='cases'><span class='nr'>0</span> <span class='txt'>cases</span></div>
</div>
<div id='graph--south-asian'>
<div class='background'></div>
<div class='cases'><span class='nr'>0</span> <span class='txt'>cases</span></div>
</div>
</div>
</section> -->
<div id='affdex'>* according to the 2018 edition of Affectiva's gender detection toolkit</div>
<div id='signature'>
<!-- &mdash; -->
Ruben van de Ven
<!-- &mdash; -->
</div>
<script type="text/javascript" src="reconnecting-websocket.min.js"></script>
<script type="text/javascript" src="composite_loader.js"></script>
</body>
</html>

View file

@ -6,6 +6,7 @@
html{
/* filter: invert(1); */
background: var(--secundary-color);
/* background-color: darkred; */
}
body{
font-family: "CMU Serif";
@ -34,7 +35,7 @@ html{
padding: 20px 0 20px 0;
}
section#gender{
height: 44%;
height: 47%;
border-bottom: solid 4px var(--primary-color);
}
@ -53,7 +54,7 @@ html{
font-size: 40%;
display: inline-block;
position: relative;
top: -12px;
top: -27px;
}
.genders{
@ -63,11 +64,6 @@ html{
#gender h2{
letter-spacing: 2px;
}
#ethnicity h2{
/* letter-spacing: 2px; */
}
#affdex{
position: absolute;
top: calc(100% - 18px);
@ -85,15 +81,68 @@ html{
text-align: right;
}
.genders {
display: flex;
/* flex-direction: row; */
justify-content: space-between;
/* vertical align: */
align-items: center;
margin: 30px;
}
.gender-wrap{
position: relative;
}
.genders > div{
font-size: 20pt;
width: 195px;
}
.genders .composite img{
width: 100%;
/* height: 180px; */
}
/* .genders #front--composite{
margin-top: 10px;
} */
.genders #front--composite{
margin-top: 30px;
}
.genders .type{
text-transform: uppercase;;
display:block;
margin-top: -45px;
}
#graph{
display: flex;
flex-direction: row;
margin: 0 20px;
padding-top: 50px;
clear:both;
font-size: 22pt;
}
#graph > div{
width: 33%;
flex: 1;
min-width: 5%;
overflow: hidden;
}
#lines--side, #lines--front{
position: absolute;
top: 100px;
left: 200px;
z-index: -1;
}
#lines--side{
top: 120px;
left: 580px;
transform: scale(-1);
}
@ -127,28 +176,73 @@ html{
#graph .cases{
text-align: center;
text-wrap: wrap;
height: 1.2em;;
}
#differences{
border-bottom: solid 4px var(--primary-color);
padding-bottom: 50px;
padding-bottom: 10px;
display:flex;
height: 497px;
}
#differences::after{
content: '';
clear:both;
display: block;
}
#differences .gender {
width: 50%;
margin: 50px 0 0 0;
float:left;
font-size: 18pt;
}
#differences > div{
flex: 1;
}
#differences .gender {
width: 100%;
margin: 40px 0 0 0;
/* float:left; */
text-align: center;
position: relative;
text-transform: uppercase;
}
#differences .gender + .gender{
margin-top: 20px;
}
#differences .gender.explanation {
text-align: left;
text-transform: none;
padding-left: 85px;
padding-right: 50px;
box-sizing: border-box;
}
#differences .gender.explanation h3{
margin: 60px 0 0 0;
padding:0;
}
#differences .gender.explanation p{
margin: 0;
}
#differences .gender.explanation + .gender{
margin-top: 40px;
}
#differences .gender .diff-layer{
position: absolute;
top:0;
right:0;
left:0;
mix-blend-mode: difference;
}
#differences .gender .original-layer img{
/* black yields no difference for blend-mode */
border-color: black;
}
#differences .gender img{
width: 180px;
height: 180px;
width: 170px;
height: 170px;
border: solid 2px var(--primary-color);
}
@ -156,3 +250,57 @@ html{
border-bottom: none;
margin-top: 40px;
}
#bar-wrap{
position: relative;
}
#legend{
position: absolute;
border-top: solid 4px var(--primary-color);
border-right: solid 4px var(--primary-color);
border-bottom: solid 1px var(--secundary-color);
background-color: var(--secundary-color);
bottom: calc(100%);
left:0;
width: 46%;
padding: 20px 22px 0 22px;
font-size: 20pt;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
#legend div{
display: inline-block;
text-transform: uppercase;
position: relative;
top: 15px;
font-size: 18pt;
}
#legend div::before{
content: '';
display: inline-block;
width: 1em;
height: 1em;
border: solid 2px var(--primary-color);
outline: 2px solid var(--secundary-color);
margin-right: 10px;
background-image:url('texture-cross.png');
background-size: 12px;
filter:invert(1);
font-size: 15pt;
/* vertical-align: baseline; */
position: relative;
top: 2px;
}
#legend div:nth-child(2)::before{
background-image:url('texture-diagonal.png');
background-size: 12px;
}
#legend div:nth-child(3)::before{
background-image:url('texture-ball.png');
background-size: 12px;
}