What's New
Q & A
Tip Jar
C# Helper...
Follow VBHelper on Twitter
MSDN Visual Basic Community
TitleMake CAPTCHA images (version 4) in Visual Basic 6
DescriptionThis example shows how to make CAPTCHA images (version 4) in Visual Basic 6.
KeywordsCAPTCHA, Turing test, image, image processing, distort image
CategoriesGraphics, Software Engineering

This program uses image processing techniques described in my book Ready-to-Run Visual Basic Graphics Programming.

CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) images are those distorted pictures of words that some Web sites make you enter to prove you are a human and not an automated process. The idea is to distort the characters in the image so it would be hard for an optical character recognition (OCR) application to read them but so it would still be easy for a person to read them.

This example draws text that has been warped by sine functions. When you click the Go button, the code draw the string on a source PictureBox. It then draws a grid on top if desired. This is useful for seeing how the image is deformed later but it might make it easier to automatically recognize the text so you might want to not use it in real use.

Next the code generates some random parameters for the equations that map the pixels in the image to the warped version. It displays the parameters in a label so you can see what's happening. You can change the way the parameters are generated (i.e. change the upper and lower bounds for the random numbers) to see how if changes the result.

Next the code loops through each pixel in the warped output image. For each pixel, it calls subroutine InvertXY to see what location in the original image would be mapped to that output location. It then uses bilinear interpolation to average the nearby pixels at integer locations in the original image to assign a value to the output image. The result is a smoothly transformed image.

Finally the code draws and erases some random lines over the top of the result if desired to make the image harder for an automated system to read.

Private Sub cmdGo_Click()
Dim wid As Integer
Dim hgt As Integer
Dim src_bm As Picture
Dim x As Integer
Dim y As Integer
Dim mag0 As Single
Dim per0 As Single
Dim mag1 As Single
Dim per1 As Single
Dim mag2 As Single
Dim per2 As Single
Dim ix As Integer
Dim iy As Integer
Dim dx0 As Single
Dim dy0 As Single
Dim dx1 As Single
Dim dy1 As Single
Dim r As Integer
Dim g As Integer
Dim b As Integer
Dim r00 As Integer
Dim g00 As Integer
Dim b00 As Integer
Dim r01 As Integer
Dim g01 As Integer
Dim b01 As Integer
Dim r10 As Integer
Dim g10 As Integer
Dim b10 As Integer
Dim r11 As Integer
Dim g11 As Integer
Dim b11 As Integer
Dim clr As OLE_COLOR
Dim i As Integer

    ' Draw the source image.
    wid = picSource.ScaleWidth
    hgt = picSource.ScaleHeight


    ' Draw the text.
    picSource.CurrentX = (wid - _
        picSource.TextWidth(txtString.Text)) / 2
    picSource.CurrentY = (hgt - _
        picSource.TextHeight(txtString.Text)) / 2
    picSource.Print txtString.Text

    ' Draw a grid if desired.
    If chkDrawGrid.Value = vbChecked Then
        For x = 0 To wid Step 20
            For y = 0 To hgt Step 20
                picSource.Line (x, 0)-(x, hgt), vbRed
                picSource.Line (0, y)-(wid, y), vbRed
            Next y
        Next x
    End If

    mag0 = Random(2, 6)
    per0 = Random(20, 30)
    mag1 = Random(3, 6)
    per1 = Random(20, 40)
    mag2 = Random(1, 3)
    per2 = Random(10, 20)
    lblParameters.Caption = _
        mag0 & ", " & per0 & "; " & _
        mag1 & ", " & per1 & "; " & _
        mag2 & ", " & per2

    ' Make the destination bitmap.
    For x = 0 To wid - 1
        For y = 0 To hgt - 1
            ' Find source (x, y).
            Dim sx As Single
            Dim sy As Single
            InvertXY mag0, per0, mag1, per1, mag2, per2, x, _
                y, sx, sy

            ' Interpolate.
            ix = Int(sx)
            iy = Int(sy)

            If ix < 0 Or ix >= wid - 1 Or _
               iy < 0 Or iy >= hgt - 1 _
                picDest.PSet (x, y), vbWhite
                ReverseRGB picSource.Point(ix, iy), r00, _
                    g00, b00
                ReverseRGB picSource.Point(ix + 1, iy), _
                    r10, g10, b10
                ReverseRGB picSource.Point(ix, iy + 1), _
                    r01, g01, b01
                ReverseRGB picSource.Point(ix + 1, iy + 1), _
                    r11, g11, b11
                dx0 = sx - ix
                dy0 = sy - iy
                dx1 = 1 - dx0
                dy1 = 1 - dy0
                r = dy1 * (dx1 * r00 + dx0 * r10) + _
                    dy0 * (dx1 * r01 + dx0 * r11)
                g = dy1 * (dx1 * g00 + dx0 * g10) + _
                    dy0 * (dx1 * g01 + dx0 * g11)
                b = dy1 * (dx1 * b00 + dx0 * b10) + _
                    dy0 * (dx1 * b01 + dx0 * b11)
                picDest.PSet (x, y), RGB(r, g, b)
            End If
        Next y
    Next x

    ' Draw random lines if desired.
    If chkRandomLines.Value = vbChecked Then
        For i = 1 To 10
            picDest.Line (Random(0, wid), 0)-(Random(0, _
                wid), hgt), vbBlack
            picDest.Line (Random(0, wid), 0)-(Random(0, _
                wid), hgt), vbWhite
        Next i
    End If
End Sub
Helper function Random uses Rnd to generate a random Single value between lower and upper bounds.
Private Function Random(ByVal min_v As Single, ByVal max_v _
    As Single) As Single
    Random = min_v + Rnd * (max_v - min_v)
End Function
Helper routine InvertXY finds the source point (sx, sy) that maps to the result position (x, y) under the function defined by the ranomized parameters.
Private Sub InvertXY(ByVal mag0 As Single, ByVal per0 As _
    Single, ByVal mag1 As Single, ByVal per1 As Single, _
    ByVal mag2 As Single, ByVal per2 As Single, ByVal x As _
    Integer, ByVal y As Integer, ByRef sx As Single, ByRef _
    sy As Single)
Const PI As Single = 3.14159265

    sx = x - mag0 * Cos(y * PI / per0)
    sy = y - mag1 * Sin(x * PI / per1) - mag2 * Sin(x * PI _
        / per2)
End Sub
The ReverseRGB helper function breaks a color value into red, green, and blue components.
Public Sub ReverseRGB(ByVal Value As Long, Red As Integer, _
    Green As Integer, Blue As Integer)
Dim lngRed As Long
Dim lngGreen As Long
Dim lngBlue As Long

    lngBlue = Int(Value / 65536)
    lngGreen = Int((Value - (65536 * lngBlue)) / 256)
    lngRed = Value - ((lngBlue * 65536) + (lngGreen * 256))
    Red = lngRed
    Green = lngGreen
    Blue = lngBlue
End Sub
Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.