[PYTHON] Try montage your face with an AB test

abface

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.

How it works

Let's actually look at the contents. Only 150 lines. Yes, We can.

  1. Take some images of eyebrows, eyes, nose and mouth and number them.
  2. Randomly select each part.

First, the server side. Python code.

Source

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.

Summary

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

Try montage your face with an AB test
Try an autoencoder with Pytorch
Try face recognition with Python
Try face recognition with Generated Photos
Try face recognition with python + OpenCV
Try to generate an image with aliasing
Try to make your own AWS-SDK with bash
[Python3] [Ubuntu16] [Docker] Try face recognition with OpenFace
Try building an environment for MayaPython with VisualStudioCode
Now, let's try face recognition with Chainer (prediction phase)
Easy face recognition try with Jetson Nano and webcam
Create an environment for test automation with AirtestIDE (Tips)
Now, let's try face recognition with Chainer (learning phase)