Título: Duda con PictureBox y Saturación de Color
Publicado por: FJDA en 16 Septiembre 2016, 16:45 pm
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.
Título: Re: Duda con PictureBox y Saturación de Color
Publicado por: 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!
Título: Re: Duda con PictureBox y Saturación de Color
Publicado por: FJDA en 16 Septiembre 2016, 21:06 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.
Título: Re: Duda con PictureBox y Saturación de Color
Publicado por: 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:
estoy 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.
Dim 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
Dim 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.
Intenté 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.
Título: Re: Duda con PictureBox y Saturación de Color
Publicado por: Eleкtro en 17 Septiembre 2016, 00:24 am
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. 2VxoHrHWoxA Espero que todo esto te haya servido de algo. Saludos
Título: Re: Duda con PictureBox y Saturación de Color
Publicado por: FJDA en 17 Septiembre 2016, 11:03 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
Título: Re: Duda con PictureBox y Saturación de Color
Publicado por: Eleкtro en 17 Septiembre 2016, 13:51 pm
Le 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!
Título: Re: Duda con PictureBox y Saturación de Color
Publicado por: FJDA en 17 Septiembre 2016, 20:35 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.
|