Buenas amigos, estoy intentando saturar una imagen y lo he conseguido pero el programa tarda mucho en procesar los cambios. Estoy orgulloso de lo que he conseguido porque no ha sido fácil averiguarlo, pero no va bien. He usado los miembro
ColorMatrix y
SetColorMatrix y he conseguido crear una matriz que realiza la saturación de una imagen, pero al usar un TrackBar el proceso de los cambios es demasiado lento.
S = (TrackBar1.Value) + 100
Dim Rojo As Single = CSng(299 * (100 - S) / 100000)
Dim Verde As Single = CSng(587 * (100 - S) / 100000)
Dim Azul As Single = CSng(114 * (100 - S) / 100000)
S = CSng(S / 100)
Dim colorMatrixVal As Single()() = {
New Single() {Rojo + S, Rojo, Rojo, 0, 0}, _
New Single() {Verde, Verde + S, Verde, 0, 0}, _
New Single() {Azul, Azul, Azul + S, 0, 0}, _
New Single() {0, 0, 0, 1, 0}, _
New Single() {0, 0, 0, 0, 1}}
Este lo he hecho como ejemplo para ponerlo aquí:
Imports System.IO
Imports System.Drawing.Imaging
Public Class Form1
Private originalBitmap As Bitmap = Nothing
Private previewBitmap As Bitmap = Nothing
Private resultBitmap As Bitmap = Nothing
Dim PictureBox1 As New PictureBox With {.Location = New Point(10, 10),
.Size = New Size(400, 240),
.SizeMode = PictureBoxSizeMode.StretchImage,
.BackColor = Color.DarkGray}
Dim Button1 As New Button With {.Location = New Point(PictureBox1.Width + 20, 10), .Text = "Cargar"}
Dim Button2 As New Button With {.Location = New Point(PictureBox1.Width + 20, 40), .Text = "Guardar"}
Dim TrackBar1 As New TrackBar With {.Location = New Point(10, PictureBox1.Height + 20),
.AutoSize = True,
.Size = New Size(PictureBox1.Width, 10),
.TickStyle = TickStyle.TopLeft,
.BackColor = Color.DarkGray,
.TickFrequency = 50,
.Maximum = 100,
.Minimum = -100,
.Value = 0,
.Orientation = Orientation.Horizontal}
Dim Label1 As New Label With {.Location = New Point(TrackBar1.Width + 20, TrackBar1.Top + 10),
.AutoSize = True,
.Text = "0",
.Font = New Font("Arial", 20, FontStyle.Bold)}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Controls.Add(PictureBox1)
Me.Controls.Add(Button1)
Me.Controls.Add(Button2)
Me.Controls.Add(TrackBar1)
Me.Controls.Add(Label1)
Label1.BringToFront()
Me.Size = New Size(520, 400)
AddHandler TrackBar1.ValueChanged, AddressOf TrackBar1_ValueChanged
AddHandler Button1.Click, AddressOf Button1_Click
AddHandler Button2.Click, AddressOf Button2_Click
End Sub
Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs)
Label1.Text = TrackBar1.Value.ToString()
AplicarSaturacion(True)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs)
Dim ofd As New OpenFileDialog()
ofd.Title = "Abrir imagen"
ofd.Filter = "Jpeg Images(*.jpg)|*.jpg|Png Images(*.png)|*.png"
ofd.Filter += "|Bitmap Images(*.bmp)|*.bmp"
If ofd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
Dim streamReader As New StreamReader(ofd.FileName)
originalBitmap = DirectCast(Bitmap.FromStream(streamReader.BaseStream), Bitmap)
streamReader.Close()
previewBitmap = Image.FromFile(ofd.FileName)
PictureBox1.Image = previewBitmap
AplicarSaturacion(True)
End If
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs)
AplicarSaturacion(False)
If resultBitmap IsNot Nothing Then
Dim sfd As New SaveFileDialog()
sfd.Title = "Guardar imagen"
sfd.Filter = "Jpeg Images(*.jpg)|*.jpg|Png Images(*.png)|*.png"
sfd.Filter += "|Bitmap Images(*.bmp)|*.bmp"
If sfd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
Dim fileExtension As String = Path.GetExtension(sfd.FileName).ToUpper()
Dim imgFormat As ImageFormat = ImageFormat.Png
If fileExtension = "BMP" Then
imgFormat = ImageFormat.Bmp
ElseIf fileExtension = "JPG" Then
imgFormat = ImageFormat.Jpeg
End If
Dim streamWriter As New StreamWriter(sfd.FileName, False)
resultBitmap.Save(streamWriter.BaseStream, imgFormat)
streamWriter.Flush()
streamWriter.Close()
resultBitmap = Nothing
End If
End If
End Sub
Public Sub AplicarSaturacion(Byval preview As Boolean)
If previewBitmap Is Nothing Then
Return
End If
Dim Imagen As Image = previewBitmap
Dim Pic As PictureBox = PictureBox1
Dim Grafico As Graphics
Dim Rectangulo As Rectangle
Pic.Image = New Bitmap(Pic.Width, Pic.Height, PixelFormat.Format32bppArgb)
Grafico = Graphics.FromImage(Pic.Image)
Rectangulo = New Rectangle(0, 0, Pic.Width, Pic.Height)
Grafico.DrawImage(Imagen, Rectangulo)
Dim S As Single
S = (TrackBar1.Value) + 100
Dim Rojo As Single = CSng(299 * (100 - S) / 100000)
Dim Verde As Single = CSng(587 * (100 - S) / 100000)
Dim Azul As Single = CSng(114 * (100 - S) / 100000)
S = CSng(S / 100)
Dim colorMatrixVal As Single()() = {
New Single() {Rojo + S, Rojo, Rojo, 0, 0}, _
New Single() {Verde, Verde + S, Verde, 0, 0}, _
New Single() {Azul, Azul, Azul + S, 0, 0}, _
New Single() {0, 0, 0, 1, 0}, _
New Single() {0, 0, 0, 0, 1}}
Dim colorMatrix As New ColorMatrix(colorMatrixVal)
Dim ImagenAtributos As New ImageAttributes
ImagenAtributos.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
Grafico.DrawImage(Imagen, Rectangulo, 0, 0, Imagen.Width, Imagen.Height, GraphicsUnit.Pixel, ImagenAtributos)
Pic.Refresh()
resultBitmap = Pic.Image
End Sub
End Class
Va bien pero al deslizar el TrackBar se producen trompicones al moverlo.
He conseguido convertir por mi cuenta un código de VB viejo a NET que usa API. Que no sé, seré el único que lo he hecho porque puse en google -SetColorAdjustment VB.NET- y ni rastro, las declaraciones que encontraba eran como en VB6.
Conseguí crear un código basado en API con
SetColorAdjustment y
GetColorAdjustment , y funciona bien. Los cambios son suaves pero tiene un problema y es que
no puedo guardar los cambios y no consigo refrescar el PictureBox correctamente.
Este es el código:
Imports System.IO
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Imports WindowsApplication1.NativeMethods
Public Class Form1
Private originalBitmap As Bitmap = Nothing
Private previewBitmap As Bitmap = Nothing
Private resultBitmap As Bitmap = Nothing
Dim picLoad As Boolean
Dim PictureBox1 As New PictureBox With {.Location = New Point(10, 10),
.Size = New Size(400, 240),
.SizeMode = PictureBoxSizeMode.StretchImage,
.BackColor = Color.DarkGray}
Dim Button1 As New Button With {.Location = New Point(PictureBox1.Width + 20, 10), .Text = "Cargar"}
Dim Button2 As New Button With {.Location = New Point(PictureBox1.Width + 20, 40), .Text = "Guardar"}
Dim TrackBar1 As New TrackBar With {.Location = New Point(10, PictureBox1.Height + 20),
.AutoSize = True,
.Size = New Size(PictureBox1.Width, 10),
.TickStyle = TickStyle.TopLeft,
.BackColor = Color.DarkGray,
.TickFrequency = 50,
.Maximum = 100,
.Minimum = -100,
.Value = 0,
.Orientation = Orientation.Horizontal}
Dim Label1 As New Label With {.Location = New Point(TrackBar1.Width + 20, TrackBar1.Top + 10),
.AutoSize = True,
.Text = "0",
.Font = New Font("Arial", 20, FontStyle.Bold)}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Controls.Add(PictureBox1)
Me.Controls.Add(Button1)
Me.Controls.Add(Button2)
Me.Controls.Add(TrackBar1)
Me.Controls.Add(Label1)
Label1.BringToFront()
Me.Size = New Size(520, 400)
AddHandler TrackBar1.ValueChanged, AddressOf TrackBar1_ValueChanged
AddHandler Button1.Click, AddressOf Button1_Click
AddHandler Button2.Click, AddressOf Button2_Click
AddHandler PictureBox1.Paint, AddressOf PictureBox1_Paint
End Sub
Public Sub PictureBox1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs)
If picLoad = True Then
AplicarSaturacion(True)
End If
End Sub
Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs)
Label1.Text = TrackBar1.Value.ToString()
PictureBox1.Refresh()
PictureBox1_Paint(1, Nothing)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs)
Dim ofd As New OpenFileDialog()
ofd.Title = "Guardar imagen"
ofd.Filter = "Jpeg Images(*.jpg)|*.jpg|Png Images(*.png)|*.png"
ofd.Filter += "|Bitmap Images(*.bmp)|*.bmp"
If ofd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
Dim streamReader As New StreamReader(ofd.FileName)
originalBitmap = DirectCast(Bitmap.FromStream(streamReader.BaseStream), Bitmap)
streamReader.Close()
previewBitmap = Image.FromFile(ofd.FileName)
PictureBox1.Image = previewBitmap
picLoad = True
AplicarSaturacion(True)
End If
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs)
AplicarSaturacion(False)
If resultBitmap IsNot Nothing Then
Dim sfd As New SaveFileDialog()
sfd.Title = "Abrir Imagen"
sfd.Filter = "Jpeg Images(*.jpg)|*.jpg|Png Images(*.png)|*.png"
sfd.Filter += "|Bitmap Images(*.bmp)|*.bmp"
If sfd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
Dim fileExtension As String = Path.GetExtension(sfd.FileName).ToUpper()
Dim imgFormat As ImageFormat = ImageFormat.Png
If fileExtension = "BMP" Then
imgFormat = ImageFormat.Bmp
ElseIf fileExtension = "JPG" Then
imgFormat = ImageFormat.Jpeg
End If
Dim streamWriter As New StreamWriter(sfd.FileName, False)
resultBitmap.Save(streamWriter.BaseStream, imgFormat)
streamWriter.Flush()
streamWriter.Close()
resultBitmap = Nothing
End If
End If
End Sub
Public Sub AplicarSaturacion(ByVal preview As Boolean)
Dim ca As COLORADJUSTMENT
ca.caSize = CType(Marshal.SizeOf(ca), Short)
Dim HDcPic As IntPtr = CType(PictureBox1.CreateGraphics.GetHdc, IntPtr) 'GetDC(PictureBox1.Handle)
SetStretchBltMode(HDcPic, HALFTONE)
GetColorAdjustment(HDcPic, ca)
ca.caColorfulness = TrackBar1.Value
SetColorAdjustment(HDcPic, ca)
StretchBlt(HDcPic, 0, 0, PictureBox1.Image.Width, PictureBox1.Image.Height, _
HDcPic, 0, 0, PictureBox1.Image.Width, PictureBox1.Image.Height, TernaryRasterOperations.SRCCOPY)
resultBitmap = PictureBox1.Image
End Sub
NativeMethods > GetColorAdjustment y SetColorAdjustment para VB.NET<Security.SuppressUnmanagedCodeSecurity>
Friend Class NativeMethods
Inherits Attribute
Private Sub New()
End Sub
<DllImport("gdi32.dll")> _
Public Shared Function BitBlt(hObject As IntPtr,
nXDest As Integer,
nYDest As Integer,
nWidth As Integer,
nHeight As Integer,
hObjSource As IntPtr,
nXSrc As Integer,
nYSrc As Integer,
dwRop As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("gdi32.dll")>
Shared Function SetColorAdjustment(hdc As IntPtr,
<MarshalAs(UnmanagedType.Struct)>
ByRef lpca As COLORADJUSTMENT) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("gdi32.dll")>
Shared Function GetColorAdjustment(ByVal hdc As IntPtr,
<MarshalAs(UnmanagedType.Struct)>
ByRef lpca As COLORADJUSTMENT) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll")>
Shared Function GetDC(ByVal hWnd As IntPtr) As IntPtr
End Function
<DllImport("gdi32.dll")> _
Public Shared Function StretchBlt(hdc As IntPtr, _
x As Integer,
y As Integer,
nHeight As Integer,
hSrcDC As Integer,
hObjSource As IntPtr, _
xSrc As Integer,
ySrc As Integer,
nSrcWidth As Integer,
nSrcHeight As Integer,
dwRop As TernaryRasterOperations) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("gdi32.dll")> _
Public Shared Function SetStretchBltMode(ByVal hObject As IntPtr,
ByVal nStretchMode As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<StructLayout(LayoutKind.Sequential)>
Public Structure COLORADJUSTMENT
Public caSize As Short
Public caFlags As Short
Public caIlluminantIndex As Short
Public caRedGamma As Short
Public caGreenGamma As Short
Public caBlueGamma As Short
Public caReferenceBlack As Short
Public caReferenceWhite As Short
Public caContrast As Short
Public caBrightness As Short
Public caColorfulness As Short
Public caRedGreenTint As Short
End Structure
Public Enum caIlluminantIndex
ILLUMINANT_DEVICE_DEFAULT = 0 'Device's default. Standard used by output devices.
ILLUMINANT_A = 1 'Tungsten lamp.
ILLUMINANT_B = 2 'Noon sunlight.
ILLUMINANT_C = 3 'NTSC daylight.
ILLUMINANT_D50 = 4 'Normal print.
ILLUMINANT_D55 = 5 'Bond paper print.
ILLUMINANT_D65 = 6 'Standard daylight. Standard for CRTs and pictures.
ILLUMINANT_D75 = 7 'Northern daylight.
ILLUMINANT_F2 = 8 'Cool white lamp.
ILLUMINANT_DAYLIGHT = ILLUMINANT_C 'Same as ILLUMINANT_C.
ILLUMINANT_FLUORESCENT = ILLUMINANT_F2 'Same as ILLUMINANT_F2.
ILLUMINANT_MAX_INDEX = ILLUMINANT_F2 'Same as ILLUMINANT_F2.
ILLUMINANT_NTSC = ILLUMINANT_C 'Same as ILLUMINANT_C.
ILLUMINANT_TUNGSTEN = ILLUMINANT_A 'Same as ILLUMINANT_A.
End Enum
Public Enum TernaryRasterOperations
SRCCOPY = &HCC0020 ' dest = source
SRCPAINT = &HEE0086 ' dest = source OR dest
SRCAND = &H8800C6 ' dest = source AND dest
SRCINVERT = &H660046 ' dest = source XOR dest
SRCERASE = &H440328 ' dest = source AND (NOT dest)
NOTSRCCOPY = &H330008 ' dest = (NOT source)
NOTSRCERASE = &H1100A6 ' dest = (NOT src) AND (NOT dest)
MERGECOPY = &HC000CA ' dest = (source AND pattern)
MERGEPAINT = &HBB0226 ' dest = (NOT source) OR dest
PATCOPY = &HF00021 ' dest = pattern
PATPAINT = &HFB0A09 ' dest = DPSnoo
PATINVERT = &H5A0049 ' dest = pattern XOR dest
DSTINVERT = &H550009 ' dest = (NOT dest)
BLACKNESS = &H42 ' dest = BLACK
WHITENESS = &HFF0062 ' dest = WHITE
End Enum
Public Const HALFTONE = 4
<DllImport("gdi32.dll")> _
Public Shared Function SetBkColor(hdc As IntPtr, crColor As Integer) As Integer
End Function
End Class
Se ve muy largo el código pero tenía que ponerlo para que puedan analizar.
Espero que me puedan ayudar. No puedo darles nada solo mi agradecimiento. gracias
He conseguido redibujar correctamente el PictureBox en la versión para API metiendo la saturación en el evento PAINT, pero sigo sin poder guardar los cambios
Añadí la configuración al código anterior.