An Image is Worth a Thousand Words in PHP Continued

Picking up where we left off in part one, we will work on some more helper methods and our conversion method. By the time we are done, we will have a fully functional image to text converter.

Ramp Up

Welcome back! We are about to pick up where we left off in part one, but first a quick summary of what we have done so far. We began by creating our conversion class and naming it ‘img_to_txt’. Inside this class we defined several basic accessory methods, to allow our users a variety of options. Last, we also set up a method for returning the appropriate CSS markup based on the user’s browser type.

That’s what we’ve done so far – but there’s a lot more to do, so let’s get started. If you have not yet read part one of “An Image is Worth a Thousand Words,” you should read through it before continuing.

Characters and Colors

We’re almost ready to start working on our conversion method, but there are still a few associated, helper methods that we should define before proceeding. We can split them into two main categories: characters and colors. Let’s take a closer look at each.

As we’ve already mentioned, our ‘img_to_txt’ class will allow a user to specify a custom set of characters with which to build our converted image. These characters can be printed in ascending order (i.e. “THISISASTRING”) or in random order (i.e. “IAHTISAGEHG”). Furthermore, certain output modes (“matrix” for example) will be comprised of a separate, custom set of chars. To keep things clean, we can split this logic of the application into a couple of helper ‘get’ and ‘init’ methods:

 # return next char for ‘color’ and ‘grayscale’ output modes
 function get_char() {
  if ( $this->char_mode == ‘NORMAL’ )
   return $this->chars[ ( $this->char_index++ % strlen( $this-
>chars ) ) ];
  else
   return $this->chars[ rand( 0, strlen( $this->chars ) – 1 ) ];
 } # END get_char()
 
 # return next char for ‘matrix’ output mode
 function get_matrix_char() {
  return $this->matrix_chars[ rand( 0, strlen( $this-
>matrix_chars ) – 1 ) ];
 } # END get_matrix_char()

The above functions simply return a single char at a time, depending on the output mode selected. If ‘color’ or ‘grayscale’, the function also checks to see if the user has specified a char_mode (of ‘NORMAL’ or ‘RANDOM’) and acts accordingly. (Some of you may have noticed that the functions listed only deal with ‘color’, ‘grayscale’, and ‘matrix’ modes. This is because our ‘monochrome’ mode requires a slightly different approach, but we’ll get into that shortly.)

Our application also allows a user to convert an image into one of several output modes (color, grayscale, monochrome, etc.). Because of this, we should probably split this area of logic into a few ‘get’ helper functions. Let’s list them below, then take a closer look at each:

 # specify list of all possible HEX values, in ascending order,
and store in array
 var $hex_array   = array();
 var $hex_values   = array( ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’ );
 
 # specify list of all possible monochrome values, in ascending
order, and store in array
 var $monochrome_values = ‘ .:xW©';
 var $monochrome_array  = array();
 
 # specify list of all possible ‘matrix’ chars
 var $matrix_chars  =
‘¶µ®@&¥$£«#©§¦’;
 # initializes (builds) the HEX color array
 function init_hex_array() {
  for( $a = 0; $a < count( $this->hex_values ); $a++ )
   for( $b = 0; $b < count( $this->hex_values ); $b++ )
    array_push( $this->hex_array, $this->hex_values[ $a ] .
$this->hex_values[ $b ] );
 } # END init_hex_array() 

 # initializes (builds) the monochrome color array
 function init_monochrome_array() {
  for( $a = 0; $a < strlen( $this->monochrome_values ); $a++ )
   array_push( $this->monochrome_array, $this->monochrome_values
[ $a ] );
 } # END init_monochrome_array()
 
 # converts and returns a (color) HEX equivalent of an RGB color
value
 function rgb_2_color( $red, $green, $blue ) {
  return $this->rgb_2_hex( $red ) . $this->rgb_2_hex( $green ) .
$this->rgb_2_hex( $blue );
 } # END rgb_2_color()
 
 # converts and returns a (grayscale) HEX equivalent of an RGB
color value
 function rgb_2_grayscale( $red, $green, $blue ) {
  $rgb_avg = ( $red + $green + $blue ) / 3;
  $hex_avg = $this->rgb_2_hex( $rgb_avg );
  return $hex_avg . $hex_avg . $hex_avg;
 } # END rgb_2_grayscale()

 # converts and returns a grayscaled HEX equivalent of an 256-
color, index value
 function rgb_2_hex( $rgb_color ) {
  if ( count( $this->hex_array ) <= 0 ) $this->init_hex_array();
  
  $color_index = ( 255 / ++$rgb_color );
  $color_index = round( count( $this->hex_array ) /
$color_index );
  $color_index = ( $color_index >= count( $this->hex_array ) ) ?
count( $this->hex_array ) – 1 : $color_index;
  
  return $this->hex_array[ $color_index ];
 
 } # END rgb_2_hex()
 
 # converts and returns a (green) HEX equivalent of an RGB color
value
 function rgb_2_matrix( $red, $green, $blue ) {
  $rgb_avg = ( $red + $green + $blue ) / 3;
  return ’00’ . $this->rgb_2_hex( $rgb_avg ) . ’00′;
 } # END rgb_2_matrix()
 
 # averages RGB colors passed, and returns a similarly weighted
character
 function rgb_2_monochrome( $red, $green, $blue ) {
  # init monochrome array if necessary
  if ( count( $this->monochrome_array ) <= 0 ) $this-
>init_monochrome_array();
  
  # determine character from MONOCHROME array to display
  $rgb_avg = ( $red + $green + $blue ) / 3;
  $char_index = ( 255 / ++$rgb_avg );
  $char_index = round( count( $this->monochrome_array ) /
$char_index );
  $char_index = ( $char_index >= count( $this->monochrome_array ) ) ? count( $this->monochrome_array ) – 1 :
$char_index;

  # return char
  return $this->monochrome_array[ $char_index ];
 } # END rgb_2_monochrome()

{mospagebreak title=Examining the Code}

We’ve just added a lot of code at once, but each method is pretty straight-forward. First we have our ‘init’ methods: init_hex_array and init_monochrome_array. These methods simply make sure that our ‘HEX’ (hexadecimal) and ‘monochrome’ value arrays have been created and indexed properly. The monochrome array is probably pretty straightforward, but the HEX array may be a little more confusing. Basically, we start by specifying a string of all possible HEX values, in ascending order (0-9, A-F). Our ‘init’ function simply iterates through that list and expands it to a two-character representation (00 through FF).

Next we have our various conversion methods, rgb_2_*. Each of these methods is highly specified, and takes a series of arguments specifying the red, green, and blue color values for a given image pixel. Each function then uses the helper, ‘rgb_2_hex’ function to convert the various pieces of color data into an appropriate return format.

The color function then simply converts the RGB value to a direct HEX equivalent (0,0,0 = 000000 and 255, 255, 255 = FFFFFF). Next, our grayscale function averages the three color values together, and returns a HEX string representing that average. (This is because a grayscale image is comprised of an equal amount of red, green, and blue, with a varying brightness or intensity). Next, our matrix conversion function averages the RGB values, and then returns a modified HEX string including only green values (000000 through 00FF00). (Similar to our grayscale function, a matrix image is made up of all green colors, so our function simply discards any red or blue color information and averages in the brightness of all three values).

Finally, our monochrome conversion function doesn’t return a color at all; it returns a character. Why is this? Because a monochrome image consists of only two colors – in our case, white and black (white characters on a black background). So rather than return a color, the ‘rgb_2_monochrome’ function determines the brightness of the pixel (by averaging the three colors present) and then returns a character of text that represents that approximate brightness level. For our example application, we have specified the characters ‘ .:xW©’. As you may be able to tell, each character is slightly heavier than the character before it. In our application this will result in a very cool, ‘monochrome’ effect – but we’ll see that shortly.

{mospagebreak title=Final Preparation}

We’re almost there! Just a few last knots to tie before we implement our conversion function. Let’s go ahead and add in the rest of our class variables:

# number of pixels (X-axis) to average together to determine a
color
var $skip_factor = 5;
 
# store user-specified image information
var $image_path = NULL;
var $image_name = NULL;
var $image_type = NULL;

# store pointer to PHP image object
var $img_obj  = NULL;

# store image dimensions
var $image_width = 0;
var $image_height = 0;

The above variables and comments are fairly self-explanatory, with the exception of ‘skip_factor’. This variable simply specifies the number of pixels (along the X axis) we will be averaging in order to determine the color for each individual text character our class returns. The default value of 5 means that we will take the average value of every ‘5’ pixels, and return exactly one text character. (Although this value is configurable, it should remain ‘5’ for our application as changing it would require the CSS to be modified as well.)

Next, let’s add a method that allows our user to specify the location of the image file they wish to convert. (Remember, this can be a local or remote image – so long as a valid path has been provided.)

# new load function – returns TRUE or FALSE to indicate success
function load_image( $image_path ) {
 $this->image_path = $image_path;
 return ( $this->init_img() != FALSE );
} # END load_image()

And now for the last bit of preparation before we dive head-first into the conversion function:

function init_img() {
 
 $this->image_name = end( explode( ‘/’, $this->image_path ) );
 
 # determine type of file, and create it
 switch( end( explode( ‘.’, $this->image_name ) ) ) {
  case ‘jpg':
  case ‘jpeg': $this->img_obj = @ imagecreatefromjpeg( $this-
>image_path ); break;
  case ‘png': $this->img_obj = @ imagecreatefrompng( $this-
>image_path ); break;
  case ‘gif': $this->img_obj = @ imagecreatefromgif( $this-
>image_path ); break;
  case ‘bmp': $this->img_obj = @ imagecreatefromwbmp( $this-
>image_path ); break;
 }
 
 $this->image_width  = @ imagesx( $this->img_obj );
 $this->image_height  = @ imagesy( $this->img_obj );
 
 # return TRUE / FALSE to indicate success
 return ( $this->img_obj != ” );
 
} # END init_img()

The init() function, as you can see, simply checks the file extension of the user-specified image, and opens an image object for the appropriate file-type. It also stores the image dimensions within the class variables ‘image_width’ and ‘image_height’.

Both functions will return TRUE upon success or FALSE upon failure. If FALSE is returned, the user either specified an invalid image path, or an unsupported input format.

{mospagebreak title=Converting An Image}

Now it’s time to take a look at the meat of our application: the conversion process. It’s a lot of code at first glance, but we’ll break it into smaller sections. First, let’s add the function to our class:

function get_image() {
 $text_image = NULL;
 $switch   = FALSE;
 
 # init image, if not already setup
 if ( !isset( $this->img_obj ) )
  if ( !$this->init_img() )
   return NULL;
 
 for( $y = 0; $y < $this->image_height; $y += ( $switch ) ? 2 :
1 ) {
  $switch = !$switch;
  $red  = 0;
  $green = 0;
  $blue  = 0;
  
  for( $x = 0; $x < $this->image_width; $x++ ) {
  
   // retrieve current pixel color info
   $rgb_color = imagecolorat( $this->img_obj, $x, $y );
   $rgb_color = imagecolorsforindex( $this->img_obj,
$rgb_color );
   $red    += $rgb_color['red'];
   $green   += $rgb_color['green'];
   $blue    += $rgb_color['blue'];
   
   # average colors and output a block
   if ( $y % $this->skip_factor == $this->skip_factor – 1 && $x %
$this->skip_factor == $this->skip_factor – 1 ) {
    $red  /= $this->skip_factor;
    $green /= $this->skip_factor;
    $blue  /= $this->skip_factor;
    
    # if monochrome – the ‘color’ is actually the char, (the real
color is always white
    if ( $this->return_format == ‘MONOCHROME’ ) {
     $hex_color = ‘FFFFFF';
     $char    = $this->rgb_2_monochrome( $red, $green, $blue );
     
    # if matrix mode, get a green color and a special char
    } else if ( $this->return_format == ‘MATRIX’ ) {
     $hex_color = $this->rgb_2_matrix( $red, $green, $blue );
     $char = $this->get_matrix_char();
     
    # otherwise, get appropriate color & leave the char alone
    } else if ( $this->return_format == ‘GRAYSCALE’ ) {
     $hex_color = $this->rgb_2_grayscale( $red, $green, $blue );
     $char = $this->get_char();
    } else {
     $hex_color = $this->rgb_2_color( $red, $green, $blue );
     $char = $this->get_char();
    }
    
    # output colored char
    $text_image .= “<font color=’#$hex_color’>$char</font>”;
   
    # re-set color totals for block
    $red  = 0;
    $green = 0;
    $blue  = 0;
 
   } # END average colors
  } # END foreach-y ($b)
  
  # output a new line if appropriate
  if ( $y % $this->skip_factor == 1 ) $text_image .= ‘<br>';
  
 } # END foreach-x ($a)
 return ‘<div ‘ . $this->get_css() . ‘>’ . $text_image .
‘</div>';
 
} # END get_image()

The above code can be split into four main sections: checking to make sure our image has been set up properly, walking through the image and retrieving color information, converting the information we have retrieved into colored text, and finally, returning the text we have created, complete with browser-specific CSS code, to be displayed.

The first step is rather easy. If an image has not been set up correctly, our function will return a NULL string, indicating failure.

Next our code enters a series of for loops. The outer loop walks through each pixel on the Y axis, and the inner walks through each pixel on the X axis. Using these loops, our function traverses each pixel of the specified image and collects information about the color of each pixel. This information is temporarily stored in loop variables ‘red’, ‘green’, and ‘blue’ and averaged every ‘skip_factor’ number of times through the loop. Once each skip factor is reached, and the colors have been averaged, our function checks for the user specified return format and uses our helper color and character functions in order to append the appropriate character to our return string.

Finally, once our function has completed its conversion loops, it calls out to ‘get_css’ to retrieve the appropriate browser-specific styling information. Without this information our image would still be recognizable, but not as pretty. And that’s it.

{mospagebreak title=In Conclusion}

That’s it. We now have a working conversion library – but we still haven’t seen how it works! Let’s try it out by converting a familiar graphic. Go ahead and create a file – (you can name it anything you like, so long as it has a .php extension) – and put in it the following code:

<html>
<head>
<title>IMG_TO_TXT</title>
<style type=”text/css”>
 strong, b {
  color: #FFFFFF;
  font-family: Tahoma, Arial, Verdana, Helvetica, sans-serif;
  font-size: 11px;
 }
</style>
</head>

<body bgcolor=”#000000″>

<?php
 include_once ‘img_to_txt.php';
 $img_to_txt = new img_to_txt();
 $img_to_txt->set_chars( ‘MYNAMEISBRIAN’ );
?>

 <b>Color:</b><br>
<?php
 $img_to_txt->load_image( ‘http://www.google.com/intl/en/images/logo.gif’ );
 echo $img_to_txt->get_image();
?>

 <br><b>Grayscale:</b><br>
<?php
 $img_to_txt->set_return_format( ‘grayscale’ );
 echo $img_to_txt->get_image();
?>


 <br><b>Matrix:</b><br>
<?php
 $img_to_txt->set_return_format( ‘matrix’ );
 echo $img_to_txt->get_image();
?>


 <br><b>Monochrome:</b><br>
<?php
 $img_to_txt->set_return_format( ‘monochrome’ );
 echo $img_to_txt->get_image();
?>

</body>
</html>

The above demonstration converts the familiar Google image into several text-only modes. You may view this example online here:

http://images.devshed.com/ds/stories/Image_is_Words/IMG_TO_TXT.htm

If you have not followed along but would still like to download the source code, it may be found online here:

http://images.devshed.com/ds/stories/Image_is_Words/img_to_txt.zip

I hope this has been a useful exercise for any of you who have taken the time to complete it. Thank you for your time, and please let me know if you have any questions/comments. Until next time…

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye