Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

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

0.00/5 (No votes)
23 Sep 2014 1  
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
 '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

'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

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

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.

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

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

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here