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.
lo estás complicando todo demasiado.
en un rato despues de cenar editaré este mensaje para mostrarte una solución completa y funcional (sin ningún tipo de "retraso" o "bloqueo") e indicarte (algunos de) los fallos del código actual que tienes.
hasta ahora!
Cita de: Eleкtro en 16 Septiembre 2016, 21:01 PM
lo estás complicando todo demasiado.
en un rato despues de cenar editaré este mensaje para mostrarte una solución completa y funcional (sin ningún tipo de "retraso" o "bloqueo") e indicarte (algunos de) los fallos del código actual que tienes.
hasta ahora!
Cambie el COLORADJUSTMENT todo a Short. Porque hace una hora edité mi mensaje y cambié caBlueGamma, caRedGamma, caGreenGamma, a Integer por que ví que como Short BlueGamma no funcionaba, pero si hago esto no funcionan los demás ajustes. :-\
Intenté usar funciones que PictureBox tiene en C# que simplifican mucho todo pero no están disponibles en VB
El código del principio lo saqué de aquí, solo cambié la matriz para conseguir el efecto de saturación
https://onedrive.live.com/?authkey=%21AGLMrn20g0JGtX0&id=C9DBA2BC5A16373B%21115&cid=C9DBA2BC5A16373B (https://onedrive.live.com/?authkey=%21AGLMrn20g0JGtX0&id=C9DBA2BC5A16373B%21115&cid=C9DBA2BC5A16373B)
Son códigos de muestra, solo necesito saber que falla, gracias. No hace falta que modifique todo, no quisiera dar demasiado trabajo, pero vi en otras preguntas que se quejan que no ponen código.
el 80% del código es sólo para que lo puedan ejecutar, sin tener que crear nada.
Bueno, como te prometí, vamos al tema...
Desconozco cual es el código original que estarás usando, pero el código de ejemplo que has compartido tiene varios problemas, e imagino que los mismos problemas estarán replicados en el código original...
Por orden de importancia:
Cita de: FJDA en 16 Septiembre 2016, 16:45 PMestoy intentando saturar una imagen
...
el programa tarda mucho en procesar los cambios.
...
no va bien.
...
al usar un TrackBar el proceso de los cambios es demasiado lento.
El problema principal (y el más grave) por el cual se aprecia una elevada lentitud de respuesta al mover el TrackBar, es por que estás manipulando la imagen a tamaño real cada vez que mueves la posición del TrackBar, y eso causa un gran impacto negativo en el rendimiento de la aplicación.
Si tenemos un PictureBox donde quieres previsualizar la imagen, entonces lo primero que debemos hacer para evitar problemas de rendimiento es redimensaionar la imagen original al tamaño de ese PictureBox, eso lo puedes hacer "manualmente" creando un objeto Bitmap del tamaño deseando y dibujando la imagen, o simplemente llamando a la función
Image.GetThumbnailImage(), y entonces, manipular la imagen redimensionada.
Cita de: FJDA en 16 Septiembre 2016, 16:45 PMDim Grafico As Graphics
...
Grafico.DrawImage(...)
El segundo problema es que estás utilizando los parámetros por defecto de calidad de imagen al dibujar la imagen modificada y, al tratarse de una previsualización, logicamente deberiamos personalizar esos parámetros para crear una imagen de baja calidad, es decir, más rápida de procesar.
Los parámetros (o propiedades) que debes ajustar son las siguientes:
- Graphics.CompositingQuality = CompositingQuality.HighSpeed
- Graphics.InterpolationMode = InterpolationMode.NearestNeighbor
- Graphics.PixelOffsetMode = PixelOffsetMode.None
- Graphics.SmoothingMode = SmoothingMode.None
Cita de: FJDA en 16 Septiembre 2016, 16:45 PMDim PictureBox1 As New PictureBox With {...
.SizeMode = PictureBoxSizeMode.StretchImage,
...}
Si a todo lo anterior le sumamos que cada vez que asignas la imagen modificada a tamaño real, ésta debe ser vuelta a modificar para ser estrechada, esto logicamente disminuirá todavía más el rendimiento ...de hecho el estrechamiento de cualquier imagen es expensivo de por si.
La solución es sencilla y obvia, debemos restaurar el valor por defecto (
PictureBoxSizeMode.Normal) para no forzar otra redimensión adicional de la imagen, y en su lugar utilizar una imagen del tamaño del PictureBox, como ya expliqué más arriba.
El último problema (pero no menos importante), es que todo el procedimiento o algoritmo del código que has publicado se lleva a cabo de forma sincrónica, y esto supone que cada vez que mueves el TrackBar, el hilo de la interfáz de usuario debe esperar a que la imagen se procese y la modificación de imagen se genere para poder procesar el siguiente evento de "mover" del TrackBar, y esto se aprecia en forma de desagradables congelamientos de respuesta o "trompicones" al intentar mover el TrackBar demasiado rápido, puesto que se necesita mucho más tiempo para procesar todo lo que haces con esa imagen a tamaño real, que para mover el TrackBar.
Tanto en C#/VB.NET como en cualquier otro lenguaje, a mayor cantidad de datos que procesar (y una imagen cotidiana contiene muchos datos que procesar) necesitará mayor tiempo de procesado, y hay que esperar ese tiempo, es así de simple. Dicho de otro modo: estás saturando el hilo de la interfáz de usuario.
También cabe mencionar que no estás haciendo un buen uso, o mejor dicho directamente no haces ningún uso de la correcta administración del Garbage Collector para ayudarle a recolectar los recursos de los objetos que ya no sean necesarios por tu aplicación.
Debes asegurarte siempre, sin excepción, de liberar los objetos que sean deshechables llamando al método
Object.Dispose(), incluso con los objetos que se liberen automáticamente al liberar un Form, no pierdas la costumbre.
Esto no afecta en el rendimiento de la interfáz de usuario (al menos no en el código de ejemplo que has publicado), pero si que afectará al consumo de memoria, y mucho. Literalmente hablando hay una fuga muy grave de memoria en el código que has publicado, basta con cargar una imagen de varios megabytes (+/-10 mb) y mover la posición del trackbar un par de veces para ver como el espacio reservado va subiendo, y subiendo, y subiendo... y nunca baja, puesto que nunca liberas los recursos no-administrados (Win32) de las imágenes llamando al método
Dispose.
Cita de: FJDA en 16 Septiembre 2016, 21:06 PMIntenté usar funciones que PictureBox tiene en C# que simplifican mucho todo pero no están disponibles en VB
No se a que te estarás refiriendo pero eso no es así.
C# y VB.NET son dos lenguajes que trabajan bajo el mismo framework de Microsoft, por ende, ambos pueden utilizar todas las funciones que estén definidas en la librería de clases de .NET Framework. Ambos lenguajes son idénticos en esencia, excepto algunas peculiaridades muy concretas de cada lenguaje.
Para resumir brevemente todas las explicaciones que di en el post de arriba, lo que debes hacer para solucionar este tipo de problemas es:
1. Manipular una imagen en miniatura de la imagen a tamaño real.
2. Ajustar los parámetros de calidad de GDI+ al manipular la imagen, para reducir la calidad del resultado (solo en la previsualización, claro).
3. Utilizar técnicas de
multi-threading para evitar bloquear el thread de la UI con instrucciones o metodologías expensivas que resulten en congelamientos o "trompicones" al manejar la UI.
Y... en lo último que debes pensar al manipular gráficos en .NET es en recurrir a la API de Windows, los gráficos 2D/3D es una temática que requiere mucha experiencia profesional, no solo de gráficos en general, sino de los tipos de datos que maneja Windows (y la forma especial de utilizar cada uno y de liberar los recursos de cada uno), así que por muy cuidadoso que intentes ser siempre te vas a dejar alguna imperfección por el camino, y una imperfección utilizando código no administrado puede traernos serios problemas y ser muy dificil de localizar la raíz del problema, además, con la inmensa cantidad de miembros administrados que de por si ya expone la libreria de clases de .NET Framework para la manipulación de gráficos e imágenes, es que simplemente no es necesario usar la WinAPI.
He escrito el siguiente código para mostrarte como puedes corregir esos problemas, pero recuerda que esto también es solo un código de ejemplo (no me he esmerado como lo haría para un trabajo).
La matriz de color no la he cambiado, he dejado la misma que estabas usando tú.
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
Public NotInheritable Class Form1 : Inherits Form
Friend WithEvents PcbPreview As PictureBox
Friend WithEvents BtLoad As Button
Friend WithEvents BtSave As Button
Friend WithEvents TrckHue As TrackBar
Friend WithEvents LblHue As Label
Private srcImg As Image
Private thumbnail As Image
Public Sub New()
MyClass.InitializeComponent()
Me.Size = New Size(520, 400)
Me.DoubleBuffered = True
Me.PcbPreview = New PictureBox With {
.Location = New Point(10, 10),
.Size = New Size(400, 240),
.SizeMode = PictureBoxSizeMode.Normal,
.BackColor = Color.DarkGray
}
Me.BtLoad = New Button With {
.Location = New Point(Me.PcbPreview.Width + 20, 10),
.Text = "Load from..."
}
Me.BtSave = New Button With {
.Location = New Point(Me.PcbPreview.Width + 20, 40),
.Text = "Save to..."
}
Me.TrckHue = New TrackBar With {
.Location = New Point(10, Me.PcbPreview.Height + 20),
.AutoSize = True,
.Size = New Size(Me.PcbPreview.Width, 10),
.TickStyle = TickStyle.TopLeft,
.BackColor = Color.DarkGray,
.TickFrequency = 50,
.Maximum = 100,
.Minimum = -100,
.Value = 0,
.Orientation = Orientation.Horizontal
}
Me.LblHue = New Label With {
.Location = New Point(Me.TrckHue.Width + 20, Me.TrckHue.Top + 10),
.AutoSize = True,
.Text = "0",
.Font = New Font("Arial", 20, FontStyle.Bold)
}
Me.LblHue.BringToFront()
End Sub
Private Sub Form1_Shown(ByVal sender As Object, ByVal e As EventArgs) _
Handles Me.Shown
Me.SuspendLayout()
Me.Controls.AddRange({Me.PcbPreview, Me.BtLoad, Me.BtSave, Me.TrckHue, Me.LblHue})
Me.ResumeLayout()
End Sub
Private Sub BtLoad_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles BtLoad.Click
Using ofd As New OpenFileDialog()
ofd.Title = "Open image"
ofd.Filter = "Image files|*.jpg;*.png;*.bmp"
If (ofd.ShowDialog() = DialogResult.OK) Then
If (Me.srcImg IsNot Nothing) Then
Me.srcImg.Dispose()
End If
Me.srcImg = Image.FromFile(ofd.FileName)
If (Me.thumbnail IsNot Nothing) Then
Me.thumbnail.Dispose()
End If
Dim thumbSize As Size = Me.PcbPreview.Size
Me.thumbnail = Me.srcImg.GetThumbnailImage(thumbSize.Width, thumbSize.Heigth, Nothing, Nothing)
' Force TrackBar.OnValueChanged event to trigger.
Me.TrckHue.Value = 1
Me.TrckHue.Value = 0
End If
End Using ' ofd
End Sub
Private Sub BtSave_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles BtSave.Click
Using sfd As New SaveFileDialog()
sfd.Title = "Save image"
sfd.Filter = "Bitmap image|*.bmp|Jpeg image|*.jpg|PNG image|*.png"
sfd.AddExtension = True
If (sfd.ShowDialog() = DialogResult.OK) Then
Dim file As New FileInfo(sfd.FileName)
Dim ext As String = file.Extension.ToLower()
Dim format As ImageFormat
Select Case ext
Case ".png"
format = ImageFormat.Png
Case ".bmp"
format = ImageFormat.Bmp
Case Else ' jpg
format = ImageFormat.Jpeg
End Select
Dim factor As Single = (Me.TrckHue.Value + 100)
Using img As Image = Me.srcImg.SetImageSaturation(factor, highQuality:=True)
img.Save(file.FullName, format)
End Using ' img
End If
End Using ' sfd
End Sub
Private Sub TrckHue_ValueChanged(ByVal sender As Object, ByVal e As EventArgs) _
Handles TrckHue.ValueChanged
Dim trck As TrackBar = DirectCast(sender, TrackBar)
Dim value As Integer = trck.Value
Dim factor As Single = (trck.Value + 100)
Dim pcbUpdate As Action =
Sub()
Me.LblHue.Invoke(Sub()
Me.LblHue.Text = CStr(value)
Me.Update()
End Sub)
Dim lockImg As Image = Me.thumbnail
SyncLock lockImg
lockImg = lockImg.SetImageSaturation(factor, highQuality:=False)
Me.PcbPreview.Image = lockImg
End SyncLock
End Sub
Task.Factory.StartNew(pcbUpdate)
End Sub
End Class
Public Module ImageExtensions
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function SetImageSaturation(ByVal srcImg As Image, ByVal factor As Single,
ByVal highQuality As Boolean) As Image
Dim r As Single = (299 * (100 - factor) / 100000.0F)
Dim g As Single = (587 * (100 - factor) / 100000.0F)
Dim b As Single = (114 * (100 - factor) / 100000.0F)
factor = (factor / 100.0F)
Dim bmp As New Bitmap(srcImg.Width, srcImg.Height, srcImg.PixelFormat)
Dim matrix As Single()() = {
New Single() {(r + factor), r, r, 0.0F, 0.0F},
New Single() {g, (g + factor), g, 0.0F, 0.0F},
New Single() {b, b, (b + factor), 0.0F, 0.0F},
New Single() {0.0F, 0.0F, 0.0F, 1.0F, 0.0F},
New Single() {0.0F, 0.0F, 0.0F, 0.0F, 1.0F}
} ' Esta matriz de color la ideó el usuario @FJDA con el propósito de saturar una imagen.
Using ia As New ImageAttributes
ia.ClearColorMatrix()
ia.SetColorMatrix(New ColorMatrix(matrix), ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
ia.SetGamma(1.0F, ColorAdjustType.Bitmap)
Using gdi As Graphics = Graphics.FromImage(bmp)
With gdi
If highQuality Then
.CompositingQuality = CompositingQuality.HighQuality
.InterpolationMode = InterpolationMode.HighQualityBicubic
.PixelOffsetMode = PixelOffsetMode.HighQuality
.SmoothingMode = SmoothingMode.HighQuality
Else
.CompositingQuality = CompositingQuality.HighSpeed
.InterpolationMode = InterpolationMode.NearestNeighbor
.PixelOffsetMode = PixelOffsetMode.None
.SmoothingMode = SmoothingMode.None
End If
.DrawImage(srcImg, New Rectangle(0, 0, bmp.Width, bmp.Height),
0, 0, srcImg.Width, srcImg.Height,
GraphicsUnit.Pixel, ia)
End With
End Using ' gdi
End Using ' ia
Return bmp
End Function
End Module
Como puedes comprobar, al igual que en tu código de ejemplo, yo también utilizo un único método para manipular ambas imágenes, sin embargo lo hago de una forma muy distinta, en base a un factor booleano de calidad gráfica (parámetro
highQuality) que es lo que nos ayuda a aumentar el rendimiento produciendo una imagen de baja calidad al generar la imagen de previsualización, y una imagen de máxima calidad al guardar la imagen de tamaño real en el disco.
La extensión de método la extraí de mi API gratuita
ElektroKit y posteriormente la adapté a tus necesidades.
(http://i.imgur.com/tIffdpp.png)
- https://github.com/ElektroStudios/ElektroKit/blob/master/Solution/Elektro.Imaging/Extensions/Image/ImageCorrection.vb
En mi librería gratuita
ElektroKit puedes encontrar muchas otras utilidades interesantes que te podrían ayudar a manipular imágenes de forma general, y también para ayudarte en otras muchas diversas tareas de la programación cotidiana en .NET que nada tengan que ver con la manipulación de imágenes.
Si quieres conocer un poco más sobre
ElektroKit, lee este tema:
- ElektroKit Framework v2.0 | ( Complemento para el núcleo de .Net Framework ) (http://foro.elhacker.net/net/elektrokit_framework_v20_complemento_para_el_nucleo_de_net_framework-t444997.0.html)
Aquí te dejo una demostración visual para que se aprecie la mejora de la modificación que le hice al código que publicaste.
En la demo primero cargo y manipulo una imagen de un tigre, a una resolución de 5184x3456 px. y de 18 mb, y luego una imagen de un cachorro, a 1920x1080 px. y de 3 mb. El único tiempo de espera apreciable sería al cargar y/o guardar la imagen, puesto que para el resto como ya expliqué se manipula una imagen a tamaño redimensionado.
[youtube=640,360]https://www.youtube.com/watch?v=2VxoHrHWoxA[/youtube]
Espero que todo esto te haya servido de algo.
Saludos
Cita de: Eleкtro en 17 Septiembre 2016, 00:21 AM
Bueno, como te prometí, vamos al tema...
Desconozco cual es el código original que estarás usando, pero el código de ejemplo que has compartido tiene varios problemas, e imagino que los mismos problemas estarán replicados en el código original...
Por orden de importancia:
El problema principal (y el más grave) por el cual se aprecia una elevada lentitud de respuesta al mover el TrackBar, es por que estás manipulando la imagen a tamaño real cada vez que mueves la posición del TrackBar, y eso causa un gran impacto negativo en el rendimiento de la aplicación.
Si tenemos un PictureBox donde quieres previsualizar la imagen, entonces lo primero que debemos hacer para evitar problemas de rendimiento es redimensaionar la imagen original al tamaño de ese PictureBox, eso lo puedes hacer "manualmente" creando un objeto Bitmap del tamaño deseando y dibujando la imagen, o simplemente llamando a la función Image.GetThumbnailImage(), y entonces, manipular la imagen redimensionada.
El segundo problema es que estás utilizando los parámetros por defecto de calidad de imagen al dibujar la imagen modificada y, al tratarse de una previsualización, logicamente deberiamos personalizar esos parámetros para crear una imagen de baja calidad, es decir, más rápida de procesar.
Los parámetros (o propiedades) que debes ajustar son las siguientes:
- Graphics.CompositingQuality = CompositingQuality.HighSpeed
- Graphics.InterpolationMode = InterpolationMode.NearestNeighbor
- Graphics.PixelOffsetMode = PixelOffsetMode.None
- Graphics.SmoothingMode = SmoothingMode.None
Si a todo lo anterior le sumamos que cada vez que asignas la imagen modificada a tamaño real, ésta debe ser vuelta a modificar para ser estrechada, esto logicamente disminuirá todavía más el rendimiento ...de hecho el estrechamiento de cualquier imagen es expensivo de por si.
La solución es sencilla y obvia, debemos restaurar el valor por defecto (PictureBoxSizeMode.Normal) para no forzar otra redimensión adicional de la imagen, y en su lugar utilizar una imagen del tamaño del PictureBox, como ya expliqué más arriba.
El último problema (pero no menos importante), es que todo el procedimiento o algoritmo del código que has publicado se lleva a cabo de forma sincrónica, y esto supone que cada vez que mueves el TrackBar, el hilo de la interfáz de usuario debe esperar a que la imagen se procese y la modificación de imagen se genere para poder procesar el siguiente evento de "mover" del TrackBar, y esto se aprecia en forma de desagradables congelamientos de respuesta o "trompicones" al intentar mover el TrackBar demasiado rápido, puesto que se necesita mucho más tiempo para procesar todo lo que haces con esa imagen a tamaño real, que para mover el TrackBar.
Tanto en C#/VB.NET como en cualquier otro lenguaje, a mayor cantidad de datos que procesar (y una imagen cotidiana contiene muchos datos que procesar) necesitará mayor tiempo de procesado, y hay que esperar ese tiempo, es así de simple. Dicho de otro modo: estás saturando el hilo de la interfáz de usuario.
También cabe mencionar que no estás haciendo un buen uso, o mejor dicho directamente no haces ningún uso de la correcta administración del Garbage Collector para ayudarle a recolectar los recursos de los objetos que ya no sean necesarios por tu aplicación.
Debes asegurarte siempre, sin excepción, de liberar los objetos que sean deshechables llamando al método Object.Dispose(), incluso con los objetos que se liberen automáticamente al liberar un Form, no pierdas la costumbre.
Esto no afecta en el rendimiento de la interfáz de usuario (al menos no en el código de ejemplo que has publicado), pero si que afectará al consumo de memoria, y mucho. Literalmente hablando hay una fuga muy grave de memoria en el código que has publicado, basta con cargar una imagen de varios megabytes (+/-10 mb) y mover la posición del trackbar un par de veces para ver como el espacio reservado va subiendo, y subiendo, y subiendo... y nunca baja, puesto que nunca liberas los recursos no-administrados (Win32) de las imágenes llamando al método Dispose.
No se a que te estarás refiriendo pero eso no es así.
C# y VB.NET son dos lenguajes que trabajan bajo el mismo framework de Microsoft, por ende, ambos pueden utilizar todas las funciones que estén definidas en la librería de clases de .NET Framework. Ambos lenguajes son idénticos en esencia, excepto algunas peculiaridades muy concretas de cada lenguaje.
Vaya es usted una auténtico maestro. Ahora mismo no tengo tiempo de probar los cambios y la información que me facilita más tarde lo haré y le comentaré cualquier problema o circunstancia.
Ya pedí libros en otra pregunta pero no me respondió, nadie. Me interesa mucho sobre todo trabajar con imágenes pero es difícil encontrar. Ya imaginé que había que tratar la imagen adecuadamente para poder modificar de forma eficiente pero es difícil saber sin la información adecuada.
En cuanto a C# y VB, he comprobado que en C# PictureBox tiene filtros en sus propiedades y miembros que no veo que aparezcan en VB. A eso me refería.
hasta luego y gracias por su amabilidad.
No he podido evitar curiosear a falta de tiempo su modificación. Le agradecería cambie el nombramiento de esta función a
SetImageSaturation:
Public Function SetImageSaturation(ByVal srcImg As Image, ByVal factor As Single,
ByVal highQuality As Boolean) As Image
factor+= 100
Dim r As Single = (299 * (100 - factor) / 100000.0F)
Dim g As Single = (587 * (100 - factor) / 100000.0F)
Dim b As Single = (114 * (100 - factor) / 100000.0F)
factor = (factor / 100.0F)
Dim bmp As New Bitmap(srcImg.Width, srcImg.Height, srcImg.PixelFormat)
Dim matrix As Single()() = {
New Single() {(r + factor), r, r, 0.0F, 0.0F},
New Single() {g, (g + factor), g, 0.0F, 0.0F},
New Single() {b, b, (b + factor), 0.0F, 0.0F},
New Single() {0.0F, 0.0F, 0.0F, 1.0F, 0.0F},
New Single() {0.0F, 0.0F, 0.0F, 0.0F, 1.0F}
}
Esta matriz la he desarrollado yo y es única. Imagino habrán otras pero yo no encontré ninguna buscando que funcionara igual que en API. Existen otras configuraciones, para brillo, escala de grises, gamma, etc. Es importante recalcar que esta matriz es exclusivamente para Saturación. Si lo nombra
SetImageCorrection no es posible saber a simple vista para que se ha implementado dicha función.
Es posible añadir 'If factor > 0 then', para multiplicar por diez el efecto cuando sea mayor que cero para conseguir una mayor saturación de color.
hasta luego y gracias de nuevo
Cita de: FJDA en 17 Septiembre 2016, 11:03 AMLe agradecería cambie el nombramiento de esta función a SetImageSaturation:
Como dije en el post, solo es un código de ejemplo, un ejemplo que se debe adaptar a las necesidades que tengas, de todas formas aceptando tu petición le cambié el nombre a la función y también especifiqué que la matriz de color es una idea original tuya.
Saludos!
Cita de: Eleкtro en 17 Septiembre 2016, 13:51 PM
Como dije en el post, solo es un código de ejemplo, un ejemplo que se debe adaptar a las necesidades que tengas, de todas formas aceptando tu petición le cambié el nombre a la función y también especifiqué que la matriz de color es una idea original tuya.
Saludos!
Esta tarde he podido ver más tranquilamente su código y probarlo. He visto un problema que miraré mas tarde no quiero darle trabajo pues ya a aportado una respuesta excelente. Lo único que no he visto correcto es sacar la operación
factor+= 100 de la función pues forma parte de la misma. De todos modos he conseguido sintentizar las operaciones, y ya no hace falta el 'factor+= 100', quedando así:
Dim r As Single = CSng(factor * (299 * -10 ^ -5))
Dim g As Single = CSng(factor * (587 * -10 ^ -5))
Dim b As Single = CSng(factor * (114 * -10 ^ -5))
factor = (factor / 100.0F) + 1
Dim matrix As Single()() = {
New Single() {(r + factor), r, r, 0.0F, 0.0F},
New Single() {g, (g + factor), g, 0.0F, 0.0F},
New Single() {b, b, (b + factor), 0.0F, 0.0F},
New Single() {0.0F, 0.0F, 0.0F, 1.0F, 0.0F},
New Single() {0.0F, 0.0F, 0.0F, 0.0F, 1.0F}
}
o también:
Dim r As Single = CSng(factor * -(299 * 10 ^ -5))
Dim g As Single = CSng(factor * -(587 * 10 ^ -5))
Dim b As Single = CSng(factor * -(114 * 10 ^ -5))
...
Dim r As Single = CSng(factor * -(299 / 100000.0F))
Dim g As Single = CSng(factor * -(587 / 100000.0F))
Dim b As Single = CSng(factor * -(114 / 100000.0F))
Conseguí eliminar otra ecuación más
factor = (factor / 100.0F)
''' <summary>
''' Función para saturación de color //por @Elektro y @FJDA
''' </summary>
''' <param name="srcImg">Objeto de tipo System.Drawing.Image</param>
''' <param name="Factor">Valor de saturación de color; de -100 a 100</param>
''' <param name="highQuality">Composición de calidad</param>
''' <returns></returns>
''' <remarks></remarks>
Public Function SetImageSaturation(ByVal srcImg As Image, ByVal Factor As Single, ByVal highQuality As Boolean) As Image
Dim F As Single = Factor
Dim R As Single = CSng(F * -(299 / 100000))
Dim G As Single = CSng(F * -(587 / 100000))
Dim B As Single = CSng(F * -(114 / 100000))
Dim bmp As New Bitmap(srcImg.Width, srcImg.Height, srcImg.PixelFormat)
Dim matrix As Single()() = {
New Single() {CSng((0.00701 * F) + 1), R, R, 0.0F, 0.0F},
New Single() {G, CSng((0.00413 * F) + 1), G, 0.0F, 0.0F},
New Single() {B, B, CSng((0.00886 * F) + 1), 0.0F, 0.0F},
New Single() {0.0F, 0.0F, 0.0F, 1.0F, 0.0F},
New Single() {0.0F, 0.0F, 0.0F, 0.0F, 1.0F}
}
Using ia As New ImageAttributes
ia.ClearColorMatrix()
ia.SetColorMatrix(New ColorMatrix(matrix), ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
ia.SetGamma(1.0F, ColorAdjustType.Bitmap)
Using gdi As Graphics = Graphics.FromImage(bmp)
With gdi
If highQuality Then
.CompositingQuality = CompositingQuality.HighQuality
.InterpolationMode = InterpolationMode.HighQualityBicubic
.PixelOffsetMode = PixelOffsetMode.HighQuality
.SmoothingMode = SmoothingMode.HighQuality
Else
.CompositingQuality = CompositingQuality.HighSpeed
.InterpolationMode = InterpolationMode.NearestNeighbor
.PixelOffsetMode = PixelOffsetMode.None
.SmoothingMode = SmoothingMode.None
End If
.DrawImage(srcImg, New Rectangle(0, 0, bmp.Width, bmp.Height),
0, 0, srcImg.Width, srcImg.Height,
GraphicsUnit.Pixel, ia)
End With
End Using ' gdi
End Using ' ia
Return bmp
End Function
Buenas de nuevo.
@Elektro. he estado analizando su código y lo he ejecutado. Como comenté había algún problema y es que a pesar de que introdujo el trabajo en modo asincrónico seguía habiendo una ligera lentitud para mostrar el cambio si se movía muy rápido el TrackBar, especialmente se percibía al mostrar el valor de saturación en el Label.
Finalmente hemos conseguido eficiencia cien por cien, gracias a usted. ¡¡Buen trabajo!!
Tan solo tuve que realizar pequeños cambios. sin importancia. También mediante matemáticas eliminar ecuaciones innecesarias.
Aquí muestro los cambios y he reducido el código para que sea más fácil de entender y sea más corto.
*Deben crear un PictureBox, un TrackBar1 y un Label* Coloquen el directorio para cargar la imagen en:
.ImagenPrevia = Image.FromFile(".\Imagen.jpg")
Imports System.Runtime.CompilerServices
Imports System.ComponentModel
Imports System.Drawing.Imaging
Imports System.Drawing.Drawing2D
Public NotInheritable Class Form1
Inherits Form
Private ImagenPrevia As Image
Private ImagenDeTrabajo As Image
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
With Me
'Propiedades para el TrackBar1
With .TrackBar1
.TickFrequency = 10
.TickStyle = TickStyle.TopLeft
.Maximum = 100
.Minimum = -100
.Value = 0
End With
'Propiedades para el PictureBox1
With .PictureBox1
.BackColor = Color.DarkGray
.SizeMode = PictureBoxSizeMode.Normal
End With
'Carga la imagen desde un directorio
.ImagenPrevia = Image.FromFile(".\Imagen.jpg")
'Tamaño de la imagen para trabajar
Dim TamañoImagenDeTrabajo As Size = .PictureBox1.Size
'Obtiene imagen previa para trabajar
.ImagenDeTrabajo = .ImagenPrevia.GetThumbnailImage(TamañoImagenDeTrabajo.Width, _
TamañoImagenDeTrabajo.Height, _
Nothing, Nothing)
.PictureBox1.Image = .ImagenDeTrabajo
End With
End Sub
Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs) Handles TrackBar1.ValueChanged
'Ejecutar de forma asincrónica
Task.Factory.StartNew(Sub()
Dim lockImg As Image = ImagenDeTrabajo
SyncLock lockImg 'Bloqueo de instrucción antes de procesar
Me.Invoke(Sub()
Dim vSat As Single = TrackBar1.Value 'Obtiene el valor de saturación de color
Label1.Text = CStr(vSat) 'Muestra el valor de saturación de color
lockImg = lockImg.SetImageSaturation(sngSatVal:=vSat,
highQuality:=False)
Me.PictureBox1.Image = lockImg
End Sub) 'Me.Invoke
End SyncLock
End Sub) 'Task
End Sub
End Class
Public Module ImageExtensions
''' <summary>
''' Función para saturación de color //por @Elektro y @FJDA
''' </summary>
''' <param name="srcImg">Objeto de tipo System.Drawing.Image</param>
''' <param name="sngSatVal">Valor de saturación de -100 a 100</param>
''' <param name="highQuality">Composición de calidad</param>
''' <returns></returns>
''' <remarks></remarks>
<DebuggerStepThrough> <Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function SetImageSaturation(ByVal srcImg As Image, ByVal sngSatVal As Single, ByVal highQuality As Boolean) As Image
Dim R As Single = CSng(sngSatVal * -(299 / 100000))
Dim G As Single = CSng(sngSatVal * -(587 / 100000))
Dim B As Single = CSng(sngSatVal * -(114 / 100000))
Dim FactR As Single = CSng(((0.00701F * sngSatVal) + 1))
Dim FactG As Single = CSng(((0.00413F * sngSatVal) + 1))
Dim FactB As Single = CSng(((0.00886F * sngSatVal) + 1))
Dim matrix As Single()() = {
New Single() {FactR, R, R, 0, 0},
New Single() {G, FactG, G, 0, 0},
New Single() {B, B, FactB, 0, 0},
New Single() {0, 0, 0, 1, 0},
New Single() {0, 0, 0, 0, 1}
}
Dim bmp As New Bitmap(srcImg.Width, srcImg.Height, srcImg.PixelFormat)
Using ia As New ImageAttributes
ia.ClearColorMatrix()
ia.SetColorMatrix(New ColorMatrix(matrix), ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
ia.SetGamma(1.0F, ColorAdjustType.Bitmap)
Using gdi As Graphics = Graphics.FromImage(bmp)
With gdi
If highQuality Then
.CompositingQuality = CompositingQuality.HighQuality
.InterpolationMode = InterpolationMode.HighQualityBicubic
.PixelOffsetMode = PixelOffsetMode.HighQuality
.SmoothingMode = SmoothingMode.HighQuality
Else
.CompositingQuality = CompositingQuality.HighSpeed
.InterpolationMode = InterpolationMode.NearestNeighbor
.PixelOffsetMode = PixelOffsetMode.None
.SmoothingMode = SmoothingMode.None
End If
.DrawImage(srcImg, New Rectangle(0, 0, bmp.Width, bmp.Height),
0, 0, srcImg.Width, srcImg.Height,
GraphicsUnit.Pixel, ia)
End With
End Using ' gdi
End Using ' ia
Return bmp
End Function
End Module
Hoy he modificado un poco la matriz para que sea más ordenado y legible. Ya que el uso del punto fijo 'F' junto con la variable single F (mal puesta por mi parte), lo hacía confuso. No veo necesario su uso en valores 0 o 1, así que lo he suprimido en estos valores. También he sacado las ecuaciones de la matriz pasándolas a FactR, FactG y FactB, nombrandolos Fact(Letra color) porque son los verdaderos
factores de rojo, verde y azul.
Dim R As Single = CSng(sngSatVal * -(299 / 100000))
Dim G As Single = CSng(sngSatVal * -(587 / 100000))
Dim B As Single = CSng(sngSatVal * -(114 / 100000))
Dim FactR As Single = CSng(((0.00701F * sngSatVal) + 1))
Dim FactG As Single = CSng(((0.00413F * sngSatVal) + 1))
Dim FactB As Single = CSng(((0.00886F * sngSatVal) + 1))
Dim matrix As Single()() = {
New Single() {FactR, R, R, 0, 0},
New Single() {G, FactG, G, 0, 0},
New Single() {B, B, FactB, 0, 0},
New Single() {0, 0, 0, 1, 0},
New Single() {0, 0, 0, 0, 1}
}
y
recuerden que para guardar la imagen deben usar la imagen previa (la original en tamaño y calidad)
y modificar la saturación en alta calidad en el momento de guardar. Como bien ha hecho @Elektro, para realizar los cambios previos se trabaja con una imagen de bajo tamaño y calidad para acelerar el trabajo, pero a la hora de guardar hay que trabajar con la imagen original en tamaño y calidad que hemos cargado.
Using img As Image = SetImageSaturation(ImagenPrevia, TrackBar1.Value, highQuality:=True)
img.Save(".\Cambios.jpg", ImageFormat.Jpeg)
End Using
Aquí dejo finalizado el tema, creo que se ha conseguido y espero sea de utilidad para otros programadores.
Agradecimientos de nuevo a @Elektro por su gran ayuda.
Hasta luego.