Tag Archives: Canvas

Basic4Android: A first look at ABPlay, a game engine library

ABPlay Screenshot

ABPlay Screenshot


It has been a while since I’ve written a new article on this blog because it have been busy months both at work and in my personal life. One of the things I wanted to do was writing an easy to use Game Engine for B4A. I have written the ABPhysics engine in the past and recently Informatix pointed out I had started another engine (ABGameEngine) before that. Development on ABGameEngine was stopped early for several reasons: time was one of them, but also because several other developers were working on an engine themselves. It seemed a little bit pointless to continue.

However, the other engines were not further developed either. Until recently Erel came up with the GameView. This is an excellent View that will cover the needs of a lot of beginning programmers. I definitely would like to urge starting game developers to take a look at this. Registered users of B4A can download the GameView lib from here.

But still, I wanted something more. I looked at ABgameEngine again, as it had some great ideas like layers, animated sprites, gamepads etc. But it was also very old code and not written very well. And it crashed all the time :-)

I decided to restart from scratch a couple of weeks ago. Some weekends and evenings later ABPlay was born! And with a lot of goodies!

Here is a small demo video of what I got so far. it demonstrates the following:

1. Layers 
     (Layer 1) the moving background with the Odies as animated sprites
     (Layer 2) the black foreground layer with a weird dancing creature that passes now and then
2. Animated sprites, not bound to a layer 
     (our hero Garfield is back!)
3. A sprite can have different animations. 
     (like one for standing, one for walking, one for fighting, etc)
4. A sprite can have different predefined 'Walks'
     (A walk can be build like you would build a path. It's a sequence of lines, bezier curves, wait periods etc. It's like a simple flash movie)
5. Gamepad controls
     (the 'joystick' pad on the left)
     (the 'action' pad on the right)
     (the 'direction' pad as an alternative to the joystick, not shown in this video)
6. In the demo I cannot demonstrate it with a mouse, but it is completely multi touch
     (You can control Garfield AND press the Action button X AND do a swipe anywhere at the same time)

But let’s have a look (beware this is running on the Emulator. On a real device it is much smoother):

I’m actually very pleased with the result. The graphics and handling is very smooth.
Above all, it’s still very easy to program. Here is the whole code for the demo app:

#Region  Project Attributes 
	#ApplicationLabel: ABPlayTest
	#VersionCode: 1
	#VersionName: 
	'SupportedOrientations possible values: unspecified, landscape or portrait.
	#SupportedOrientations: landscape
	#CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes 
	#FullScreen: true
	#IncludeTitle: false
#End Region

Sub Process_Globals

End Sub

Sub Globals
	Dim myPlay As ABPlay
	' the panel that will hold the ABPlay
	Dim myPanel As Panel
	
	' a background and foregroud layer
	Dim bgLayer As ABLayer
	Dim fgLayer As ABLayer
	
	' some colors to show the multi touch points in the demo
	Dim myColors() As Int = Array As Int(Colors.Red, Colors.Green, Colors.Blue, Colors.Cyan, Colors.Yellow, Colors.Gray, Colors.White, Colors.Magenta, Colors.LightGray, Colors.DarkGray)
	
	' our hero
	Dim Hero As ABSprite
	' our enemies
	Dim Enemies As List
	
	' param to set the set the speed
	Dim Speed As Float = 0.2
	
	' some variables to hold the current state of the hero
	Dim currentAction As String
	Dim currentDirection As String
	Dim currentIsStanding As Boolean
End Sub

Sub Activity_Create(FirstTime As Boolean)
	' initialize ABPlay with myPanel
	myPanel.Initialize("")
	Activity.AddView(myPanel, 0,0,100%x,100%y)
	myPlay.Initialize(myPanel, "myPlay")
	
	'////////////// BEGIN loading sprite sequences
	' Load Sprite Sequences
	myPlay.LoadSpriteSequence("GarStandingLeft",LoadBitmap(File.DirAssets, "garleftstill.png"),6, 1, 1000)
	myPlay.LoadSpriteSequence("GarStandingLeft",LoadBitmap(File.DirAssets, "garleftstill.png"),6, 1, 1000)
	myPlay.LoadSpriteSequence("GarStandingRight",LoadBitmap(File.DirAssets, "garrightstill.png"),6, 1, 1000)
	myPlay.LoadSpriteSequence("GarWalkingLeft",LoadBitmap(File.DirAssets, "garleft.png"),8, 1, 1000)
	myPlay.LoadSpriteSequence("GarWalkingRight",LoadBitmap(File.DirAssets, "garright.png"),8, 1, 1000)
	myPlay.LoadSpriteSequence("GarStandingLeftFight",LoadBitmap(File.DirAssets, "garleftstillfight.png"),2, 1, 250)
	myPlay.LoadSpriteSequence("GarStandingRightFight",LoadBitmap(File.DirAssets, "garrightstillfight.png"),2, 1, 250)
	myPlay.LoadSpriteSequence("GarWalkingLeftFight",LoadBitmap(File.DirAssets, "garleftfight.png"),7, 1, 1000)
	myPlay.LoadSpriteSequence("GarWalkingRightFight",LoadBitmap(File.DirAssets, "garrightfight.png"),7, 1, 1000)
	' and the ones for Odie
	myPlay.LoadSpriteSequence("OdieStandingLeft", LoadBitmap(File.DirAssets, "odieleftstill.png"),7,1,Rnd(800,1200))
	myPlay.LoadSpriteSequence("OdieStandingRight", LoadBitmap(File.DirAssets, "odierightstill.png"),7,1,Rnd(800,1200))
	myPlay.LoadSpriteSequence("OdieWalkingLeft", LoadBitmap(File.DirAssets, "odieleft.png"),5,1,Rnd(800,1200))
	myPlay.LoadSpriteSequence("OdieWalkingRight", LoadBitmap(File.DirAssets, "odieright.png"),5,1,Rnd(800,1200))
	' and the one for the creature
	myPlay.LoadSpriteSequence("CreaturePassToRight", LoadBitmap(File.DirAssets, "creature.png"), 3, 4, 1200)
	'////////////// END loading sprite sequences
	
	'////////////// BEGIN building the Hero Garfield
	' initialize the hero
	Hero.Initialize("Hero", 50%x, 50%y)
	' add sprite sequences
	Hero.AddSpriteSequence("GarStandingLeft")
	Hero.AddSpriteSequence("GarStandingRight")
	Hero.AddSpriteSequence("GarWalkingLeft")
	Hero.AddSpriteSequence("GarWalkingRight")
	Hero.AddSpriteSequence("GarStandingLeftFight")
	Hero.AddSpriteSequence("GarStandingRightFight")
	Hero.AddSpriteSequence("GarWalkingLeftFight")
	Hero.AddSpriteSequence("GarWalkingRightFight")
	Hero.StartSpriteSequence("GarStandingLeft", True)
	currentDirection="LEFT"
	'//////////////  END building the Hero Garfield
	
	'////////////// BEGIN building the background layer with Odies
	' initalize a background layer
	bgLayer.Initialize("background", 0,0)
	bgLayer.SetBackground(LoadBitmap(File.DirAssets, "bga.jpg"), 1.0)
	myPlay.AddLayer(bgLayer)
	
	' initialize some enemies
	Enemies.Initialize
	Dim a As Int
	For a = 1 To 10
		Dim Odie As ABSprite
		
		' initialize an Odie with some animation sequences
		Odie.Initialize("Odie" & a, Rnd(10%x,90%x), Rnd(10%y, 90%y))
		' add multiple sprite sequences
		Odie.AddSpriteSequences(Array As String("OdieStandingLeft","OdieStandingRight","OdieWalkingLeft","OdieWalkingRight"))
		
		' create a random walk to the right
		Dim WalkRight As ABSpriteWalk
		WalkRight.Initialize("ToTheRight", True)
		Dim newX As Int = Odie.x+Rnd(20%x, 80%x)
		WalkRight.AddLine("OdieWalkingRight",Odie.x, Odie.y, newX, Odie.y, Rnd(90,100))
		WalkRight.AddWait("OdieStandingRight",newX, Odie.y, Rnd(10,50))
		WalkRight.AddLine("OdieWalkingLeft",newX, Odie.y, Odie.x, Odie.y, Rnd(90,100))
		WalkRight.AddWait("OdieStandingLeft",Odie.x, Odie.y, Rnd(10,50))
		Odie.AddWalk(WalkRight)
		
		' create a random walk to the left
		Dim WalkLeft As ABSpriteWalk
		WalkLeft.Initialize("ToTheLeft", True)
		Dim newX As Int = Odie.x-Rnd(20%x, 80%x)
		WalkLeft.AddLine("OdieWalkingLeft",Odie.x, Odie.y, newX, Odie.y , Rnd(90,100))
		WalkLeft.AddWait("OdieStandingLeft",newX, Odie.y, Rnd(10,50))
		WalkLeft.AddLine("OdieWalkingRight",newX, Odie.y, Odie.x, Odie.y, Rnd(90,100))
		WalkLeft.AddWait("OdieStandingRight",Odie.x, Odie.y, Rnd(10,50))
		Odie.AddWalk(WalkLeft)
		
		' pick random a walk, left or right
		Dim GoLeft As Int = Rnd(0,2)
		If GoLeft = 0 Then			
			Odie.StartWalk("ToTheRight")
		Else
			Odie.StartWalk("ToTheLeft")
		End If
		bgLayer.AddSprite(Odie)		
	Next
	'////////////// END building the background layer with Odies
	
	'////////////// BEGIN building the foreground layer with creature
	' initalize a foreground layer
	Dim fgLayer As ABLayer
	fgLayer.Initialize("foreground", 0,0)
	fgLayer.SetBackground(LoadBitmap(File.DirAssets, "fga.png"), 100%y/400)
	myPlay.AddLayer(fgLayer)
	
	' and a weird creature...
	Dim creature As ABSprite
	creature.Initialize("creature", -100%x, 100%y-240)
	creature.AddSpriteSequence("CreaturePassToRight")
	' with a walk
	Dim pass As ABSpriteWalk
	pass.Initialize("DanseToTheRight", True)
	pass.AddLine("DansingRight",creature.x, creature.y, 200%x, creature.y, 300)
	creature.AddWalk(pass)
	' and start the walk
	creature.StartWalk("DanseToTheRight")
	' add the creature to the foreground layer
	fgLayer.AddSprite(creature)
	'////////////// END building the foreground layer with creature
	
	'////////////// BEGIN Add game pads
	' initialize and start a Joystick Gamepad
	myPlay.InitializeJoystickPad(18, 100%y-210, 192, 192 , LoadBitmap(File.DirAssets, "joystick_bg.png"),LoadBitmap(File.DirAssets, "joystick.png"))
	myPlay.ShowJoystickPad(True)	
	
	' initialize and start a Action Gamepad
	myPlay.InitializeActionPad(100%x-210, 100%y-210, 192, 192 , LoadBitmap(File.DirAssets, "action_active.png"),LoadBitmap(File.DirAssets, "action_inactive.png"),LoadBitmap(File.DirAssets, "action_mask.png"),True, False, True, False)
	myPlay.ShowActionPad(True)		
	'////////////// END Add game pads
End Sub

Sub Activity_Resume
	
End Sub

Sub Activity_Pause (UserClosed As Boolean)
	' NEEDED FOR THE MOMENT TO CATCH THE HOME KEY, RESUME/PAUSE NOT YET SUPPORTED!
	myPlay.StopAndRecycle	
	Activity.Finish
End Sub

Sub Activity_KeyPress (KeyCode As Int) As Boolean 'Return True to consume the event
	Select Case KeyCode
		Case KeyCodes.KEYCODE_BACK
			' NEEDED: stop the drawing thread and recycle stuff
			myPlay.StopAndRecycle	
			Activity.Finish
			Return True
		Case KeyCodes.KEYCODE_HOME
			Return True
	End Select
End Sub

Sub myPlay_Draw(c As Canvas, State As ABState)
	' the actual drawing, do NOT set a debug stop in here!
	
	'////////////// BEGIN Calculation stuff
	' calculate hero stuff depending on what buttons we pressed on the gamepad
	currentAction = ""
	If State.UsingActionPad Then
		' go into fight state
		If State.ACTION_X Then
			currentAction = "X"
		End If
		' go back to center
		If State.ACTION_Y Then
			Hero.SetPostition(50%x,50%y)
		End If
	End If
	
	Dim XMovement As Int 
	Dim YMovement As Int
	If State.UsingJoystickPad Then
		XMovement = State.JOYSTICK_X*Speed
		YMovement = State.JOYSTICK_Y*Speed
		' update the hero's position and direction
		If XMovement<0 Then
			currentDirection = "LEFT"
		Else
			currentDirection = "RIGHT"
		End If
		Hero.SetPostition(Hero.x+XMovement, Hero.y+YMovement)		
		currentIsStanding = False
	Else
		currentIsStanding = True
	End If
	
	' set the animation type
	Select Case currentDirection
		Case "LEFT"
			Select Case currentAction
				Case "X"
					If currentIsStanding Then
						Hero.StartSpriteSequence("GarStandingLeftFight", False)
					Else
						Hero.StartSpriteSequence("GarWalkingLeftFight", False)
					End If				
				Case Else
					If currentIsStanding Then
						Hero.StartSpriteSequence("GarStandingLeft", False)
					Else
						Hero.StartSpriteSequence("GarWalkingLeft", False)
					End If
			End Select
		Case "RIGHT"
			Select Case currentAction
				Case "X"
					If currentIsStanding Then
						Hero.StartSpriteSequence("GarStandingRightFight", False)
					Else
						Hero.StartSpriteSequence("GarWalkingRightFight", False)
					End If				
				Case Else
					If currentIsStanding Then
						Hero.StartSpriteSequence("GarStandingRight", False)
					Else
						Hero.StartSpriteSequence("GarWalkingRight", False)
					End If				
			End Select
	End Select	
	
	' update the hero animation
	Hero.Update
	
	' move our backgrounds, does not make sense but shows the possibilities
	Dim newX, newY As Int
	Dim newMovementX, newMovementY As Int
	newMovementX=Min(Abs(XMovement),1)
	newMovementY=Min(Abs(YMovement),1)
	If currentIsStanding = False Then
		If currentDirection = "LEFT" Then		
			newX = bgLayer.ViewX
			newY = bgLayer.ViewY
			If newX-newMovementX >= 0 Then
				newX = newX-newMovementX
			End If
			If newY-newMovementY >= 0 Then
				newY = newY-newMovementY
			End If
			bgLayer.SetLayerPostion(newX, newY)			
		Else
			newX = bgLayer.ViewX
			newY = bgLayer.ViewY
			If newX+newMovementX <= bgLayer.OuterWidth - myPlay.Width Then
				newX = newX+newMovementX
			End If
			If newY+newMovementY <= bgLayer.OuterHeight - myPlay.Height Then
				newY = newY+newMovementY
			End If
			bgLayer.SetLayerPostion(newX, newY)			
		End If
	End If
	
	'////////////// END Calculation stuff
	
	'////////////// BEGIN Drawing stuff
	'ok, all the calulations are done, let's draw!
	
	' draw the background layer with all its sprites on it and advance all sprite animations and walks
	myPlay.DrawLayer("background", c)
	
	Dim a As Int
	' draw the multitouch points that are not on the gamepad, no action here but just to show the possibilities
	For a = 0 To State.touchPoints.Size - 1
		Dim tmpP As ABTouchPoint		
		tmpP = State.touchPoints.GetValueAt(a)		
		c.DrawCircle(tmpP.X, tmpP.Y, 50dip, myColors(tmpP.id), True, 1dip)		
	Next
	
	' draw the hero
	Hero.Draw(c)
	
	' draw the foreground layer with all its sprites on it and advance all sprite animations and walks
	myPlay.DrawLayer("foreground", c)
	'////////////// END Drawing stuff
	
	
	' NEEDED: very last line of the Draw event. Let ABPlay know it may process touches again!
	myPlay.DrawDone()
End Sub

I’m not there yet, but it’s a good start. It needs a lot more testing and a lot of new features.

Until next time!
Alwaysbusy

Click here to Donation if you like my work


Let’s shrink the world: A Tilt Shift Effect

Everybody has seen the cute pictures floating around showing a mini world! Some examples:

But how is it done? And can it be done fast?

Answers: Easy and Yes!

How does it work?
For a digital Tilt-Shift we use some kind of gradient blur. What I mean is a blur that is very light where the focus lens is and gets blurrier the further something is in the picture. Some ‘faster’ systems only use one blur and then put the focused part back into the picture but this creates an ugly edge between the sharp and blurred part of the picture. And we do not want this!

To get his effect we could do several blurs where we enlarge the focus box in the loop so we get some gradience. Eg. If we do it 8 times we get a gradient of 8 layers. Not bad but…we want more!

Doing a blur in languages like Realstudio and Basic4Android (or Java) is not that fast. This is the first issue we’ll have to tackle because we want to be close to doing it real-time.

Second, a lot of focusses can only be a rectangle or a circle. This has a side effect: some objects in the picture will be in focus while we do not want that because it spoils the illusion. So our focus needs to be able to be irregular. In this tutorial the example uses also a rectangle but I’ll show you how to change this to anything you want.

In some cases changing the saturation of the picture can even more give the illusion of a miniature.

So in short we want:
1. few blurs, but still have 255 gradient levels
2. the blur must be fast
3. an irregular focus
4. some kind of saturation
5. did I mention fast?

How will Basic4Android (or java) and Realbasic be able to do this?

Well, we’re gonna cheat! :D

Instead of creating a gradient blur, we are going to write one heavy blur and then resharpen it. In fact you’ll see we can do the 255 levels of sharpness with only two blurs!

In Realbasic we can take advantage of the great mask property in a picture we’ve used a lot in the past. In Basic4Android I’m going to simulate this so the code can be very similar.

For our example let’s say we want to make this:

Let me show you in a schema what the steps are we’re going to use in our Tilt-Shift (click the picture to see full size):

1. our orignal picture. Make sure it is a sharp one
2. a blurred version of (1)
3. our focus area.
4. a blurred version of (3)
5. we use our blurred (4) as a mask on our original (1). As you can see in the schema, (5) fades out to its background (the alpha channel increases), in this case white.
6. so if we draw (5) on our blurred (2) the blurred pixels will re-sharpen! Do you see the magic happening here :-)
7. the saturation is optional but may make the effect more real.

And the clever part is step 3: you can draw any shape you want here! Some examples for Realbasic:

And remember, in Basic4Android (java) it needs to be inverted:

And now for the code.
(For B4A you’ll need my ABExtDrawing library version 1.7 here)

First we’ll need a fast blur. Again we’ll cheat as we’re not doing a real gaussian blur. We are ging to exploit our mask feature again and do a diagonal blur.

We’ll need some help functions:
Resize()

Realbasic:

Function Resize(pic as picture, Percentage as integer) As Picture
  #pragma BackgroundTasks false
  #pragma BoundsChecking false
  #pragma NilObjectChecking false
  #pragma StackOverflowChecking false
  
  dim p as Picture
  dim w,h as Integer
  dim by as Double = Percentage / 100
  
  w = pic.Width * by
  h = pic.Height * by
  
  p = NewPicture(w,h,32)
  p.Graphics.UseOldRenderer=true
  p.Graphics.DrawPicture pic,0,0,w,h, 0,0,pic.Width, pic.Height
  
  Return p
End Function

Java (B4A library):

public Bitmap Resize(Bitmap bmp, float Percentage)
{
	float by = (float) (Percentage/100.0);
	
	int w = (int) (bmp.getWidth()*by);
	int h = (int) (bmp.getHeight()*by);
	
	if (Percentage>100) {
		return Bitmap.createScaledBitmap(bmp, w, h, true);
	} else {
		return Bitmap.createScaledBitmap(bmp, w, h, false);
	}
	
}

Innerblur()
We create a mask on our picture and draw it a number of times on each other, just shifting bits to the left, top, right and bottom.

Realbasic:

Function InnerBlur(Pic as picture, Level as double) As Picture
  #pragma BackgroundTasks false
  #pragma BoundsChecking false
  #pragma NilObjectChecking false
  #pragma StackOverflowChecking false
  
  dim p,t as Picture
  dim L,w,h as Integer
  
  w=pic.Width
  h=pic.Height
  p=NewPicture(w,h,32)
  p.Graphics.UseOldRenderer=true
  p.Graphics.DrawPicture pic,0,0
  
  t=NewPicture(w,h,32)
  t.Graphics.UseOldRenderer=true
  t.Mask.Graphics.UseOldRenderer=true
  t.Mask.Graphics.ForeColor=&c7F7F7F
  t.Mask.Graphics.FillRect 0,0,t.Width,t.Height
  
  for L=abs(Level) DownTo 1
    t.Graphics.DrawPicture p,0,0
    p.Graphics.DrawPicture t,-L,-L   'upper left
    
    t.Graphics.DrawPicture p,0,0
    p.Graphics.DrawPicture t,-L,L    'lower left
    
    t.Graphics.DrawPicture p,0,0
    p.Graphics.DrawPicture t,L,L     'lower right
    
    t.Graphics.DrawPicture p,0,0
    p.Graphics.DrawPicture t,L,-L    'upper right
  next
  
  Return p
End Function

Java (B4A library):

private Bitmap InnerBlur(Bitmap bmp, float Level) {
	int w = bmp.getWidth();
	int h = bmp.getHeight();
	
	Paint paint = new Paint();
	paint.setAlpha(0x7F);
	
	Bitmap t = Bitmap.createBitmap(w, h, Config.ARGB_8888);
	Canvas tc = new Canvas(t);
	
	Bitmap p = Bitmap.createBitmap(w, h, Config.ARGB_8888);
	Canvas pc = new Canvas(p);
	pc.drawBitmap(bmp, 0, 0, null);
	
	int Lev = (int) Level;
	
	for (int L=Lev;L>0;L--) {
		tc.drawBitmap(p, 0, 0, paint);
		pc.drawBitmap(t, -L, -L, null);
		
		tc.drawBitmap(p, 0, 0, paint);
		pc.drawBitmap(t, -L, L, null);
		
		tc.drawBitmap(p, 0, 0, paint);
		pc.drawBitmap(t, L, L, null);
		
		tc.drawBitmap(p, 0, 0, paint);
		pc.drawBitmap(t, L, -L, null);
	}
	
	t.recycle();
	return p;
}

Blur()
The actual blur function is a mix of Resizes and Innerblurs.
1. Resize to smaller
2. Innerblur to level
3. Resize back to original size
4. Innerblur with level 1 to make it smooth

Realbasic:

Function Blur(Pic as picture, Level as double, Speed as Integer) As Picture
  #pragma BackgroundTasks false
  #pragma BoundsChecking false
  #pragma NilObjectChecking false
  #pragma StackOverflowChecking false
  
  dim p as Picture
  dim L,w,h as Integer
  
  w=pic.Width
  h=pic.Height
  p=NewPicture(w,h,32)
  p.Graphics.UseOldRenderer=true
  p.Graphics.DrawPicture pic,0,0
  
  L=Level-round(Level*.4)
  
  select case speed
  case 0
    // shrink the image, blur it
    p=Resize(p,50)
    p=InnerBlur(p, L )
    // back to normal, smooth it
    p=Resize(p, 200)
    p=InnerBlur(p, 1)
  case 1
    // shrink the image, blur it
    p=Resize(p,25)
    p=InnerBlur(p, L )
    // back to normal, smooth it
    p=Resize(p, 400)
    p=InnerBlur(p, 1)
    
  end select
  
  Return p
End Function

Java (B4A library):

/**
 * blur and image. 
 * Speed: 0 = normal, 1 = fast
 */
public Bitmap Blur(Bitmap bmp, float Level, int Speed)
{
	int L = (int) (Level - Math.round(Level*.4));
	Bitmap p = null;	 
	
	switch (Speed) {
	case 0:
		p = Resize(bmp, 50);
		p = InnerBlur(p, L);
		p = Resize(p,200);
		p = InnerBlur(p, 1);
		break;
	case 1:
		p = Resize(bmp, 25);
		p = InnerBlur(p, L);
		p = Resize(p,400);
		p = InnerBlur(p, 1);
		break;		
	}		
	return p;
}

This is, as far as I know, the fastest way to do a blur in Realbasic and Java. If someone knows of a faster one, please contact me, I’ll be very interested. :-)

Before we continue, I’m going to make a function in B4A to merge a Mask layer with a picture so we can do the tilt Shift in a way similar to Realbasic.
As mentioned in a previous article, you have to make sure your images are in a certain format on Android otherwise you can strange colors or resizes.
Here is a function that makes sure the picture is in ARGB_8888 format:

Basic4Android:

' exDraw was dimmed before as Dim ExDraw As ABExtDrawing '(version 1.70 or higher required)
Sub Convert_RGB565_To_ARGB8888(iFolder As String, iFile As String) As Bitmap	
	Dim iBmp As Bitmap
	iBmp.Initialize(iFolder, iFile)

	' only convert it if it is a RGB565
	If ExDraw.GetConfig(iBmp) = ExDraw.RGB_565 Then
		Dim w, h As Int
		
		w = iBmp.Width
		h = iBmp.Height
	
		Dim tmpBmp As Bitmap
		tmpBmp.InitializeMutable(w,h)
		Dim cnvRect As Rect
		cnvRect.Initialize(0,0,w, h)
		Dim cnvCanv As Canvas
		cnvCanv.Initialize2(tmpBmp)
		
		Dim aRect As Rect
		aRect.Initialize(0,0,w,h)
		
		cnvCanv.DrawBitmap(iBmp,Null, aRect)		
		Return tmpBmp
	Else
		Return iBmp
	End If
End Sub

And here is our merge function:

Basic4Android:

Sub MergeWithAlphaLayer(iBmp As Bitmap, iLayerBmp As Bitmap) As Bitmap
	Dim MergedBmp As Bitmap
	Dim MergedBmpCanv As Canvas
	
	Dim w, h As Int
	
	w = iBmp.Width
	h = iBmp.Height
	
	Dim aRect As Rect
	aRect.Initialize(0,0,w,h)
	
	MergedBmp.InitializeMutable(w,h)
	MergedBmpCanv.Initialize2(MergedBmp)
	
	Dim Alpha As Bitmap
	Alpha.InitializeMutable(w,h)
	
	' pic
	MergedBmpCanv.DrawBitmap(iBmp, Null, aRect)
	
	' mask
	Dim Pixels(w*h) As Int
	ExDraw.getPixels(iLayerBmp,Pixels , 0, w, 0, 0, w, h)
	Dim count As Int
	count = (w*h) - 1
	For i = 0 To count
    	    Pixels(i) = Bit.ShiftLeft(Pixels(i),8) 'move the red pixel value to the alpha channel
	Next
	ExDraw.setPixels(Alpha, Pixels, 0, w, 0, 0, w, h)
	
	Dim AlphaP As ABPaint
	AlphaP.Initialize
	AlphaP.SetAntiAlias(True)
	AlphaP.SetPorterDuffXfermode(ExDraw.PorterDuffMode_DST_IN) 'DST_IN only takes over the alpha values
	
	ExDraw.drawBitmap2(MergedBmpCanv, Alpha, 0, 0, AlphaP)
	
	ExDraw.Recycle(Alpha)
	
	Return MergedBmp
End Sub

Ok, back on track! The saturation function. I admit the one in Java has some room for improvement.

Realbasic:

Sub Saturation(Pic as picture, Ammount as integer)
  #pragma BackgroundTasks false
  #pragma BoundsChecking false
  #pragma NilObjectChecking false
  #pragma StackOverflowChecking false
  
  dim c as color
  dim d as Double
  dim a,x,y,w,h,m() as Integer
  dim s as RGBSurface
  dim p as Picture
  dim ir,ig,ib as Integer
  
  s=pic.RGBSurface
  d=Ammount/100
  w=pic.Width-1
  h=pic.Height-1
  
  Redim m(510)
  for x=0 to 510
    m(x)=(x-255)*d
  Next
  
  for y=0 to h
    for x=0 to w
      c=s.Pixel(x,y)
      ir=c.red
      ig=c.green
      ib=c.blue
      a=( ir + ig + ib ) \ 3
      s.Pixel(x,y)=rgb( ir+m(ir-a+255) , ig+m(ig-a+255) , ib+m(ib-a+255) )
    next
  next
End Sub

Java (B4A library):

public Bitmap Saturate(Bitmap bmp,float Ammount) {
	//Initialize the ColorMatrix object  
	ColorMatrix colorMatrix = new ColorMatrix();  
	//Initialize the ColorMatrixColorFilter object  
	ColorMatrixColorFilter cmFilter = new ColorMatrixColorFilter(colorMatrix);  

	//Initialize the cmPaint  
	Paint cmPaint = new Paint();  
	//Set 'cmFilter' as the color filter of this paint  
	cmPaint.setColorFilter(cmFilter);  
	
	colorMatrix.setSaturation(Ammount/(float)100);  
	//Create a new ColorMatrixColorFilter with the recently altered colorMatrix  
	cmFilter = new ColorMatrixColorFilter(colorMatrix);  

	//Assign the ColorMatrix to the paint object again  
	cmPaint.setColorFilter(cmFilter);  

	//Draw the Bitmap into the mutable Bitmap using the canvas. Don't forget to pass the Paint as the last parameter
	Bitmap alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
	Canvas canvas = new Canvas(alteredBitmap);
	canvas.drawBitmap(bmp, 0, 0, cmPaint);
	
	return alteredBitmap;
}

The actual Tilt Shift. This is nothing more that going over the steps I’ve shown in the schema.

Realbasic:

Sub DoTiltShiftRect(bmp as Picture, BlurRadius as integer, focusX as integer, focusY as integer, focusW as integer, focusH as integer, DoSaturtion as boolean)
  mBuffer = new Picture(640,480, 32)
  tmpPic = NewPicture(640,480, 32)
  tmpMask = NewPicture(640,480, 32)
  
  mBuffer = Blur(bmp,blurRadius,0)
  
  tmpPic.Graphics.DrawPicture bmp,0,0
  
  tmpMask.Graphics.FillRect(focusX,focusY - focusH/2, focusW,focusH)
  tmpMask = Blur(tmpMask, 7,1)
  
  tmpPic.Mask.Graphics.DrawPicture tmpMask,0,0
  
  mBuffer.Graphics.DrawPicture tmpPic,0,0
  
  if DoSaturtion then
    Saturation mBuffer,80
  end if
  
  Canvas1.Refresh(false)
End Sub

Basic4Android:

Sub DoTiltShiftRect(iBmp As Bitmap, BlurRadius As Int, focusR As Rect, DoSaturation As Boolean) As Bitmap
	Dim tgtCanv As Canvas
	
	Dim w, h As Int
		
	w = iBmp.Width
	h = iBmp.Height

	Dim tgtBmp As Bitmap
	tgtBmp.InitializeMutable(w, h)
	tgtCanv.Initialize2(tgtBmp)
	
	' blur the picture
	Dim srcBlurred As Bitmap
	srcBlurred = ExDraw.Blur(iBmp, BlurRadius, 0)	
	
	' create the sharp box
	Dim Alpha As Bitmap
	Alpha.InitializeMutable(w, h)
	Dim AlphaCanv As Canvas
	AlphaCanv.Initialize2(Alpha)
	' draw a full white rect on a black background
	Dim tmpR As Rect
	tmpR.Initialize(0,0,w, h)
	AlphaCanv.DrawRect(tmpR, Colors.black, True, 0dip)
	AlphaCanv.DrawRect(focusR, Colors.white, True, 0dip)
	' blur the white box to emulate a gradient blur, may be fast and ugly
	Dim AlphaBlurred As Bitmap
	AlphaBlurred = ExDraw.Blur(Alpha, 7, 1)
	
	' merge the blurred alpha layer with the bitmap into the overlay
	Dim Overlay As Bitmap
	Overlay = MergeWithAlphaLayer(iBmp, AlphaBlurred)		
	
	' saturate the picture to make it more life like
	Overlay = ExDraw.Saturate(Overlay, 80)
		
	Dim aRect As Rect
	aRect.Initialize(0,0,w,h)
	
	' draw the blurred image
	tgtCanv.DrawBitmap(srcBlurred, aRect, aRect)
	' draw the overlay
	tgtCanv.DrawBitmap(Overlay, aRect, aRect)	
	
	' saturate
	If DoSaturation Then
		tgtBmp = ExDraw.Saturate(tgtBmp, 90)
	End If
	
	ExDraw.Recycle(srcBlurred)
	ExDraw.Recycle(Alpha)
	ExDraw.Recycle(AlphaBlurred)
	ExDraw.Recycle(Overlay)
	
	Return tgtBmp
End Sub

And that’s it! Remember I’ll only did it for a rectangle here in the tutorial, but you can use anything.

Notes about the programs:

1. you can point anywhere on the y-axe in the picture to re-position the focusbox.
2. in the Realbasic version you can change the picture in the main.open() event: CurrentPicture = beach2
3. for the compiled RB version you can use the command line:

	Syntax: TiltShift.exe /PIC=picturename.jpg;focusleft;focustop;focuswidth;focusheight;dosaturation

		focus variables = the focus box
		DoSaturation: 0 = false, 1 = true

		Example:

		TiltShift.exe /PIC=beach2.jpg;30;300;580;240;1

4. in the Basic4Android version you can change the picture in the Activity_Create() sub: srcBmp = ABBitmapTools.Convert_RGB565_To_ARGB8888(File.DirAssets, "beach2.jpg")
5. the TiltShift.apk can be found in the B4A version in the folder \Objects\.

It would be nice if you give me credit if you use this code in your own program. ;-)

The full source code can be downloaded from:
Realbasic: download here
Basic4Android: download here

And some more eye candy!

Click here to Donation if you like my work


RealBasic: Robot A.I. – Part 4 – Robot Localization


In this final introduction chapter about robotics we are going to do something very exciting: Localization! In all our previous endeavours Robby not only knew exactly the map of the house, it also knew exactly its x and y position on this map.

But now we are going to take away this knowledge. When we put Robby on the map, the robot does not know if it is in room A of B. We are going to have to find a way so Robby can find its own postion with a calculated guess.

Luckily, some great algorithmes have been found to do this very quickly. The one we are going to use is my interpretation of the Monte Carlo Localization (MCL) technique. Sounds very James Bond, doesn’t it :-)

Before we get into how it works, let’s have a look how Robby can find its position in the map of our house:

So how does MCL work? Here is a brief description of the principle:

The Monte Carlo approach to localization is based on a collection of samples (which are also known as particles). Each sample consists of a possible location the robot may currently occupy, along with a value which represents the probability that the robot is currently at that location. Strictly speaking these are not probabilities, so they are often referred to as importance weights, and we will use that terminology here.

How many samples should be used? The more you have, the more quickly the program will converge to a correct solution, but at the cost of more computational time and hence slower robot movement.

When the program begins the robot does not know where it is, so the current samples (really the locations the samples contain) are evenly distributed over the range of possible locations, and the importance weights are all the same. Over time the samples near the actual current position should become more likely, and those further away less likely. If we created a line graph which plotted the samples using location verses importance weight, the graph should spike over the actual location. Think of this curve as representing the robot’s belief state about the robot’s location. In the beginning it is a flat line (since the robot has no idea where it is), but over time it should show humps over likely locations, with (hopefully) a large spike over the actual location.

The general idea is as follows:

a. Initialize the set of samples (the current samples) so that their locations are evenly distributed and their importance weights are equal.
b. Repeat until done with the current set of samples:
—-1. Move the robot a fixed distance and then take a sensor reading.
—-2. Update the location of each of the samples (using the movement model)..
—-3. Assign the importance weights of each sample to the likelihood of that sensor reading given that new location (using the sensor model).

Remember that the actual distance moved may not be the expected distance. So model that by adding (or subtracting) a random error factor. Similarly, if the sensor reading seems to indicate a door, remember that is might be wrong, and take that into consideration. These are called, respectively, the movement and sensor models. One could stop refining the algorithm here – over time the importance weights would tend towards a solution.

However, the samples with low importance weights add little to figuring out the robot’s current location. That fact leads us to the answer of the next question: “Where does the Monte Carlo part come in – doesn’t that name suggest games of chance?” Yes, it does. And this is where the big improvement comes in, making the algorithm converge to a solution more quickly. In the loop above, we now add the following steps:

—-4. Create a new collection of samples by sampling (with replacement) from the current set of samples based on their importance weights.
—-5. Let this be new collection become the current set of samples.

In step four, samples with a high importance weight are more likely to be chosen than those with low probability, and some sample may be repeatedly chosen. So we (usually) end up with a set of samples with a higher cumulative importance weight that those we had before. This is similar to the approach genetic algorithms take – survival of the fittest!

It is now possible that more than one sample may be at the same location, and clearly samples will cluster around likely locations. So where is the robot likely to be? At the location (or area) with the most samples.

This is an extract taken from here

Read it a couple of times if you don’t understand it. You’ll see it is not that difficult and actually quite genius!

As we work with some kind of randomness, the algorithm may fail in some very rare cases. In my interpretation I made one extra step: reset. When it looks like the algorithm will fail, I’ll restart the whole thing from step a.

In our algorithms folder you can find an extra map with the MCL localizer:

My version of this algoithm may not follow MCL to the letter, but I’ve found it worked very well in RealBasic this way.
The first thing we do is spawn all the particles all around our map: Initially our robot could be in any position:

Private Sub SpawnParticles()
  redim Particles(ParticleCount)
  dim i as Integer
  
  for i = 0 to ParticleCount
    Particles(i) = RandomizePosition
  next
  dim Weight as Double = 1 / (ParticleCount+1)
  for i = 0 to ParticleCount
    Particles(i).Weight = weight
    Particles(i).CumulWeight = (i+1)/(ParticleCount+1)
  next
End Sub

The rest of the algorithm can be found in the SetNewDelta function.

Initially, we observe our particles (we look how far they are from a wall in our case) and give each particle a new weight.

  if isInit then
    for i = 0 to ParticleCount
      Particle = Particles(i)
      
      ParticleObserve(Particle)
      Particle.Weight = Correlation()
      if Particle.Weight < minWeight then
        minWeight = Particle.Weight
      end if
      if Particle.Weight > maxWeight then
        maxWeight = Particle.Weight
      end if
      totalWeight = totalWeight + Particle.Weight
      NewParticles.Append Particle
    next
  else ...

For each next loop we pick a particle taking its weight into account with ‘Particle = PickParticle()’ Then we move this particle a certain random delta ‘RandomizeMove() function’. We observe it again and give it a new weight. We do a spawn again if it seems the algorithm will fail.

  ...
  else
    for i = 0 to ParticleCount
      Particle = PickParticle()
      RandomizeDelta = RandomizeMove(DeltaX, DeltaY)
      Particle.x = particle.x + RandomizeDelta.x
      Particle.y = particle.y + RandomizeDelta.y
      ClearCounter = 0
      while not IsClearSpace(Particle.x, Particle.y)
        Particle = PickParticle()
        Particle.x = particle.x + RandomizeDelta.x
        Particle.y = particle.y + RandomizeDelta.y
        ClearCounter = ClearCounter + 1
        if ClearCounter > ParticleCount then
          ' we are probably completely wrong, let's restart our measurements
          app.DoEvents
          SpawnParticles
          Return
        end if
      wend
      
      ParticleObserve(Particle)
      Particle.Weight = Correlation()
      if Particle.Weight < minWeight then
        minWeight = Particle.Weight
      end if
      if Particle.Weight > maxWeight then
        maxWeight = Particle.Weight
      end if
      totalWeight = totalWeight + Particle.Weight
      NewParticles.Append Particle
    next
  end if

An intersting function to look at is the PickPaticle function. It does pick a particle taking its weight into account, but still with a certain amount of randomness.

Private Function PickParticle() As ABPoint
  dim low, up as integer
  up = UBound(Particles)
  dim ran as Double = rand.Number
  dim PickedIndex as integer
  dim Particle as ABPoint
  
  while true
    PickedIndex = floor((low + up)/2)
    Particle = Particles(PickedIndex)
    if Particle.CumulWeight > ran then
      up = PickedIndex
    else
      low = PickedIndex
    end if
    if up = low then
      Return new ABPoint(Particle.x, Particle.y,0)
    end if
    if up - low = 1 then
      Return new ABPoint(Particles(up).x, Particles(up).y,0)
    end if
  wend
End Function

More details can be found into the source code. There is also a compiled version where you can test it out with your own maps. The same syntax can be used as in Part 3.

Let’s see it one more time at work on a different map like our labyrinth! You’ll see it can find its likely position very fast!

You can download the source code and project here.

This concludes the robotics AI (for now). Some of it may have been heavier to take in for some of you but don’t let this discourage you from playing around with it.

Next time we’ll do something much easier. A neat image effect that does go around lately is the Tilt Shift Effect. You may know it from apps like Instagram and produces a miniaturize effect like this:

I’ll tackle Tilt Shift in our next article in both RealBasic and Basic4Android!

See you next time and happy coding!

Click here to Donation if you like my work


RealBasic: Robot A.I. – Part 3 – Pathfinding with obstacles


Let’s learn Robby to react to an obstacle on its way to the target!

Just like your GPS has to recalculate the route to your destination if a road is blocked, Robby must adept and recalculate a new path to his target if a door is closed in front of him. You can close or open a doorway by rightclicking on the grey parts of the map.

When Robby starts a walk, his belief state is that all doors are open. If he encounters a closed door, he changes his belief state and recalculates an alternative route. Once the robot arrives at his destination, the belief state is reset:

Example:

Robby wants to go from point A to point B. The robot calculates its route and starts the walk (Dark blue path). Suddenly it finds a closed door in its path. Robby recalculates an alternative route to B and continues its walk (Light blue path). Pretty nifty hè! :-)

Let’s look at it at work on our familiar floor plan:

As you can see I’ve also added the possibility to set Robby at any start position within the map and you can choose between a number of sensors.
Less sensors is faster but also less accurate and the algorithm may fail!

Note:
I’ve also been asked to extend the pathfinding example for the people that do not have RealBasic so they can use their own maps. To use your own maps with the ABExplorer.exe file you can give a map at the command line:

Systax: ABExplorer.exe /M=mapfile.png where mapfile is a picture file.

The map file specs:
1. must a picture size 500×500 pixels.
2. only contain 3 colors:

black RGB(0,0,0) for a wall
white RGB(255,255,255) for empty space
grey RGB(192,192,192) for a doorway that can be opened and closed

3. doorways that can be closed must be either horizontal or vertical, not diagonal.
4. make sure the map is ‘closed’: there is no way Robby can escape from the map.

The code and program can be downloaded from here

Try it out with you own maps!

In the next part of this series we’re going to learn Robby to find his location within the map. As for now, when we put Robby somewhere on the map, we tell him in which room and at what x,y coordinates he is. But what if he has to find this information for himself?

Until next time!

Click here to Donation if you like my work


RealBasic: Robot A.I. – Part 2 – Pathfinding


In this article we’ll going to learn Robby to use efficient pathfinding. The algortihm we’re going to use is spatial A-star. I used the implementation in C# of Christoph Husse to get me started. His original code can be found here.

Here you can see the algorithm at work on a labyrinth:

An explanation of the algorithm itself can be found here and for beginners here is an excellent tutorial.

In the structure of our project we’ve added a new folder: Algorithms. This will be the place where we will put all the algorithmes we’re going to use.

ABSpatialAStar: The main class that can do a search.
ABOpenCloseMap: The grid that contains our known map (made of ABPathNodes).
ABPathNode: A single node that will be checked if Robby can walk here or if it is a wall.
ABPriorityQueue: A good A* implementation does not use a standard Array for the open nodes. If a standard Array is used, the algorithm will spend a huge amount of time searching for nodes in that list; instead, a priority queue should be used. The one I used is a modified version of the one BenDi wrote. The original one can be found here.

I’ve added some ‘dirty’ tricks to speed up the process. e.g blowing up de map for a faster search. It scales down the map and blows up all the walls so they appear a lot thicker. This is easier for the algorithm to find the shortest path.

By using a reasonable blowup factor this has as a side effect that Robby stays away from the walls a little further :-) . The result of the blowup can be seen here:

The code for the BlowUpMap function:

Function BlowUpMap(inPic as Picture, OddFactor as Integer, ScaleFactor as double) As Picture
  Dim tmpPic As Picture
  Dim w,h As Integer
  Dim x,y As Integer
  
  w = inPic.Width
  h = inPic.Height
  tmpPic = NewPicture(w*ScaleFactor, h*ScaleFactor, 32)
  
  Dim inRGB As RGBSurface
  Dim tmpRGB As RGBSurface
  
  inRGB = inPic.RGBSurface
  tmpRGB = tmpPic.RGBSurface
  
  Dim FactorDev2 As Integer = Floor(OddFactor/2)
  Dim FactorDev2Plus1 As Integer = FactorDev2 + 1
  
  Dim a, b As Integer
  
  For x = FactorDev2Plus1 To w - FactorDev2Plus1
    For y = FactorDev2Plus1 To h - FactorDev2Plus1
      If inRGB.Pixel(x,y) = &c000000 then
        For a = x - FactorDev2 To x + FactorDev2 
          For b = y - FactorDev2 To y + FactorDev2
            tmpRGB.Pixel(a*ScaleFactor,b*ScaleFactor) = inRGB.Pixel(x,y)
          Next
        Next
      End If
    Next
  Next
  
  Return tmpPic
End Function

Implemented on our house plan this is what happens when Robby knows how to find the shortest path to the point we click on (make sure there is a path to the point you click):

The source code for the project can be downloaded from:

http://www.gorgeousapps.com/Rob2.zip

In the next episode we are going to make it a little more difficult for Robby to go somewhere by opening and closing doors so Robby has to adapt to the new situation.
Bye for now!

Click here to Donation if you like my work


RealBasic: Robot A.I. – Part 1


The next series of articles will give a brief initiation in Robotic Artificial Intelligence. We’ll build a simulation of a robot with sensors. At first it will have no intelligence at all. But in the upcomming weeks we’ll learn it some tricks like finding the shortest path within a maze or house, handle closed passage ways and recalculate a new path to its destination. In another article we’ll learn the robot to find its location within a known map when it is dropped at an unknown location. Some exciting stuff!

But we’ll have to start at the beginning: building our robot Robby :-)

Robby will have a couple of depth sensors (in reality these can be e.g. Kinect Depth cameras). They are placed on Robby so the robot can ‘feel’ everywhere around itself. They also have a limited range so Robby can only ‘see’ a limited distance. Robby is not a superhero, he cannot see through walls.
Robby changes to a red color when he is close to a wall, and colors green when he is at a safe distance.

Example of Robby:

You can see the robot, its sensors and a circle showing the range of the sensors.

In our first program, Robby is just like remote driven toy. We can go forward, turn left and turn right (with the cursor keys).

A framework will be build so we can later expand this program and even create other types of robots, sensors, etc…

Here is the layout of the framework:

ABSpace: This will hold our entry point to the world of robotics. It will describe the real world: what is the space Robby is walking in, where are the robots, etc…
Sensors: A simulation of sensors that can be attached to a robot. They can do measurements that the Robot A.I. can use.
Robots: A simulation of different types of robots that.
Global: Some classes we’ll need like Points, Maps, etc…

ABSpace is build like a movie with several frames. The steps we take are:

1. Initalize our space (give it a map, initialize our robot
2. Calculate a step (let all the sensors do measurements, calculate the new postition of the robot, let the Robot A.I. do it’s stuff)
3. Draw it so we’ll see the progress of our robots and what they have measured
4. Check if we’ll given some input to our robot (move, turn, etc)
5. Repeat from step 2

Here is a video to show what Robby Version 1 can do:

You can download the full source code at:

http://www.gorgeousapps.com/Rob1.zip

In the next part we’ll let Robby find the shortest path to a point on our map.

Happy programming!

Click here to Donation if you like my work


RealBasic: Computer Vision -> FAST Corner Detection


As promised, here is the FAST (Features from Accelerated Segment Test) implementation in RealBasic! And as you’ll notice, this is extremely fast!

Wiki:

AST is an acronym standing for Accelerated Segment Test. This test is a relaxed version of the SUSAN corner criterion. Instead of evaluating the circular disc only the pixels in a Bresenham circle of radius r around the candidate point are considered. If n contiguous pixels are all brighter than the nucleus by at least t or all darker than the nucleus by t, then the pixel under the nucleus is considered to be a feature. This test is reported to produce very stable features.[17] The choice of the order in which the pixels are tested is a so called Twenty Questions problem. Building short decision trees for this problem results in the most computationally efficient feature detectors available.

The first corner detection algorithm based on the AST is FAST (Features from Accelerated Segment Test). Although r can in principle take any value, FAST uses only a value of 3 (corresponding to a circle of 16 pixels circumference), and tests show that the best results are achieved with n being 9. This value of n is the lowest one at which edges are not detected. The order in which pixels are tested is determined by the ID3 algorithm from a training set of images. Confusingly, the name of the detector is somewhat similar to the name of the paper describing Trajkovic and Hedley’s detector.

It is the RealBasic translation of the nonmaximal FAST-9 written by Edward Rosten http://www.edwardrosten.com/work/fast.html
With this library you can find Corners very fast within a picture.

and it can do this under e.g. different light intensities:

Again, the RealBasic engine has a simple interface so you can focus on the fun stuff!

    dim FCD as new ABFastCornerDetector
  
  dim tmpXYTraining(-1) as ABXY
  dim i as integer
  dim j as integer
  
  dim pic as Picture
  dim tmpPic as Picture
  
  dim DownSample as integer
  dim build2Dim as Boolean = true
  
  Dim f as FolderItem
  
  dim ft1 as new filetype
  ft1.name = "picture"
  ft1.Extensions = ".png;.bmp;.jpg;.jpeg"
  
  dim dlg as OpenDialog
  dlg = new OpenDialog
  
  dlg.Title = "Pick picture"
  dlg.PromptText = "Pick a picture to compare"
  dlg.Filter=ft1
  if dlg.InitialDirectory = nil then
    dlg.InitialDirectory = GetFolderItem("")
  end if
  
  f = dlg.ShowModal
  if f <> nil then
    if f.Exists then
      pic = f.OpenAsPicture
    else
      Return
    end if
  else
    Return
  end if
  
  ' these are then numbers you can play with
  FCD.threshold_detection = 20
  FCD.threshold_scoring = 0
  DownSample = 2
  ' do the FAST detection
  tmpXYTraining = FCD.detectFASTCornerFeatures(pic,DownSample, build2Dim)
  
  tmpPic = NewPicture(pic.Width, pic.Height, 32)
  tmpPic.Graphics.DrawPicture pic,0,0
  
  tmpPic.Graphics.ForeColor = &cFF0000
  
  ' and draw them
  for i = 0 to UBound(tmpXYTraining)
    tmpPic.Graphics.DrawLine tmpXYTraining(i).x*DownSample - 3, tmpXYTraining(i).y*DownSample, tmpXYTraining(i).x*DownSample + 3, tmpXYTraining(i).y*DownSample
    tmpPic.Graphics.DrawLine tmpXYTraining(i).x*DownSample, tmpXYTraining(i).y*DownSample-3, tmpXYTraining(i).x*DownSample, tmpXYTraining(i).y*DownSample+3
  next
  
  Canvas1.Graphics.FillRect 0,0, 640,480
  Canvas1.Graphics.DrawPicture tmpPic,0,0

You can download the FAST engine from: http://www.gorgeousapps.com/ABFAST.zip

Until the next post!

Click here to Donation if you like my work


Follow

Get every new post delivered to your Inbox.

Join 79 other followers