B4J Question [Solved] Rotating an image through 20 degrees

Peter Meares

Member
Licensed User
Longtime User
I wanted to rotate a drone image to set it in line with the heading, and then plot them on Google Earth using a KML file.
The drone data is in the exif file which one can extract with Phil Harvey's excellent Exiftool. Link. It has way more information that meta-extractor code.
The KML code was reasonably simple, leaving images and tracks on Google earth.
But what has proved very hard to sort out is rotating the image.

Obviously when the rectangular image is rotated the edges exceed the view and get cut off. So one needs to work out the maximum size of the new shape with trig, and then shrink the image to fit the fit the view, and save it as a neat rectangle. This all worked.
At the same time one has to move the centre of the image. This all proved to be quite hard to get it to be consistent so I moved to plan B.

Plan B was to try BitMapCreator because there appears to be a rotate and transform function, and I had not played with it.
There is an example here Link but what is the "source" in the CreateDrawTask. I have tried a few things but get lots of error messages.

B4X:
Dim dt As DrawTask = bc.CreateDrawTask(source, SrcRect, 50, 50, True) '(50, 50) defines the target center
dt.SrcScaleX = 2.5
dt.SrcScaleY = 3
dt.Degrees = 180
bc.DrawBitmapCreatorTransformed(dt)

Thanks for any guidance. And of course if I am in the wrong direction then do say.
 

TILogistic

Expert
Licensed User
Longtime User
tips:


ref.
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
This code, taken from X2 framework, use XUI2D to calculate this:
B4X:
Private Sub FindRotatedRect(Input As BitmapCreator, Degrees As Int) As B4XRect
    Transform.Angle = X2.DegreesToB2Angle(Degrees)
    RectShape.SetAsBox(Input.mWidth / 2, Input.mHeight / 2)
    RectShape.ComputeAABB(OutputAABB, Transform)
    Dim r As B4XRect
    r.Initialize(0, 0, Ceil(OutputAABB.TopRight.X - OutputAABB.BottomLeft.X), Ceil(OutputAABB.TopRight.Y - OutputAABB.BottomLeft.Y))
    Return r
End Sub
 
Upvote 0

Peter Meares

Member
Licensed User
Longtime User
Thanks I will have a look at this.

Just so I understand, what is "source" in the code above (repeated below)?
B4X:
Dim dt As DrawTask = bc.CreateDrawTask(source, SrcRect, 50, 50, True) '(50, 50) defines the target center
 
Upvote 0

Peter Meares

Member
Licensed User
Longtime User
Thanks to Erel for his amazing X2 code. I have fished out the bits needed to do exactly what I wanted.
Read an image (would be from a drone), then rotate and save the image to suit the heading of the drone.

B4X:
#Region Project Attributes
    #MainFormWidth: 800
    #MainFormHeight: 650
#End Region

' Requires jBitmapCreator, jXUI, XUI2D, and the ones provided by the project template.
' Requires Layout file with a simple ImageView as a B4XView.
' As it was created form a project there is a button as I left it there.
'
' This routine loads an image into a bitmap set to the size of the Imgaeview.
' This means that is will generally be shrunk to in this example 800 x 600
' Then finds the rotated image size and creates an new bitmap to take the new size.
' Then

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
' From XUI
    Private xui As XUI
    Private Button1 As B4XView
    Private ImageView1 As B4XView 'using ImageView this time to make it cross platform.
' From XUI2D   
    Private Transform As B2Transform
    Private RectShape As B2PolygonShape
    Private OutputAABB As B2AABB

End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    ' Loads layout file which only has an image view and
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    
    Dim Angle As Float = 110
    Dim imageFolder As String = "Folder of your choice"

    ' First Bitmap to load the starting image into
    Dim bc1 As BitmapCreator
    bc1.Initialize(ImageView1.Width, ImageView1.Height)
    ' Load bitmap with image
    Dim bmp As B4XBitmap = xui.LoadBitmapResize(imageFolder, "JunkImage.JPG", 8000, 6000, True)
    ' Load bmp into BitmapCreator
    bc1.DrawBitmap(bmp,bc1.TargetRect,True)

    ' Draws a red box round the original image for debug purposes
'    bc1.DrawRect(bc1.TargetRect, xui.Color_red, False, 2)

    ' Loads the bitmapCreator into the imageview
    bc1.SetBitmapToImageView(bc1.Bitmap, ImageView1)

    ' Calculate a Rectangle with the rotated image size
    Dim r1 As B4XRect = FindRotatedRect(bc1, Angle)
    ' Creates a new target bitmap for the rotated image.
    ' Uses the sizes from the Rectangle
    Dim bc2 As BitmapCreator
    bc2.Initialize(r1.Width, r1.Height)

    ' Does not appear to add a Transparent background
    ' Draws a transparent rectangle before loading the image bitmap
    bc2.DrawRect(bc2.TargetRect, xui.Color_Transparent,True, 1)

    ' Creates a task to run through the BitmapCreator
    Dim dt As DrawTask = bc2.CreateDrawTask(bc1, bc1.TargetRect, 0, 0, True) '(50, 50) defines the target center
    ' Sets a few task basics
    dt.Degrees = Angle
    ' must have the centre defined
    dt.TargetX = bc2.TargetRect.Width / 2
    dt.TargetY = bc2.TargetRect.Height / 2
    ' Creates the rotated bitmap
    bc2.DrawBitmapCreatorTransformed(dt)
    ' Draws a red rectangle on the bitmap to show final image size
    bc2.DrawRect(bc2.TargetRect, xui.Color_red, False, 2)
    ' Loads the roatted bitmap into the Imgaeview.
    bc2.SetBitmapToImageView(bc2.Bitmap, ImageView1)
    ' Now save the output
    Dim out As OutputStream = File.OpenOutput(imageFolder, "Output.jpg", False)
    bc2.bitmap.WriteToStream(out, 100, "JPEG")
    out.Close
End Sub


' Creates a rectangle that is the size of the incoming rotated bitmap
Private Sub FindRotatedRect(Input As BitmapCreator, Degrees As Int) As B4XRect
    Private Transform As B2Transform
    Private RectShape As B2PolygonShape
    Private OutputAABB As B2AABB

    Transform.Initialize
    RectShape.Initialize
    OutputAABB.Initialize

    Transform.Angle = Degrees * cPI / 180
    RectShape.SetAsBox(Input.mWidth / 2, Input.mHeight / 2)
    RectShape.ComputeAABB(OutputAABB, Transform)
    Dim r As B4XRect
    r.Initialize(0, 0, Ceil(OutputAABB.TopRight.X - OutputAABB.BottomLeft.X), Ceil(OutputAABB.TopRight.Y - OutputAABB.BottomLeft.Y))
    Return r
End Sub

' Button from the project tmeplate turned into an exit button.
Sub Button1_Click
ExitApplication
End Sub
 
Upvote 0
Top