Monthly Archives: October 2011

Realbasic: Canvas tutorial 9 – Finding objects by its color

One of the techniques to find an object in a picture is by searching for its color. This sounds easy, as for a human we can easily see what is red or blue or any other color. But for a computer it is not that easy. What is red? Or blue?

Looking for a green object in a picture cannot be done by looking for the RGB value 00FF00. There are many shades of green that we as a human consider green. It may be lighter, darker, warmer, colder, etc.

So we need to find another approach. This is where HSV comes in. HSV stands for Hue, Saturation, and Value. If we can convert our color to those values, we can then build a range which we will consider as ‘the green’ ones.

Let’s dive into the code.

Create a class Blob with the following properties. This will simply hold our result pictures:

MyPic as picture
MyRealPic as picture

Now, we are going to find the blob of our desired color in the function GetBlobFromPic().

For this tutorial I’ll created the ranges for Red, Green, Blue and Yellow objects. We first convert the RGB pixel to it’s HSV values. To do this I used some math functions I found in a math article on the internet. I can’t recall which one, but I’ll try to find it back so I can post it here.

Once we have our HSV values, we’ll compare if they fall within the range we are looking for. If so, we mark it in our blob by setting the pixel to &C000001. You can also do this in a table, but for the purpose of this tuorial I also want to show the result of this search (shown in canvas2).

Here is the main part from this function:

  ' these values are precaulculated 
  select case FindColor
  case &c00FF00 ' green
    minH = 48
    maxH = 120
    minS  = 40
    maxS = 255
  case &cFF0000 ' red
    minH = 236
    maxH = 14
    minS  = 40
    maxS = 255
  case &cFFFF00 ' yellow
    minH = 25
    maxH = 47
    minS  = 100
    maxS = 255
  case &c0000FF 'blue
    minH = 121
    maxH = 170
    minS  = 40
    maxS = 255
  end select
  ' search for the color within our range
  for ax = 0 to SrcPic.Width - 1
    for ay = 0 to SrcPic.Height - 1
      C = SrcRGB.Pixel(ax,ay)
      H = C.Hue * 255
      V = C.Value* 255
      S = C.Saturation * 200
      minV = 50 - (30/215 * (S - 40))
      maxV = 170 + (70/215 * (S - 40))
      if minH - MaxH < 0 then
        if H >= MinH and H <= maxH and S >= minS and S <= maxS and V >= minV and S <= maxV then
          TgtRGB.Pixel(ax,ay) = &c000001
        end if
        if (h >= minH or H <= maxH) and S >= minS and S <= maxS and V >= minV and S <= maxV then
          TgtRGB.Pixel(ax,ay) = &c000001
        end if
      end if

Then, we do some cleanup. If more than half the pixels surrounding our found pixel are not marked, unmark it (&cFFFFFF). We’ll search like this:


Where X is our marked pixel we want to check and A the ones around it. For speed, I also check X itself so I don’t have to use an IF in the loop.

Here is the code that does this:

  for ax = 0 to tgtPic.Width - 1
    for ay = 0 to tgtPic.Height - 1
      Teller = 0
      for zx = ax - 1 to ax + 1
        for zy = ay - 1 to ay + 1
          if TgtRGB.Pixel(zx,zy) <> &c000001 then
            Teller = Teller + 1
          end if
        if Teller > 4 then
          TgtRGB.Pixel(ax,ay) = &cFFFFFF
        end if

Last, We’ll use our ‘map’ with marked pixels to get the original pixels that we can show in Canvas3:

  for ax = 0 to tgtPic.Width - 1
    for ay = 0 to tgtPic.Height - 1
      if TgtRGB.Pixel (ax,ay) = &c000001 then
        realRGB.Pixel(ax,ay) = OrigRGB.Pixel(ax,ay)
      end if

That’s it!

Here are some other results we get from our program:

To make this even better, you can first do a Gaussian Blur on the picture so most of the holes in the found object are closed. The gaussian blur is not in this tutorial, but a quick seach on the internet will give you several sites where this is explained.

I hope this explained a little bit how we can do a fast search of an colored object in a photo.

The source code for this project:

Keep on programming!

Click here to Donation if you like my work

RealBasic: XBOX 360 Kinect source code

Note: This is a PC only project.

I had some requests for the XBOX 360 Kinect source code so I decided to put it up on the blog. It contains an encrypted object KinectCanvas that can be freely used in your own projects and an example on how to use it.

The blob detection is done in pure RealBasic. Provided is a PC driver for the kinect. The only thing this gives back are pictures, just like a normal webcam. As it is a Kinect, we also get a depth picture and that is the one we’ll use to do the fun stuff. The depth picture is something like this:

For the attached demo project this is my setup at home:

This is how you install the included driver:

1. Unzip everything to a folder (let’s say c:\KinectCanvas).
2. Start CL-NUI-Platform- to install the kinect driver
3. plug in your Kinect and power
4. start KinectCanvas.exe or open the source code

You may have to change the position of the kinect camera by using the motor functions:

ret = KinectCanvas1.startMotor
' value between -8000 and 8000
ret = Kinectcanvas1.SetMotorPosition(-4500)

The most import function is the FindBlobs() function:

' for the moment use these params as they are for hand detection
aantalblobs= KinectCanvas1.FindBlobs(110, 170, 15, 150, false) 

Here are the available functions, but not all of them are tested yet:

FindBlobs (MinRangeCM as integer, MaxRangeCM as integer, DepthOfBlobCM as integer, SizeOfBlobPixels as integer, Optional CheckGestures as Boolean = false) as integer
GetCameraColorFrameRAW(MakePicture as boolean, timeOut as integer) as boolean
GetCameraColorFrameRGB24(MakePicture as boolean, timeOut as integer) as boolean
GetCameraColorFrameRGB32(MakePicture as boolean, timeOut as integer) as boolean
GetCameraDepthFrameCorrected12(MakePicture as boolean, timeOut as integer) as boolean
GetCameraDepthFrameCorrected8(MakePicture as boolean, timeOut as integer) as boolean
GetCameraDepthFrameRAW(MakePicture as boolean, timeOut as integer) as boolean
GetCameraDepthFrameRGB32(MakePicture as boolean, timeOut as integer) as boolean
GetMotorAccelerometer(byref x as integer, byref y as integer, byref z as integer) as boolean
SetMotorLED(mode as integer) as boolean
SetMotorPosition(Position as integer) as boolean
StartCamera(tmpTimer as KinectTimer) as boolean
StartMotor() as boolean

The makepicture param is not used yet. It may be build in to speed up things. For the moment this is what you do if you want to get the picture for example from the depth camera in 32 bit colors:

' in the KinectTimer1.Action
Dim Ret as boolean
Dim a as integer
Dim b as integer
Dim tmpPic as picture
Dim surf as RGBSurface
Dim Tel as integer

Ret = KinectCanvas1.GetCameraDepthFrameRGB32(false, 1000)

tmpPic = newpicture(640, 480, 32)
surf = tmpPic.RGBSurface

for a =0 to 479
    for b =0 to 639
        surf.Pixel(b,a) = rgb(KinectCanvas1.CameraData.byte(Tel),KinectCanvas1.CameraData.byte(Tel+1), KinectCanvas1.CameraData.byte(Tel+2))
        Tel = Tel + 4
next tmpPic, 0,0

This code will result into the depth picture you see on top of this article.

The code is very experimental but you can use it to play with the Kinect in RealBasic!

The source code and driver:
A video of what the demo does, but in West-Flemish 🙂

Until next time!

Click here to Donation if you like my work

12Scan: additional

Some people asked me what the output of 12Scan looks like. Well, it’s a plain TXT file with the matrix in it. A zero if it is not checked, A one if the user checked it. Any other program can then use this data how they want.

Here is the output of the demo:

New project at OneTwo: 12Scan

At OneTwo, I’m working at a new program with the work title 12Scan. It is a tool written in RealBasic to scan a page and analyze its contents.

It needs to be able to ‘read’ a page where people color in preprinted empty circles and return a txt file with a grid that represents the users selections. The picture can be taken with any device like flat scanners, webcams or mobile phone cameras.

Here is an example of such an empty page that could be used to check the attendance in a school:

It turns out it was not that easy to build as it looks. Especially the light and the environment were quite a challenge. With the knowledge I learned by building the augmented reality library for android, I could use some of the same techniques in this program.

A couple of things needed to be done. I wanted to avoid any rotation or deformation of the picture to keep the maximum quality of the picture. That meant manipulating the picture in other ways to find the hand colored points.

After some grey scaling, smoothing, blurring, sharpening and the other usual picture manipulations I’ve got a workable picture. The next function searched the blobs. I’ve used some functions I made in the augmented reality lib and that seems to work very well. Next was finding the square shape of the found blobs so I could create the matrix for all hand colored blobs. This looks very simple for a human, but for a computer it is quite a challenge.

This could be solved using a technique called PointsCloud. It searched for the corners within a ‘cloud of points’. The last part was aligning the hand colored points into the found matrix.

We are very satisfied with our preliminary results. It works very well with different light intensities and even on wrinkled or with coffee smothered pages. Here is a little video that shows our results:

This is done by dirty experimental code so my next task is rewriting it in a nice framework and optimizing the code for speed.

Visit us at to see more of our innovating projects.

Until next time!

Realbasic: Canvas Tutorial Lesson 8

I had some questions about glowing and reflection. This tutorial will go a little more in depth on this subject.

I also show an alternative way to do the glowing with an inverted mask. This way we can put the glow and reflection mask into one picture.

This is what we going to build:

First let’s analyse our mask picture and explain the different parts:

1. The glow:
As you can see the glow is inverted compared with the glow we used in our previous lessons. The more black a pixel is, the less transparent it will be and thus the white layer will glow more.

2. The CD
Again, here the more black, the less transparent. We want to mimic a plastic case and we want to see our CD cover so we’ll make it completely white (transparent)

3. The reflection
And also so for the reflection. The more black a pixel is, the less we want to see our background through.

This is a complete schematic of all the layers we are going to use:

Click picture to see full size

As you can see, we’ll use some temporary pictures to build the glow part and the reflection part.

Let’s go to the code:

In the LoadReflectPicture() function we’ll build all our layers. I commented the code to show where each part goes.

Sub LoadReflectPicture(tmpBackground as String, tmpImage as string, tmpMask as String, StartReflectMask as integer, Left as integer, Top as integer)
  dim f as FolderItem
  dim bgPic as Picture
  dim imgPic as Picture
  dim mskPic as Picture
  ' this picture will hold our flipped an cropped image
  dim flpcropPic as Picture
  ' load the pictures
  f = GetFolderItem(tmpBackground)
  bgPic = f.OpenAsPicture
  f = GetFolderItem(tmpImage)
  imgPic = f.OpenAsPicture
  f = GetFolderItem(tmpMask)
  mskPic = f.OpenAsPicture
  ' calculate the Height of the reflection mask
  dim HeightReflectMask as integer
  HeightReflectMask =mskPic.Height - StartReflectMask
  ' draw the background
  gBuffer.drawPicture bgPic,0,0
  ' Make a temporary picture that will hold our image with the glow and reflection
  dim tmpPic as Picture
  tmpPic = NewPicture(mskPic.Width, mskPic.Height, 32)
  ' first the glow, because we want to reflect this also
  ' draw our image
  tmpPic.Graphics.DrawPicture imgPic,0,0
  ' to draw our glow, we need another temporary picture
  dim glowPic as Picture
  glowPic = NewPicture(mskPic.Width, StartReflectMask, 32)
  ' a white layer to do the glow
  glowPic.Graphics.ForeColor = &cFFFFFF
  glowPic.Graphics.FillRect 0,0, glowPic.Width, glowPic.Height
  ' the mask
  glowPic.Mask.Graphics.DrawPicture mskPic, 0,0, glowPic.Width, glowPic.Height, 0,0, glowPic.Width, glowPic.Height
  ' and write it to our temporary picture
  tmpPic.Graphics.DrawPicture glowPic,0,0
  ' now let's do the cropping and flipping
  flpcropPic = FlipAndCrop(tmpPic, HeightReflectMask)
  ' draw it to our temporary picture
  tmpPic.Graphics.DrawPicture flpcropPic, 0, StartReflectMask
  ' and last our reflection mask
  tmpPic.Mask.Graphics.DrawPicture mskPic, 0, StartReflectMask, flpcropPic.Width, flpcropPic.Height, 0,StartReflectMask,flpcropPic.Width, flpcropPic.Height
  ' and write it to our buffer
  gBuffer.DrawPicture tmpPic,Left, Top
End Sub

In the FlipAndCrop() function we do the actual flip and crop. We crop the CD cover to half the height of the original to mimic some perspective.

Function FlipAndCrop(srcPic as picture, HeightReflectMask as integer) As picture
  dim tgtPic as Picture
  dim srcRGB as RGBSurface
  dim tgtRGB as RGBSurface
  dim x,y as Integer
  dim tmpc as Color
  dim y2 as integer
  Dim picWidth as integer
  Dim picHeight as integer
  picWidth = srcPic.Width
  picHeight = srcPic.Height - HeightReflectMask ' only the CD, not the part ready for the reflection
  tgtPic = NewPicture(picWidth, HeightReflectMask, 32)
  srcRGB = srcPic.RGBSurface
  tgtRGB = tgtPic.RGBSurface
  ' the part of the original picture we want to reflect and crop
  ' the height = 2 x HeightReflectMask
  y2 = HeightReflectMask
  for y = picHeight - (HeightReflectMask * 2) to picHeight step 2 ' step 2 so we'll crop
    for x = 0 to picWidth
      tgtRGB.Pixel(x,y2) = srcRGB.Pixel(x,y)
    y2 = y2 - 1 ' we go from bottom to top, so flipping it
  Return tgtPic
End Function

I hope this makes things a little bit clearer, but feel free to ask questions.

The source code:

See ya!

Click here to Donation if you like my work

Realbasic: Canvas Tutorial Lesson 7

In this next tutorial we’ll going to add a menu to our elements so the users can really interact with them.

The idea would be that when we click on an element, this element becomes active and gets a menu with a play and stop button. When we click on another element, the menu is removed from the previous element and opened on the new element. When we click on something else than an element, it disappears.

The picture below shows what we want to do:

Let’s get started with the code:

We create a new class ABMenu that will hold our play and stop buttons. We give it a type and a picture.

X as integer
Y as integer
W as integer
H as integer
Image as picture

Sub Constructor(tmpType as integer, tmpImage as picture)
  me.Type = tmpType
  me.Image = tmpImage 
End Sub

Sub Destructor()
End Sub

Sub CleanUp()
  Image = nil
End Sub

In the ABelement, add a new property HasMenu. This way we can choose if an element has a menu or not. Also add a table that holds our MenuButtons.

Hasmenu as Boolean
MenuButtons(-1) as ABMenu

Change the Init() function to set our HasMenu variable:

Sub Init(tmpID as integer, tmpType as integer, tmpX as integer, tmpY as integer, tmpInnerX as integer, tmpInnerY as integer, tmpImage as picture, tmpMask as picture, tmpGlow as picture, tmpVisible as boolean, tmpHasMenu as boolean )
  me.ID = tmpID
  me.x = tmpX
  me.y = tmpY
  me.Visible = tmpVisible
  me.w = tmpImage.Width
  me.h = tmpImage.Height
  me.Type = tmpType
  me.MyGlow = tmpGlow
  me.MyMask = tmpMask
  me.InnerX = tmpInnerX
  me.innerY = tmpInnerY
  me.HasMenu = tmpHasMenu ' new
  Image = NewPicture(w,h, 32)
  image.Graphics.DrawPicture tmpImage,0,0
  if MyMask <> nil then
    me.w = MyMask.Width
    me.h = MyMask.Height
  end if
  pBuffer = NewPicture(w,h, 32)
  gBuffer = pBuffer.Graphics
End Sub

A new function AddMenuButton() will allow us to add Menu buttons to each element:

Sub AddMenuButton(tmpType as integer, tmpPicture as picture)
  Dim tmpMenu as new ABMenu(tmpType, tmpPicture)
  MenuButtons.Append tmpMenu
End Sub

Now for the changes in the ABCanvas class. We’ll add a new property CurrentMenuElement. This is similar to our DragElement from a previous lesson: it holds a temporary link to the element that currently has the menu.

CurrentMenuElement as ABElement

We make a change to our RefreshElement fuction because we want also to refresh the space that our menu is taking:

Sub RefreshElement(tmpElem as ABElement, Extra as integer) ' new
  Dim iLeft, iTop as Integer
  iLeft = tmpElem.x - tmpElem.w \ 2 - Extra ' changed
  iTop = tmpElem.y - tmpElem.h \ 2 - Extra ' changed
  ' redraw only me and all other ABElements we are covering
  DrawMe iLeft,iTop,tmpElem.w + Extra * 2,tmpElem.h + Extra * 2 ' changed
End Sub

Two new functions will handle a click on a menu button and if we hover over a menu button. We go through our current menu buttons and see if the mouse is over one of them. In that case we can start or stop playing the CD, DVD or comic. The code to play things is not included here as it would take us to far from our Canvas tutorials. A google search or the RealStudio forum can help you with that.

' to handle a click on one of our menu buttons
Function HandleMenuClick(X as integer, Y as integer) As boolean
  Dim a as integer
  dim tmpMenu as ABMenu
  if CurrentMenuElement <> nil then
    for a = 0 to Ubound(CurrentMenuElement.MenuButtons)
      tmpMenu = CurrentMenuElement.MenuButtons(a)
      if X >= tmpMenu.X and X <= tmpMenu.X + tmpMenu.W and Y >= tmpMenu.Y and Y <= tmpMenu.Y + tmpMenu.H then
        ' a button was clicked, handle what needs to happen (like start or stop playing)
        MsgBox "You pressed button " + Str(tmpMenu.Type) + " on element type " + str(CurrentMenuElement.Type) + " with ID " + str(CurrentMenuElement.ID)
        Return true
      end if
  end if
  Return false
End Function

' to change our mouse cursor if we are hovering above a button
Function HandleMenuMove(X as integer, Y as integer) As boolean
  Dim a as integer
  dim tmpMenu as ABMenu
  if CurrentMenuElement <> nil then
    for a = 0 to Ubound(CurrentMenuElement.MenuButtons)
      tmpMenu = CurrentMenuElement.MenuButtons(a)
      if X >= tmpMenu.X and X <= tmpMenu.X + tmpMenu.W and Y >= tmpMenu.Y and Y <= tmpMenu.Y + tmpMenu.H then
        Return true
      end if
  end if
  Return false
End Function

Now for the main event: the function to show our menu around our active element. Once we’ve clicked a Element, the menu should be drawn around it. We’ll start with an orange frame around the Element and then we’ll draw our menu buttons. As all our elements have their own buttons, we iterate over the MenuButtons() table and draw them from right to left. We do this because we do not know how many buttons will be drawn and we want them right aligned.

Only when the menu is completely drawn we’ll push the changed part of the buffer to the canvas.

Sub ShowMenu(tmpElem as ABelement)
  Dim iLeft, iTop as Integer
  Dim a as integer
  Dim posX as integer
  ' get the real left and right from our center x and y
  iLeft = tmpElem.x - tmpElem.w \ 2
  iTop = tmpElem.y - tmpElem.h \ 2
  ' draw our menu
  gBuffer.ForeColor = &cFF5E0F
  gBuffer.PenWidth = 3
  gBuffer.PenHeight = 3
  ' we do minus 5 because our shadow = 5
  gBuffer.DrawRect iLeft - 2, iTop - 23, tmpElem.w + 3 - 5, tmpElem.h + 24 - 5
  gBuffer.PenWidth = 1
  gBuffer.PenHeight = 1
  gBuffer.FillRect iLeft - 2, iTop - 23, tmpElem.w + 3 - 5, 24
  ' and our buttons
  PosX = iLeft + tmpElem.w - 23
  for a = 0 to Ubound(tmpElem.MenuButtons) 
    gBuffer.DrawPicture tmpElem.MenuButtons(a).Image, PosX, iTop - 21
    tmpElem.MenuButtons(a).X = posX
    tmpElem.MenuButtons(a).Y = iTop - 21
    tmpElem.MenuButtons(a).W = tmpElem.MenuButtons(a).Image.width
    tmpElem.MenuButtons(a).H = tmpElem.MenuButtons(a).Image.height
    posX = posX - 20
  ' and draw the result to the canvas
  self.Graphics.DrawPicture pBuffer, iLeft - 3, iTop - 24, tmpElem.w + 6, tmpElem.h + 27, iLeft - 3, iTop - 24, tmpElem.w + 6, tmpElem.h + 27
End Sub

Let’s also write a function to hide our menu:

Sub HideMenu()
  if CurrentMenuElement <> nil then
    ' let's redraw
    ' we add some extra pixels around the object when we redraw because we want the menu removed
    RefreshElement CurrentMenuElement, 25
    ' and we reset our CurrentMenuElement
    CurrentMenuElement = nil
  end if
End Sub

Almost there! All we now have to do is handle the mouse down and mouse up event so that they can show or hide the menu, and if a menu button is clicked handle the button action.

First our MouseDown(). A very import part is that if we click on our canvas, we want to check if we are clicking a menu button. This has to be done first and before we’ll handle any other actions. If a menu button is handled, we can exit the function with return false to stop any other mouse events. If we click on the desktop, the existing menu is hidden.

Function MouseDown(X As Integer, Y As Integer) As Boolean
  ' the first thing we do is checking if we clicked on a menu
  if HandleMenuClick(X,Y) then
    ' it is handled
    Return false
  end if
  Dim tmpElem as ABElement
  tmpElem = ElementHit(X,Y)
  if tmpElem  nil then
    ' does someone have a menu that is now visible?
    HideMenu ' new
    if IsContextualClick then
      ' right mouse button
      ' bring the found ABelement to the front
      BringToFront tmpElem
      ' group them together
      GroupMyType tmpElem.type, X, Y, 500
      return false
      ' left mouse button
      ' remember our current position
      mLastX = X
      mLastY = Y
      ' bring the found ABelement to the front
      BringToFront tmpElem
      ' refresh the element so it is redrawn
      RefreshElement tmpElem, 0
      ' remember this ABElement so we can drag it around
      DragElem = tmpElem
      Return true
    end if
    ' does someone else have a menu that is now visible?
    HideMenu ' new
  end if
  ' continue with the default MouseDown event
  return MouseDown(X,Y) 
End Function

In our MouseUp() event we’ll show our menu.

Sub MouseUp(X As Integer, Y As Integer)
  if DragElem <> nil then
    ' refresh one last time so we have the very last position
    RefreshElement DragElem,0
    ' set our DragElem to nothing
    DragElem = Nil
  end if
  Dim tmpElem as ABElement
  tmpElem = ElementHit(X,Y)
  if tmpElem <> nil then
    if IsContextualClick = false then
      ' left mouse button
      ' show our menu, if any
      if tmpElem.HasMenu then
        ShowMenu tmpElem
        ' remember this ABelement having the menu
        CurrentMenuElement = tmpElem
      end if
    end if
  end if
  ' continue with the default MouseUp event
  MouseUp X,Y
End Sub

And in the MouseMove() event we’ll handle the mouse cursor if we are hovering above a menu button:

Sub MouseMove(X As Integer, Y As Integer)
  ' first check if we are over a menu
  if HandleMenuMove(X,Y) then
    self.MouseCursor = System.Cursors.FingerPointer
  end if
  ' if we are above one of our Elements, we change the mouse cursor to a little hand
  if ElementHit(X,Y) <> nil then
    self.MouseCursor = System.Cursors.FingerPointer
    self.MouseCursor = System.Cursors.StandardPointer
  end if
  ' continue with the default MouseMove event
  MouseMove X,Y
End Sub

Finally we’ll change the LoadType() function in the Main form to add our menu buttons to our elements. The CD and DVD we’ll give a play and stop button, the Comic only a play button. Here is the changed code:

        tmpElem.Init(Counter,tmpType, rnd * (me.Width - tmpPic.width) + tmpPic.Width / 2, rnd * (me.Height - tmpPic.Height) + tmpPic.Height / 2,InnerX,InnerY, tmpPic, tmpPicMask, tmpPicGlow, true, true) ' new we use true to add a menu
        ' and add the menu items
        ' load our play button for our menu
        select case tmpType
        case 1 ' a comic has only a play button
          Menuf = GetFolderItem("PlayButton.png")
          tmpElem.AddMenuButton(1, menuf.OpenAsPicture)
        case 2,3 ' a CD and DVD has a play and stop button
          Menuf = GetFolderItem("PlayButton.png")
          tmpElem.AddMenuButton(1, menuf.OpenAsPicture)
          Menuf = GetFolderItem("StopButton.png")
          tmpElem.AddMenuButton(2, menuf.OpenAsPicture)
        end select

Phew, Done! Let’s run our application with the added menu. It should work like this:

As always you can download the source code for this tutorial:

Until next time!

Click here to Donation if you like my work

Realbasic Canvas Tutorials RB 2011+ users

As some of you correctly pointed out the LoadType() function does not work in RB version 2011+. It seems from 2011 on, the items list starts from 1 instead of 0. So change the line

for a = 0 to all.Count - 1


for a = 1 to all.Count

Thanks to everyone for bringing this to my attention.

%d bloggers like this: