elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Recuerda que debes registrarte en el foro para poder participar (preguntar y responder)


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  Duda con PictureBox y Saturación de Color
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: Duda con PictureBox y Saturación de Color  (Leído 5,535 veces)
FJDA


Desconectado Desconectado

Mensajes: 321


Ver Perfil
Duda con PictureBox y Saturación de Color
« 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.

Código:
    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í:
Código
  1. Imports System.IO
  2. Imports System.Drawing.Imaging
  3.  
  4.  
  5.  
  6. Public Class Form1
  7.    Private originalBitmap As Bitmap = Nothing
  8.    Private previewBitmap As Bitmap = Nothing
  9.    Private resultBitmap As Bitmap = Nothing
  10.    Dim PictureBox1 As New PictureBox With {.Location = New Point(10, 10),
  11.                                            .Size = New Size(400, 240),
  12.                                            .SizeMode = PictureBoxSizeMode.StretchImage,
  13.                                            .BackColor = Color.DarkGray}
  14.    Dim Button1 As New Button With {.Location = New Point(PictureBox1.Width + 20, 10), .Text = "Cargar"}
  15.    Dim Button2 As New Button With {.Location = New Point(PictureBox1.Width + 20, 40), .Text = "Guardar"}
  16.    Dim TrackBar1 As New TrackBar With {.Location = New Point(10, PictureBox1.Height + 20),
  17.                                        .AutoSize = True,
  18.                                        .Size = New Size(PictureBox1.Width, 10),
  19.                                        .TickStyle = TickStyle.TopLeft,
  20.                                        .BackColor = Color.DarkGray,
  21.                                        .TickFrequency = 50,
  22.                                        .Maximum = 100,
  23.                                        .Minimum = -100,
  24.                                        .Value = 0,
  25.                                        .Orientation = Orientation.Horizontal}
  26.    Dim Label1 As New Label With {.Location = New Point(TrackBar1.Width + 20, TrackBar1.Top + 10),
  27.                                  .AutoSize = True,
  28.                                  .Text = "0",
  29.                                 .Font = New Font("Arial", 20, FontStyle.Bold)}
  30.  
  31.    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
  32.        Me.Controls.Add(PictureBox1)
  33.        Me.Controls.Add(Button1)
  34.        Me.Controls.Add(Button2)
  35.        Me.Controls.Add(TrackBar1)
  36.        Me.Controls.Add(Label1)
  37.  
  38.        Label1.BringToFront()
  39.        Me.Size = New Size(520, 400)
  40.  
  41.        AddHandler TrackBar1.ValueChanged, AddressOf TrackBar1_ValueChanged
  42.        AddHandler Button1.Click, AddressOf Button1_Click
  43.        AddHandler Button2.Click, AddressOf Button2_Click
  44.    End Sub
  45.  
  46.    Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs)
  47.  
  48.        Label1.Text = TrackBar1.Value.ToString()
  49.        AplicarSaturacion(True)
  50.    End Sub
  51.  
  52.  
  53.    Private Sub Button1_Click(sender As Object, e As EventArgs)
  54.        Dim ofd As New OpenFileDialog()
  55.        ofd.Title = "Abrir imagen"
  56.        ofd.Filter = "Jpeg Images(*.jpg)|*.jpg|Png Images(*.png)|*.png"
  57.        ofd.Filter += "|Bitmap Images(*.bmp)|*.bmp"
  58.  
  59.        If ofd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
  60.            Dim streamReader As New StreamReader(ofd.FileName)
  61.            originalBitmap = DirectCast(Bitmap.FromStream(streamReader.BaseStream), Bitmap)
  62.            streamReader.Close()
  63.            previewBitmap = Image.FromFile(ofd.FileName)
  64.            PictureBox1.Image = previewBitmap
  65.  
  66.            AplicarSaturacion(True)
  67.        End If
  68.    End Sub
  69.  
  70.    Private Sub Button2_Click(sender As Object, e As EventArgs)
  71.        AplicarSaturacion(False)
  72.  
  73.  
  74.        If resultBitmap IsNot Nothing Then
  75.            Dim sfd As New SaveFileDialog()
  76.            sfd.Title = "Guardar imagen"
  77.            sfd.Filter = "Jpeg Images(*.jpg)|*.jpg|Png Images(*.png)|*.png"
  78.            sfd.Filter += "|Bitmap Images(*.bmp)|*.bmp"
  79.  
  80.            If sfd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
  81.                Dim fileExtension As String = Path.GetExtension(sfd.FileName).ToUpper()
  82.                Dim imgFormat As ImageFormat = ImageFormat.Png
  83.  
  84.                If fileExtension = "BMP" Then
  85.                    imgFormat = ImageFormat.Bmp
  86.                ElseIf fileExtension = "JPG" Then
  87.                    imgFormat = ImageFormat.Jpeg
  88.                End If
  89.  
  90.                Dim streamWriter As New StreamWriter(sfd.FileName, False)
  91.                resultBitmap.Save(streamWriter.BaseStream, imgFormat)
  92.                streamWriter.Flush()
  93.                streamWriter.Close()
  94.  
  95.                resultBitmap = Nothing
  96.            End If
  97.        End If
  98.    End Sub
  99.    Public Sub AplicarSaturacion(Byval  preview As Boolean)
  100.        If previewBitmap Is Nothing Then
  101.            Return
  102.        End If
  103.        Dim Imagen As Image = previewBitmap
  104.        Dim Pic As PictureBox = PictureBox1
  105.        Dim Grafico As Graphics
  106.        Dim Rectangulo As Rectangle
  107.        Pic.Image = New Bitmap(Pic.Width, Pic.Height, PixelFormat.Format32bppArgb)
  108.        Grafico = Graphics.FromImage(Pic.Image)
  109.        Rectangulo = New Rectangle(0, 0, Pic.Width, Pic.Height)
  110.        Grafico.DrawImage(Imagen, Rectangulo)
  111.        Dim S As Single
  112.  
  113.        S = (TrackBar1.Value) + 100
  114.  
  115.        Dim Rojo As Single = CSng(299 * (100 - S) / 100000)
  116.        Dim Verde As Single = CSng(587 * (100 - S) / 100000)
  117.        Dim Azul As Single = CSng(114 * (100 - S) / 100000)
  118.  
  119.        S = CSng(S / 100)
  120.        Dim colorMatrixVal As Single()() = {
  121.        New Single() {Rojo + S, Rojo, Rojo, 0, 0}, _
  122.        New Single() {Verde, Verde + S, Verde, 0, 0}, _
  123.        New Single() {Azul, Azul, Azul + S, 0, 0}, _
  124.        New Single() {0, 0, 0, 1, 0}, _
  125.        New Single() {0, 0, 0, 0, 1}}
  126.  
  127.        Dim colorMatrix As New ColorMatrix(colorMatrixVal)
  128.        Dim ImagenAtributos As New ImageAttributes
  129.  
  130.        ImagenAtributos.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
  131.        Grafico.DrawImage(Imagen, Rectangulo, 0, 0, Imagen.Width, Imagen.Height, GraphicsUnit.Pixel, ImagenAtributos)
  132.        Pic.Refresh()
  133.        resultBitmap = Pic.Image
  134.    End Sub
  135. End Class
  136.  

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:
Código
  1. Imports System.IO
  2. Imports System.Drawing.Imaging
  3. Imports System.Runtime.InteropServices
  4. Imports WindowsApplication1.NativeMethods
  5.  
  6.  
  7.  
  8. Public Class Form1
  9.    Private originalBitmap As Bitmap = Nothing
  10.    Private previewBitmap As Bitmap = Nothing
  11.    Private resultBitmap As Bitmap = Nothing
  12.    Dim picLoad As Boolean
  13.    Dim PictureBox1 As New PictureBox With {.Location = New Point(10, 10),
  14.                                            .Size = New Size(400, 240),
  15.                                            .SizeMode = PictureBoxSizeMode.StretchImage,
  16.                                            .BackColor = Color.DarkGray}
  17.    Dim Button1 As New Button With {.Location = New Point(PictureBox1.Width + 20, 10), .Text = "Cargar"}
  18.    Dim Button2 As New Button With {.Location = New Point(PictureBox1.Width + 20, 40), .Text = "Guardar"}
  19.    Dim TrackBar1 As New TrackBar With {.Location = New Point(10, PictureBox1.Height + 20),
  20.                                        .AutoSize = True,
  21.                                        .Size = New Size(PictureBox1.Width, 10),
  22.                                        .TickStyle = TickStyle.TopLeft,
  23.                                        .BackColor = Color.DarkGray,
  24.                                        .TickFrequency = 50,
  25.                                        .Maximum = 100,
  26.                                        .Minimum = -100,
  27.                                        .Value = 0,
  28.                                        .Orientation = Orientation.Horizontal}
  29.    Dim Label1 As New Label With {.Location = New Point(TrackBar1.Width + 20, TrackBar1.Top + 10),
  30.                                  .AutoSize = True,
  31.                                  .Text = "0",
  32.                                 .Font = New Font("Arial", 20, FontStyle.Bold)}
  33.  
  34.    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
  35.        Me.Controls.Add(PictureBox1)
  36.        Me.Controls.Add(Button1)
  37.        Me.Controls.Add(Button2)
  38.        Me.Controls.Add(TrackBar1)
  39.        Me.Controls.Add(Label1)
  40.  
  41.        Label1.BringToFront()
  42.        Me.Size = New Size(520, 400)
  43.  
  44.        AddHandler TrackBar1.ValueChanged, AddressOf TrackBar1_ValueChanged
  45.        AddHandler Button1.Click, AddressOf Button1_Click
  46.        AddHandler Button2.Click, AddressOf Button2_Click
  47.        AddHandler PictureBox1.Paint, AddressOf PictureBox1_Paint
  48.    End Sub
  49.    Public Sub PictureBox1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs)
  50.        If picLoad = True Then
  51.            AplicarSaturacion(True)
  52.        End If
  53.  
  54.    End Sub
  55.    Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs)
  56.        Label1.Text = TrackBar1.Value.ToString()
  57.        PictureBox1.Refresh()
  58.        PictureBox1_Paint(1, Nothing)
  59.    End Sub
  60.  
  61.  
  62.    Private Sub Button1_Click(sender As Object, e As EventArgs)
  63.        Dim ofd As New OpenFileDialog()
  64.        ofd.Title = "Guardar imagen"
  65.        ofd.Filter = "Jpeg Images(*.jpg)|*.jpg|Png Images(*.png)|*.png"
  66.        ofd.Filter += "|Bitmap Images(*.bmp)|*.bmp"
  67.  
  68.        If ofd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
  69.            Dim streamReader As New StreamReader(ofd.FileName)
  70.            originalBitmap = DirectCast(Bitmap.FromStream(streamReader.BaseStream), Bitmap)
  71.            streamReader.Close()
  72.            previewBitmap = Image.FromFile(ofd.FileName)
  73.            PictureBox1.Image = previewBitmap
  74.            picLoad = True
  75.            AplicarSaturacion(True)
  76.        End If
  77.    End Sub
  78.  
  79.    Private Sub Button2_Click(sender As Object, e As EventArgs)
  80.        AplicarSaturacion(False)
  81.  
  82.  
  83.        If resultBitmap IsNot Nothing Then
  84.            Dim sfd As New SaveFileDialog()
  85.            sfd.Title = "Abrir Imagen"
  86.            sfd.Filter = "Jpeg Images(*.jpg)|*.jpg|Png Images(*.png)|*.png"
  87.            sfd.Filter += "|Bitmap Images(*.bmp)|*.bmp"
  88.  
  89.            If sfd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
  90.                Dim fileExtension As String = Path.GetExtension(sfd.FileName).ToUpper()
  91.                Dim imgFormat As ImageFormat = ImageFormat.Png
  92.  
  93.                If fileExtension = "BMP" Then
  94.                    imgFormat = ImageFormat.Bmp
  95.                ElseIf fileExtension = "JPG" Then
  96.                    imgFormat = ImageFormat.Jpeg
  97.                End If
  98.  
  99.                Dim streamWriter As New StreamWriter(sfd.FileName, False)
  100.                resultBitmap.Save(streamWriter.BaseStream, imgFormat)
  101.                streamWriter.Flush()
  102.                streamWriter.Close()
  103.  
  104.                resultBitmap = Nothing
  105.            End If
  106.        End If
  107.    End Sub
  108.    Public Sub AplicarSaturacion(ByVal preview As Boolean)
  109.  
  110.  
  111.        Dim ca As COLORADJUSTMENT
  112.        ca.caSize = CType(Marshal.SizeOf(ca), Short)
  113.        Dim HDcPic As IntPtr = CType(PictureBox1.CreateGraphics.GetHdc, IntPtr) 'GetDC(PictureBox1.Handle)
  114.  
  115.  
  116.        SetStretchBltMode(HDcPic, HALFTONE)
  117.        GetColorAdjustment(HDcPic, ca)
  118.        ca.caColorfulness = TrackBar1.Value
  119.        SetColorAdjustment(HDcPic, ca)
  120.  
  121.        StretchBlt(HDcPic, 0, 0, PictureBox1.Image.Width, PictureBox1.Image.Height, _
  122.        HDcPic, 0, 0, PictureBox1.Image.Width, PictureBox1.Image.Height, TernaryRasterOperations.SRCCOPY)
  123.  
  124.        resultBitmap = PictureBox1.Image
  125.  
  126.    End Sub
  127.  
  128.  


NativeMethods > GetColorAdjustment y SetColorAdjustment  para VB.NET

Código
  1. <Security.SuppressUnmanagedCodeSecurity>
  2. Friend Class NativeMethods
  3.    Inherits Attribute
  4.    Private Sub New()
  5.    End Sub
  6.    <DllImport("gdi32.dll")> _
  7.    Public Shared Function BitBlt(hObject As IntPtr,
  8.                                  nXDest As Integer,
  9.                                  nYDest As Integer,
  10.                                  nWidth As Integer,
  11.                                  nHeight As Integer,
  12.                                  hObjSource As IntPtr,
  13.                                  nXSrc As Integer,
  14.                                  nYSrc As Integer,
  15.                                  dwRop As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
  16.    End Function
  17.    <DllImport("gdi32.dll")>
  18.    Shared Function SetColorAdjustment(hdc As IntPtr,
  19.                                       <MarshalAs(UnmanagedType.Struct)>
  20.                                       ByRef lpca As COLORADJUSTMENT) As <MarshalAs(UnmanagedType.Bool)> Boolean
  21.    End Function
  22.  
  23.    <DllImport("gdi32.dll")>
  24.    Shared Function GetColorAdjustment(ByVal hdc As IntPtr,
  25.                                       <MarshalAs(UnmanagedType.Struct)>
  26.                                       ByRef lpca As COLORADJUSTMENT) As <MarshalAs(UnmanagedType.Bool)> Boolean
  27.    End Function
  28.  
  29.    <DllImport("user32.dll")>
  30.    Shared Function GetDC(ByVal hWnd As IntPtr) As IntPtr
  31.    End Function
  32.  
  33.    <DllImport("gdi32.dll")> _
  34.    Public Shared Function StretchBlt(hdc As IntPtr, _
  35.                                      x As Integer,
  36.                                     y As Integer,
  37.                                     nHeight As Integer,
  38.                                     hSrcDC As Integer,
  39.                                     hObjSource As IntPtr, _
  40.                                    xSrc As Integer,
  41.                                    ySrc As Integer,
  42.                                    nSrcWidth As Integer,
  43.                                    nSrcHeight As Integer,
  44.                                    dwRop As TernaryRasterOperations) As <MarshalAs(UnmanagedType.Bool)> Boolean
  45.    End Function
  46.  
  47.    <DllImport("gdi32.dll")> _
  48.    Public Shared Function SetStretchBltMode(ByVal hObject As IntPtr,
  49.                                             ByVal nStretchMode As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
  50.    End Function
  51.  
  52.    <StructLayout(LayoutKind.Sequential)>
  53.    Public Structure COLORADJUSTMENT
  54.        Public caSize As Short
  55.        Public caFlags As Short
  56.        Public caIlluminantIndex As Short
  57.        Public caRedGamma As Short
  58.        Public caGreenGamma As Short
  59.        Public caBlueGamma As Short
  60.        Public caReferenceBlack As Short
  61.        Public caReferenceWhite As Short
  62.        Public caContrast As Short
  63.        Public caBrightness As Short
  64.        Public caColorfulness As Short
  65.        Public caRedGreenTint As Short
  66.    End Structure
  67.  
  68.   Public Enum caIlluminantIndex
  69.        ILLUMINANT_DEVICE_DEFAULT = 0 'Device's default. Standard used by output devices.
  70.        ILLUMINANT_A = 1 'Tungsten lamp.
  71.        ILLUMINANT_B = 2 'Noon sunlight.
  72.        ILLUMINANT_C = 3 'NTSC daylight.
  73.        ILLUMINANT_D50 = 4 'Normal print.
  74.        ILLUMINANT_D55 = 5 'Bond paper print.
  75.        ILLUMINANT_D65 = 6 'Standard daylight. Standard for CRTs and pictures.
  76.        ILLUMINANT_D75 = 7 'Northern daylight.
  77.        ILLUMINANT_F2 = 8 'Cool white lamp.
  78.        ILLUMINANT_DAYLIGHT = ILLUMINANT_C 'Same as ILLUMINANT_C.
  79.        ILLUMINANT_FLUORESCENT = ILLUMINANT_F2 'Same as ILLUMINANT_F2.
  80.        ILLUMINANT_MAX_INDEX = ILLUMINANT_F2 'Same as ILLUMINANT_F2.
  81.        ILLUMINANT_NTSC = ILLUMINANT_C 'Same as ILLUMINANT_C.
  82.        ILLUMINANT_TUNGSTEN = ILLUMINANT_A 'Same as ILLUMINANT_A.
  83.    End Enum
  84.  
  85.  
  86.    Public Enum TernaryRasterOperations
  87.        SRCCOPY = &HCC0020 ' dest = source
  88.        SRCPAINT = &HEE0086 ' dest = source OR dest
  89.        SRCAND = &H8800C6 ' dest = source AND dest
  90.        SRCINVERT = &H660046 ' dest = source XOR dest
  91.        SRCERASE = &H440328 ' dest = source AND (NOT dest)
  92.        NOTSRCCOPY = &H330008 ' dest = (NOT source)
  93.        NOTSRCERASE = &H1100A6 ' dest = (NOT src) AND (NOT dest)
  94.        MERGECOPY = &HC000CA ' dest = (source AND pattern)
  95.        MERGEPAINT = &HBB0226 ' dest = (NOT source) OR dest
  96.        PATCOPY = &HF00021 ' dest = pattern
  97.        PATPAINT = &HFB0A09 ' dest = DPSnoo
  98.        PATINVERT = &H5A0049 ' dest = pattern XOR dest
  99.        DSTINVERT = &H550009 ' dest = (NOT dest)
  100.        BLACKNESS = &H42 ' dest = BLACK
  101.        WHITENESS = &HFF0062 ' dest = WHITE
  102.    End Enum
  103.    Public Const HALFTONE = 4
  104.  
  105.    <DllImport("gdi32.dll")> _
  106.    Public Shared Function SetBkColor(hdc As IntPtr, crColor As Integer) As Integer
  107.    End Function
  108.  
  109. End Class
  110.  


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.







« Última modificación: 16 Septiembre 2016, 21:03 pm por FJDA » En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.788



Ver Perfil
Re: Duda con PictureBox y Saturación de Color
« Respuesta #1 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!


En línea

FJDA


Desconectado Desconectado

Mensajes: 321


Ver Perfil
Re: Duda con PictureBox y Saturación de Color
« Respuesta #2 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

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.
« Última modificación: 16 Septiembre 2016, 21:42 pm por FJDA » En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.788



Ver Perfil
Re: Duda con PictureBox y Saturación de Color
« Respuesta #3 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.
« Última modificación: 17 Septiembre 2016, 01:27 am por Eleкtro » En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.788



Ver Perfil
Re: Duda con PictureBox y Saturación de Color
« Respuesta #4 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ú.

Código
  1. Imports System.Drawing.Drawing2D
  2. Imports System.Drawing.Imaging
  3.  
  4. Public NotInheritable Class Form1 : Inherits Form
  5.  
  6.    Friend WithEvents PcbPreview As PictureBox
  7.    Friend WithEvents BtLoad As Button
  8.    Friend WithEvents BtSave As Button
  9.    Friend WithEvents TrckHue As TrackBar
  10.    Friend WithEvents LblHue As Label
  11.  
  12.    Private srcImg As Image
  13.    Private thumbnail As Image
  14.  
  15.    Public Sub New()
  16.  
  17.        MyClass.InitializeComponent()
  18.  
  19.        Me.Size = New Size(520, 400)
  20.        Me.DoubleBuffered = True
  21.  
  22.        Me.PcbPreview = New PictureBox With {
  23.            .Location = New Point(10, 10),
  24.            .Size = New Size(400, 240),
  25.            .SizeMode = PictureBoxSizeMode.Normal,
  26.            .BackColor = Color.DarkGray
  27.        }
  28.  
  29.        Me.BtLoad = New Button With {
  30.            .Location = New Point(Me.PcbPreview.Width + 20, 10),
  31.            .Text = "Load from..."
  32.        }
  33.  
  34.        Me.BtSave = New Button With {
  35.            .Location = New Point(Me.PcbPreview.Width + 20, 40),
  36.            .Text = "Save to..."
  37.        }
  38.  
  39.        Me.TrckHue = New TrackBar With {
  40.            .Location = New Point(10, Me.PcbPreview.Height + 20),
  41.            .AutoSize = True,
  42.            .Size = New Size(Me.PcbPreview.Width, 10),
  43.            .TickStyle = TickStyle.TopLeft,
  44.            .BackColor = Color.DarkGray,
  45.            .TickFrequency = 50,
  46.            .Maximum = 100,
  47.            .Minimum = -100,
  48.            .Value = 0,
  49.            .Orientation = Orientation.Horizontal
  50.        }
  51.  
  52.        Me.LblHue = New Label With {
  53.            .Location = New Point(Me.TrckHue.Width + 20, Me.TrckHue.Top + 10),
  54.            .AutoSize = True,
  55.            .Text = "0",
  56.            .Font = New Font("Arial", 20, FontStyle.Bold)
  57.        }
  58.        Me.LblHue.BringToFront()
  59.  
  60.    End Sub
  61.  
  62.    Private Sub Form1_Shown(ByVal sender As Object, ByVal e As EventArgs) _
  63.    Handles Me.Shown
  64.  
  65.        Me.SuspendLayout()
  66.        Me.Controls.AddRange({Me.PcbPreview, Me.BtLoad, Me.BtSave, Me.TrckHue, Me.LblHue})
  67.        Me.ResumeLayout()
  68.  
  69.    End Sub
  70.  
  71.    Private Sub BtLoad_Click(ByVal sender As Object, ByVal e As EventArgs) _
  72.    Handles BtLoad.Click
  73.  
  74.        Using ofd As New OpenFileDialog()
  75.            ofd.Title = "Open image"
  76.            ofd.Filter = "Image files|*.jpg;*.png;*.bmp"
  77.  
  78.            If (ofd.ShowDialog() = DialogResult.OK) Then
  79.                If (Me.srcImg IsNot Nothing) Then
  80.                    Me.srcImg.Dispose()
  81.                End If
  82.                Me.srcImg = Image.FromFile(ofd.FileName)
  83.  
  84.                If (Me.thumbnail IsNot Nothing) Then
  85.                    Me.thumbnail.Dispose()
  86.                End If
  87.                Dim thumbSize As Size = Me.PcbPreview.Size
  88.                Me.thumbnail = Me.srcImg.GetThumbnailImage(thumbSize.Width, thumbSize.Heigth, Nothing, Nothing)
  89.  
  90.                ' Force TrackBar.OnValueChanged event to trigger.
  91.                Me.TrckHue.Value = 1
  92.                Me.TrckHue.Value = 0
  93.  
  94.            End If
  95.        End Using ' ofd
  96.  
  97.    End Sub
  98.  
  99.    Private Sub BtSave_Click(ByVal sender As Object, ByVal e As EventArgs) _
  100.    Handles BtSave.Click
  101.  
  102.  
  103.        Using sfd As New SaveFileDialog()
  104.            sfd.Title = "Save image"
  105.            sfd.Filter = "Bitmap image|*.bmp|Jpeg image|*.jpg|PNG image|*.png"
  106.            sfd.AddExtension = True
  107.  
  108.            If (sfd.ShowDialog() = DialogResult.OK) Then
  109.                Dim file As New FileInfo(sfd.FileName)
  110.                Dim ext As String = file.Extension.ToLower()
  111.                Dim format As ImageFormat
  112.  
  113.                Select Case ext
  114.                    Case ".png"
  115.                        format = ImageFormat.Png
  116.                    Case ".bmp"
  117.                        format = ImageFormat.Bmp
  118.                    Case Else ' jpg
  119.                        format = ImageFormat.Jpeg
  120.                End Select
  121.  
  122.                Dim factor As Single = (Me.TrckHue.Value + 100)
  123.                Using img As Image = Me.srcImg.SetImageSaturation(factor, highQuality:=True)
  124.                    img.Save(file.FullName, format)
  125.                End Using ' img
  126.  
  127.            End If
  128.        End Using ' sfd
  129.  
  130.    End Sub
  131.  
  132.    Private Sub TrckHue_ValueChanged(ByVal sender As Object, ByVal e As EventArgs) _
  133.    Handles TrckHue.ValueChanged
  134.  
  135.        Dim trck As TrackBar = DirectCast(sender, TrackBar)
  136.        Dim value As Integer = trck.Value
  137.        Dim factor As Single = (trck.Value + 100)
  138.  
  139.        Dim pcbUpdate As Action =
  140.            Sub()
  141.                Me.LblHue.Invoke(Sub()
  142.                                     Me.LblHue.Text = CStr(value)
  143.                                     Me.Update()
  144.                                 End Sub)
  145.                Dim lockImg As Image = Me.thumbnail
  146.                SyncLock lockImg
  147.                    lockImg = lockImg.SetImageSaturation(factor, highQuality:=False)
  148.                    Me.PcbPreview.Image = lockImg
  149.                End SyncLock
  150.            End Sub
  151.  
  152.        Task.Factory.StartNew(pcbUpdate)
  153.  
  154.    End Sub
  155.  
  156. End Class

Código
  1. Public Module ImageExtensions
  2.  
  3.    <DebuggerStepThrough>
  4.    <Extension>
  5.    <EditorBrowsable(EditorBrowsableState.Always)>
  6.    Public Function SetImageSaturation(ByVal srcImg As Image, ByVal factor As Single,
  7.                                       ByVal highQuality As Boolean) As Image
  8.  
  9.        Dim r As Single = (299 * (100 - factor) / 100000.0F)
  10.        Dim g As Single = (587 * (100 - factor) / 100000.0F)
  11.        Dim b As Single = (114 * (100 - factor) / 100000.0F)
  12.  
  13.        factor = (factor / 100.0F)
  14.        Dim bmp As New Bitmap(srcImg.Width, srcImg.Height, srcImg.PixelFormat)
  15.        Dim matrix As Single()() = {
  16.                New Single() {(r + factor), r, r, 0.0F, 0.0F},
  17.                New Single() {g, (g + factor), g, 0.0F, 0.0F},
  18.                New Single() {b, b, (b + factor), 0.0F, 0.0F},
  19.                New Single() {0.0F, 0.0F, 0.0F, 1.0F, 0.0F},
  20.                New Single() {0.0F, 0.0F, 0.0F, 0.0F, 1.0F}
  21.        } ' Esta matriz de color la ideó el usuario @FJDA con el propósito de saturar una imagen.
  22.  
  23.        Using ia As New ImageAttributes
  24.            ia.ClearColorMatrix()
  25.            ia.SetColorMatrix(New ColorMatrix(matrix), ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
  26.            ia.SetGamma(1.0F, ColorAdjustType.Bitmap)
  27.  
  28.            Using gdi As Graphics = Graphics.FromImage(bmp)
  29.                With gdi
  30.                    If highQuality Then
  31.                        .CompositingQuality = CompositingQuality.HighQuality
  32.                        .InterpolationMode = InterpolationMode.HighQualityBicubic
  33.                        .PixelOffsetMode = PixelOffsetMode.HighQuality
  34.                        .SmoothingMode = SmoothingMode.HighQuality
  35.  
  36.                    Else
  37.                        .CompositingQuality = CompositingQuality.HighSpeed
  38.                        .InterpolationMode = InterpolationMode.NearestNeighbor
  39.                        .PixelOffsetMode = PixelOffsetMode.None
  40.                        .SmoothingMode = SmoothingMode.None
  41.  
  42.                    End If
  43.                    .DrawImage(srcImg, New Rectangle(0, 0, bmp.Width, bmp.Height),
  44.                                                     0, 0, srcImg.Width, srcImg.Height,
  45.                                                     GraphicsUnit.Pixel, ia)
  46.                End With
  47.  
  48.            End Using ' gdi
  49.        End Using ' ia
  50.  
  51.        Return bmp
  52.  
  53.    End Function
  54.  
  55. 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.


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:



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.



Espero que todo esto te haya servido de algo.

Saludos
« Última modificación: 17 Septiembre 2016, 13:21 pm por Eleкtro » En línea

FJDA


Desconectado Desconectado

Mensajes: 321


Ver Perfil
Re: Duda con PictureBox y Saturación de Color
« Respuesta #5 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:

Código
  1. Public Function SetImageSaturation(ByVal srcImg As Image, ByVal factor As Single,
  2.                                       ByVal highQuality As Boolean) As Image
  3.        factor+= 100
  4.        Dim r As Single = (299 * (100 - factor) / 100000.0F)
  5.        Dim g As Single = (587 * (100 - factor) / 100000.0F)
  6.        Dim b As Single = (114 * (100 - factor) / 100000.0F)
  7.  
  8.        factor = (factor / 100.0F)
  9.        Dim bmp As New Bitmap(srcImg.Width, srcImg.Height, srcImg.PixelFormat)
  10.        Dim matrix As Single()() = {
  11.                New Single() {(r + factor), r, r, 0.0F, 0.0F},
  12.                New Single() {g, (g + factor), g, 0.0F, 0.0F},
  13.                New Single() {b, b, (b + factor), 0.0F, 0.0F},
  14.                New Single() {0.0F, 0.0F, 0.0F, 1.0F, 0.0F},
  15.                New Single() {0.0F, 0.0F, 0.0F, 0.0F, 1.0F}
  16.        }
  17.  

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
« Última modificación: 17 Septiembre 2016, 20:03 pm por FJDA » En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.788



Ver Perfil
Re: Duda con PictureBox y Saturación de Color
« Respuesta #6 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!
« Última modificación: 17 Septiembre 2016, 14:15 pm por Eleкtro » En línea

FJDA


Desconectado Desconectado

Mensajes: 321


Ver Perfil
Re: Duda con PictureBox y Saturación de Color
« Respuesta #7 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í:


Código
  1.        Dim r As Single = CSng(factor * (299 * -10 ^ -5))
  2.        Dim g As Single = CSng(factor * (587 * -10 ^ -5))
  3.        Dim b As Single = CSng(factor * (114 * -10 ^ -5))
  4.        factor = (factor / 100.0F) + 1
  5.  
  6.        Dim matrix As Single()() = {
  7.                New Single() {(r + factor), r, r, 0.0F, 0.0F},
  8.                New Single() {g, (g + factor), g, 0.0F, 0.0F},
  9.                New Single() {b, b, (b + factor), 0.0F, 0.0F},
  10.                New Single() {0.0F, 0.0F, 0.0F, 1.0F, 0.0F},
  11.                New Single() {0.0F, 0.0F, 0.0F, 0.0F, 1.0F}
  12.        }

o también:

Código:
       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))
...

Código:
       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)


Código
  1.  
  2.    ''' <summary>
  3.    ''' Función para saturación de color //por @Elektro y @FJDA
  4.    ''' </summary>
  5.    ''' <param name="srcImg">Objeto de tipo System.Drawing.Image</param>
  6.    ''' <param name="Factor">Valor de saturación de color; de -100 a 100</param>
  7.    ''' <param name="highQuality">Composición de calidad</param>
  8.    ''' <returns></returns>
  9.    ''' <remarks></remarks>
  10.    Public Function SetImageSaturation(ByVal srcImg As Image, ByVal Factor As Single, ByVal highQuality As Boolean) As Image
  11.        Dim F As Single = Factor
  12.        Dim R As Single = CSng(F * -(299 / 100000))
  13.        Dim G As Single = CSng(F * -(587 / 100000))
  14.        Dim B As Single = CSng(F * -(114 / 100000))
  15.  
  16.        Dim bmp As New Bitmap(srcImg.Width, srcImg.Height, srcImg.PixelFormat)
  17.        Dim matrix As Single()() = {
  18.                New Single() {CSng((0.00701 * F) + 1), R, R, 0.0F, 0.0F},
  19.                New Single() {G, CSng((0.00413 * F) + 1), G, 0.0F, 0.0F},
  20.                New Single() {B, B, CSng((0.00886 * F) + 1), 0.0F, 0.0F},
  21.                New Single() {0.0F, 0.0F, 0.0F, 1.0F, 0.0F},
  22.                New Single() {0.0F, 0.0F, 0.0F, 0.0F, 1.0F}
  23.        }
  24.  
  25.        Using ia As New ImageAttributes
  26.            ia.ClearColorMatrix()
  27.            ia.SetColorMatrix(New ColorMatrix(matrix), ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
  28.            ia.SetGamma(1.0F, ColorAdjustType.Bitmap)
  29.  
  30.            Using gdi As Graphics = Graphics.FromImage(bmp)
  31.                With gdi
  32.                    If highQuality Then
  33.                        .CompositingQuality = CompositingQuality.HighQuality
  34.                        .InterpolationMode = InterpolationMode.HighQualityBicubic
  35.                        .PixelOffsetMode = PixelOffsetMode.HighQuality
  36.                        .SmoothingMode = SmoothingMode.HighQuality
  37.  
  38.                    Else
  39.                        .CompositingQuality = CompositingQuality.HighSpeed
  40.                        .InterpolationMode = InterpolationMode.NearestNeighbor
  41.                        .PixelOffsetMode = PixelOffsetMode.None
  42.                        .SmoothingMode = SmoothingMode.None
  43.  
  44.                    End If
  45.                    .DrawImage(srcImg, New Rectangle(0, 0, bmp.Width, bmp.Height),
  46.                                                     0, 0, srcImg.Width, srcImg.Height,
  47.                                                     GraphicsUnit.Pixel, ia)
  48.                End With
  49.  
  50.            End Using ' gdi
  51.        End Using ' ia
  52.  
  53.        Return bmp
  54.  
  55.    End Function
  56.  




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:
Código:
.ImagenPrevia = Image.FromFile(".\Imagen.jpg")


Código
  1. Imports System.Runtime.CompilerServices
  2. Imports System.ComponentModel
  3. Imports System.Drawing.Imaging
  4. Imports System.Drawing.Drawing2D
  5.  
  6.  
  7. Public NotInheritable Class Form1
  8.    Inherits Form
  9.    Private ImagenPrevia As Image
  10.    Private ImagenDeTrabajo As Image
  11.    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  12.        With Me
  13.            'Propiedades para el TrackBar1
  14.            With .TrackBar1
  15.                .TickFrequency = 10
  16.                .TickStyle = TickStyle.TopLeft
  17.                .Maximum = 100
  18.                .Minimum = -100
  19.                .Value = 0
  20.            End With
  21.            'Propiedades para el PictureBox1
  22.            With .PictureBox1
  23.                .BackColor = Color.DarkGray
  24.                .SizeMode = PictureBoxSizeMode.Normal
  25.            End With
  26.  
  27.            'Carga la imagen desde un directorio
  28.            .ImagenPrevia = Image.FromFile(".\Imagen.jpg")
  29.  
  30.            'Tamaño de la imagen para trabajar
  31.            Dim TamañoImagenDeTrabajo As Size = .PictureBox1.Size
  32.  
  33.            'Obtiene imagen previa para trabajar
  34.            .ImagenDeTrabajo = .ImagenPrevia.GetThumbnailImage(TamañoImagenDeTrabajo.Width, _
  35.                                                                TamañoImagenDeTrabajo.Height, _
  36.                                                                Nothing, Nothing)
  37.            .PictureBox1.Image = .ImagenDeTrabajo
  38.        End With
  39.    End Sub
  40.    Private Sub TrackBar1_ValueChanged(sender As Object, e As EventArgs) Handles TrackBar1.ValueChanged
  41.        'Ejecutar de forma asincrónica
  42.        Task.Factory.StartNew(Sub()
  43.                                  Dim lockImg As Image = ImagenDeTrabajo
  44.                                  SyncLock lockImg 'Bloqueo de instrucción antes de procesar
  45.                                      Me.Invoke(Sub()
  46.                                                    Dim vSat As Single = TrackBar1.Value 'Obtiene el valor de saturación de color
  47.                                                    Label1.Text = CStr(vSat) 'Muestra el valor de saturación de color
  48.                                                    lockImg = lockImg.SetImageSaturation(sngSatVal:=vSat,
  49.                                                                                        highQuality:=False)
  50.                                                    Me.PictureBox1.Image = lockImg
  51.                                                End Sub) 'Me.Invoke
  52.                                  End SyncLock
  53.                              End Sub) 'Task
  54.    End Sub
  55.  
  56. End Class
  57. Public Module ImageExtensions
  58.    ''' <summary>
  59.    ''' Función para saturación de color //por @Elektro y @FJDA
  60.    ''' </summary>
  61.    ''' <param name="srcImg">Objeto de tipo System.Drawing.Image</param>
  62.    ''' <param name="sngSatVal">Valor de saturación de -100 a 100</param>
  63.    ''' <param name="highQuality">Composición de calidad</param>
  64.    ''' <returns></returns>
  65.    ''' <remarks></remarks>
  66.    <DebuggerStepThrough> <Extension>
  67.    <EditorBrowsable(EditorBrowsableState.Always)>
  68.    Public Function SetImageSaturation(ByVal srcImg As Image, ByVal sngSatVal As Single, ByVal highQuality As Boolean) As Image
  69.  
  70.        Dim R As Single = CSng(sngSatVal * -(299 / 100000))
  71.        Dim G As Single = CSng(sngSatVal * -(587 / 100000))
  72.        Dim B As Single = CSng(sngSatVal * -(114 / 100000))
  73.        Dim FactR As Single = CSng(((0.00701F * sngSatVal) + 1))
  74.        Dim FactG As Single = CSng(((0.00413F * sngSatVal) + 1))
  75.        Dim FactB As Single = CSng(((0.00886F * sngSatVal) + 1))
  76.  
  77.        Dim matrix As Single()() = {
  78.                New Single() {FactR, R, R, 0, 0},
  79.                New Single() {G, FactG, G, 0, 0},
  80.                New Single() {B, B, FactB, 0, 0},
  81.                New Single() {0, 0, 0, 1, 0},
  82.                New Single() {0, 0, 0, 0, 1}
  83.        }
  84.  
  85.        Dim bmp As New Bitmap(srcImg.Width, srcImg.Height, srcImg.PixelFormat)
  86.        Using ia As New ImageAttributes
  87.            ia.ClearColorMatrix()
  88.            ia.SetColorMatrix(New ColorMatrix(matrix), ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
  89.            ia.SetGamma(1.0F, ColorAdjustType.Bitmap)
  90.  
  91.            Using gdi As Graphics = Graphics.FromImage(bmp)
  92.                With gdi
  93.                    If highQuality Then
  94.                        .CompositingQuality = CompositingQuality.HighQuality
  95.                        .InterpolationMode = InterpolationMode.HighQualityBicubic
  96.                        .PixelOffsetMode = PixelOffsetMode.HighQuality
  97.                        .SmoothingMode = SmoothingMode.HighQuality
  98.  
  99.                    Else
  100.                        .CompositingQuality = CompositingQuality.HighSpeed
  101.                        .InterpolationMode = InterpolationMode.NearestNeighbor
  102.                        .PixelOffsetMode = PixelOffsetMode.None
  103.                        .SmoothingMode = SmoothingMode.None
  104.  
  105.                    End If
  106.                    .DrawImage(srcImg, New Rectangle(0, 0, bmp.Width, bmp.Height),
  107.                                                     0, 0, srcImg.Width, srcImg.Height,
  108.                                                     GraphicsUnit.Pixel, ia)
  109.                End With
  110.  
  111.            End Using ' gdi
  112.        End Using ' ia
  113.  
  114.        Return bmp
  115.  
  116.    End Function
  117.  
  118. End Module
  119.  


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.


Código:
       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.


Código
  1. Using img As Image = SetImageSaturation(ImagenPrevia, TrackBar1.Value, highQuality:=True)
  2.            img.Save(".\Cambios.jpg", ImageFormat.Jpeg)
  3.        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.
« Última modificación: 18 Septiembre 2016, 13:03 pm por FJDA » En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
duda pictureBox
Programación Visual Basic
visualfree 3 1,811 Último mensaje 3 Abril 2007, 00:35 am
por visualfree
[DUDA] Transparencia de un color
Programación Visual Basic
HJZR4 5 2,032 Último mensaje 19 Diciembre 2007, 11:15 am
por LeandroA
[DUDA] Combinación de teclas y cambiar color a textos
Programación C/C++
Ch1n0Cr 1 2,209 Último mensaje 22 Noviembre 2010, 04:15 am
por Beakman
[DUDA] Color de la Consola incrustada en NetBeans
Programación General
RyogiShiki 0 3,959 Último mensaje 3 Marzo 2011, 01:12 am
por RyogiShiki
(Python)duda sobre color de texto y captura de teclas.
Scripting
S3kh 0 4,688 Último mensaje 2 Septiembre 2011, 00:42 am
por S3kh
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines