Lo ke yo he visto ke siempre se hace, es se contruye un string random, y se guarda en una variable de session, se imprime en una imagen y depsues se compara con el valor enviado por el formulario si es igual se ejecuta el script si no parar el codigo y mostrar el error.
  
creas tu script de imagen.php 
 Código PHP:
    session_start();  
$captcha = '';
for ( $c = 0; $c < 5; $c++ ) 
{  
        $captcha .= chr(rand(97, 122));  
}
$_SESSION['captcha'] = $captcha;  
// definir el directorio de tipo de letras
$fonts = 'fonts/';  
// imagen base de 200px de ancho por 80 de alto
$imagen = imagecreatetruecolor( 200, 80 );  
// definir colores
$negro  = imagecolorallocate( $imagen, 0, 0, 0 );  
$rojo    = imagecolorallocate( $imagen, 200, 100, 90 ); 
$blanco = imagecolorallocate( $imagen, 255, 255, 255 ); 
// crear un rectangulo de fondo blanco
imagefilledrectangle( $imagen, 0, 0, 399, 99, $blanco );  
// imprimir el texto random creado en la imagen de color rojo, con tamano 30, con angulo 0, 10px a la izierda y 40 de alto  
imagettftext ( $imagen, 30, 0, 10, 40, $rojo, $fonts . 'arial.ttf', $_SESSION['captcha'] );  
// le dices al navegador ke tipo de imagen haras
header( 'Content-type: image/jpeg' );  
// imprimir la imagen
imagejpeg( $imagen ); 
    
  tu formulario 
 Código PHP:
    
<form method="post" action="script.php">
   <input type="hiddden" name="envio" value="1" />
   <label for="nombre">Nombre: </label>
   <input type="text" name="nombre" id="nombre" />  
   <label for="email">Email: </label>
   <input type="text" name="email" id="email" />
   <img src="imagen.php"/>  
   <label for="captcha">Entra el codigo: </label>
   <input type="text" name="captcha" id="captcha" />  
   
   <input type="submit" value="Enviar" /> 
</form> 
    
  tu script.php 
 Código PHP:
    
session_start();  
if ( isset( $_POST['enviado'] ) && $_POST['enviado'] == 1 )
{  
   if ( $_POST['captcha'] == $_SESSION['captcha'] ) 
   {  
      echo 'Codigo verificado OK';     
   }
   else
   {
       echo 'Codigo NO valido';  
    }  
}