Fixing The National Rail Mobile Website For iPhones

I’ve been frustrated that the National Rail Enquiries mobile website doesn’t work properly on my Apple iPhone 4s, running iOS7, so I decided to take a look and see what is actually going on.

You can turn on debugging on your iPhone in the settings, so I turned this on, and loaded Safari on my Macbook Pro to take a look at what was actually happening.

The “Go” button on the mobile website acts as a submit button so I put a breakpoint on it to see what happened.

The event handler calls a function called setRecents, and it fails when it tries to save my recent stations to localStorage.

if (hasStorage) {
    localStorage.setItem("recentStations", recVal);
} else {
    setCookie("recentStations", recVal, 365);
}

The hasStorage global variable has a true value, allowing it to try to save to local storage, so I tried setting this to false and tried again. This time to website worked as it falls back to using cookies. So the problem is with localStorage on mobile Safari, I’m not sure why.

I then took a look to see how hasStorage was set. It was being set in the following function.

function checkStorage() {
    // Feature detect + local reference
    var storage,
    fail,
    uid;
    try {
        uid = new Date;
        (storage = window.localStorage).setItem(uid, uid);
        fail = storage.getItem(uid) != uid;
        storage.removeItem(uid);
        fail && (storage = false);
    } catch (e) {}
    if (storage) {
        hasStorage = true; //if localstorage available
    }
};

I set breakpoints through the function, reloaded and single stepped what was happening in the debugger.

The uid was correctly set to a Date object with the current date and time. The next line was where the problem occured. storage was assigned as a Storage object, but the setItem method failed. This meant that the storage variable was set, but an exception was thrown. The exception handler does nothing, so the line after was executed. This sees that storage is available, but not that it didn’t work, so sets hasStorage incorrectly to true.

The fix would be to modify the exception handler to set storage to false instead of doing nothing.

If any developers of the National Rail website are reading this, please add this line so I can use your website to plan my train journeys again using my iPhone!

The National Rail mobile website on an iPhone

All information correct at time of writing and publishing. 20th April 2014.

Converting An HTML5 Canvas To Black And White

As part of the OCR in my Draw Something Solver, I needed to turn animage into a two tone black and white image. This is really simple to do. Firstly you need to turn the image to grayscale, then you need to turn all pixels with a value over a certain threshold to 255 for the red, green and blue values, or to 0 if under the threshold.

I’ve already covered Grayscaling an HTML 5 Canvas so I will assume this has already taken place and you have the ImageData from an HTML 5 Canvas in an object called imageData.

To turn this to a black and white image we can use the following code…

for (var i = 0; i < imageData.data.length; i+=4) {
  imageData.data[i] = imageData.data[i+1] = imageData.data[i+2] = imageData.data[i] > threshold ? 255 : 0;
}

You just need to change the value of the threshold variable to adjust the sensitivity of the conversion.

Taking the previous red channel image conversion…

grayscale image

Setting the threshold to 200, the converted black and white image looks like this…

black and white image

Draw Something Solver – A Working OCR System

I’ve already mentioned about my first OCR attempt for my Draw Something Solver, and the disappointing results.

I said the letters did not appear to be consistant, this turned out to be on my iPhone 4S, the spacing of the letters is not consistant, there are a few pixels difference between the letters on the left and those on the right. Once I had discovered this, the letters matched.

However, taking this approach meant only images taken from a retina enabled iPhone would work, those from a lower resolution screen would not match. I would need to take screenshots of every letter on every device at every resolution to be consistant. Not a very good idea.

My next approach was to scale to image down to a common size using an HTML5 canvas, that didn’t produce consistant results. I tried to grayscale, and to turn to black and white, but although close, they were still too different.

My final approach, and one that seems to work for now is to still scale and turn the images to black and white, but instead of hashing this result, I am instead doing some pixel comparison and generating probabilities of matching a letter. This is a simple neural network. I have trained it with all the letters from my iPhone 4S so that runs consistantly each time. When I ran against images from an iPhone 3GS, not all images matched, letters like “I” and “J” caused confusion, so where a match was wrong I added some extra training data for those letters so they all now match. I have yet to try images from an iPad as I don’t have one, but I hope they will match first time without further training.

Using The DOM Ready Event

One of the great advantages of using a library like jQuery is that is provides functions like ready() so you can activate your JavaScript code when the DOM is ready, and not when everything has been loaded. This can be significantly earlier, especially if you an image heavy page.

The ready() function is quite chunky, and has to account for a variety of browsers and versions. However, if you know you are running on a recent browser, you can always look at hooking directly into the DOMContentLoaded event. This is part of the HTML5 spec.

To use it you need to add an event listener using the document.addEventListener method. This takes two parameters, the name of the event – in this case DOMContentLoaded, and a callback function to call when the event is triggered.

document.addEventListener("DOMContentLoaded", function domready() {
  document.removeEventListener("DOMContentLoaded", domready);
  console.log("DOM Ready");
});

Notice how I have given the function a name – domready(), this is so I can unhook the function from the event listener once the event has fired.

Grayscaling An HTML5 Canvas

I’ve been looking at how to turn a colour image into a grayscale one using JavaScript on an HTML 5 Canvas. I had done something similar a few years back to an image using Perl.

Wikipedia has a good article on converting colour to grayscale, and it’s very simple formula based on the luminance of the image. The recommendation is to add together 30% of the red value, 59% of the green value and 11% of the blue value.

var luma = red * 0.3 + green * 0.59 + blue * 0.11;

We can easily apply this formula over the ImageData in an HTML 5 Canvas Context object to turn the image held in the Canvas to grayscale. The ImageData.data holds an array with four pieces of data per pixel, the red, the green, the blue and the alpha values. These are integer values between 0 and 255. The code to grayscale the ImageData would look like this.

for (var i = 0; i < imageData.data.length; i+=4) {
  var luma = Math.floor(imageData.data[i] * 0.3 +
  imageData.data[i+1] * 0.59 +
  imageData.data[i+2] * 0.11);
  imageData.data[i] = imageData.data[i+1] = imageData.data[i+2] = luma;
  imageData.data[i+3] = 255;
}

Notice the use of Math.floor(), this is just to round the luminance down into an integer value.

Colour

Grayscale

There is another common way to grayscale an image, and that is to get the red, green and blue pixels to all match. You can pick the colour you want to be the key, for example red, then just make sure the values for the green and blue for each pixel match that of the red one. This is making use of a single channel, and can give very different effects depending on your source image.

To use the red channel we could the following code over some ImageData.

for (var i = 0; i < imageData.data.length; i+=4) {
  imageData.data[i+1] = imageData.data[i+2] = imageData.data[i];
  imageData.data[i+3] = 255;
}

Grayscale Red Channel

To use the green channel, use the following.

for (var i = 0; i < imageData.data.length; i+=4) {
  imageData.data[i] = imageData.data[i+2] = imageData.data[i+1];
  imageData.data[i+3] = 255;
}

Grayscale Green Channel

To use the blue channel, use the following.

for (var i = 0; i < imageData.data.length; i+=4) {
  imageData.data[i] = imageData.data[i+1] = imageData.data[i+2];
  imageData.data[i+3] = 255;
}

Grayscale Blue Channel

Scaling An Image In An HTML5 Canvas

As part of my Draw Something Solver, I’ve been working with HTML 5 Canvas objects quite a bit. As I want my application to work on all iPhones, I need to support both regular and retina displays. As I need to detect the letters on the image, I really need the images to be of the same size, however a regular image is 320×480, whereas a retina image is 640×960.

The eagle eyed amongst you will see that this is a factor of 2 difference between the two.

The Canvas Context object provides a scale() method, that you pass in x and y scaling factors into.

So if I have a Canvas object called screenshotCanvas, and an Image object called screenshotImage I want to scale down by half, I can use the following JavaScript code.

screenshotCanvasContext = screenshotCanvas.getContext('2d');
screenshotCanvasContext.scale(0.5, 0.5);
screenshotCanvasContext.drawImage(screenshotImage,
                                  0,
                                  0,
                                  screenshotImage.width,
                                  screenshotImage.height);

If I want to double the image in size, I just change the scale factor to 2.

screenshotCanvasContext = screenshotCanvas.getContext('2d');
screenshotCanvasContext.scale(2, 2);
screenshotCanvasContext.drawImage(screenshotImage,
                                  0,
                                  0,
                                  screenshotImage.width,
                                  screenshotImage.height);

Draw Something Solver – First OCR Attempt

As part of my Draw Something Solver, I have worked out how to load and extract images from an HTML 5 canvas, now I need to work out what letters the images represent.

I am hoping that as the images of the letters are all computer generated I can simply compare them and create a lookup table.

I initially tried a basic serialisation by running join() over the ImageData as it was type of array, but this didn’t work as it wasn’t an array as such.

Canvases offer a toDataURL() method, that saves the canvas out as a base64 encoded png or jpeg image. Base64 is a text based encoding, so could be suitable. When I tried this, I found the string to be very long, and not very pratical to use.

So I have a long unique string I need to perform a lookup and match against, this is crying out for a hashing function to turn it into something more manageable.

JavaScript doesn’t have a native hashing function like some languages. I found a good JavaScript SHA1 script so used this to get the encoded images down to a more manageable 40 characters.

Running the resulting code over several images has produced some disappointing results. Not all letters appear to consistantly encode to the same SHA1 hash, which means there is some tiny difference in the images of the letters sometimes, even though visually they look the same to the human eye.

I now need to look at another approach for letter detection. There are two I can think of, the first is to convert the image into one less complex, a simple black and white image that should hopefully be more likely to match. The second is to investigate full OCR technology.

I have looked at Grayscaling Image With Perl in the past, so I hope I can reuse that knowledge again with this project.

Extracting And Drawing Part Of An Image Into An HTML5 Canvas

As part of my Draw Something Solver, I need to be able to extract the letters from the screenshot image.

I’ve already looked at loading an image into a HTML 5 canvas object in an earlier post, so I will assume that code is already in place.

It’s actually very easy to extract part of an image from a canvas, we just need to use the getImageData() method from the canvas context object. This takes 4 parameters, the x and y coordinates for the top left corner of the image, and width and height to extract.

var letterImageData = dsCanvasContext.getImageData(27, 782, 72, 71);

In this case, I’m cutting out a 72×71 block starting from the coordinates 27,782.

If you need to paste this extract into another canvas, or even the same canvas, you can use the putImageData method on the canvas context object. This takes 3 parameters, the ImageData you wish to draw into the canvas, and the top left x and y coordinates to draw it into.

dsCanvasContext.putImageData(letterImageData,0,0);

This will post into the top left hand corner of the original canvas.

Loading An Image Into An HTML5 Canvas

As part of my Draw Something Solver, I need to be able to load an image into an HTML 5 Canvas. I thought I’d share the solution to help others.

Firstly, assume you have an HTML page with an empty canvas element called drawsomething. Secondly, assume the image you want to load is next to the HTML page and is called testimage.png.

I’m not usng a framework like jQuery in this example, so the code may a bit verbose.

Firstly, you want the code to run when the page has finished loading, the easiest way to do this is to tie into the window.onload event. If yoou were using jQuery or similar, ready would be a good point to use.

window.onload = function() {
  // code goes here
}

Now we need to get the canvas element and it’s context. We can do this with the following code.

var dsCanvas = document.getElementById('drawsomething');
var dsCanvasContext = dsCanvas.getContext('2d');

We need an Image object to load the image into…

var screenshotImage = new Image();

When the image has been loaded, we need to draw it into the canvas. Do to this we can hook into the image objects onload event and place our canvas manipulation code into an anonymous function that is called when the event triggers.

Inside the anonymous function, we resize the canvas to the same size as the image and call the canvas context’s drawImage() method, passing in the image, it’s start x and y coordinates, and the width and height of the image.

screenshotImage.onload = function() {
  dsCanvas.width = screenshotImage.width;
  dsCanvas.height = screenshotImage.height;
  dsCanvasContext.drawImage(screenshotImage,0,0,screenshotImage.width, screenshotImage.height);
}

Finally, we assign a filename to the image object’s src attribute. This will cause the image object to load the image, and when loaded it will trigger the onload event we have just written to draw the image into the canvas.

screenshotImage.src = "testimage.png";

The complete example looks like this…

window.onload = function() {
  var dsCanvas = document.getElementById('drawsomething');
  var dsCanvasContext = dsCanvas.getContext('2d');
  var screenshotImage = new Image();
  screenshotImage.onload = function() {
    dsCanvas.width = screenshotImage.width;
    dsCanvas.height = screenshotImage.height;
    dsCanvasContext.drawImage(screenshotImage,0,0,screenshotImage.width, screenshotImage.height);
  }
  screenshotImage.src = "testimage.png";
}

Next time, I will look at how to manipulate the image that has just been loaded into the canvas.

Draw Something Solver – The Platform

I have been putting some more thought into how to build my Draw Something Solver.

I have decided as I use Draw Something on my iPhone 4S, the solver should really run on there. This means coding using Objective C, or using HTML 5 and JavaScript.

As I haven’t written an HTML and JavaScript application for a while, and as it’s more fun, I think this should be the technology to use.

The only difficulty I can see is in getting screenshots from Draw Something into the application. Phonegap looks like it exposes some functionality to achieve this in JavaScript, so it looks like a good idea to use this as well. It also gives me the bonus of being able to create a native iOS application I could choose to upload to the App Store if I wanted, and also look a producing a version for Android in the future.

To give the application a native look and feel, using something like jQTouch or jQuery Mobile would make sense. jQuery Mobile has quite a lot of buzz at the moment and I’ve not had the opportunity to use it before, so I think I’ll try it out in this project.