Ir para o conteúdo

Inverter, aclarar e escurecer as cores de uma imagem

Introdução

De todos os algoritmos para efeitos de imagem, a inversão, aclaramento e escurecimento de cor são de longe os mais fáceis de alcançar. Neste artigo vamos tratar de inverter, aclarar e escurecer as cores de uma imagem, através de um algoritmo lento, mas de fácil percepção. Basicamente, todos os pixeis são corridos, através de ciclos, e a sua cor é calculada e alterada.

Como é constituída a cor?

Existem dois sistemas de cor. Existem os sistemas de subtracção de cor (CMY), como a tinta das impressoras e existem os sistemas de adição de cor (RGB), como a luz. Na tecnologia, os componentes de imagem como monitores ou televisões, obtêm as cores através da luz e por consequência utilizam o sistema de adição para apresentarem todas as cores que temos vemos nos ecrans. O sistema RGB consiste na adição de 256 tons de 3 cores, podendo teóricamente formar 256x256x256 (16.777.216) cores distintas. Neste sistema, a adição de todas as 3 cores nos seus tons máximos produzem branco e as 3 cores nos seus tons mínimos, ou na ausência de cor, produzem preto. As 3 cores ou canais do sistema RGB são na verdade as 3 iniciais do sistema. RGB = Red Green Blue.

A inversão

Teóricamente, para obter o inverso de uma cor, é apenas necessário calcular o inverso de cada cor do sistema RGB. Sendo assim, e sabendo que existem 256 tons para cada cor, devemos subtraír de 255 o tom da cor que queremos inverter, em cada uma das 3 cores. Por exemplo:

R=200, G=100, B=50

R=255-200, G=255-100, B=255-50

E a cor inversa de R=200, G=100, B=50 é R=55, G=155, B=205.

O aclaramento

Se estamos a trabalhar sobre o sistema de adição, para tornar uma cor mais viva basta aumentar de forma igual todos os 3 canais. Com isto, os valores vão aproximando do tom máximo e a cor vai tender para branco.

O escurecimento

Exactamente o contrário do aclaramento, para obter um escurecimento basta diminuir de forma igual todos os 3 canais. Com isto, os valores vão aproximando do tom máximo e a cor vai tender para preto.

A prática na nossa realidade

A melhor forma de trabalhar imagem pixel a pixel é na sua forma abstracta. Como tal, todos os tratamentos são aplicados numa variável do tipo Bitmap e só depois apresentadas em qualquer controlo ou gravadas para o disco.

Importante: Todos os controlos podem e devem ser adicionados na fase de desenho. São programáticamente inseridos para minimizar as possíveis falhas ao reproduzir o exemplo

Importante: É utilizada para imagem do exemplo o ícone da própria aplicação para minimizar as possíveis falhas ao reproduzir o exemplo

Comecemos por declarar uma variável do tipo Bitmap e instanciar todos os controlos necessários, fora dos subs para acesso global dentro da mesma classe.

Public Class Form1

    Private Imagem As Bitmap
    Private WithEvents BtInverter As New Button
    Private WithEvents BtAclarar As New Button
    Private WithEvents BtEscurecer As New Button
    Private PBApresenta As New PictureBox

End Class

De seguida, vamos definir o tamanho do form, adicionar, configurar e centrar os controlos no evento Load do form

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None

        Me.Size = New Size(124, 140)
        PBApresenta.Size = New Size(32, 32)
        BtInverter.Size = New Size(84, 23)
        BtAclarar.Size = New Size(84, 23)
        BtEscurecer.Size = New Size(84, 23)

        PBApresenta.Top = 12
        BtInverter.Top = 50
        BtAclarar.Top = 79
        BtEscurecer.Top = 108

        PBApresenta.Left = (Me.Width / 2) - (PBApresenta.Width / 2)
        BtInverter.Left = (Me.Width / 2) - (BtInverter.Width / 2)
        BtAclarar.Left = (Me.Width / 2) - (BtAclarar.Width / 2)
        BtEscurecer.Left = (Me.Width / 2) - (BtEscurecer.Width / 2)

        BtInverter.Text = "Inverter"
        BtAclarar.Text = "Aclarar"
        BtEscurecer.Text = "Escurecer"

        PBApresenta.BorderStyle = BorderStyle.FixedSingle
        Imagem = Me.Icon.ToBitmap
        PBApresenta.Image = Imagem

        Me.Controls.Add(PBApresenta)
        Me.Controls.Add(BtInverter)
        Me.Controls.Add(BtAclarar)
        Me.Controls.Add(BtEscurecer)
    End Sub

Passemos ao código de cada um dos efeitos. Comecemos por a inversão.

    Private Sub BtInverter_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BtInverter.Click
        For X As Integer = 0 To (Imagem.Width) - 1
            For Y As Integer = 0 To (Imagem.Height) - 1
                Dim C As Color = Imagem.GetPixel(X, Y)
                Imagem.SetPixel(X, Y, Color.FromArgb(C.A, 255 - C.R, 255 - C.G, 255 - C.B))
            Next
        Next

        PBApresenta.Image = Imagem
    End Sub

A lógica dos dois ciclos percorre, por cada pixel na horizontal todos os pixeis da vertical, até chegar ao último pixel horizontal e vertical, percorrendo assim todos os pixeis da imagem. Dentro do segundo ciclo, onde temos disponível a coordenada do pixel actual, vamos apanhar a cor desse pixel para uma variável e de seguida, defenir a cor desse mesmo pixel para a cor calculada com as inversões dos canais.

Passemos depois para o efeito de aclarar

    Private Sub BtAclarar_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BtAclarar.Click
        For H As Integer = 0 To Imagem.Width - 1
            For V As Integer = 0 To Imagem.Height - 1

                Dim C As Color = Imagem.GetPixel(H, V)
                Dim R As Integer = C.R + 10
                Dim G As Integer = C.G + 10
                Dim B As Integer = C.B + 10

                If R > 255 Then R = 255
                If G > 255 Then G = 255
                If B > 255 Then B = 255

                Imagem.SetPixel(H, V, Color.FromArgb(C.A, R, G, B))

            Next
        Next

        PBApresenta.Image = Imagem
    End Sub

Utilizando a mesma lógica para percorrer todos os pixeis, em cada pixel retiramos a cor pelo mesmo método e adicionamos 10 tons a cada canal. De seguida temos de verificar se o tom actual mais 10 não ultrapassa os 255 tons de cada canal, e se ultrapassar temos de o definir para o seu valor máximo. Depois de efectuar todas as verificações está na altura de alterar o pixel actual com a cor calculada.

Por fim, o efeito de escurecer.

    Private Sub BtEscurecer_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BtEscurecer.Click
        For H As Integer = 0 To Imagem.Width - 1
            For V As Integer = 0 To Imagem.Height - 1

                Dim C As Color = Imagem.GetPixel(H, V)
                Dim R As Integer = C.R - 10
                Dim G As Integer = C.G - 10
                Dim B As Integer = C.B - 10

                If R < 0 Then R = 0
                If G < 0 Then G = 0
                If B < 0 Then B = 0

                Imagem.SetPixel(H, V, Color.FromArgb(C.A, R, G, B))

            Next
        Next

        PBApresenta.Image = Imagem
    End Sub

Este efeito utiliza exactamente a mesma lógica do efeito de aclarar, mas subtraíndo 10 tons e verificando o tom mínimo.

Aviso: O canal Alpha, que determina a opacidade da cor (manipulado por software) é passado inalterado para manter as transparências de formatos que suportem canal Alpha, como PNG, caso contrário qualquer pixel com um tom de alpha diferente do máximo tornar-se-á preto.

O exemplo completo

Crie um novo projecto, e substitua todo o código do form de arranque por este:

Public Class Form1

    Private Imagem As Bitmap
    Private WithEvents BtInverter As New Button
    Private WithEvents BtAclarar As New Button
    Private WithEvents BtEscurecer As New Button
    Private PBApresenta As New PictureBox

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None

        Me.Size = New Size(124, 140)
        PBApresenta.Size = New Size(32, 32)
        BtInverter.Size = New Size(84, 23)
        BtAclarar.Size = New Size(84, 23)
        BtEscurecer.Size = New Size(84, 23)

        PBApresenta.Top = 12
        BtInverter.Top = 50
        BtAclarar.Top = 79
        BtEscurecer.Top = 108

        PBApresenta.Left = (Me.Width / 2) - (PBApresenta.Width / 2)
        BtInverter.Left = (Me.Width / 2) - (BtInverter.Width / 2)
        BtAclarar.Left = (Me.Width / 2) - (BtAclarar.Width / 2)
        BtEscurecer.Left = (Me.Width / 2) - (BtEscurecer.Width / 2)

        BtInverter.Text = "Inverter"
        BtAclarar.Text = "Aclarar"
        BtEscurecer.Text = "Escurecer"

        PBApresenta.BorderStyle = BorderStyle.FixedSingle
        Imagem = Me.Icon.ToBitmap
        PBApresenta.Image = Imagem

        Me.Controls.Add(PBApresenta)
        Me.Controls.Add(BtInverter)
        Me.Controls.Add(BtAclarar)
        Me.Controls.Add(BtEscurecer)
    End Sub

    Private Sub BtInverter_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BtInverter.Click
        For X As Integer = 0 To (Imagem.Width) - 1
            For Y As Integer = 0 To (Imagem.Height) - 1
                Dim C As Color = Imagem.GetPixel(X, Y)
                Imagem.SetPixel(X, Y, Color.FromArgb(C.A, 255 - C.R, 255 - C.G, 255 - C.B))
            Next
        Next

        PBApresenta.Image = Imagem
    End Sub

    Private Sub BtAclarar_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BtAclarar.Click
        For H As Integer = 0 To Imagem.Width - 1
            For V As Integer = 0 To Imagem.Height - 1

                Dim C As Color = Imagem.GetPixel(H, V)
                Dim R As Integer = C.R + 10
                Dim G As Integer = C.G + 10
                Dim B As Integer = C.B + 10

                If R > 255 Then R = 255
                If G > 255 Then G = 255
                If B > 255 Then B = 255

                Imagem.SetPixel(H, V, Color.FromArgb(C.A, R, G, B))

            Next
        Next

        PBApresenta.Image = Imagem
    End Sub

    Private Sub BtEscurecer_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BtEscurecer.Click
        For H As Integer = 0 To Imagem.Width - 1
            For V As Integer = 0 To Imagem.Height - 1

                Dim C As Color = Imagem.GetPixel(H, V)
                Dim R As Integer = C.R - 10
                Dim G As Integer = C.G - 10
                Dim B As Integer = C.B - 10

                If R < 0 Then R = 0
                If G < 0 Then G = 0
                If B < 0 Then B = 0

                Imagem.SetPixel(H, V, Color.FromArgb(C.A, R, G, B))

            Next
        Next

        PBApresenta.Image = Imagem
    End Sub
End Class