Click here to Skip to main content
15,884,425 members
Articles / Programming Languages / Visual Basic
Tip/Trick

Plotting a Specific Location on an Image While Zooming In(+) or Out(-)

Rate me:
Please Sign up or sign in to vote.
4.60/5 (3 votes)
23 Sep 2014CPOL5 min read 11K   4   2
Retain a plotted location on an image while zooming in(+)/out(-).

Introduction

This tip will provide you with the code on how to accurately retain the location of a point of interest (area) on an image while zooming in(+) or out(-). Think of Google Maps to get an idea of where the tip is going.

Background

I am writing an application which uses images extensively and one of the requirements I have was to allow "notes" to be placed on the image as an overlay. The notes cannot be saved onto the image because the images may not be altered in any way. I started off by capturing and saving various pieces of data to a MySQL database and then read it back into a DataGridView for use later in the application.

The information I recorded included: Mouse.X, Mouse.Y, Zoom Factor, Horizontal and Vertical Scroll position of the Panel control in which my Picturebox control was placed to allow for panning. I also recorded the width and height of the image at the time the note was placed over the image. I would then draw triangle markers over the areas of interest from the information in the DataGridView for each note.

To maintain accuracy during the process of adding or viewing a note, I had to lock the zoom factor at 100%. This meant that the user was locked into a preset zoom factor and had no means of zooming in or out of the image while having accurate plotting of the notes when the zoom factor was at anything other than 100%.

I started to look around for more information on how to accurately plot a predetermined area of an image while zooming in or out - very much the same you get to see when using Google Maps and place markers on areas of interest on the globe. Sadly, I came up empty handed.

The following code will demonstrate how easy it is to accurately plot a specific area of an image while zooming in or out. Please keep in mind that the code, as it is, is not optimized for smooth plotting of the markers over the image. Suggestions from the community will be greatly appreciated to rectify this little problem.

Using the Code

Unfortunately, the code and the control I use for Zooming and Panning, and adding Notes to images are part of a much larger application and it will take a lot of hours to strip it out of the main application and put some source code up for you to look at.

However, I will provide you with code in order for you to achieve exactly the same result in your applications.

When I first started researching this, I received one reply to a question I posted regarding this topic.

A reply was posted which suggested I look at the Right and Bottom properties of the Picturebox, calculate the change and then apply the result on the marker to relocate them accordingly. Well needless to say, that did not work at all.

Being unfamiliar to the behaviour of a Picturebox and what happens when it becomes bigger or smaller according to either a larger image being loaded or it being zoom'ed. I noted anything and everything that could change when an image is zoomed in/out. I even recorded how the position of a certain location changed across the screen to try and find some trace of over all consistency to changes.

I tested image aspect ration - always remained 1.3333.

I tested the aspect ration of the Picturebox control. That was 2.7 for Width and 2.6 for Height.

I then tested the distance, a specific location on an image, it changes by every time the image was Zoom'ed in / out. This is where things started to become clearer, however there were significant changes when the image was either Zoom'ed greater than the original Zoom factor and then when the Zoom factor was smaller than the original Zoom factor.

In the end, I determined that a specific point on an image can only be accurately recalculated based on the New Zoom Factor (+ or -). This means that you can accurately plot the location of a specific point on an image no matter what the current Zoom factor is, as long as you have the original Zoom factor at which you recorded / plotted the location on the image.

Here is the code to provide you with the calculation for accurately plotting the location of an area on an image based on the current Zoom factor.

Note: I set .Visible=False on the UserControl that I use as the "marker" of the Note on the image everytime the Zoom factor changes. This prevents the markers (Notes) from jumping around while calculations are conducted. There is probably a better way to do it but for now this has to do.

Some clarification to understand the code better:

  • tbImageZoom = TrackBar
  • dgvNotes = DataGridView
  • Original Zoom Factor = dgvNotes.Rows(i).Cells(8).Value
  • pbImage = Picturebox
  • Zoom(ZoomFactor) = Function
VB.NET
'Zoom image
   Private Sub tbImageZoom_Scroll(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles tbImageZoom.Scroll, tbImageZoom.ValueChanged
       'Test to see if an image is loaded, if not then exit sub
       If IsNothing(pbImage.Image) Then
           tbImageZoom.Value = 0
           Return
       End If

       If tbImageZoom.Value > tbImageZoom.Maximum Then Return

       'Zoom image according to user setting
       Dim ZoomFactor As Double

       For Each Pin As Control In pbImage.Controls
           If TypeOf Pin Is UserControl Then
               Pin.Visible = False
           End If
       Next
       pbImage.Refresh()

       ZoomFactor = tbImageZoom.Value / 100
       Zoom(ZoomFactor)
       lblZoomFactor.Text = tbImageZoom.Value & "%"
       tbImageZoom.Update()

       For Each ctrl As Control In pbImage.Controls
           If TypeOf (ctrl) Is UserControl Then
               For i = 0 To dgvNotes.Rows.Count - 1
                   If dgvNotes.Rows(i).Cells(0).Value = ctrl.Tag.ToString Then
                       'check to see if the zoomFactor when it was created is greater
                       'or less than the current zoomFactor. If greater then *, else / to get zoom ratio
                       If Val(dgvNotes.Rows(i).Cells(8).Value) < Val(tbImageZoom.Value) Then
                           'Need to multiply the zoom ratio with the current Location.X and Location.Y
                           Dim zoomRation As Double = Val(tbImageZoom.Value) / Val(dgvNotes.Rows(i).Cells(8).Value)
                           ctrl.Location = New Point(CInt(Val(dgvNotes.Rows(i).Cells(4).Value) * _
                           zoomRation) - 14, CInt(Val(dgvNotes.Rows(i).Cells(5).Value) * zoomRation) - 24)
                       ElseIf Val(dgvNotes.Rows(i).Cells(8).Value) > Val(tbImageZoom.Value) Then
                           'Need to divide the zoom ratio with the current Location.X and Location.Y
                           Dim zoomRation As Double = Val(dgvNotes.Rows(i).Cells(8).Value) / Val(tbImageZoom.Value)
                           ctrl.Location = New Point(CInt(Val(dgvNotes.Rows(i).Cells(4).Value) / _
                           zoomRation) - 14, CInt(Val(dgvNotes.Rows(i).Cells(5).Value) / zoomRation) - 24)
                       Else
                           ctrl.Location = New Point(Val(dgvNotes.Rows(i).Cells(4).Value) - 14, _
                           Val(dgvNotes.Rows(i).Cells(5).Value) - 24)
                       End If
                   End If
               Next i
           End If
           ctrl.Visible = True
       Next ctrl

   End Sub

You will notice that if the Current Zoom Factor is great than the Original Zoom Factor we Multiply (+). When the Current Zoom Factor is smaller than the Original Zoom Factor, we Divide (-).

Zoom Function - for the purpose of clarity

VB.NET
'Resize Image on initial load into picturebox
    Private Sub Zoom(ByVal Factor As Double)
        Try
            'zoomFactor = Factor
            Dim sourceBitmap As New Bitmap(currentImage)
            myScreen = Screen.AllScreens(0)
            If Factor < 0.01 Then Return
            Dim destinationBitmap As New Bitmap(CInt(sourceBitmap.Width * Factor), _
                                        CInt(sourceBitmap.Height * Factor))
            Dim destinationGraphic As Graphics = Graphics.FromImage(destinationBitmap)

            destinationGraphic.DrawImage(sourceBitmap, 0, 0, destinationBitmap.Width + 1, _
                                  destinationBitmap.Height + 1)
            screenImageWidth = destinationBitmap.Width
            screenImageHeight = destinationBitmap.Height
            pbImage.Image = destinationBitmap
            destinationGraphic.Dispose()
            'Force Garbage Collection else app runs out of memory and Picturebox crashes.
            GC.Collect()
        Catch ex As Exception

        End Try
    End Sub

Here are a few images to demonstrate the accurate calculations to replotting on an image.

Fig 1.0 - Note is added at 101% zoom factor

Image 1

Fig 1.1 - Shows another marker (Note) being placed on the image

Image 2

Fig 1.3 - Shows the image Zoom'ed in to 50% while the markers (Notes) with their position over the original location on the image.

Image 3

Fig 1.4 - Zoom factor at 74% and markers retain their accurate plot over the image

Image 4

Finally Fig 1.5 shows markers accurately plotted at a zoom factor of 104%. Even with the smallest offset, the plot is 100% accurate.

Image 5

Points of Interest

In this project, I learned to keep bashing my head, the wall will give in at some point.

History

  • 23rd September, 2014: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
South Africa South Africa
Studied Software Development many moons ago, when the sun was still high for Cobol, Fortran, Basic, etc. programmers. However, I found corporate networks and systems more challenging and rewarding than sitting behind a desk all day long doing mostly maintenance on code. Mainframes were the super computers at the time and Tatung made very fancy desktop computers and to soop them up you had to have a TsengLabs VGA card, EGA CRT and a SoundBlaster sound card installed into the system - only then could you call yourself a Boss!

Later in life I reached a turning point where the saying "Jack of all trades, Master of none" became my driving force. I will do everything I know nothing about. Since then, life has become an adventure and not just a journey.

At the moment I write software tools to connect users with disconnected data.

Comments and Discussions

 
GeneralMy vote of 5 Pin
John B Oliver6-Oct-14 11:45
John B Oliver6-Oct-14 11:45 
GeneralRe: My vote of 5 Pin
Tino Fourie7-Oct-14 4:55
Tino Fourie7-Oct-14 4:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.