Which face do you prefer, A or B?
A / B testing is a test in which two different patterns of Web pages are prepared and actually used by users to compare the effects. (ASCII.jp Digital Glossary)
Show a slightly different version of the page to about one in thousands of users who visit your website and compare the difference in click-through rate with the regular version. If you get better results than usual, adopt it as the regular version and test a slightly modified version ...
A / B testing is a method like this. It also became famous for being used by President Obama in elections.
The coat color is a little different from the general A / B test for large users, This is what I tried to do like an A / B test with a face montage.
AB face http://xiidec.appspot.com/abface.html
Click on the face of A or B, whichever you like. Then, the face of the chosen one remains, and a new option is presented. As you choose one after another, your ideal face will gradually be created.
Let's actually look at the contents. Only 150 lines. Yes, We can.
First, the server side. Python code.
abface.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import webapp2
import os
import random
from google.appengine.ext.webapp import template
from google.appengine.api import memcache
class Abface(webapp2.RequestHandler):
def get(self):
self.draw()
def post(self):
if not self.request.get('choice') is None and self.request.get('choice')!="":
memcache.set("face",[int(i) for i in self.request.get('choice').split(",")], 3600)
memcache.set("sel",self.request.get('sel'), 3600)
self.draw()
def draw(self):
face1 = self.makeface()
face2=[i for i in face1]
r=random.randint(0,3)
face2[r] = int(random.choice("12345".replace(str(face2[r]), "")))
if memcache.get("sel")=="B":
face1,face2=face2,face1
template_values={
'brow1':'brow_' + str(face1[0]) + '.jpg',
'eye1':'eye_' + str(face1[1]) + '.jpg',
'nose1':'nose_' + str(face1[2]) + '.jpg',
'mouth1':'mouth_' + str(face1[3]) + '.jpg',
'brow2':'brow_' + str(face2[0]) + '.jpg',
'eye2':'eye_' + str(face2[1]) + '.jpg',
'nose2':'nose_' + str(face2[2]) + '.jpg',
'mouth2':'mouth_' + str(face2[3]) + '.jpg',
'face1':",".join(map(str,face1)),
'face2':",".join(map(str,face2)),
}
path = os.path.join(os.path.dirname(__file__), 'html/abface.html')
self.response.out.write(template.render(path, template_values))
def makeface(self):
face = memcache.get("face")
if face is None:
face = [random.randint(1,5) for i in range(4)]
memcache.set("face", face, 3600)
return face
app = webapp2.WSGIApplication([
('/abface.html', Abface)
], debug=True)
First, the get () method is called when the user accesses it. The get () method calls the draw () method. that's all.
And the contents of the draw () method. The page is generated here. First, call makeface () on the first line. It's just been turned around.
makeface (). The key here is memcache.
A cache that disappears over time.
The key "face" contains the best faces at the moment in an array like [1,3,4,5]
(should).
This means eyebrows: 1, eyes: 3, nose: 4, mouth: 5.
It may or may not contain data like this. There is no guarantee.
Because memcache is a cache, it may disappear.
Therefore, if the face is empty, a face is randomly generated and saved. Lifespan is 3600 seconds = 1 hour.
If you want to save it permanently, you have to save it in a proper DB, but this is a play, so if it disappears, it should disappear.
So go back to draw (). Based on the face made with makeface (), play with one of the eyebrows, nose and mouth. This is another option. Here comes memcache again. If the face selected last time is B, replace face 1 and face 2. Then, the value is set in the template and passed to the client. This is the mechanism of face display on the server side.
Finally, a description of post (). Receives the selected face combination from the client, AB, and sets it in memcache.
Then on the client side.
abface.html
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<title>ABface</title>
<style>
#canv1,#canv2 {
border: solid 3px #000;
border-radius: 25px 25px 25px 25px / 25px 25px 25px 25px;
border-color: #ff0000;
}
#canv2 {
border: solid 3px #000;
border-radius: 25px 25px 25px 25px / 25px 25px 25px 25px;
border-color: #0000ff;
}
#canv1:hover {
box-shadow: 0px 0px 20px 5px #ff0000
}
#canv2:hover {
box-shadow: 0px 0px 20px 5px #0000ff
}
#main {
width: 100%;
text-align : center
}
.hid{
display:none;
}
</style>
</head>
<body>
<div id="main">
<h1><span style="color:#ff0000">A</span> or <span style="color:#0000ff">B</span></h1>
<canvas id="canv1" width="150" height="150"></canvas>
<canvas id="canv2" width="150" height="150"></canvas>
</div>
<form action="/abface.html" method="post" id ="frm">
<input type="text" name="choice" id="choice" value="" style="display:none">
<input type="text" name="sel" id="sel" value="" style="display:none">
</form>
<img src="img/face/{{ brow1 }}" id ="brow_1" class="hid">
<img src="img/face/{{ eye1 }}" id ="eye_1" class="hid">
<img src="img/face/{{ nose1 }}" id ="nose_1" class="hid">
<img src="img/face/{{ mouth1 }}" id ="mouse_1" class="hid">
<img src="img/face/{{ brow2 }}" id ="brow_2" class="hid">
<img src="img/face/{{ eye2 }}" id ="eye_2" class="hid">
<img src="img/face/{{ nose2 }}" id ="nose_2" class="hid">
<img src="img/face/{{ mouth2 }}" id ="mouse_2" class="hid">
<script>
var ctx1 = document.getElementById('canv1').getContext('2d');
var ctx2 = document.getElementById('canv2').getContext('2d');
var face1 = "{{ face1 }}"
var face2 = "{{ face2 }}"
function makeface(){
ctx1.drawImage(document.getElementById('brow_1'), 0, 0)
ctx1.drawImage(document.getElementById('eye_1'), 0, 25)
ctx1.drawImage(document.getElementById('nose_1'), 0, 50)
ctx1.drawImage(document.getElementById('mouse_1'), 0, 110)
ctx2.drawImage(document.getElementById('brow_2'), 0, 0)
ctx2.drawImage(document.getElementById('eye_2'), 0, 25)
ctx2.drawImage(document.getElementById('nose_2'), 0, 50)
ctx2.drawImage(document.getElementById('mouse_2'), 0, 110)
}
window.onload = function(){
makeface()
$("#canv1").bind("click",function(){
ctx1.beginPath();
ctx1.lineWidth = 10;
ctx1.strokeStyle="#ff0000";
ctx1.arc(75, 75, 50, 0, Math.PI*2, false)
ctx1.stroke();
$("#choice").val(face1);
$("#sel").val("A");
$('#frm').submit();
});
$("#canv2").bind("click",function(){
ctx2.beginPath();
ctx2.strokeStyle="#0000ff";
ctx2.lineWidth = 10;
ctx2.arc(75, 75, 50, 0, Math.PI*2, false)
ctx2.stroke();
$("#choice").val(face2);
$("#sel").val("B");
$('#frm').submit();
});
}
</script>
</body>
</html>
The first thing to look at is makeface (). The image is pasted on the canvas. It's stuck on two canvases. The IMG element switches the URL according to the part number received from the server side. This is the only part of the display.
Next is the event when you click canvas. I'm doing a lot of things, but it's a cosmetic issue so I can almost ignore it. The last three lines are important. Set the selected parts, set A or B, and submit. Then post () is called on the server side.
that's all. You can make something like an AB test with just this.
If you can increase the types of parts and choose the contour, hairstyle and arrangement, your dreams will spread.
All the materials are made from female faces, but the finished faces are all male faces. Is it a problem of the balance of parts arrangement?
Recommended Posts