Website Logo. Upload to /source/logo.png ; disable in /source/_includes/logo.html

Hacker School Log

Laura Skelton | Summer 2014 Batch

Hacker School Day 16: ASCII Photo Booth and Unit Testing

I added a filter to my photo booth that turns each photo into an ASCII art image, then deployed the GIF Photo Booth mode I wrote to the live Block Party server.

It was amazing how easy it was to extend the Photo Booth filter set to add this new filter. I’m using the Mosaic filter from GPUImage, an open-source Objective-C framework that runs filtering calculations in Open-GL for fast image processing. I used an image containing 64 tiles ranging from lightest to darkest, and the calculations in the Open-GL mosaic filter took a stepped luminosity and selected which tile to display.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(
 precision highp float;

 varying vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;

 uniform vec2 inputTileSize;
 uniform vec2 displayTileSize;
 uniform float numTiles;
 uniform int colorOn;

 void main()
 {
     vec2 xy = textureCoordinate;
     xy = xy - mod(xy, displayTileSize);

     vec4 lumcoeff = vec4(0.299,0.587,0.114,0.0);

     vec4 inputColor = texture2D(inputImageTexture2, xy);
     float lum = dot(inputColor,lumcoeff);
     lum = 1.0 - lum;

     float stepsize = 1.0 / numTiles;
     float lumStep = (lum - mod(lum, stepsize)) / stepsize;

     float rowStep = 1.0 / inputTileSize.x;
     float x = mod(lumStep, rowStep);
     float y = floor(lumStep / rowStep);

     vec2 startCoord = vec2(float(x) *  inputTileSize.x, float(y) * inputTileSize.y);
     vec2 finalCoord = startCoord + ((textureCoordinate - xy) * (inputTileSize / displayTileSize));

     vec4 color = texture2D(inputImageTexture, finalCoord);
     if (colorOn == 1) {
         color = color * inputColor;
     }
     gl_FragColor = color;

 }
);

I grouped the ASCII filter with a filter I already had that tends to increase facial feature definition, and that seemed to make the ASCII images a bit clearer.

It was so fast to implement the ASCII filter that I had time to do both a light and a dark version, and I was happy to see that I had set up the Block Party app intelligently enough that the filtering menu extended automatically once the new filters were added, and everything just worked!

The GIF mode of the photo booth takes 4 photos in rapid succession, saves them to the camera roll, combines them into a GIF animation, and combines them into a movie file playable on the iPhone (as GIF animations are not supported outside of the browser), then it uploads everything to the server with metadata indicating to the app that it is an animated GIF instead of a JPG so it can be displayed accordingly.

In the process of working with GPUImage’s Mosaic filter, I discovered a small bug in the project, and with Allison’s help I submitted my first Pull request! It was good to get some practice working with a large existing code base and working to improve a small part of it.

In the evening, I learned how to use XCode’s built-in Testing framework.

In the process of rewriting my code so that it was easier to test, everything was just better. The classes and methods naturally became more modular and reusable, it was easy and quickly repeatable to test all of the edge cases, and my code seemed much clearer. I learned that testing can be seen as a sort of specification set for the code and its features. If the class has some expected properties, or a function is expected to behave in a particular way, I can in a way “define” that class’s features by the way that I write the expected outputs of the test functions, sort of like an outline for how I expect my program to behave. I actually think it would help me to be clearer and more organized in the way I develop my code if I start writing these feature-specification tests before I even write the methods and classes they are testing, and build the method code to match the spec, instead of the other way around. This may not always be feasible if something needs to be mocked up very quickly, but I think in general this is how I will try to work moving forward.

This work with testing feels like great progress towards the goal of becoming a better programmer. I’m going to work on rewriting several of my existing projects in this way as it seems to add stability and clarity without having to spend so much time manually checking that everything works.