Quantcast
Viewing all articles
Browse latest Browse all 16

Need code optimization tips for a content aware image resizing program

Hi,

I am writing a program that uses content aware resizing (http://en.wikipedia.org/wiki/Seam_carving) to resize an image. As I understand it, this type of resizing essentially comes down to adding/removing slices from an image with the lowest amount of detail.

Any way, the code works except for the fact that is rather slow. Reducing the width for an image of 1024x768 pixels by half takes about 3 minutes! :eek: And for some reason, the method I use only works when adding/removing vertical slices (to change the width). I eventually decided to simply rotate the image before and after resizing. It doesn't take much code or processing time, but it's a bit odd.

Below is the program's main module which contains everything required for resizing an image.
Code:

'This module's imports and settings.
Option Compare Binary
Option Explicit On
Option Infer Off
Option Strict On

Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Linq
Imports System.Math
Imports System.Runtime.InteropServices.Marshal
Imports System.Windows.Forms

'This module contains this program's core procedures.
Public Module CAIRModule

  'This enumeration contains a list of the supported seam directions.
  Private Enum DirectionsE As Integer
      Horizontal
      Vertical
  End Enum

  'This structure defines a seam.
  Private Structure SeamStr
      Dim Energy As Integer              'Contains the seam's energy.
      Dim Indexes As List(Of Integer)    'Contains the indexes of the seam's pixels.
  End Structure

  Private Const ARGBSize As Integer = 4  'The number of bytes in an alpha, red, green, and blue color value.

  'This procedure adds/cuts the specified seam from the specified image.
  Private Function AddOrCutSeam(ByRef Pixels As List(Of Byte), ByRef Seam As SeamStr, ByRef Add As Boolean) As List(Of Byte)
      Try
        With Seam
            .Indexes.Sort()
            .Indexes.Reverse()

            If Add Then
              For Each Index As Integer In .Indexes
                  Pixels.InsertRange(Index + ARGBSize, Pixels.GetRange(Index, ARGBSize))
              Next Index
            Else
              For Each Index As Integer In .Indexes
                  Pixels.RemoveRange(Index, ARGBSize)
              Next Index
            End If
        End With

        Return Pixels
      Catch ExceptionO As Exception
        HandleError(ExceptionO)
      End Try

      Return Nothing
  End Function

  'This procedure returns the best vertical seam.
  Private Function BestVerticalSeam(ByRef Pixels As List(Of Byte), ByRef Stride As Integer, ByRef Height As Integer) As SeamStr
      Try
        Dim BestSeam As SeamStr = VerticalSeam(Pixels, Stride, Height, 0)
        Dim Seam As New SeamStr With {.Energy = 0, .Indexes = New List(Of Integer)}

        If BestSeam.Energy = 0 Then Return BestSeam

        For x As Integer = ARGBSize To Stride - ARGBSize Step ARGBSize
            Seam = VerticalSeam(Pixels, Stride, Height, x)
            If Seam.Energy < BestSeam.Energy Then BestSeam = Seam
        Next x

        Return BestSeam
      Catch ExceptionO As Exception
        HandleError(ExceptionO)
      End Try

      Return Nothing
  End Function

  'This procedure returns the difference between the two specified colors.
  Private Function ColorDifference(ByRef Color1() As Byte, ByRef Color2() As Byte) As Integer
      Try
        Dim Difference As Integer = 0

        For Index As Integer = Color1.GetLowerBound(0) To Color1.GetUpperBound(0)
            Difference += Abs(CInt(Color2(Index)) - CInt(Color1(Index)))
        Next Index

        Return CInt(Difference / 3)
      Catch ExceptionO As Exception
        HandleError(ExceptionO)
      End Try

      Return Nothing
  End Function

  'This procedure returns the index of the lowest difference.
  Private Function GetLowest(ByRef Differences() As Integer) As Integer
      Try
        Dim Lowest As Integer = 1

        For Index As Integer = Differences.GetLowerBound(0) To Differences.GetUpperBound(0)
            If Differences(Index) < Differences(Lowest) Then Lowest = Index
        Next Index

        Return Lowest
      Catch ExceptionO As Exception
        HandleError(ExceptionO)
      End Try

      Return Nothing
  End Function

  'This procedure handles any errors that occur.
  Public Sub HandleError(ByRef ExceptionO As Exception)
      Try
        If MessageBox.Show(ExceptionO.Message, My.Application.Info.Title, MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) = DialogResult.Cancel Then
            InterfaceWindow.Close()
        End If
      Catch
        InterfaceWindow.Close()
      End Try
  End Sub

  'This procedure resizes the specified image.
  Public Function ResizeImage(ByRef ResizedImage As Bitmap, ByRef Resizes As Integer) As Bitmap
      Try
        Dim BitmapDataO As BitmapData = Nothing
        Dim BitmapStride As Integer = Nothing
        Dim Buffer() As Byte = Nothing
        Dim ImagePixels As New List(Of Byte)

        If ResizedImage.Width + Resizes < 1 Then Resizes = -(ResizedImage.Width - 1)

        With ResizedImage
            BitmapDataO = .LockBits(New Rectangle(0, 0, .Width, .Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb)
            ReDim Buffer(BitmapDataO.Stride * .Height)
            Copy(BitmapDataO.Scan0, Buffer, Buffer.GetLowerBound(0), Buffer.GetUpperBound(0))
            BitmapStride = BitmapDataO.Stride
            .UnlockBits(BitmapDataO)
            ImagePixels = Buffer.ToList
        End With

        For Resize As Integer = 1 To Abs(Resizes)
            ImagePixels = AddOrCutSeam(ImagePixels, BestVerticalSeam(ImagePixels, BitmapStride, ResizedImage.Height), Add:=(Resizes > 0))
            BitmapStride += If(Resizes > 0, ARGBSize, -ARGBSize)
        Next Resize

        ResizedImage = New Bitmap(CInt(BitmapStride / ARGBSize), ResizedImage.Height)

        With ResizedImage
            Buffer = ImagePixels.ToArray
            BitmapDataO = .LockBits(New Rectangle(0, 0, .Width, .Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb)
            Copy(Buffer, Buffer.GetLowerBound(0), BitmapDataO.Scan0, Buffer.GetUpperBound(0))
            .UnlockBits(BitmapDataO)
        End With

        Return ResizedImage
      Catch ExceptionO As Exception
        HandleError(ExceptionO)
      End Try

      Return Nothing
  End Function

  'This procedure returns a vertical seam through the specified locked bitmap.
  Private Function VerticalSeam(ByRef Pixels As List(Of Byte), ByRef Stride As Integer, ByRef Height As Integer, ByVal x As Integer) As SeamStr
      Try
        Dim CurrentIndex As Integer = Nothing
        Dim Differences(0 To 2) As Integer
        Dim NextIndex As Integer = Nothing
        Dim Lowest As Integer = Nothing
        Dim Seam As New SeamStr With {.Energy = 0, .Indexes = New List(Of Integer)}

        For y As Integer = 0 To Height - 1
            CurrentIndex = (y * Stride) + x

            NextIndex = (y * Stride) + (x - ARGBSize)
            Differences(0) = If(x >= ARGBSize, ColorDifference({Pixels(CurrentIndex), Pixels(CurrentIndex + 1), Pixels(CurrentIndex + 2)}, {Pixels(NextIndex), Pixels(NextIndex + 1), Pixels(NextIndex + 2)}), Byte.MaxValue)
            NextIndex = ((y + 1) * Stride) + x
            Differences(1) = If(y < Height - 1, ColorDifference({Pixels(CurrentIndex), Pixels(CurrentIndex + 1), Pixels(CurrentIndex + 2)}, {Pixels(NextIndex), Pixels(NextIndex + 1), Pixels(NextIndex + 2)}), Byte.MaxValue)
            NextIndex = (y * Stride) + (x + ARGBSize)
            Differences(2) = If(x + ARGBSize < Stride, ColorDifference({Pixels(CurrentIndex), Pixels(CurrentIndex + 1), Pixels(CurrentIndex + 2)}, {Pixels(NextIndex), Pixels(NextIndex + 1), Pixels(NextIndex + 2)}), Byte.MaxValue)
            Lowest = GetLowest(Differences)
            Seam.Energy += Differences(Lowest)
            Seam.Indexes.Add(CurrentIndex)
            If Lowest = 0 Then
              x -= ARGBSize
            ElseIf Lowest = 2 Then
              x += ARGBSize
            End If
        Next y

        Return Seam
      Catch ExceptionO As Exception
        HandleError(ExceptionO)
      End Try

      Return Nothing
  End Function
End Module

I did a search on the internet for code optimization in vb.net, but as far I can tell I either already did what was suggested or it didn't apply to my code. Does any one has any ideas of how to optimize my code?

I also attached the complete program in a .zip file. Usage:
-The program will display a file dialog at start up to allow the user to select an image.
-Press the "H" and "V" keys to select horizontal or vertical resizing.
-Specifying positive values will increase an image's size, and negative values will decrease the size.

Viewing all articles
Browse latest Browse all 16

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>