28.12.13

BubbleCAPTCHA

I was recently flexing my Star Trek trivia muscles when I suddenly had a CAPTCHA related epiphany.

There is a Star Trek TNG episode called The Game where some of the crew gets hooked on a game, which provides satisfaction to the user who drags and drops something circular into another circular something. 

Anyway, I thought of the bouncing ball CAPTCHA idea where the user has to bounce the ball and hit a particular obstacle with random coordinates in the process to prove their humanity instead of repeating a string of characters.

It sounds very nice in theory but in practice it requires a lot of consideration. For example, the coordinates of the particular obstacle must not appear in the HTML source code, or otherwise a simple scraper will be able to pass the CAPTCHA every time. In addition, the coordinates should not be easily reproduced with any function.

The whole idea behind the bouncing ball was to make the CAPTCHA experience more pleasant but it wasn't very pleasant for me because I had to code it and in the process I discovered that apparently I can't hit a target even if I rig the physics of the ball.

So I opted out for dragging and dropping of circular objects into other circular objects just like in the Ktarian game in that episode. I call it BubbleCAPTCHA

The idea is simple: drag the ball to the darkest circle to prove your humanity.

Once again this CAPTCHA uses the wonderful properties of transparency and overlapping colour.

The intersection of dark coloured circles with various levels of transparency becomes the darkest point in the image. When light coloured circles overlap dark coloured circles, the overlapping area gets lighter

This is how a simple problem for humans becomes a nightmare for current bots. The human eye can easily differentiate between the darkest transparent circle, partially covered by lighter circles and the intersection of circles, which becomes the darkest point in the image when there are 3 or more colours overlapping.

When a user visits a page with the BubbleCAPTCHA, a unique CAPTCHA is generated on the server side and the coordinates of the darkest circle are temporarily stored in a db table and only retrieved if the user provides correct unique reference id along with some trial coordinates.

The user generates the trial coordinates by dragging the ball to the generated CAPTCHA below the canvas where the ball is drawn. If the trial coordinates are within the stored coordinates within the given timestamp and within the given reference id, the user passes the CAPTCHA.

There are a lot of things to be considered here and I'm planning to consider them in the next few days.

Demo below. BubbleCAPTCHA Beta .

19.11.13

Post Search for Facebook Pages

I'm releasing the code for a completely self indulgent project, a search tab for FB pages. I just wanted to find a post I saw 2 months ago on one particular fan page and I couldn't  because it was buried somewhere under 2 months worth of posts so I put on my jamming hat and the result is now on github

A full working version has been around since last December and it can be found in canvas form here and here's how it looks like once it's installed to a page.

It's all wrapped up in CodeIgniter awesomeness but it's actually quite small, and can easily exist outside of a framework. 

But it doesn't.  Because structure and discipline, man! 

Anyway. Page Search works well for pages with a small number of posts and it does not break for pages with tons of posts but since it only searches within a particular time frame, the user is required to keep pressing the Older Posts button until it either finds a match or it doesn't, in which case it will say so.

8.8.13

On the subject of CAPTCHAs

I have a bit of a fascination with CAPTCHAs. And by fascination I mean fear. Mostly because I fail them. Over and over again. In fact, I suck at them so bad that if empirical evidence didn't suggest otherwise, I'd be considered an automaton. After all, that's all these pesky rectangular boxes are supposed to test for - whether the inputer is human or not. Nothing more, nothing less. Somewhere along the way, however, CAPTCHAs have extended their function to include everything from testing one's ability to extract individual letters or words from a lump of skewed characters to testing one's ability to remain calm under severe stress caused by one's inability to extract individual letters from a lump of skewed characters.

Volumes have been written on the pain induced by this lump of skewed characters and its tendency to get skewier and skewier each year. Even when I first started exploring CAPTCHAs, there were hopes and dreams of sexier ways to whip out the Turing test on innocent users that will require them to complete less painful tasks in order to prove their humanity. Like for example, by rearranging an image, or by rotating an image to match the rotation of a set image, or some other much simpler task than extracting individual letters from a lump of skewed characters. There were rumours how Google would switch to such slicker CAPTCHAs entirely. That was many years ago.

Meanwhile I created my first CAPTCHA with PHP's GD. Well, I first created it with pens and pencils on a piece of paper as a part of a blog entry for one of my favourite comp sci classes at the UofA.

Every class I got an A in was a favourite.  

It is still just an image with a bunch of characters in it. It is still painful to look at. But this is programming for programming's sake.

Instead of skewing the characters in the string and glueing them closer and closer together, I just repeated the string underneath the original string so that the bottom half of the first string overlaps the top half of the second string. Also, both strings are slightly transparent so that their overlaps are the most saturated areas in the entire CAPTCHA, thus the rest becomes noise to bots. It is banking on a typical human trait - ability to differentiate between different hues instantaneously. To help humans differentiate the characters, each string is in a different hue.

Most CAPTCHA scanners are not equipped to deal with these sorts of things, I assume.

 my CAPTCHA
 my CAPTCHA scanned







Presumably the bot does not have a built in hue differentiation so the darkest spots on the image, which it presumably interprets as being part of the string are the spots where the two strings intersect, and since these points of intersection do not represent actual letters, the scanner gets nada. Presumably. Personally, I know this CAPTCHA is good because I fail it over and over again. *code is at the end

But let's face it, it still sucks. Humans shouldn't have to suffer while proving their humanity to machines. Instead, they should get some satisfaction from it, even if it is a very small one. Like for example, the satisfaction you get from throwing a ball and getting it through some obstacle.

Instead of a randomly generated string of characters that a person has to reenter, the CAPTCHA could be generating random points in space, drawing a line between them and having the user bounce a ball off of this particular line, or land the ball at a particular location, that's generated dynamically as soon as the user grabs the ball.

Something like this, for example, which uses canvas instead of DOM elements. If you're viewing this on Chrome, just grab the ball and see how much fun it is to throw it around. Keep in mind that this is still a very rough draft and while it's remotely entertaining in its current state, it is not particularly smart and it does no do any checks.




If you can read this, your browser doesnt support this type of stuff.


Below is the code for the first CAPTCHA. The second one is just a bouncing ball drawn on HTML5 canvas.
 <?php   
/* ------------------------------------------------------------------------
 This content is released under the MIT License
 http://www.opensource.org/licenses/mit-license.php
 Author: Marina Ibrishimova (http://ibrius.net)
 Version: 1.0
------------------------------------------------------------------------- */ 
/*--- Note that this is just generating the image captcha  
 ----- You would need to do the checks yourself ------*/  
 //text looks better on png images  
 $im = imagecreatefrompng("path_to_a_blank_box.png");  
 if($im == false) die("Uh oh, hotdog.");  
 //Select Font  
 $font = "path_to_font";  
 //Generate the random string  
 $chars = array("a","A","b","B","c","C","d","D","e","E","f","F","g",  
 "G","h","H","i","I","j","J","k",  
 "K","l","L","m","M","n","N","o","O","p","P","q","Q",  
 "r","R","s","S","t","T","u","U","v",  
 "V","w","W","x","X","y","Y","z","Z","1","2","3","4",  
 "5","6","7","8","9");  
 $length = 8;  
 $code_string = "";  
 for ($i=0; $i<$length; $i++) {  
   $code_string .= $chars[rand(0, count($chars)-1)];  
 }  
 //Create random size, angle, color, and shadow  
 $size = rand(40, 54);  
 $angle = rand(-5, 5);  
 $color = imagecolorallocatealpha($im, rand(0, 100), rand(0, 100), rand(0, 100), 75);  
 $shadow = imagecolorallocatealpha($im, rand(10, 100), rand(10, 100), rand(10, 100), 85);  
 //Determine text size, and use dimensions to generate x & y coordinates  
 $textsize = imagettfbbox($size, $angle, $font, $code_string);  
 $twidth = abs($textsize[2]-$textsize[0]);  
 $theight = abs($textsize[5]-$textsize[3]);  
 $x = (imagesx($im)/2)-($twidth/2)+(rand(-20, 30));  
 $y = (imagesy($im))-($theight/2);   
 $shadowy=$y;  
 $shadowy += 5;  
 $shadowx=$x;  
 $shadowx += 10;  
 //Add text to image  
 imagettftext($im, $size, $angle, $x, $y, $color, $font, $code_string);  
 //Add shadow to the text  
 imagettftext($im, $size, $angle, $shadowx, $shadowy, $shadow, $font, $code_string);  
 header("Content-Type: image/png");  
 imagepng($im);  
 //Destroy the image to free memory  
 imagedestroy($im);  
 ?>  

25.7.13

Creating A Color Space

So far I've shown a few tricks that I haven't been able to find on other people's blogs but they're nothing too spectacular. Now it's time to flex that programming muscle, which in my case is located in the middle finger of my right hand, my most dominant finger when typing, and show some really bad code.

It's almost as bad as this but with an added twist of converting to an entirely new colour space. Because every exercise should have some purpose, the purpose of this one is to replicate the way some people experience colour, in particular the group of people with a colour deficiency most certainly named after an undiscovered Saturn moon, Tritanopia.

People with tritanopia experience the world differently in that they don't experience blue and yellow the way most people do. Instead, they experience it in a red-green hue. Below are two images: the original I took several weeks ago, and the same image with Tritanopia filter applied to it. If you don't see any difference between the two images, I have bad news for you.

Tritanopia Hot Pokers 

Original Hot Pokers






















Making an educated guess about how someone else's algorithm might work or should work without actually looking at the code is a superpower, of which I'm quite proud. Rest assured, I only use it for legal causes. The rgb2tritan function below is just that - an educated guess. However, you can test it with the rainbow from wikipedia, or whatever test you see fit. Below is the code all in one .php file.

 <?php  
 /* -------------------------------------------------  
 This content is released under the MIT License  
 http://www.opensource.org/licenses/mit-license.php  
 Author: Marina Ibrishimova (http://ibrius.net)  
 Version: 1.0  
 ---------------------------------------------------- */   
 function rgb2tritan($r, $g, $b)  
 {  
      //set the tritanopia vector  
      $v1=125;$v2=140;$v3=150;  
      $rgb1 = $v1 + $v2 + $v3;  
      $rgb2 = ($r + $g + $b)/3;   
      $vector = array($v1/$rgb1,$v2/$rgb1,$v3/$rgb1);  
      //blue has the strongest intensity so convert the pixel to tritanopia equivalent   
      if($b > $g && $g >= $r)   
      {  
           $newR = $r;  
           $newG = $rgb2;  
           $newB = $r*$vector[0] + $g*$vector[2] + $b*$vector[1];   
      }   
      else  
      {  
           $newR = $r;  
           $newG = $newB = $g;  
      }  
      return array($newR, $newG, $newB);  
 }  
 function tritanopia($filename)  
 {  
      $im = '/path_to_your/images/'.$filename;  
      if (file_exists($im))  
      {  
           $im = imagecreatefromjpeg($im);  
           if ($im == FALSE)  
           return FALSE;  
           $height = imagesy($im);  
           $width = imagesx($im);  
           //because you really don't want to loop through more that this  
           if ($width >= 900 || $height >= 1000)  
           return FALSE;  
           //let the looping begin  
           for($x=0; $x<$width; $x++)  
           {  
            for($y=0; $y<$height; $y++)  
            {  
                 $rgb = ImageColorAt($im, $x, $y);  
                 $r = ($rgb >> 16) & 0xFF;  
                 $g = ($rgb >> 8) & 0xFF;  
                 $b = $rgb & 0xFF;  
                 list($newR, $newG, $newB) = rgb2tritan($r, $g, $b);  
                 imagesetpixel($im, $x, $y, imagecolorallocate($im, $newR, $newG, $newB));  
             }  
          }   
          if(imagejpeg($im, '/path_to_your/temp_imgs/'.$filename))  
          return TRUE;  
          imagedestroy($im);  
     }  
 return FALSE ;  
 }  
 ?>  
 <!DOCTYPE HTML>  
 <head>  
 <title>Tritanopia</title>  
 <meta charset="utf-8" />  
 </head>  
 <body>  
  <?php  
  //the crop will be 50% of the original, and it will be at the center of the image  
  if(tritanopia('your_filename.jpg'))  
  {  
        echo "<img src=\"/path_to_your/temp_imgs/your_filename.jpg\" > <img src=\"/path_to_your/images/your_filename.jpg\" style=\"float:right;\">";  
  }  
  else  
  {  
        echo "Oops, you either messed up the path, gave the wrong file, or the wrong file size. Happy bug hunting!";  
  }  
  ?>  
 </body>  
 </html>  

19.7.13

Saturating Images with PHP

Saturating an image using PHP's GD is not an easy task since there's no built-in color space with a separate saturation channel. In other words, if you want to properly saturate an image, you would need to loop through each pixel and convert its RGB values to HSL or HSV or some other color space with a separate saturation channel, then convert back to rgb, and finally use imagesetpixel to piece it all together. Sounds pretty bad and it is. And that's why there's an entry on it in this blog.

Sure, you can fake your way around saturation like most iOS developers do but can you get a result like the one below? Nope. Click on the images for a bigger version and look at the tan that whitey beach bum is sporting in the saturated image. Willy Wonka would be so jealous.

Original, unfiltered image
Saturated image
























And here's the saturate function. A demo of it can be found on the Coloroid Photo Editor where it is a part of the Postcard filter.

 function saturate($filename)  
 {  
 $im = '/path_to_your_/images/'.$filename;  
      if (file_exists($im))  
      {  
           $im = imagecreatefromjpeg($im);  
           if ($im == FALSE)  
           return FALSE;   
           $width = imagesx($im);  
           $height = imagesy($im);  
           //because you really don't want to loop through more that this  
           if ($width >= 900 || $height >= 950)  
           return FALSE;  
           //let the looping begin  
           for($x = 0; $x < $width; $x++)   
           {  
                for($y = 0; $y < $height; $y++)   
                {  
                     $rgb = imagecolorat($im, $x, $y);  
                     $r = ($rgb >> 16) & 0xFF;  
                     $g = ($rgb >> 8) & 0xFF;  
                     $b = $rgb & 0xFF;  
                     $alpha = ($rgb & 0xFF000000) >> 24;  
                     //convert to hsl  
                     list($h, $s, $v) = rgb2hsv($r, $g, $b);  
                     //add a touch of saturation but not too much  
                     $s = $s * 1.5;  
                     if($s > 1) $s=1;  
                     //convert back to rgb  
                     list($r, $g, $b) = hsv2rgb($h, $s, $v);   
                     //piece everything together  
                     imagesetpixel($im, $x, $y, imagecolorallocatealpha($im, $r, $g, $b, $alpha));  
                }  
           }  
           if(imagejpeg($im, 'temp_imgs/'.$filename))  
           return TRUE;  
           imagedestroy($im);  
      }  
 return FALSE;  
 }  

And here are the standart implementations of the RGB to HSV and HSV to RGB algorithms.

 function rgb2hsv($r, $g, $b)   
 {  
      $newR = ($r / 255);  
      $newG = ($g / 255);  
      $newB = ($b / 255);  
      $rgbMin = min($newR, $newG, $newB);  
      $rgbMax = max($newR, $newG, $newB);  
      $chroma = $rgbMax - $rgbMin;  
      $v = $rgbMax;  
      if ($chroma == 0)   
      {  
        $h = 0;  
        $s = 0;  
      }   
      else   
      {  
        $s = $chroma / $rgbMax;  
        $chromaR = ((($rgbMax - $newR)/6) + ($chroma/2))/$chroma;  
        $chromaG = ((($rgbMax - $newG)/6) + ($chroma/2))/$chroma;  
        $chromaB = ((($rgbMax - $newB)/6) + ($chroma/2))/$chroma;  
        if ($newR == $rgbMax) $h = $chromaB - $chromaG;  
        else if ($newG == $rgbMax) $h = ( 1 / 3 ) + $chromaR - $chromaB;  
        else if ($newB == $rgbMax) $h = ( 2 / 3 ) + $chromaG - $chromaR;  
        if ($h < 0) $h++;  
        if ($h > 1) $h--;  
      }  
      return array($h, $s, $v);  
 }
 
 function hsv2rgb($h, $s, $v)   
 {  
      if($s == 0)   
      {  
        $r = $g = $b = $v * 255;  
      }   
      else   
      {  
        $newH = $h * 6;  
        $i = floor( $newH );  
        $var_1 = $v * ( 1 - $s );  
        $var_2 = $v * ( 1 - $s * ( $newH - $i ) );  
        $var_3 = $v * ( 1 - $s * (1 - ( $newH - $i ) ) );  
        if ($i == 0) { $newR = $v ; $newG = $var_3 ; $newB = $var_1 ; }  
        else if ($i == 1) { $newR = $var_2 ; $newG = $v ; $newB = $var_1 ; }  
        else if ($i == 2) { $newR = $var_1 ; $newG = $v ; $newB = $var_3 ; }  
        else if ($i == 3) { $newR = $var_1 ; $newG = $var_2 ; $newB = $v ; }  
        else if ($i == 4) { $newR = $var_3 ; $newG = $var_1 ; $newB = $v ; }  
        else { $newR = $v ; $newG = $var_1 ; $newB = $var_2 ; }  
        $r = $newR * 255;  
        $g = $newG * 255;  
        $b = $newB * 255;  
      }   
      return array($r, $g, $b);  
 }  

11.7.13

Instagram Tilt - Shift - Style Filter With PHP's GD Lib

I decided to create an Instagram tilt shift style filter that blurs a specified area of an image while keeping another specified area intact. To spice things up, I used PHP's GD lib. Image manipulation is hard on the CPU and this might not be the most efficient implementation but hey, it's fun and dangerous and as young people say these days, YOLO! 

Besides, it's a very simple hack that uses imagecopyresampled() to position a cropped, unfiltered copy of the original image onto a full-sized tilt-shifted copy of the original. You can specify how big the crop is, its shape, and its position within the big blurred copy.

Original image
Tilt-shifted copy

 Here's the code:

 <?php  
   function tiltshift($filename, $percent_x, $percent_y, $padding_x, $padding_y )  
   {  
    $im = '/path_to_your_image/'.$filename;  
    if (file_exists($im))  
   {  
    list($width, $height) = getimagesize($im);  
    $original = imagecreatefromjpeg($im);  
    $crop = imagecreatefromjpeg($im);  
     
    //Set new crop dimensions  
    $new_width = $width*$percent_x;  
    $new_height = $height*$percent_y;  
     
    //New crop position within original image  
    $newx = $width*$padding_x;  
    $newy = $height*$padding_y;  
     
    //Blur the hell out of the original image  
    imagefilter($original, IMG_FILTER_SELECTIVE_BLUR);  
    imagefilter($original, IMG_FILTER_GAUSSIAN_BLUR);  
     
    //Position crop on top of the original image  
    imagecopyresampled($original, $crop, $newx, $newy, $newx, $newy, $new_width,  $new_height,$new_width, $new_height);  
     
    //Add a dash of contrast to the new merge  
    imagefilter($original, IMG_FILTER_CONTRAST, -10);  
     
    if (imagejpeg($original, 'path_to_your_temp_imgs/'.$filename))  
    {  
    return TRUE;  
    }  
     
    imagedestroy($crop);  
    imagedestroy($original);  
      
   }  
   return FALSE;  
   }  
   ?>  

Obviously, you can use jQuery to pass the padding_x and padding_y based on user input within the image but I'm not going to get that fancy here.

 <?php
 //the crop will be 50% of the original, and it will be at the center of the image
 if(tiltshift('your_image.jpg', '0.5', '0.5', '0.25', '0.25'))
 {
     echo "<img src=\"temp_imgs/your_image.jpg\" > <img src=\"images/your_image.jpg\" style=\"float:right;\">";
 }
?>