2
votes

Having an image like this (original size increased by 100%):

close button image

Under C# or else Vb.Net, I wonder if I could use a color matrix or other workaround to increase the brightness/luminosity only to the white/greyed color of that image keeping the black background color untouched?

In other words, I would like to make the white cross brighter (only the cross, the white/grey pixels).

This is the brightness result that I expect to achieve:

enter image description here

Both images are taken from a 3rd-party application, the first image I shown is from a not focused close-button and the second image is the same button focused, the black portion of the image is the background of that 3rd-party app

The thing is that for personal reasons in my own application I'm using that image as a close-button and the same background color then I just wondered to simulate a button-focus in that way by giving brightness to the "X" in that image, I need to do the same with other images.

My knowledges of image processing are very poor, I've just been looking to approachs like this below, but I cannot get the expected result because the brightness increases in the entire image:

Adjust brightness contrast and gamma of an image

Update

I'm trying to use @Paul Ishak approach.

I have the image set on a picturebox, the idea is to increase the brightness when the user is hovering the control, and reset the image when the mouse leaves,

the second time that I try to adjust the brightness it throws an ArgumentException at this line:

 bm = New Bitmap(image.Width, image.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)

with this message:

A first chance exception of type 'System.ArgumentException' occurred in System.Drawing.dll

This is the code that I'm using, what is wrong?:

    Private minimizeBitmap As Bitmap

    Private Sub PictureBox_MinimizeButton_MouseEnter(ByVal sender As Object, ByVal e As EventArgs) _
    Handles PictureBox_MinimizeButton.MouseEnter

        Dim pcb As PictureBox = DirectCast(sender, PictureBox)

        If Me.minimizeBitmap Is Nothing Then
            Me.minimizeBitmap = New Bitmap(pcb.ClientRectangle.Width, pcb.ClientRectangle.Height)
            pcb.DrawToBitmap(Me.minimizeBitmap, pcb.ClientRectangle)
        End If

        pcb.BackgroundImage = BrightenPixels(Me.minimizeBitmap, threshold:=33, modTimes:=5D)

    End Sub

    Private Sub PictureBox_MinimizeButton_MouseLeave(ByVal sender As Object, ByVal e As EventArgs) _
    Handles PictureBox_MinimizeButton.MouseLeave

        If Me.minimizeBitmap IsNot Nothing Then
            Me.minimizeBitmap.Dispose()
        End If

        DirectCast(sender, PictureBox).BackgroundImage = Nothing

    End Sub

    Public Function BrightenPixels(ByVal image As Bitmap, threshold As Decimal, modTimes As Decimal) As Bitmap

        Dim bmp As Bitmap
        modTimes = Math.Abs(modTimes)
        If image Is Nothing Then
            Throw New ArgumentNullException(paramname:="image")
        End If

        Try

            bmp = New Bitmap(image.Width, image.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        Catch ex As Exception
            MsgBox(ex.GetType.Name)
            MsgBox(ex.Message)

        End Try

        Dim g As Graphics = Graphics.FromImage(bmp)
        g.DrawImage(image, New Point(0, 0))
        Dim rect As New Rectangle(New Point(0, 0), bmp.Size)
        Dim bitmapData As System.Drawing.Imaging.BitmapData = bmp.LockBits(rect, Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        Dim ptr As IntPtr = bitmapData.Scan0
        Dim byteCount As Integer = bitmapData.Stride * bitmapData.Height
        Dim argb(byteCount - 1) As Byte
        Marshal.Copy(ptr, argb, 0, byteCount)

        For i As Integer = 0 To byteCount - 1 Step 4

            Dim red As Decimal = argb(i + 2)
            Dim green As Decimal = argb(i + 1)
            Dim blue As Decimal = argb(i)
            Dim percentR As Decimal = (red / 255) * 100
            Dim percentG As Decimal = (green / 255) * 100
            Dim percentB As Decimal = (blue / 255) * 100
            Dim aboveThresholdCount As Integer = 0
            If percentR > threshold Then aboveThresholdCount += 1
            If percentG > threshold Then aboveThresholdCount += 1
            If percentB > threshold Then aboveThresholdCount += 1
            ' Label3.Text = aboveThresholdCount.ToString
            If aboveThresholdCount = 3 Then
                red = red * modTimes
                green = green * modTimes
                blue = blue * modTimes
                If red > 255 Then red = 255
                If green > 255 Then green = 255
                If blue > 255 Then blue = 255
            End If
            argb(i + 2) = CByte(Math.Round(red, 0))
            argb(i + 1) = CByte(Math.Round(green, 0))
            argb(i) = CByte(Math.Round(blue, 0))

        Next

        Marshal.Copy(argb, 0, ptr, byteCount)
        bmp.UnlockBits(bitmapData)
        Return bmp

    End Function
1
The link, using a Color Matrix is a good, simple and fast solution. You need to adjust the gamma and possibly the contrast, not the brightness. Use trackbars to play with the values!TaW
@TaW thanks for comment, that is the approach that more I like it but I've tried to modify the matrix values and always get undesired results, no way for non expertsElektroStudios
I find that using 1.5 for contrast and gamma leads to a very nice result.TaW
Not really at 1.5 but its true what you suggested about changing contrasts and gamma helped me to solve the problem, thanksElektroStudios

1 Answers

2
votes

This example will show the image being modified as the mouse enters and leaves the 'X'.
(example 2 will shrink the image by 50%)
enter image description here
Please try creating a new Visual Basic Winforms app for this example. Please try this example As-Is before modifying!

1.) Create new project
2.) Replace all of Form1's code with this code(only modifying path to image, nothing else)
3.) Click run.
4.) Move mouse into and out of the "X"

Example 1:

Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
    Private originalPic As Bitmap = Nothing
    Friend WithEvents PictureBox1 As New PictureBox With {.Parent = Me, .Location = New Point(10, 10)}
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'Only modify this line to the location of your "x" image'
        Dim exPath As String = System.IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.Desktop, "picexample.jpg")
        AddHandler PictureBox1.MouseMove, AddressOf PictureBox1_MouseMove
        If System.IO.File.Exists(exPath) Then
            originalPic = CType(Image.FromFile(exPath), Bitmap)
            PictureBox1.Size = originalPic.Size
            PictureBox1.Image = originalPic
        Else
            Application.Exit()
        End If
    End Sub
    Public Function BrightenPixels(ByVal Image As Bitmap, threshold As Decimal, modTimes As Decimal) As Bitmap
        modTimes = Math.Abs(modTimes)
        If Image Is Nothing Then Return Nothing
        Dim bm As New Bitmap(Image.Width, Image.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        Dim g As Graphics = Graphics.FromImage(bm)
        g.DrawImage(Image, New Point(0, 0))
        Dim rect As New Rectangle(New Point(0, 0), bm.Size)
        Dim bitmapData As System.Drawing.Imaging.BitmapData = bm.LockBits(rect, Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        Dim ptr As IntPtr = bitmapData.Scan0
        Dim byteCount As Integer = bitmapData.Stride * bitmapData.Height
        Dim argb(byteCount - 1) As Byte
        System.Runtime.InteropServices.Marshal.Copy(ptr, argb, 0, byteCount)
        For i As Integer = 0 To byteCount - 1 Step 4
            Dim alpha As Decimal = argb(i + 3)
            Dim red As Decimal = argb(i + 2)
            Dim green As Decimal = argb(i + 1)
            Dim blue As Decimal = argb(i)
            Dim percentR As Decimal = (red / 255) * 100
            Dim percentG As Decimal = (green / 255) * 100
            Dim percentB As Decimal = (blue / 255) * 100
            Dim aboveThresholdCount As Integer = 0
            If percentR > threshold Then aboveThresholdCount += 1
            If percentG > threshold Then aboveThresholdCount += 1
            If percentB > threshold Then aboveThresholdCount += 1
            If aboveThresholdCount = 3 Then
                red = red * modTimes
                green = green * modTimes
                blue = blue * modTimes
                If red > 255 Then red = 255
                If green > 255 Then green = 255
                If blue > 255 Then blue = 255
            End If
            argb(i + 2) = CByte(Math.Round(red, 0))
            argb(i + 1) = CByte(Math.Round(green, 0))
            argb(i) = CByte(Math.Round(blue, 0))
        Next
        System.Runtime.InteropServices.Marshal.Copy(argb, 0, ptr, byteCount)
        bm.UnlockBits(bitmapData)
        Return bm
    End Function
    Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
        Dim intersectionRect As New Rectangle(13, 18, 22, 21)
        Dim mouseRect As New Rectangle(PictureBox1.PointToClient(MousePosition), New Size(1, 1))
        Me.Text = mouseRect.ToString
        If intersectionRect.IntersectsWith(mouseRect) Then
            Dim tmp As Bitmap = BrightenPixels(originalPic, 33, 1.5D)
            '---------------------Delete this section of code(inbetween lines) to remove circular highlight----------'
            Using g As Graphics = Graphics.FromImage(tmp)
                Using gp As New System.Drawing.Drawing2D.GraphicsPath
                    gp.AddRectangle(intersectionRect)
                    Using pgp As New System.Drawing.Drawing2D.PathGradientBrush(gp)
                        Dim center As New PointF(intersectionRect.Left + (intersectionRect.Width \ 2), intersectionRect.Top + (intersectionRect.Height \ 2))
                        pgp.CenterPoint = center
                        pgp.CenterColor = Color.FromArgb(64, Color.White)
                        pgp.SurroundColors = {Color.FromArgb(0, Color.White), Color.FromArgb(0, Color.White)}
                        g.FillPath(pgp, gp)
                    End Using
                End Using
            End Using
            '-----------------------------------------------------------------------------------------------------------'
            PictureBox1.Image = tmp
        Else
            PictureBox1.Image = originalPic
        End If
    End Sub
End Class

Example 2 (image shrunken by 50%):

enter image description here

Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
    Private originalPic As Bitmap = Nothing
    Private intersectionPic As Bitmap = Nothing
    Private intersectionRect As New Rectangle(6, 9, 11, 10)
    Friend WithEvents PictureBox1 As New PictureBox With {.Parent = Me, .Location = New Point(10, 10)}
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'Only modify this line to the location of your "x" image'
        Dim exPath As String = System.IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.Desktop, "picexample.jpg")
        AddHandler PictureBox1.MouseMove, AddressOf PictureBox1_MouseMove
        If System.IO.File.Exists(exPath) Then
            Dim img As Bitmap = CType(Image.FromFile(exPath), Bitmap)
            Dim bm As New Bitmap(img.Width \ 2, img.Height \ 2, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
            Dim g1 As Graphics = Graphics.FromImage(bm)
            Dim cRect As New Rectangle(New Point(0, 0), bm.Size)
            g1.DrawImage(CType(Image.FromFile(exPath), Bitmap), cRect)
            originalPic = bm
            Dim tmp As Bitmap = BrightenPixels(originalPic, 33, 1.5D)
            '---------------------Delete this section of code(inbetween lines) to remove circular highlight----------'
            Using g2 As Graphics = Graphics.FromImage(tmp)
                Using gp As New System.Drawing.Drawing2D.GraphicsPath
                    gp.AddRectangle(intersectionRect)
                    Using pgp As New System.Drawing.Drawing2D.PathGradientBrush(gp)
                        Dim center As New PointF(intersectionRect.Left + (intersectionRect.Width \ 2), intersectionRect.Top + (intersectionRect.Height \ 2))
                        pgp.CenterPoint = center
                        pgp.CenterColor = Color.FromArgb(128, Color.White)
                        pgp.SurroundColors = {Color.FromArgb(0, Color.White), Color.FromArgb(0, Color.White)}
                        g2.FillPath(pgp, gp)
                    End Using
                End Using
            End Using
            '-----------------------------------------------------------------------------------------------------------'
            intersectionPic = tmp
            PictureBox1.Size = originalPic.Size
            PictureBox1.Image = originalPic
        Else
            Application.Exit()
        End If
    End Sub
    Public Function BrightenPixels(ByVal Image As Bitmap, threshold As Decimal, modTimes As Decimal) As Bitmap
        modTimes = Math.Abs(modTimes)
        If Image Is Nothing Then Return Nothing
        Dim bm As New Bitmap(Image.Width, Image.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        Dim g As Graphics = Graphics.FromImage(bm)
        g.DrawImage(Image, New Point(0, 0))
        Dim rect As New Rectangle(New Point(0, 0), bm.Size)
        Dim bitmapData As System.Drawing.Imaging.BitmapData = bm.LockBits(rect, Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        Dim ptr As IntPtr = bitmapData.Scan0
        Dim byteCount As Integer = bitmapData.Stride * bitmapData.Height
        Dim argb(byteCount - 1) As Byte
        System.Runtime.InteropServices.Marshal.Copy(ptr, argb, 0, byteCount)
        For i As Integer = 0 To byteCount - 1 Step 4
            Dim alpha As Decimal = argb(i + 3)
            Dim red As Decimal = argb(i + 2)
            Dim green As Decimal = argb(i + 1)
            Dim blue As Decimal = argb(i)
            Dim percentR As Decimal = (red / 255) * 100
            Dim percentG As Decimal = (green / 255) * 100
            Dim percentB As Decimal = (blue / 255) * 100
            Dim aboveThresholdCount As Integer = 0
            If percentR > threshold Then aboveThresholdCount += 1
            If percentG > threshold Then aboveThresholdCount += 1
            If percentB > threshold Then aboveThresholdCount += 1
            If aboveThresholdCount = 3 Then
                red = red * modTimes
                green = green * modTimes
                blue = blue * modTimes
                If red > 255 Then red = 255
                If green > 255 Then green = 255
                If blue > 255 Then blue = 255
            End If
            argb(i + 2) = CByte(Math.Round(red, 0))
            argb(i + 1) = CByte(Math.Round(green, 0))
            argb(i) = CByte(Math.Round(blue, 0))
        Next
        System.Runtime.InteropServices.Marshal.Copy(argb, 0, ptr, byteCount)
        bm.UnlockBits(bitmapData)
        Return bm
    End Function
    Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
        Dim mouseRect As New Rectangle(PictureBox1.PointToClient(MousePosition), New Size(1, 1))
        Me.Text = mouseRect.ToString
        If intersectionRect.IntersectsWith(mouseRect) Then
            PictureBox1.Image = intersectionPic
        Else
            PictureBox1.Image = originalPic
        End If
    End Sub
End Class