<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Franklin Ta]]></title><description><![CDATA[Franklin Ta]]></description><link>https://franklinta.com/</link><image><url>https://franklinta.com/favicon.png</url><title>Franklin Ta</title><link>https://franklinta.com/</link></image><generator>Ghost 2.30</generator><lastBuildDate>Sat, 13 Dec 2025 08:28:30 GMT</lastBuildDate><atom:link href="https://franklinta.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Migrated to Ghost]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>Hosting for my old wordpress blog expired so I'm trying out <a href="https://ghost.org/">Ghost</a> now. Let me know if you see anything broken.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></description><link>https://franklinta.com/2017/05/17/migrated-to-ghost/</link><guid isPermaLink="false">59fcf3bb4f7a1769cf48acf3</guid><dc:creator><![CDATA[Franklin Ta]]></dc:creator><pubDate>Wed, 17 May 2017 21:01:38 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>Hosting for my old wordpress blog expired so I'm trying out <a href="https://ghost.org/">Ghost</a> now. Let me know if you see anything broken.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Image diffing using CSS]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>Since the <a href="http://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/">last post</a> on CSS was popular here is another simple CSS trick that I found useful in the past.</p>
<p>Let’s say we want to find the difference between these two images:</p>
<p><img src="https://fta.li/nks/blog/2014-11-30/Spot_the_difference1.png" alt><img src="https://fta.li/nks/blog/2014-11-30/Spot_the_difference2.png" alt></p>
<p>You can do this by simply inverting the colors and then stacking them together at 50%</p>]]></description><link>https://franklinta.com/2014/11/30/image-diffing-using-css/</link><guid isPermaLink="false">59fcf3bb4f7a1769cf48acec</guid><dc:creator><![CDATA[Franklin Ta]]></dc:creator><pubDate>Sun, 30 Nov 2014 21:33:33 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>Since the <a href="http://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/">last post</a> on CSS was popular here is another simple CSS trick that I found useful in the past.</p>
<p>Let’s say we want to find the difference between these two images:</p>
<p><img src="https://fta.li/nks/blog/2014-11-30/Spot_the_difference1.png" alt><img src="https://fta.li/nks/blog/2014-11-30/Spot_the_difference2.png" alt></p>
<p>You can do this by simply inverting the colors and then stacking them together at 50% opacity. This can be achieved with a single <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter">CSS filter</a>:<br>
<code>-webkit-filter: invert(100%) opacity(50%);</code></p>
<div style="position:relative;">
  <style type="text/css">
    #overlay {
      position:absolute;
      top:0;
      left:0;
      bottom: 0;
      right: 0;
      filter: url(#invert);
      -webkit-filter: invert(100%) opacity(50%);
    }
  </style>
  <svg xmlns="http://www.w3.org/2000/svg" style="display:none">
    <filter id="invert" color-interpolation-filters="srgb">
      <fecolormatrix color-interpolation-filters="srgb" in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 0 0.5"/>
    </filter>
  </svg>
  <img src="https://fta.li/nks/blog/2014-11-30/Spot_the_difference1.png">
  <div id="overlay">
    <img src="https://fta.li/nks/blog/2014-11-30/Spot_the_difference2.png">
  </div>
</div>
<p>What you should get back is a gray image except for places where the two images differ. This works because inverting will flip the color (<code>255 - c</code>) and opacity will blend the two colors equally (<code>c1 + c2 / 2</code>). So the resulting color is <code>((255 - c1) + c2) / 2</code> which is just <code>rgb(127.5, 127.5, 127.5)</code> whenever the overlapping pixel colors <code>c1</code> and <code>c2</code> are the same.</p>
<p>Firefox 34 and older doesn’t support this but you can fallback to an equivalent SVG filter:<br>
<code>filter: url(&quot;data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg'&gt;&lt;filter id='invert' color-interpolation-filters='srgb'&gt;&lt;feColorMatrix color-interpolation-filters='srgb' in='SourceGraphic' type='matrix' values='-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 0 0.5'/&gt;&lt;/filter&gt;&lt;/svg&gt;#invert&quot;);</code><br>
Or use <a href="http://dev.w3.org/fxtf/compositing-1/#blendingdifference">mix-blend-mode: difference</a> which actually produces prettier diffs <del datetime="2015-10-03T01:48:49+00:00">but isn’t supported on Chrome</del>. <em>EDIT(10-2015): Ed in the comments below verified that mix-blend-mode now works on Chrome too! Check out his <a href="http://jsfiddle.net/ekeewj53/">example</a>.</em></p>
<p>Doing the visual diff using pure CSS is nice because it is possible even if you don’t have access to the raw pixel data (which is usually only available by loading the image into a canvas or using Chrome extension’s <a href="https://developer.chrome.com/extensions/tabs#method-captureVisibleTab">captureVisibleTab</a>). But with CSS you can do a visual diff of any element as long as you can overlap things on top of it.</p>
<p>In particular, let’s say your designer mocked up something in Photoshop and gave it to you as a png to build. To layout elements matching the design, the usual workflow might involve manually <a href="https://chrome.google.com/webstore/detail/dimensions/baocaagndhipibgklemoalmkljaimfdj">measuring dimensions</a> or overlaying a <a href="https://chrome.google.com/webstore/detail/perfectpixel-by-welldonec/dkaagdgjmgdmbnecmcefdhjekcoceebi">semitransparent mockup</a> as a template. This process is usually pretty tedious since humans are surprisingly bad at <a href="http://en.wikipedia.org/wiki/Change_blindness">detecting changes</a> so you might miss subtle differences between the design and what you built.</p>
<p>So you can use this CSS trick to get a quick visual diff instead. For example, let’s say someone gave you a screenshot of google.com and asked you to replicate it. To help position things you can overlay the inverted mock (see first section of the js tab) and then tweak the dimensions/margin/padding in inspector until everything is aligned:</p>
<p data-height="310" data-theme-id="8343" data-slug-hash="vEGmKR" data-default-tab="result" data-user="fta" class="codepen">See the Pen <a href="http://codepen.io/fta/pen/vEGmKR/">vEGmKR</a> by Franklin Ta (<a href="http://codepen.io/fta">@fta</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>
<p>When everything is solid gray, you know you got everything pixel perfect!</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Serverless WebRTC using QR codes]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>For those not familiar, <a href="http://en.wikipedia.org/wiki/WebRTC">WebRTC</a> is a technology for peer-to-peer communication between browsers. But despite being peer to peer, WebRTC still requires setting up <a href="http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/">servers</a> for exchanging session descriptions before the browsers can talk to each other (this is called “signaling”).</p>
<p>If you don’t want to manage your own</p>]]></description><link>https://franklinta.com/2014/10/19/serverless-webrtc-using-qr-codes/</link><guid isPermaLink="false">59fcf3bb4f7a1769cf48acf2</guid><dc:creator><![CDATA[Franklin Ta]]></dc:creator><pubDate>Sun, 19 Oct 2014 23:07:36 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>For those not familiar, <a href="http://en.wikipedia.org/wiki/WebRTC">WebRTC</a> is a technology for peer-to-peer communication between browsers. But despite being peer to peer, WebRTC still requires setting up <a href="http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/">servers</a> for exchanging session descriptions before the browsers can talk to each other (this is called “signaling”).</p>
<p>If you don’t want to manage your own servers, you can use some external service for signaling (and a public STUN server). But let’s say just for fun we don’t want to use any external services either. Then one interesting <a href="http://blog.printf.net/articles/2013/05/17/webrtc-without-a-signaling-server/">approach</a> by Chris Ball is to manually exchange the session description by pasting them into IM or email.</p>
<p>I thought it would be cool to extend his demo by transmitting the data using QR codes instead. Then you will be able to establish a WebRTC connection between a phone and a desktop (or two phones with each other) by just pointing their cameras at each other.</p>
<p>After gluing some QR code libraries together, this was the result:</p>
<iframe allowfullscreen frameborder="0" height="315" src="//www.youtube.com/embed/rw4f9LfqqpI?&rel=0" volume="0" width="560"></iframe>
<!-- more -->
<p>These two are just plain static web pages running javascript (using <a href="https://github.com/LazarSoft/jsqrcode">jsqrcode</a> for scanning QR codes, and <a href="https://github.com/jeromeetienne/jquery-qrcode">jquery-qrcode</a> for generating). On the left in the video is a <a href="https://developer.chrome.com/devtools/docs/remote-debugging">remote debugger</a> showing what the Android Chrome browser is seeing, and on the right is a regular desktop Chrome browser. The signaling process only requires one back and forth to generate an offer and reply with an answer. So both cameras are active the whole time looking for the other’s offer/answer while displaying QR codes of their own. Once the exchange is done they can open a data channel which in this case is used for a simple chat application.</p>
<p>The interesting part was actually how to encode into QR codes. The session description is about ~2000 characters which is hitting the limit of what a single QR code was designed for:</p>
<p><img src="https://fta.li/nks/blog/2014-10-19/largeqrcode.png" alt></p>
<p>This is too large to reliably scan especially since the image processing is done using javascript on a mobile device. Compressing can cut down the size by about 50% but this is still too large to read quickly. So you have to break up the data into smaller pieces and encode those into QR codes individually.</p>
<p>But then you will need to scan multiple QR codes in series and if you want to avoid requiring user interaction, you need to automatically know when to display the next QR code. At this stage, the two devices can’t communicate yet so it is hard to ‘ACK’ that you are done scanning a code unless both devices are simultaneously in view of each other’s camera which is very awkward to position physically. (Not that any of this is practical or serious in any way, but trying to build a full duplex connection out of QR code is getting way deeper into <a href="http://en.wikipedia.org/wiki/IP_over_Avian_Carriers">IP over avian carriers</a> territory.)</p>
<p>So I ended up just flashing each QR code for a brief moment over and over again. This doesn’t work super well since you can’t flip the codes too quickly or there will be tearing artifacts but flipping too slowly means long waits to get another chance to try again if it misses a frame. It did work consistently enough for a demo so I was okay with declaring this experiment done!</p>
<p>Here’s a link to the demo you can <a href="https://fta2012.github.io/serverless-webrtc-qrcode/serverless-webrtc.html">try</a>. Some warnings:</p>
<ul>
<li>Requires latest Chrome on both desktop and Android (I used some Chrome specific stuff so Firefox isn’t supported). Tested with a MacBook Air and a Nexus 5.</li>
<li>Requires camera permissions. If you don’t complete the flow, make sure to exit the page so it can release the camera.</li>
<li>The exchange must complete within 30 seconds or it will timeout. It will probably take a couple of tries to learn the sweet spots for the scanner to do this quickly enough.</li>
<li>Will probably heat up your CPU since it is naively checking for a QR code for every frame captured.</li>
</ul>
<p><a href="https://github.com/fta2012/serverless-webrtc-qrcode">Code</a> is on github.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[6DOF Positional Tracking with the Wiimote]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>In this post I am going to talk about how to track the absolute position and orientation of a wiimote in 3D space.</p>
<p>This is certainly nothing new and <a href="https://www.youtube.com/watch?v=KyvIlKSA0BA">was done over seven years ago</a>. Oliver Kreylos, the creator of the video I just linked to, wrote a very good</p>]]></description><link>https://franklinta.com/2014/09/30/6dof-positional-tracking-with-the-wiimote/</link><guid isPermaLink="false">59fcf3bb4f7a1769cf48acf1</guid><dc:creator><![CDATA[Franklin Ta]]></dc:creator><pubDate>Tue, 30 Sep 2014 19:32:06 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>In this post I am going to talk about how to track the absolute position and orientation of a wiimote in 3D space.</p>
<p>This is certainly nothing new and <a href="https://www.youtube.com/watch?v=KyvIlKSA0BA">was done over seven years ago</a>. Oliver Kreylos, the creator of the video I just linked to, wrote a very good <a href="http://graphics.cs.ucdavis.edu/~okreylos/ResDev/Wiimote/index.html">writeup</a> for how he did the motion tracking along with source code. But we will see how to reimplement the same effect with just one function using a computer vision library, OpenCV!</p>
<p>To see what we can do with the motion tracking, here’s a simple demo that plots the position of the wiimote as I am using tracing letters in the air (also shown is a white square that represents 4 LEDs along with its projected image onto the wiimote’s camera):</p>
<iframe allowfullscreen="allowfullscreen" frameborder="0" height="315" src="//www.youtube.com/embed/cEdeE87cp6w" width="420"></iframe>
<p>And another that shows my hand:</p>
<iframe allowfullscreen="allowfullscreen" frameborder="0" height="315" src="//www.youtube.com/embed/_Wc4gwqAQrE" width="420"></iframe>
<!-- more -->
<h3 id="thewiimote">The Wiimote</h3>
<p>First a bit of background about the wiimote. In terms of sensing capabilities, it has an infrared camera, an accelerometer, and if using Motion Plus, a gyroscope also. If you want the exact specs you can check out WiiBrew’s <a href="http://wiibrew.org/wiki/Wiimote">wiki</a>.</p>
<p>For our purposes we will only need the IR camera which is normally used for tracking IR LEDs on the Wii’s sensor bar. The IR camera doesn’t capture traditional images and instead has on-board image processing for detecting up to 4 blobs at 1024×768 resolution which it reports back at up to 100Hz. This is pretty awesome since this is a relatively high report rate and we only have to deal with 4 coordinates rather than 1024*768 pixels like with a regular camera.</p>
<p>To use this data we need to connect the wiimote via bluetooth. There are a lot of libraries (most of which are abandoned) for interfacing with the wiimote. It doesn’t matter which one we choose since we just need very basic functionality for connecting to the device and reading raw IR values. The one I ended up using was <a href="https://github.com/rpavlik/wiiuse">wiiuse</a>.</p>
<h3 id="cameraposeestimationcameraresectioning">Camera Pose Estimation / Camera resectioning</h3>
<p>Once we have the wiimote talking to our computer we can capture “images” of up to 4 points like this with the IR camera:</p>
<p><img src="https://fta.li/nks/blog/2014-09-30/camera-dots.png" alt></p>
<p>This might not seem like much information but if I told you that the picture above was of a square, you might guess that the wiimote camera is looking at something like this:</p>
<p><img src="https://fta.li/nks/blog/2014-09-30/camera1.png" alt></p>
<p>Or to draw it a different way, it might be looking at it from this angle:</p>
<p><img src="https://fta.li/nks/blog/2014-09-30/world1.png" alt></p>
<p>This process of guessing the camera's position/orientation is all we need for motion tracking! In other words, if we know the real shape of the object, we can just iteratively guess a location of the camera and see if it will take the same “photo” as the one we saw. In code this can be implemented with some variant of gradient descent that minimizes the reprojection error.</p>
<p>But you don’t need to implement it yourself. <a href="http://opencv.org/">OpenCV</a> has a powerful function that can do exactly that for you called <a href="http://docs.opencv.org/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#solvepnp">solvePnP</a> (where PnP stands for the perspective-n-point problem). The signature for it looks like this:</p>
<pre><code class="language-cpp">bool solvePnP(InputArray objectPoints,
              InputArray imagePoints,
              InputArray cameraMatrix,
              InputArray distCoeffs,
              OutputArray rvec,
              OutputArray tvec,
              bool useExtrinsicGuess=false,
              int flags=ITERATIVE)
</code></pre>
<p>Arguments</p>
<ul>
<li><strong>objectPoints</strong>: These are the points of the object that the camera is looking at in world coordinates. For example if we are looking at LEDs in the shape of a square this might be [(1, 1, 0), (2, 1, 0), (1, 2, 0), (2, 2, 0)].</li>
<li><strong>imagePoints</strong>: These are the 2D points representing the image that we got from the camera.</li>
<li><strong>cameraMatrix/distCoeffs</strong>: This is also known as the intrinsic matrix that describes how your camera captures an image. It consists of the focal lengths, skew, and projection center. Usually we can do camera calibration to get the exact values for these but I wasn’t getting consistent measurements so I picked something that were close to the physical specs instead. And there’s also the distortion coefficients which is also something we can get from calibration, but I just assumed zero distortion.</li>
<li><strong>rvec / tvec</strong>: This is the output camera translation/rotation! It is used to build what is known as the extrinsic matrix which gives us the rigid transform from the world frame to the camera frame. So if we had a point <code>p<sub>w</sub></code> in world coordinates and we want to know where it is in camera coordinates we would do <code>p<sub>c</sub> = R p<sub>w</sub> + t</code>, and similarly if we want to know where a point in camera coordinates is in the world we would do <code>p<sub>w</sub> = R<sup>⊤</sup> (p<sub>c</sub> – t)</code> (noting that the inverse of a rotational matrix is its transpose). For example the position of the camera is at (0, 0, 0) in the camera frame, so it is at <code>-R<sup>⊤</sup> t</code> in the world frame.</li>
<li><strong>flags</strong>: The algorithm to use. The default algorithm CV_ITERATIVE is based on Levenberg-Marquardt which is the same as the one that Kreylos implemented.</li>
</ul>
<p>Or in short, you need to give it objectPoints (3d coordinates defining the shape you’re looking at), imagePoints (the captured 2d points from the image) and camera intrinsics (how to reproject the object into a new image for calculating error). Then it will spit back out the camera position/rotation.</p>
<p>Here’s a short snippet showing how it is used:</p>
<pre><code class="language-cpp">// Intrinsic
double fx = 1700;
double fy = 1700;
double cx = image_width / 2;
double cy = image_height / 2;
cv::Mat intrinsic = (cv::Mat_&lt;double&gt;(3, 3) &lt;&lt;
    fx,  0, cx,
     0, fy, cy,
     0,  0,  1
);

// Solve for rvec and tvec
cv::Mat rvec, tvec;
solvePnP(object_points, image_points, intrinsic,
         cv::noArray(), rvec, tvec);

// Extrinsic
cv::Mat R;
Rodrigues(rvec, R);
cv::Mat extrinsic = cv::Mat::eye(4, 4, CV_64F);
R.copyTo(extrinsic.rowRange(0, 3).colRange(0, 3));
tvec.copyTo(extrinsic.rowRange(0, 3).col(3));

// Extrinsic inverse
cv::Mat extrinsic_inv = extrinsic.inv();

// Setup where to view from
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(blah blah blah);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(blah blah blah)

/*** Insert code here to draw in world frame ***/

glMultMatrixd(cv::Mat(extrinsic_inv.t()).ptr&lt;double&gt;(0));

/*** Insert code here to draw in camera frame ***/
</code></pre>
<p>Side note: This sometimes isn’t a unique solution. Especially for a symmetric shape like a square there are inherently many different positions that will explain the image you captured:</p>
<p><img src="https://fta.li/nks/blog/2014-09-30/camera2.png" alt><img src="https://fta.li/nks/blog/2014-09-30/world2.png" alt></p>
<p><img src="https://fta.li/nks/blog/2014-09-30/camera3.png" alt><img src="https://fta.li/nks/blog/2014-09-30/world3.png" alt></p>
<p><img src="https://fta.li/nks/blog/2014-09-30/camera4.png" alt><img src="https://fta.li/nks/blog/2014-09-30/world4.png" alt></p>
<p>But the wiimote can only “see” 4 points so there’s not much you can do about it.</p>
<h3 id="infraredmarker">Infrared Marker</h3>
<p>But the worst shape is probably the default wiimote sensor bar which is in a straight line:</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/2/20/Nintendo_Wii_Sensor_Bar.jpg" alt></p>
<p>If you just see a line of points you have an infinite number of camera pose that can explain your picture. To get around that we have to build our own IR LED marker!</p>
<p>It is easy to build one since all we are doing is making a circuit with four 940nm infrared LEDs. We will also need wires, resistors and batteries. I used this <a href="http://led.linear1.org/led.wiz">calculator</a> to figure out what resistors I needed.</p>
<p>To make the LEDs into a square, I poked them through a piece of cardboard, using a thumbtack to make holes first, and then taped the pins of the LEDs together (I don’t have a soldering iron). I only chose square because it was easy but it could be whatever you want. The LEDs are diodes which means direction matters so make sure you hook them up in the right order (the longer pin is positive). Here is what mine looks like:</p>
<p><img src="https://fta.li/nks/blog/2014-09-30/IR_LEDs_front.jpg" alt><img src="https://fta.li/nks/blog/2014-09-30/IR_LEDs_back.jpg" alt></p>
<p>If you are human you probably can’t see infrared but digital cameras can (as long as they don’t have an IR filter) so you can use that for debugging. Here is an image taken with my android phone that shows the LEDs glowing purple:<br>
<img src="https://fta.li/nks/blog/2014-09-30/IR_LEDs_front_lit.jpg" alt></p>
<h3 id="finalthoughts">Final thoughts</h3>
<p>You can get the <a href="https://github.com/fta2012/WiimotePositionTrackingDemo">code on github</a> if you want to experiment with it. I only tested on a Mac and with a Wii Remote Plus (which didn’t work with wiiuse at first without a small patch so it might be safer to go with the original wiimotes if you don’t need the gyro for anything else). I am not sure I would recommend using this for anything serious but it was cheap and fun to play with!</p>
<p>A few notes on limitations:</p>
<ul>
<li>The narrow field of view makes this very awkward to use since you have to keep the target visible to the camera at all times.</li>
<li>LEDs have a narrow beamwidth so the wiimote might not detect them even if it is in its field of view. Retroreflective markers might work better here.</li>
<li>Square shape for the LEDs was not a great idea because you can’t automatically tell which of the 4 (or 8 if you count looking from back) directions you are looking from. It was dealt with by having one of the buttons cycle through the 4 orderings to select one manually but it is annoying whenever the LEDs go out view since then you’ll have to do it all over again. It is probably better to have something asymmetric.</li>
<li>You can scale up the physical size of the marker if you want to track from farther distance. But regardless of physical scale it will usually start getting unusably jittery/jumpy very quickly once the projected image is smaller than let’s say about 1/10 of the imaging plane. This is probably because the camera’s real resolution is actually 128×96 (it does subpixel analysis to get to 1024×768 resolution). I didn’t do any filtering for the demos but maybe that will help (opencv actually has an implementation of a <a href="http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html#kalmanfilter">Kalman Filter</a>).</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Computing CSS matrix3d transforms]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>I was cleaning out some old notes from my previous job and found some math scribbles for computing CSS transforms and thought I would share it. For some context, I was working on a page which had an image that looked like this:</p>
<p><img src="https://fta.li/nks/blog/2014-09-08/devices.png" alt></p>
<p>I wanted to add an easter egg</p>]]></description><link>https://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/</link><guid isPermaLink="false">59fcf3bb4f7a1769cf48acf0</guid><dc:creator><![CDATA[Franklin Ta]]></dc:creator><pubDate>Mon, 08 Sep 2014 14:17:01 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>I was cleaning out some old notes from my previous job and found some math scribbles for computing CSS transforms and thought I would share it. For some context, I was working on a page which had an image that looked like this:</p>
<p><img src="https://fta.li/nks/blog/2014-09-08/devices.png" alt></p>
<p>I wanted to add an easter egg where I could use the screens of those devices to display arbitrary things. I thought it would just be a simple matter of pixel pushing with translate/rotate/scale/etc using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform">CSS transforms</a> but couldn't really get things to line up perfectly.</p>
<p>Frustrated, I started trying to solve it analytically instead. That means for any given shape, I need to solve for the perspective transform that warps an element into that shape. Once that is solved, it is easy to write a WYSIWYG helper script for outputting the CSS. Here's the final result:</p>
<p data-height="500" data-theme-id="8343" data-slug-hash="ifnqH" data-default-tab="result" data-user="fta" class="codepen">See the Pen <a href="http://codepen.io/fta/pen/ifnqH/">ifnqH</a> by Franklin Ta (<a href="http://codepen.io/fta">@fta</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<script async src="//codepen.io/assets/embed/ei.js"></script>
<p>See the coffeescript <a href="http://codepen.io/fta/pen/ifnqH?editors=001">tab</a> for the code. Or paste this <a href="https://gist.github.com/fta2012/bd63f7fd9f385870efc0">gist</a> into console to try it out on any page that has jQuery. You will need to change the selector to whatever element you want to add the dots to.</p>
<!-- more -->
<p>Using that you can drag things into whatever (convex quadrilateral) shape you want:</p>
<style type="text/css">
  /* Undo default blog styling */
  .post-full-content img.transform-background {
    margin: 0;
    padding: 0;
    max-width: none;
  }
  .transform-textarea-1, .transform-textarea-2 {
    margin: 0;
    box-sizing: content-box;
    padding: 2px 3px;
    min-width: initial;
    max-width: none;
    min-height: initial;
    max-height: none;
  }
  /* Transforms */
  .transform-background {
    position: absolute;
    top: 0;
    left: 0;
    width: 444px;
    height: 460px;
    -webkit-transform: initial;
    transform: initial;
  }
  .transform-iframe-wrapper {
    overflow-y: scroll;
    position: absolute;
    top: 0;
    left: 0;
    /*width: 804px;*/
    height: 604px;
    -webkit-transform: matrix3d(0.398873184223619, -0.00064953211319248, 0, 0.000205454078025973, 0.0184100154737032, 0.351728245823725, 0, 0.000003195079740352, 0, 0, 1, 0, 51, 18, 0, 1);
    -webkit-transform-origin: 0 0;
    transform: matrix3d(0.398873184223619, -0.00064953211319248, 0, 0.000205454078025973, 0.0184100154737032, 0.351728245823725, 0, 0.000003195079740352, 0, 0, 1, 0, 51, 18, 0, 1);
    transform-origin: 0 0;
  }
  .transform-textarea-1 {
    position: absolute;
    top: 0;
    left: 0;
    width: 192px;
    height: 256px;
    -webkit-transform: matrix3d(0.691488233652683, -0.0924290763649893, 0, 0.000945958515486931, 0.595257249281267, 0.19593206667798, 0, -0.000410400310711377, 0, 0, 1, 0, 27, 336, 0, 1);
    -webkit-transform-origin: 0 0;
    transform: matrix3d(0.691488233652683, -0.0924290763649893, 0, 0.000945958515486931, 0.595257249281267, 0.19593206667798, 0, -0.000410400310711377, 0, 0, 1, 0, 27, 336, 0, 1);
    transform-origin: 0 0;
  } 
  .transform-textarea-2 {
    position: absolute;
    top: 0;
    left: 0;
    width: 50px;
    height: 100px;
    -webkit-transform: matrix3d(0.679585, -0.152422, 0, -0.00033788, -0.18256, 0.310626, 0, -0.000984278, 0, 0, 1, 0, 340, 251, 0, 1);
    -webkit-transform-origin: 0 0;
    transform: matrix3d(0.679585, -0.152422, 0, -0.00033788, -0.18256, 0.310626, 0, -0.000984278, 0, 0, 1, 0, 340, 251, 0, 1);
    transform-origin: 0 0;
  }
</style>
<div style="position: relative; width: 444px; height: 460px; margin: 1.5em auto;">
  <img src="https://fta.li/nks/blog/2014-09-08/devices.png" alt class="transform-background">
  <div class="transform-iframe-wrapper">
    <iframe width="800" height="600" src="//en.wikipedia.org/wiki/Easter_egg_(media)" style="margin:0;">
    </iframe>
  </div>
  <textarea class="transform-textarea-1">Blah blah blah blah blah</textarea>
  <textarea class="transform-textarea-2">Blah blah blah blah blah</textarea>
</div>
<script type="text/javascript" async src="//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML">
</script>
<style type="text/css">
/* fixes clipping in narrow screens */
.MJXc-display {
    overflow-x: auto;
    overflow-y: hidden;
    padding: 1em 0;
    margin: 0;
}
</style>
<p>I ended up not using this for anything but hope someone else will find it useful!</p>
<p>The rest of this post will explain how to derive the equation for the transform since I remember not being able to find much about it back then. Looking at the code you will see that the core logic is just a few lines to set up and solve a system of linear equations. We will now see how to derive that system.</p>
<p>So let's say we have the 4 corners of the element we want to transform, \((x_i, y_i)\) where \(i \in {0, 1, 2, 3}\) and we want to map each \((x_i, y_i)\) to some \((u_i, v_i)\). From the docs of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix3d">matrix3d</a>, the transform we want is a <a href="http://en.wikipedia.org/wiki/Transformation_matrix#Perspective_projection">homogeneous matrix</a> so we have to represent each point using <a href="http://mathworld.wolfram.com/HomogeneousCoordinates.html">homogeneous coordinates</a>. In homogenous coordinates, a point \((x, y)\) is represented by \((k x, k y, k)\) for any \(k \neq 0\). For example \((3, 2, 1)\) and \((6, 4, 2)\) both represent the point \((3, 2)\).</p>
<p>Thus the transformation matrix \(H\) that we want to solve for must satisfy</p>
<p>$$<br>
\underbrace{<br>
\begin{pmatrix}<br>
h_0 &amp; h_1 &amp; h_2 \\<br>
h_3 &amp; h_4 &amp; h_5 \\<br>
h_6 &amp; h_7 &amp; h_8 \\<br>
\end{pmatrix}<br>
}_{H}<br>
\begin{pmatrix}<br>
x_i \\<br>
y_i \\<br>
1 \\<br>
\end{pmatrix}<br>
= k_i<br>
\begin{pmatrix}<br>
u_i \\<br>
v_i \\<br>
1 \\<br>
\end{pmatrix}<br>
$$</p>
<p>for each \(i\), where the knowns are \(x_i, y_i, u_i, v_i\).</p>
<p>Notice that the \(H\) that can satisfy this is not unique. For example you can scale \(H\) by some constant and the resulting matrix will still map the points correctly (since you can also scale the \(k_i\) by the same amount and still represent the same homogeneous point). So assuming \(h_8 \neq 0\) (see footnote<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>), we should always be able to scale both sides until \(h_8 = 1\), which will simplify the problem for us a little bit:</p>
<p>$$<br>
\begin{pmatrix}<br>
h_0 &amp; h_1 &amp; h_2 \\<br>
h_3 &amp; h_4 &amp; h_5 \\<br>
h_6 &amp; h_7 &amp; 1 \\<br>
\end{pmatrix}<br>
\begin{pmatrix}<br>
x_i \\<br>
y_i \\<br>
1 \\<br>
\end{pmatrix}<br>
= k_i<br>
\begin{pmatrix}<br>
u_i \\<br>
v_i \\<br>
1 \\<br>
\end{pmatrix}<br>
$$</p>
<p>Now we should try to get it into a form that we can solve. Multiplying out we get:</p>
<p>$$<br>
\begin{align*}<br>
x_i h_0 + y_i h_1 + h_2 &amp; = k_i u_i  \\<br>
x_i h_3 + y_i h_4 + h_5 &amp; = k_i v_i  \\<br>
x_i h_6 + y_i h_7 + 1 &amp; = k_i  \\<br>
\end{align*}<br>
$$</p>
<p>We can get rid of \(k_i\) by substituting it from the third into the first two equations:</p>
<p>$$<br>
\begin{align*}<br>
x_i h_0 + y_i h_1 + h_2 &amp; = u_i x_i h_6 + u_i y_i h_7 + u_i  \\<br>
x_i h_3 + y_i h_4 + h_5 &amp; = v_i x_i h_6 + v_i y_i h_7 + v_i  \\<br>
\end{align*}<br>
$$</p>
<p>Remember we are trying to solve for \(h_i\) so we should try to separate them out:</p>
<p>$$<br>
\begin{array}{rcccl}<br>
x_i h_0 + y_i h_1 + h_2 &amp; &amp; - u_i x_i h_6 - u_i y_i h_7 = u_i  \\<br>
&amp; x_i h_3 + y_i h_4 + h_5 &amp; - v_i x_i h_6 - v_i y_i h_7 = v_i  \\<br>
\end{array}<br>
$$</p>
<p>Which in matrix notation is:</p>
<p>$$<br>
\begin{pmatrix}<br>
x_i &amp; y_i &amp; 1 &amp; 0 &amp; 0 &amp; 0 &amp; -u_i x_i &amp; -u_i y_i  \\<br>
0 &amp; 0 &amp; 0 &amp; x_i &amp; y_i &amp; 1 &amp; -v_i x_i &amp; -v_i y_i  \\<br>
\end{pmatrix}<br>
\begin{pmatrix}<br>
h_0 \\<br>
h_1 \\<br>
h_2 \\<br>
h_3 \\<br>
h_4 \\<br>
h_5 \\<br>
h_6 \\<br>
h_7 \\<br>
\end{pmatrix} = \begin{pmatrix}<br>
u_i \\<br>
v_i \\<br>
\end{pmatrix}<br>
$$</p>
<p>Since we have 4 of these mappings we can write them like this:</p>
<p>$$<br>
\begin{pmatrix}<br>
x_0 &amp; y_0 &amp; 1 &amp; 0 &amp; 0 &amp; 0 &amp; -u_0 x_0 &amp; -u_0 y_0  \\<br>
0 &amp; 0 &amp; 0 &amp; x_0 &amp; y_0 &amp; 1 &amp; -v_0 x_0 &amp; -v_0 y_0  \\<br>
x_1 &amp; y_1 &amp; 1 &amp; 0 &amp; 0 &amp; 0 &amp; -u_1 x_1 &amp; -u_1 y_1  \\<br>
0 &amp; 0 &amp; 0 &amp; x_1 &amp; y_1 &amp; 1 &amp; -v_1 x_1 &amp; -v_1 y_1  \\<br>
x_2 &amp; y_2 &amp; 1 &amp; 0 &amp; 0 &amp; 0 &amp; -u_2 x_2 &amp; -u_2 y_2  \\<br>
0 &amp; 0 &amp; 0 &amp; x_2 &amp; y_2 &amp; 1 &amp; -v_2 x_2 &amp; -v_2 y_2  \\<br>
x_3 &amp; y_3 &amp; 1 &amp; 0 &amp; 0 &amp; 0 &amp; -u_3 x_3 &amp; -u_3 y_3  \\<br>
0 &amp; 0 &amp; 0 &amp; x_3 &amp; y_3 &amp; 1 &amp; -v_3 x_3 &amp; -v_3 y_3  \\<br>
\end{pmatrix}<br>
\begin{pmatrix}<br>
h_0 \\<br>
h_1 \\<br>
h_2 \\<br>
h_3 \\<br>
h_4 \\<br>
h_5 \\<br>
h_6 \\<br>
h_7 \\<br>
\end{pmatrix} = \begin{pmatrix}<br>
u_0  \\<br>
v_0  \\<br>
u_1  \\<br>
v_1  \\<br>
u_2  \\<br>
v_2  \\<br>
u_3  \\<br>
v_3  \\<br>
\end{pmatrix}<br>
$$</p>
<p>At this point we are done because it is in \(Ah = b\) form so we can just throw this at a matrix algebra library to solve for \(h\). It should spit back out the \(h_i\) which will let us recover the transform we want:</p>
<p>$$<br>
H =<br>
\begin{pmatrix}<br>
h_0 &amp; h_1 &amp; h_2 \\<br>
h_3 &amp; h_4 &amp; h_5 \\<br>
h_6 &amp; h_7 &amp; h_8 \\<br>
\end{pmatrix}<br>
$$</p>
<p>One last wrinkle is that matrix3d actually takes in a 4 by 4 matrix rather than a 3 by 3. Since we don't care about \(z\) values (because all our points are on the same plane, \(z=0\)) we can just make \(z\) map back to itself. Like so:</p>
<p>\[<br>
\begin{pmatrix}<br>
h_0 &amp; h_1 &amp; 0 &amp; h_2 \\<br>
h_3 &amp; h_4 &amp; 0 &amp; h_5 \\<br>
0 &amp; 0 &amp; 1 &amp; 0 \\<br>
h_6 &amp; h_7 &amp; 0 &amp; h_8 \\<br>
\end{pmatrix}<br>
\]</p>
<p>And that's the final matrix you use for matrix3d. Remember to specify it in column major order and also set the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin">transform-origin</a> to whatever you measured your points with respect to.</p>
<p>I didn't know what to google when I first did this so I had to derive this by hand. I have recently been reading a book on computer vision and it turns out this problem is actually a basic problem in that field (see <a href="http://docs.opencv.org/modules/imgproc/doc/geometric_transformations.html#getperspectivetransform%22">getPerspectiveTransform</a> in opencv) and the technique for solving this is called <a href="http://en.wikipedia.org/wiki/Direct_linear_transformation">direct linear transformation</a>.</p>
<p>EDIT (2016-11-09): As mentioned by Alexander in the comments, matrix3d used to be incorrect on Chrome under page zoom. I <a href="https://codereview.chromium.org/2482753002/">contributed a fix</a> to chromium so it should be fine now.</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>If you want to solve this rigorously without making the assumption that \(h_8 \neq 0\) you can still follow the same steps outlined in this post. You will just end up with a homogeneous system \(Ah = 0\) instead (where \(A\) is now a 8 by 9 matrix and \(h\) is a 9-vector). This can be solved by doing a singular value decomposition and then finding the singular vector corresponding with a singular value of zero. Then any scalar multiple of that singular vector will be a solution. The <a href="http://numericjs.com/">js library</a> I was using for math didn't support SVD very well though! <a href="#fnref1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Predicting the next Math.random() in Java]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>A while back a friend of mine hosted a programming competition where you were given 9 random numbers one at a time and you had to guess the rank (the sorted position amongst the 9) of each as they are coming in. The goal was to submit a strategy in</p>]]></description><link>https://franklinta.com/2014/08/31/predicting-the-next-math-random-in-java/</link><guid isPermaLink="false">59fcf3bb4f7a1769cf48acef</guid><dc:creator><![CDATA[Franklin Ta]]></dc:creator><pubDate>Sun, 31 Aug 2014 23:00:47 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>A while back a friend of mine hosted a programming competition where you were given 9 random numbers one at a time and you had to guess the rank (the sorted position amongst the 9) of each as they are coming in. The goal was to submit a strategy in Java that will maximize the number of these games won. The best solution can guess right about 3.3% of the time but I figured if he was going to let me execute arbitrary code I might as well cheat and guess correctly 100% of the time. So I set out to see if I can predict the rest of the numbers in the stream after seeing just one.</p>
<p>So I started digging through <code>java.util.Random</code>'s <a href="http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/Random.java">source</a> and found that it is just a <a href="http://en.wikipedia.org/wiki/Linear_congruential_generator">linear congruential generator</a> which can be easily to cracked to obtain the original seed. The rest of this post will show how (if you just want to get the code, you can find it <a href="https://github.com/fta2012/ReplicatedRandom">here</a>.</p>
<!-- more -->
<p>A quick refresher on how LCGs work: starting with a seed <code>x<sub>0</sub></code>, the next pseudorandom number generated is given by <code>x<sub>1</sub> = a * x<sub>0</sub> + c mod m</code>, for some fixed constants <code>a</code>, <code>c</code>, and <code>m</code>. So for example let's say <code>a = 3</code>, <code>c = 5</code>, <code>m = 7</code> and we start with the seed <code>x<sub>0</sub> = 0</code>, then the next few random numbers generated will be: <code>5 = 3 * 0 + 5 mod 7</code>, <code>6 = 3 * 5 + 5 mod 7</code>, <code>2 = 3 * 6 + 5 mod 7</code>, <code>4 = 3 * 2 + 5 mod 7</code>, etc.</p>
<p>It should be clear that if I gave you a &quot;random&quot; number generated from this process (e.g., <code>x<sub>i</sub> = 2</code>), you can predict the next number by applying the formula yourself (e.g, <code>x<sub>i+1</sub> = 3 * 2 + 5 mod 7 = 4</code>).</p>
<p>The relevant method in <code>java.util.Random</code> looks like this (where <code>m=2<sup>48</sup></code> or in other words, a 48-bit number is generated with each iteration):</p>
<pre><code class="language-java">public
class Random implements java.io.Serializable {
    private final AtomicLong seed;

    private static final long multiplier = 0x5DEECE66DL;
    private static final long addend = 0xBL;
    private static final long mask = (1L &lt;&lt; 48) - 1;

    protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) &amp; mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed &gt;&gt;&gt; (48 - bits));
    }
    /* ... */
}
</code></pre>
<p>Unlike our toy example, each call to <code>next</code> only returns <code>bits</code> number of the top bits instead of the whole 48-bit number generated so there's a bit more work to do.</p>
<p>Now going back to the post title, how is <code>next(bits)</code> used for <code>Math.random()</code>? If you dig into the java source code you will find that <code>Math.random()</code> is just a call to <code>nextDouble()</code> on a static instance of <code>Random</code>. And <code>nextDouble</code> calls <code>next(bits)</code> like this:</p>
<pre><code class="language-java">public double nextDouble() {
    return (((long)(next(26)) &lt;&lt; 27) + next(27))
        / (double)(1L &lt;&lt; 53);
}
</code></pre>
<p>Which means it is concatenating the top 26 and 27 bits of two iterations of <code>next()</code> to make a 53 bit number which it normalizes back into a double with a value between 0 and 1.</p>
<p>We are interested in the original values returned by <code>next(26)</code> and <code>next(27)</code> which can be recovered by reversing the operations done above. So assuming we have a value that we know was generated from calling <code>nextDouble()</code>, we can do the following:</p>
<pre><code class="language-java">long numerator = (long)(nextDoubleValue * (1L &lt;&lt; 53));
int next26 = (int)(numerator &gt;&gt;&gt; 27);
int next27 = (int)(numerator &amp; ((1L &lt;&lt; 27) - 1));
</code></pre>
<p>Now we have the top 26 and 27 bits of two previous seeds used by <code>next()</code>. But only having the top bits is not sufficient for generating future values. Fortunately, it is fairly quick to brute force the unknown remaining lower 48-26=22 bits (2<sup>22</sup> possibilities). You can do this by trying all 48-bit seeds that has the same top 26 bits as <code>next26</code> and apply the LCG formula to see if the next seed has the same top 27 bits as <code>next27</code>. Like so:</p>
<pre><code class="language-java">long upper27Of48Mask = ((1L &lt;&lt; 27) - 1) &lt;&lt; (48 - 27);
long oldSeedUpper26 = ((long)next26 &lt;&lt; (48 - 26)) &amp; mask;
long newSeedUpper27 = ((long)next27 &lt;&lt; (48 - 27)) &amp; mask;
ArrayList&lt;Long&gt; possibleSeeds = new ArrayList&lt;Long&gt;();
for (long oldSeed = oldSeedUpper26;
     oldSeed &lt;= (oldSeedUpper26 + ((1L &lt;&lt; (48 - 26)) - 1));
     oldSeed++) {
    long newSeed = (oldSeed * multiplier + addend) &amp; mask;
    if ((newSeed &amp; upper27Of48Mask) == newSeedUpper27) {
        possibleSeeds.add(newSeed);
    }
}
</code></pre>
<p>It is possible that out of the 2<sup>22</sup> seeds brute-forced there is a next value with the same top 27 bits purely by chance. Though if we make the (questionable) assumption that each <code>newSeed</code> is a random independent number, this probability is fairly small: 1 - (1 - 2 <sup>-27</sup>)<sup>2<sup>22</sup></sup> = 0.03076676574.</p>
<p>So it is likely that we will find exactly one seed with the brute force. Using that seed we can now simulate the return value for all future calls to <code>next()</code> and thus <code>nextDouble()</code> and <code>Math.random()</code> also. So we just managed to predict all future values of this generator!</p>
<p>It is well known that <code>Math.random()</code> is insecure so none of this is news to anyone. I just didn't expect it to be this easy and that you can do it by just observing a single previous value!</p>
<p>Complete code for this can be found <a href="https://github.com/fta2012/ReplicatedRandom">here</a>.</p>
<p>As for the original competition this was written for, I actually ended up losing anyway. This only managed to guess a couple thousand out of 100000 correctly before exceeding the time limit. The other cheaters did a much better job by using java reflection to modify local variables to win.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Solving CAPTCHAs on Project Euler]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>A couple weeks ago I started doing <a href="https://projecteuler.net/">Project Euler</a> problems. Unfortunately this was during their security breach so login was disabled so I couldn’t get credit for any of the problems I solved. Now that account functionality is restored, I want to resubmit my answers (about 60 of them)</p>]]></description><link>https://franklinta.com/2014/08/24/solving-captchas-on-project-euler/</link><guid isPermaLink="false">59fcf3bb4f7a1769cf48acee</guid><dc:creator><![CDATA[Franklin Ta]]></dc:creator><pubDate>Sun, 24 Aug 2014 18:00:46 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>A couple weeks ago I started doing <a href="https://projecteuler.net/">Project Euler</a> problems. Unfortunately this was during their security breach so login was disabled so I couldn’t get credit for any of the problems I solved. Now that account functionality is restored, I want to resubmit my answers (about 60 of them) on my actual login. Since they have a 30 second rate limiting between answer submissions, it would’ve taken me half an hour to manually resubmit. This was a little too tedious and boring so I wanted to automate it. But they do have a CAPTCHA you need to fill out for each submission to prevent this…</p>
<p>Fortunately their CAPTCHAs <del datetime="2014-08-30T04:23:34+00:00">are</del> were really weak (EDIT: Project Euler made their CAPTCHAs significantly harder since this post): digits only, uniform font/font-size/font-color/background, well separated, unobstructed, etc. It seems like the only variation is that each digit is slightly rotated. Here are a few typical examples:</p>
<p><img src="https://fta.li/nks/blog/2014-08-24/00.png" alt><img src="https://fta.li/nks/blog/2014-08-24/01.png" alt><img src="https://fta.li/nks/blog/2014-08-24/02.png" alt><img src="https://fta.li/nks/blog/2014-08-24/03.png" alt><img src="https://fta.li/nks/blog/2014-08-24/04.png" alt><img src="https://fta.li/nks/blog/2014-08-24/05.png" alt></p>
<p>Writing a naive solver for these CAPTCHAs is pretty easy. The rest of this post will outline how (link to code is at the end).</p>
<!-- more -->
<p>First step is to extract each digit. Since all the digits are the same solid color, you can flood-fill to find all the contiguous region with the same color as the font, sweeping left to right. Here is what that looks like:</p>
<p><img src="https://fta.li/nks/blog/2014-08-24/16940.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_raw_1_16940_0.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_raw_6_16940_1.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_raw_9_16940_2.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_raw_4_16940_3.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_raw_0_16940_4.png" alt></p>
<p>Once you have separated out the digits you need to classify them. Since there aren’t that many variations for each digit, you can actually just label enough training data to cover almost all the different cases.</p>
<p>So I went and labeled about 20 CAPTCHAs manually. This means 20 * 5 labeled digits, which is an average of 10 examples for each digit. This was good enough to cover most of the orientations each digit could be rotated in. For example these were all the labeled images for 2:</p>
<p><img src="https://fta.li/nks/blog/2014-08-24/digit_2_26201_0.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_2_26201_2.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_2_32253_1.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_2_32253_2.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_2_54127_3.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_2_62717_1.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_2_64602_4.png" alt><img src="https://fta.li/nks/blog/2014-08-24/digit_2_92749_1.png" alt></p>
<p>Then to classify new digits, you just need to match it up with the labeled digit it is most similar to. A pixel-wise comparison worked well as the distance function and can already classify more than half the CAPTCHAs correctly. Adding a threshold for matching only when the number of pixels is approximately correct made the classification perfect as far as I can tell. It makes sense that area is a good feature for distinguishing the digits since area is invariant under rotation (if not for pixel interpolation) and there are only a few digits with similar number of pixels (e.g., 6 and 9).</p>
<p>And that’s all there is to it! Once you have the solver it is easy to whip up a mechanize/beautifulsoup python script to submit/sleep(30)/repeat. Thirty minutes later it managed to submit all my answers with only 2 out of 60 CAPTCHAs failing, both succeeding on the automatic refresh. Both failures were due to overlapping digits where it only managed to extract 4 contiguous regions instead of 5 (which is a fixable problem but not worth the complexity in this case).</p>
<p>The solver is about 80 lines of python with the only external dependency being scipy for reading images.</p>
<p>The source code for the solver/submitter scripts can be found <a href="https://github.com/fta2012/ProjectEulerCaptchaSolver">here</a> (no answers included and please don’t use this for submitting answers to problems you didn’t solve yourself!).</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Hello world!]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>I wanted to start blogging for a while now but never had time. Today I will finally start! It’s probably going to be mostly about programming and math.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></description><link>https://franklinta.com/2014/08/24/hello-world/</link><guid isPermaLink="false">59fcf3bb4f7a1769cf48aceb</guid><dc:creator><![CDATA[Franklin Ta]]></dc:creator><pubDate>Sun, 24 Aug 2014 08:11:55 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><p>I wanted to start blogging for a while now but never had time. Today I will finally start! It’s probably going to be mostly about programming and math.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>