Soccer World Cup 2018 Average Face by Team

World Cup 2018 Average Player Face by Team
World Cup 2018 Average Player Face by Team


We all know that Spain is going to win this world cup ūüėČ, but watching the games is still entertaining. I thought today while watching England vs. Tunisia that soccer players look very much alike, especially within a team, and so I thought I could compute the average face by soccer team for this world cup.


Getting the data, in this case, images for each player in the current soccer world cup is arguably critical. The images need to have a plain background, similar ilumation, and ideally, equal size. Luckly, the FIFA has done all of this, and the images are available online:

If you inspect one of the player’s elemtents, you can access the URL for the player’s image, which can then be downloaded programmatically

Inspecting the player element on the browser console shows the URL.

I downloaded the page’s source code, and downloaded the 736 300 pixels squared images.

Averaging a face

There is a naive way of averaging an image, using numpy, and it only takes a few lines:

import numpy as np
import imageio

countries = ['argentina', 'belgium', 'colombia', 'croatia', ...]

for country in countries:
    images_stacked = np.zeros((23, 300, 300, 3))

    for i in range(0, 23):
        images_stacked[i] = imageio.imread('images-input/%s/%s_%s.jpg' % (country, country, i))

    results = np.zeros((300, 300, 3))

    for color in range(0, images_stacked.shape[3]):
        for x_pos in range(0, images_stacked.shape[1]):
            for y_pos in range(0, images_stacked.shape[2]):
                results[x_pos, y_pos, color] = np.mean(images_stacked[:, x_pos, y_pos, color])

    imageio.imwrite('images-output/%s_naive_averager.png' % country, results)

There is room for improving on this code, but I am not interested in that, rather on the actual results. And here is how they show:

Sweden Naive Average Face

Let’s look at another example:

Brazil Naive Average Face

Even though images are standard, averaging the pixel values doesn’t build a compeling image; sure, we can distinguish some features such as color of the t-shirt or hair, but we can’t put a face to it.

Face morpher

Luckily, there are better ways. Meet Face Morpher (FM from now own). FM works in a different way to find the average face, instead of naively averaging the pixel values, it builds a geometry of the face by identifying elements on it such as the eyes. It then proceeds to average those sections across images.

The results are much more compeling, and we are definitely able to put a face to this teams now.

Sweden FM Average Face

Or in the case of Brazil:

It looks great! I love it! And yet, aren’t we missing important and distinctive elements, such as the hair, ears, or even team t-shirt?

Face art

I decided to them combine the two images: naive + FM for a more compeling result. I blended the images using Sketch, used the naive image as background, and overlayed a semi-transparent FM face.

Quite happy with the result:

World Cup 2018 Average Player Face by Team
World Cup 2018 Average Player Face by Team


How does the average face look across all countries then?

Average face across all countries
Average face across all countries Great affordable WordPress Hosting

Web development stack

I’ve built quite a few websites for clients, friends, family and for myself too in the last few years, some examples are:,¬†or this very blog. Over time, I’ve polished the tool and techniques, to speed up development, and to increase the quality. At this point my default and preferred architecture that I recommend to most clients is WordPress + Enfold theme. There are powerful reasons behind this choice:

  • WordPress is great. Used by ~27% of the top 10M websites, with a very rich plugin ecosystem, and a visual, intuitive, web based builder. It’s built on technologies that have passed the test of time: PHP and MySQL. The web interface allows non technical customers to make tweaks and additions if they are needed after the project is finished.
  • Enfold by Kriesi is one of the most popular WordPress themes out there, and rightfully so. Their intuitive UI makes it¬†straightforward to create visually compelling websites. It comes with dozens of UI elements, including contact forms, google maps, and image sliders, and they are all personalizable.

I think the quality of the examples above prove the point. The clients were extremely happy with the result, and after 2-3 years, the websites still look sleek and responsive.



This is where the headaches begin. I started recommending my first clients to go with GoDaddy, the had made a name for themselves, their support was always available, and their cPanel web management, although far from perfect, got the job done. Finally, about the price, they weren’t so bad at the beginning, and they included some freebies such as Google Ads credit.

GoDaddy has however let me down in multiple occasions, so I have progressively pushed them down the list of recommended hosting solutions. My first problem with them were their continuous and unwarned price rises, we were paying no more than 20$ for hosting on the first year, but by the third, the bill was over 100$. They also have a very aggressive up-sale and cross-sale model, constantly and annoyingly suggesting you to buy their SSL certificate for 70$+ or to get a faster hosting (read next paragraph) for another 100$.

Secondly comes the speed, in case you don’t know, speed loading in a website is critical in this world of instant gratification, visitors don’t like waits; according to Kissmetrics, every second that passes before the web loads leads to a 7% decrease in conversions.¬† GoDaddy, in an attempt to cut costs, makes you share a server with hundreds if not thousands of other websites, reducing speed and responsiveness.

Lastly, they recently changed their support policy, you can’t email and are forced to call them. Their support team is helpful, but unfortunately they’ve been trained to try and sell you other services…

Getflywheel and WPEngine

These guys understand WordPress hosting: great infrastructure, free SSL certificates (they are free for them, so that makes sense), free backups and flawless user experience. I have tried both services, and really, they are good.

They have only one inconvenient: they are both alarmingly expensive. Their basic plans can set you back at 250$ per year, and it only goes up from there. Unfortunately, most people can’t justify that expense for a website with some dozen visitors per day.


This is where¬†comes into the game. I’ve developed Fendera using the latest cloud computing technologies: Docker + Kubernetes. Kubernetes is legendary; created by Google as a way to ensure services such as YouTube or Gmail were available 99.99999% of the time and later released to the community. It’s not by chance that it integrates very nicely with Google Cloud, arguably one of the best cloud computing provider.

I wanted to break apart from how GoDaddy did things, and I wanted to stay close to¬†Getflywheel or¬†WPEngine but without forgetting about affordability. Our current plan sets you back at only 34$ per year, yet it comes with free SSL certificates, free weekly backups,¬† 6Gb of SSD and a lot of horse power under Google Cloud’s hood. Moreover, thanks to Kubernetes we can guarantee great up times SLAs and ensure a safe and reliable service.

If you want to give it a try, please register at Questions or feedback are welcome!

Docker Alpine+Python3+Django+uWGSI+Nginx

Django works great when you are running in local, but the moment you decide to move onto production, the headaches begin…

Since a few months ago I deploy everything using Docker, the result is something much cleaner and easier to maintain, so when moving my Django app to production, I choose Docker for it. As expected someone had already taken the time to build a Docker image that using ubuntu as is base image will run your Django site with uWGSI&Nginx, a marriage made in heaven.

My problem with that image is that it uses Ubuntu, don’t get me wrong, I love Ubuntu for something like my laptop, but for a Django app in an EC2 instance, it just feels too heavy. So I created an equivalent Dockerfile, this time with the much lighter Alpine.

The Dockerfile is available in Github with instructions on how to deploy your app.

Redshift Force Drop User

Ever tried dropping a user in Redshift only to discover that user “user_1” cannot be dropped because the user has a privilege on some object.

That’s not of great help Redshift. Luckily for us, there is a solution. Kamlesh Gallani posted a response about revoking permissions for tables and schemas that I user might still be assigned to, along with changing the ownership of tables that the user might have created. After that, dropping the user is straightforward.

I created a python snippet with his response that might save you a few minutes. You will need the view v_get_obj_priv_by_user from amazon-utils, simply create the view on your Redshift cluster, then, copy and paste this Python script into your favorite editor (e.g. Sublime Text) fill in the connection information and the list of users to drop and execute it.

import psycopg2
# Connect to Redshift
conn = psycopg2.connect(host="", dbname="", user="", password="", port="5439")
conn.autocommit = True
cursor = conn.cursor()
# List of users to drop
users = ['user_to_drop_1', 'user_to_drop_2']
# New owner, used when changing ownership
new_owner = 'new_user'
# Templates to change ownership, revoke permissions, and drop users
change_ownership = "select 'alter table '+schemaname+'.'+tablename+' owner to %s;' from pg_tables where tableowner like '%s'"
revoke_schema_permissions = "select distinct 'revoke all on schema '+schemaname+' from %s;' from admin.v_get_obj_priv_by_user where usename like '%s'"
revoke_table_permissions = "select distinct 'revoke all on all tables in schema '+schemaname+' from %s;' from admin.v_get_obj_priv_by_user where usename like '%s'"
drop_user = "drop user %s;"
for user in users:
    # Change ownership
    cursor.execute(change_ownership % (new_owner, user))
    for r in cursor.fetchall():
        print("Executing: %s" % r[0])
    # Revoke schema permissions
    cursor.execute(revoke_schema_permissions % (user, user))
    for r in cursor.fetchall():
        print("Executing: %s" % r[0])
    # Revoke table permissions
    cursor.execute(revoke_table_permissions % (user, user))
    for r in cursor.fetchall():
        print("Executing: %s" % r[0])
    # Drop user
    cursor.execute(drop_user % (user))

WordPress HTTP Error when uploading files

Today I got this error when uploading a video to my WordPress site. I Googled around for a little bit, but the proposed solution wasn’t working.

It turns out that since I am using WordPress docker and a Nginx proxy, this later was complaining that the file was too large 413 Request Entity Too Large when the file was uploaded using the browser uploader, not the WordPress multi-file uploader.

Wordpress Browser Uploader
WordPress Browser Uploader

The solution was easy:
$ vim nginx.conf

http {
# Added because uploading large files to WordPress is throwing HTTP error.
client_max_body_size 64M;

In my case since I want my setting to take place for all the websites I am hosting with Nginx I applied the setting under the http section.


Home Temperature Monitor MSP 430 + Raspberry Pi


I am going to share a small project I did a few days ago. I wanted to use the Raspberry Pi to monitor the¬†temperature in my house and ideally to do it over the internet, so I started looking for a temperature sensor. The first problem is that the Raspberry Pi doesn¬īt have an Analog to Digital converter, so its GPIO are strictly digital, meaning that simple temperature sensors that output the temperature as a voltage in a range cannot be used directly.

So looking around my house I discovered I had a LaunchPad MSP430 (version G2231) from Texas Instrument, which comes luckily equipped with an internal temperature sensor. I thought I could connect the MSP 430 to the Raspberry Pi so that the latter could read the temperature values from the former. Then I could put the data in a MySQL database on the Rpi side and build a web server to plot that data.

MSP 430

First, we need to set up the MSP 430 to read the temperature values from its internal sensor using an ADC. Then we can set up the i2C  in slave transmitter mode (the RPi can only work as a master). If you are starting with the MSP 430, I would recommend taking a look at its user guide and then to analyze some of the examples that come with Code Composer. It has a great debugger that allows you to stop the execution at any moment, look and modify the registers, step by step execution, etc.

Temperature Sensor + ADC

The temperature sensor is described briefly on page 550 of the user guide. It can be sampled by the ADC if we select the register INCx1010 INCHx=1010. Let’s take a look at the code. I used a lot of it from the examples, is not bad if you know what you are doing, but you need to be careful and can¬īt simply copy and paste different examples.

#include <msp430g2231.h>         // It has the definitions for all the registers, modes, etc.
unsigned int temp;               // holds ADC result
char SLV_Data = 0;                     // Variable for transmitted data   
int main(void)
      WDTCTL = WDTPW + WDTHOLD;            // Stop watchdog
      ADC10CTL1 = INCH_10 + ADC10DIV_3;         // Select Channel 10 and clk divided by 4
      ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;      // Set Reference value Enable interrupts and turns on the ADC10
      TACCR0 = 30;                              // Delay to allow Ref to settle. Definition in the user guide
      TACCTL0 |= CCIE;                          // Interrupt enable for the Capture Compare
      TACTL = TASSEL_2 | MC_1;                  // Source clock TACLK = SMCLK, Count mode :Up
      _EINT();                                  // General Interrupt Enable
     ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
     temp = ADC10MEM - 50;
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
        SLV_Data = temp-50;             // To the data readed from the ADC (range 0-1023) substract 50.
#pragma vector=TIMERA0_VECTOR
__interrupt void ta0_isr(void)
    ADC10CTL0 |= ENC;                     // Enable conversion
    TACTL = 0;                            // Clear the Timer A control register

This is the code for the ADC conversion. If you have any doubt about what I did you can ask in the comments, and look at the User Guide from Texas Instrument. Also, I found really useful to look at the msp430g2231.h To access that file you can hold the ctrl button while you click on it, and it will open in a new tab. There you can see what all those TACTL, ADC10CTL0, etc. are

There is one thing to be noted and is that the value read by the ADC is 10 bits long, however for 2i2 we need packages of 8 bits of data, of course we could split the package of 10 into two packages of 8 bits, one of them containing 6 dummy zeroes, but here comes the trick: the whole range of the thermometer is not needed. A normal house temperature doesn¬īt range from -50 C to +80 C, so we can omit the two most significant bits (MSB), and make substract or add a value to the variable so a normal temperature stays approx. in the middle range (around 128). That’s why I subtracted 50, so now cold temperatures in the house correspond to 90 while warm ones stay around the 140.

i2C Communication Implementation

Now that we have the value of the temperature in the variable LDta SLVData we need to send it to the Raspberry Pi. We are going to use i2Ci2C for its simplicity. Other protocols such as UART or SPI are somewhat more complicated to implement.

First let’s take a look at the code, here I combined the previous part with this new one so this is the final code:

#include <msp430g2231.h>
char SLV_Data = 0;                     // Variable for transmitted data
char SLV_Addr = 0x90;                  // Address is 0x48
int I2C_State = 0;                     // State variable
unsigned int temp;            // holds  ADC result
unsigned int IntDegC;
int main(void)
  WDTCTL = WDTPW + WDTHOLD;            // Stop watchdog
  if (CALBC1_1MHZ==0xFF)               // If calibration constants erased
    while(1);                          // do not load, trap CPU!!
  DCOCTL = 0;                          // Select lowest DCOx and MODx settings
  BCSCTL1 = CALBC1_1MHZ;               // Set DCO
  ADC10CTL1 = INCH_10 + ADC10DIV_3;         // Temp Sensor ADC10CLK/4
  ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;      // Enable interrupts.
  TACCR0 = 30;                              // Delay to allow Ref to settle
  TACCTL0 |= CCIE;                          // Compare-mode interrupt.
  TACTL = TASSEL_2 | MC_1;                  // TACLK = SMCLK, Up mode.
  P1OUT = 0xC0;                        // P1.6 & P1.7 Pullups
  P1REN |= 0xC0;                       // P1.6 & P1.7 Pullups
  P1DIR = 0xFF;                        // Unused pins as outputs
  P2OUT = 0;
  P2DIR = 0xFF;
  USICTL0 = USIPE6+USIPE7+USISWRST;    // Port & USI mode setup
  USICTL1 = USII2C+USIIE+USISTTIE;     // Enable I2C mode & USI interrupts
  USICKCTL = USICKPL;                  // Setup clock polarity
  USICNT |= USIIFGCC;                  // Disable automatic clear control
  USICTL0 &= ~USISWRST;                // Enable USI
  USICTL1 &= ~USIIFG;                  // Clear pending flag
     ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
     temp = ADC10MEM - 50;
// USI interrupt service routine
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
  if (USICTL1 & USISTTIFG)             // Start entry?
    P1OUT |= 0x01;                     // LED on: Sequence start
    I2C_State = 2;                     // Enter 1st state on start
      case 0: //Idle, should not get here
      case 2: //RX Address
              USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, RX Address
              USICTL1 &= ~USISTTIFG;   // Clear start flag
              I2C_State = 4;           // Go to next state: check address
      case 4: // Process Address and send (N)Ack
              if (USISRL & 0x01)       // If read...
                SLV_Addr++;            // Save R/W bit
              USICTL0 |= USIOE;        // SDA = output
              if (USISRL == SLV_Addr)  // Address match?
                USISRL = 0x00;         // Send Ack
                P1OUT &= ~0x01;        // LED off
                I2C_State = 8;         // Go to next state: TX data
                USISRL = 0xFF;         // Send NAck
                P1OUT |= 0x01;         // LED on: error
                I2C_State = 6;         // Go to next state: prep for next Start
              USICNT |= 0x01;          // Bit counter = 1, send (N)Ack bit
      case 6: // Prep for Start condition
              USICTL0 &= ~USIOE;       // SDA = input
              SLV_Addr = 0x90;         // Reset slave address
              I2C_State = 0;           // Reset state machine
      case 8: // Send Data byte
              USICTL0 |= USIOE;        // SDA = output
              USISRL = SLV_Data;       // Send data byte
              USICNT |=  0x08;         // Bit counter = 8, TX data
              I2C_State = 10;          // Go to next state: receive (N)Ack
      case 10:// Receive Data (N)Ack
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack
              I2C_State = 12;          // Go to next state: check (N)Ack
      case 12:// Process Data Ack/NAck
              if (USISRL & 0x01)       // If Nack received...
                P1OUT |= 0x01;         // LED on: error
              else                     // Ack received
                P1OUT &= ~0x01;        // LED off
                SLV_Data++;            // Increment Slave data
              // Prep for Start condition
              USICTL0 &= ~USIOE;       // SDA = input
              SLV_Addr = 0x90;         // Reset slave address
              I2C_State = 0;           // Reset state machine
  USICTL1 &= ~USIIFG;                  // Clear pending flags
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
        SLV_Data = temp-50;
#pragma vector=TIMERA0_VECTOR
__interrupt void ta0_isr(void)
    ADC10CTL0 |= ENC;
    TACTL = 0;

Now, this part is slightly more complicated. Usually, people rely on a library to ease i2C operations, here the whole implementation of the i2C state machine is displayed. You don’t really need to go through all the operations, but if you are curious, with the user guide you should be able to decode what the code is doing. Is also complicated to debug an i2C slave transmitter code since you need a master to generate the clock signal and to send the address, etc.

Once this code is compiled and pushed into the MSP 430, we are ready to jump to the RPi side.

Raspberry Pi

The first thing to do in the RPi is the interconnection for the i2Ci2C protocol. The pin 1.6 is SCL and the pin 1.7 is SDA so the should be connected respectively to Pin 5 and Pin 3 of the RPi (Model B 512 Mbytes RAM).

SDA and SCL lines inside the square

We should also add a pull-up resistor on those lines to ensure the pins are never floating. I choose values of 2Kő©2Kő© and they are working just fine.

Turn for the software side on the raspberry pi. First is to add the i2Ci2C modules to the kernel. I will not enter on much more detail here. There is an excellent guide that will take you through the process here.

After that, if you run $ i2cdetect -y 1 you should see your MSP430 listening for your orders.

pi@raspberrypi ~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Now is the turn for the python code to obtain the values every minute (you can choose whatever you want). I decided to store the value in a MySQL database. I found this guide, that was perfect! I did mostly the same except for adding the field Zone, as I only had one thermometer, it is not needed.

sudo apt-get install mysql-server python-mysqldb

Once completed we can go ahead:

$ mysql -u root -p
Enter password:
mysql> CREATE DB temps;
mysql> USE temps;
mysql> CREATE USER 'monitor'@'localhost' IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON temps.* TO 'monitor'@'localhost'
mysql> CREATE TABLE tempdat (tdate DATE, ttime TIME, temperature NUMERIC);
#!/usr/bin/env python
import time
import os
import sys
import MySQLdb
import smbus
bus = smbus.SMBus(1)
address = 0x48;
db = MySQLdb.connect("localhost", "monitor", "password", "temps")
def readTemp():
        temp = bus.read_byte(address)
        return temp
while True:
    time.sleep(60)    # wait for 60 seconds to record next temperature
    temp = readTemp();
    print (temp)  # Used for debugging
    curs.execute ("""INSERT INTO tempdat values(CURRENT_DATE(), NOW(), %s)""",temp)

All pretty simple stuff. Now are system is mainly completed but data is useless unless we can access it! So I created a web server that will serve a website plotting the data. Now we have our database ready to be populated. Lets then get to our python code.

Web server to plot the data

First, we need to install apache on the RPi. To do so I followed this guide from Instructables. After, you should be able to introduce the local IP address of your RPi, in my case and see the “it works!” website located in the¬†/var/www¬†of your RPi.

To plot the data I used the library Charts.JS I simply downloaded the .js file from GitHub and put it in the folder /var/www. And then I coded this small website.

<!doctype html>
        ini_set('display_errors', 'On');
        // Create connection
        // Check connection
        if (mysqli_connect_errno())
            echo "Failed to connect to MySQL: " . mysqli_connect_error();
        // Query the data from the last two days of temperatures
        $data = mysql_query("SELECT * FROM temps.tempdat WHERE tdate >= DATE_SUB(CURRENT_DATE, INTERVAL 2 DAY)");
        if (!$data) { // add this check.
            die('Invalid query: ' . mysql_error());
        $Temp = array();
        $avgTemp = array();
        $contador = 0;
        // Get all the temperature records in array
        while($info = mysql_fetch_array( $data ))
            $Temp[$contador] = $info['temperature'];
            $contador = $contador + 1;
        $avgTemp = array_fill(0, count($Temp), 0);  // average temperature initialized to zeroes
        $tempData="[";      // This is the way the data should be given to JS [1,2,3,4...]
        $tempLabel = "[";
        for ($i = 0; $i < count($Temp) - 10; $i=$i+10) {
            for ($j = 0; $j < 10; $j++){
                $avgTemp[$i] += 0.10 * intval($Temp[$i+$j]);
            $tempData .= (string)$avgTemp[$i];
            $tempData .= ",";
            $tempLabel .= "\".";  // I dont care about the labels, so I used ".." if you want, you can put the date records in here.
            $tempLabel .= ".\"";
            $tempLabel .= ",";
        $tempData = substr($tempData, 0, -1); // Remove the last comma of the string    [1,3,5,6,   =>    [1,3,5,6
        $tempLabel= substr($tempLabel, 0, -1);
        $tempData .= "]";  // Close the bracket,    [1,3,5,6  =>  [1,3,5,6]
        $tempLabel .= "]";
    <title>Temperatura de la Casa</title>
    <script src="./Chart.js" type="text/javascript"></script>
    <meta name = "viewport" content = "initial-scale = 1, user-scalable = no">
    <canvas id="canvas" height="600" width="1200"></canvas>
      var lineChartData = {
      labels :  <?php echo $tempLabel ?>,
      datasets : [
      fillColor : "rgba(151,187,205,0.5)",
      strokeColor : "rgba(151,187,205,1)",
      pointColor : "rgba(151,187,205,1)",
      pointStrokeColor : "#fff",
      data : <?php echo $tempData ?>
      var myLine = new Chart(document.getElementById("canvas").getContext("2d")).Line(lineChartData);


This concludes my project. RPi + MSP430 +  i2C + MySQL + PHP + JavaScript, really complete, multidisciplinary and fun project! Comment if you like. Here is a capture of the temperature inside my house!