dcsimg
Results 1 to 6 of 6

Thread: my nearest neighbor example is not working as expected

  1. #1

    Thread Starter
    New Member
    Join Date
    Jan 2020
    Posts
    2

    my nearest neighbor example is not working as expected

    I have the following program that does image scaling. I take a 1024x1024 image and scale it to 256x256. The image gets scaled and looks alright, but comparing it to other applications such as Gimp that uses the same algorithm it turns out slightly different. Since this is a general question, I am not adding an image. Can someone test this to see if images you scale down turn out the same as perhaps a working algorithm that you have? If not, maybe you can tell me whats wrong with my code.

    I am trying to implement NearestNeighbor algorithm

    Code:
    Public Class frmMain
        Dim bm As New Bitmap("c:\images\Test.png")
    
        Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            pbView.Refresh()
        End Sub
    
        Private Sub pbView_Paint(sender As Object, e As PaintEventArgs) Handles pbView.Paint
            'e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
            e.Graphics.DrawImage(bm, 12, 12)
        End Sub
    
        Private Sub ScaleUp()
            Dim new_w As Integer = 256
            Dim new_h As Integer = 256
    
            Dim new_bm As New Bitmap(new_w, new_h)
    
            Dim srcX As Single
            Dim srcY As Single
            Dim src_col As Color
    
            For y As Integer = 0 To new_h - 1
                For x As Integer = 0 To new_w - 1
                    srcX = CInt(Math.Round(CSng(x) / CSng(new_w) * CSng(bm.Width)))
                    srcY = CInt(Math.Round(CSng(y) / CSng(new_h) * CSng(bm.Height)))
                    srcX = Math.Min(srcX, bm.Width - 1)
                    srcY = Math.Min(srcY, bm.Height - 1)
                    src_col = bm.GetPixel(CInt(srcX), CInt(srcY))
                    new_bm.SetPixel(x, y, src_col)
                Next
            Next
    
            pbView.CreateGraphics.Clear(Color.White)
            pbView.CreateGraphics.DrawImage(new_bm, 12, 12)
        End Sub
    
    
    
        Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
            ScaleUp()
        End Sub
    End Class

  2. #2

    Thread Starter
    New Member
    Join Date
    Jan 2020
    Posts
    2

    Re: my nearest neighbor example is not working as expected

    I looked around and found a simplified method - It's the same, just the code is simplified, so it should be easier to read. However, I get the same difference. Anyway, same question. Am I correct? Does Gimp not use the same nearest neighbor?

    Please don't forget, the image you use should be 1024x1024 to compare.

    Code:
    Public Class frmMain
        Dim bm As New Bitmap("d:\users\krisztian\desktop\scale_Test.png")
    
        Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            pbView.Refresh()
        End Sub
    
        Private Sub pbView_Paint(sender As Object, e As PaintEventArgs) Handles pbView.Paint
            'e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
            e.Graphics.DrawImage(bm, 12, 12)
    
        End Sub
    
        Private Sub ScaleNN()
            Dim new_w As Integer = bm.Width \ 4
            Dim new_h As Integer = bm.Height \ 4
    
            Dim new_bm As New Bitmap(new_w, new_h)
    
            Dim srcX As Double
            Dim srcY As Double
            Dim src_col As Color
    
            Dim x_ratio As Double = bm.Width / new_w
            Dim y_ratio As Double = bm.Height / new_h
    
            For y As Integer = 0 To new_h - 1
                For x As Integer = 0 To new_w - 1
                    srcX = Math.Floor(x * x_ratio)
                    srcY = Math.Floor(y * y_ratio)
                    src_col = bm.GetPixel(CInt(srcX), CInt(srcY))
                    new_bm.SetPixel(x, y, src_col)
                Next
            Next
    
            pbView.CreateGraphics.Clear(Color.White)
            pbView.CreateGraphics.DrawImage(new_bm, 12, 12)
        End Sub
    
    
        Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
            ScaleNN()
        End Sub
    End Class

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    34,701

    Re: my nearest neighbor example is not working as expected

    Well, you're certainly messing around with data conversions in both examples, and that could be the issue, or not. I didn't like the use of Single in the first example, but I see that you switched to Double in the second example. That doesn't much matter, though, because Math.Floor is going to return an Integer anyways, so srcX and srcY will both hold integers, and could be Integer type. You convert them to Integer for use anyways, so the fact that they are declared as Double doesn't mean much.

    Probably not related, but what do you expect these two lines to be doing:

    pbView.CreateGraphics.Clear(Color.White)
    pbView.CreateGraphics.DrawImage(new_bm, 12, 12)

    If you look at this link: https://docs.microsoft.com/en-us/dot...tframework-4.8

    You can see a snippet that does the work a bit more efficiently. CreateGraphics creates an object. You do that, then call a method on it, followed by creating a new object and calling a second method on it. I would expect that the first line does nothing useful, but you certainly shouldn't be calling CreateGraphics twice like that. The example text shows a better way to do it, though you might also just delete the line that calls Clear, as that's probably worthless, in which case the only issue (and one that shouldn't matter, in this case) is that you don't call Dispose() on the graphics object returned.

    There looks to be a different issue, as well, but since this is test code, it probably isn't relevant: The Paint event can be triggered all over the place. You draw the bm variable into pbView. The only time you draw the other image is in the method, which isn't called from the Paint event, so anything that invalidates the client area for pbView, should cause the image to revert to the old image. Wouldn't it be better to drop the whole CreateGraphics thing from the ScaleNN method and just change which image is being drawn in the Paint event?
    My usual boring signature: Nothing

  4. #4
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    104,112

    Re: my nearest neighbor example is not working as expected

    Quote Originally Posted by Shaggy Hiker View Post
    That doesn't much matter, though, because Math.Floor is going to return an Integer anyways
    That's not actually true. It does return whole numbers, but the return type is still the same as the argument type, which must be Double or Decimal. Anything else will be implicitly converted to one of those types - probably Double - and so one of those types will be returned. As such, you should need an explicit conversion from Double to Integer if you want an Integer value, which is exactly what the code is doing.

  5. #5
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    34,701

    Re: my nearest neighbor example is not working as expected

    Ah, I saw the "nearest integer" and made an assumption.
    My usual boring signature: Nothing

  6. #6
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,104

    Re: my nearest neighbor example is not working as expected

    Hi zpix, it's not surprising your resized image doesn't look the same as Gimp, because your code doesn't implement the nearest neighbour algorithm. And Gimp presumably does.

    When you make an image bigger in NearestNeighbor mode, the computer has to pick the colour of whichever pixel is closest in the original image. I'm not sure what happens when you make the image smaller, but it involves some mixing of colours from adjacent pixels. Anyway, I don't need to know because the graphics of Windows Forms does it for you with just one line of code. And you already have that line in your own code but it's commented out. Here's an example of how you could use it correctly:
    '
    Code:
       Dim new_W As Integer
       Dim new_H As Integer
       Dim bm As Bitmap
    
       Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
          bm = New Bitmap("c:\images\Test.png") 'get the image   
          new_W = bm.Width \ 4 'calculate the new width
          new_H = bm.Height \ 4 'calculate the new height
          pbView.Invalidate() 'trigger the Paint event
       End Sub
    
       Private Sub pbView_Paint(sender As Object, e As PaintEventArgs) Handles pbView.Paint
          Dim g As Graphics = e.Graphics 'get the Graphics object
          g.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor 'set its mode to NearestNeighbor
          If bm IsNot Nothing Then
             g.DrawImage(bm, 12, 12, new_W, new_H) 'resize and draw the image in NearestNeighbor mode
          End If
       End Sub


    See, no loops or CreateGraphics needed! If your intention is to get some practice using loops to implement NearestNeighbour, you'll have a lot of work ahead of you!

    BB

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width