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


Tema destacado: También estamos presentes en BlueSky


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  Librería de Snippets para VB.NET !! (Compartan aquí sus snippets)
0 Usuarios y 2 Visitantes están viendo este tema.
Páginas: 1 ... 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 [63] Ir Abajo Respuesta Imprimir
Autor Tema: Librería de Snippets para VB.NET !! (Compartan aquí sus snippets)  (Leído 680,236 veces)
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.992



Ver Perfil
Re: Librería de Snippets para VB.NET !! (Compartan aquí sus snippets)
« Respuesta #620 en: 22 Abril 2026, 15:47 pm »

Indagando más profundo en la automatización de Proton VPN, descubrí tres formas adicionales para alternar entre servidores, de las cuales he llegado a implementar las dos primeras:

 1- Una forma consiste en automatizar el menú contextual de la bandeja del sistema (System Tray), haciendo click en los comandos "Conectar" y "Desconectar" de dicho menú según sea el estado actual de la conexión VPN. Esto es una técnica bastante más eficiente que la estrategia de automatización de la GUI que compartí para la versión de pago en el post anterior aquí arriba, ya que no depende de coordenadas ni de averiguar el color del botón "Conectar/Desconectar", pero de todas formas si la barra de tareas estuviese oculta/colapsada o si el icono de ProtonVPN estuviese oculto en el área de notificación colapsado, supondría una barrera que requeriría implementar más lógica adicional de automatización para la barra de tareas. Al menos el método de automatización que ya compartí no tiene esas barreras.

 2- La otra forma, la cual descubrí investigando en un hilo de Reddit, y la cual considero que es la forma más efectiva de todas (o al menos la menos invasiva), consiste en enviar una señal de detención al servicio de Windows de ProtonVPN. Esto provocará una reconexión de la interfaz de red de Proton (y con ello un cambio aleatorio de servidor). El único requisito es estar corriendo ProtonVPN y tener activado el modo "País aleatorio" para que al reiniciar el servicio la nueva conexión se establezca alternando a otro servidor diferente.

 3- Existiría una tercera vía mediante la comunicación directa con la canalización con nombre (Named Pipe) de Proton. En teoría se podría lograr importando las librerías DLL necesarias de la aplicación (NET 8+), pero es una opción poco viable para poder compartirlo como solución de distribución general en un foro. En su defecto se supone que se podría implementar un proxy WCF (Windows Communication Foundation), pero esta opción tampoco sería muy viable, aparte del ingente esfuerzo para implementar todas las interfaces necesarias (ver aquí), la solución sería extremadamente frágil, ya que cualquier modificación en los contratos de interfaz del código fuente original de Proton invalidaría el proxy por completo. No llegué a implementar esta solución y, para ser sinceros, tampoco es que tenga experiencia en hacerlo.

En fin. Teniendo todo esto en cuenta, considero que llevar a cabo el "reinicio" del servicio de ProtonVPN es la solución que tiene el "trade-off" más factible entre todas las demás soluciones que descubrí. Aquí les dejo el código fuente:

Código
  1. ''' <summary>
  2. ''' Restarts the Proton VPN Windows Service to force a server reconnection.
  3. ''' </summary>
  4. '''
  5. ''' <remarks>
  6. ''' This method validates that a connection is active by checking the
  7. ''' "ProtonVPN" network interface status before attempting to restart the service.
  8. ''' </remarks>
  9. '''
  10. ''' <exception cref="InvalidOperationException">
  11. ''' Thrown when the Proton VPN network interface is not found or is not connected,
  12. ''' or if the <b>ProtonVPN</b> Windows service fails to complete the restart sequence.
  13. ''' </exception>
  14. '''
  15. ''' <exception cref="Exception">
  16. ''' Thrown when the <b>ProtonVPN</b> Windows service service restarted and network interface is UP,
  17. ''' but no data flow (Bytes Received) was detected.
  18. ''' </exception>
  19. <EditorBrowsable(EditorBrowsableState.Advanced)>
  20. <DebuggerStepThrough>
  21. Private Sub ChangeProtonVpnServerByWindowsService()
  22.  
  23.    Const ProtonVPN_NetworkInterfaceName As String = "ProtonVPN"
  24.    Const ProtonVPN_WindowsServiceName As String = "ProtonVPN Service"
  25.  
  26.    Dim isProtonVpnConnected As Boolean = False
  27.  
  28.    Dim interfaces As NetworkInterface() = NetworkInterface.GetAllNetworkInterfaces()
  29.    For Each ni As NetworkInterface In interfaces
  30.        ' We look for the specific name Proton uses for its virtual adapters
  31.        If ni.Name.Equals(ProtonVPN_NetworkInterfaceName, StringComparison.OrdinalIgnoreCase) Then
  32.            If ni.OperationalStatus = OperationalStatus.Up Then
  33.                isProtonVpnConnected = True
  34.                Exit For
  35.            End If
  36.        End If
  37.    Next ni
  38.  
  39.    If Not isProtonVpnConnected Then
  40.        Throw New InvalidOperationException(
  41.            $"The {ProtonVPN_NetworkInterfaceName} network interface is not connected to a server." & Environment.NewLine &
  42.            "The reconnection logic requires an active VPN connection to be present.")
  43.    End If
  44.  
  45.    ' Restart the ProtonVPN Windows service.
  46.    Using sc As New ServiceController(ProtonVPN_WindowsServiceName)
  47.  
  48.        Select Case sc.Status
  49.  
  50.            Case ServiceControllerStatus.StartPending, ServiceControllerStatus.Running
  51.                sc.Stop()
  52.                sc.WaitForStatus(ServiceControllerStatus.StopPending, TimeSpan.FromSeconds(10))
  53.  
  54.            Case ServiceControllerStatus.Stopped
  55.                sc.Start()
  56.  
  57.            Case Else
  58.                Throw New InvalidOperationException(
  59.                    $"The {ProtonVPN_WindowsServiceName} Windows service is in a unexpected state: {sc.Status}")
  60.        End Select
  61.  
  62.        sc.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10))
  63.    End Using
  64.  
  65.    ' Wait for the ProtonVPN network interface to return to 'Up' status and has verified data flow (bytes received).
  66.    Dim retryTimeout As TimeSpan = TimeSpan.FromSeconds(30)
  67.    Dim startTime As Date = Date.Now
  68.    Dim isDataFlowEstablished As Boolean = False
  69.    Dim initialBytes As Long = -1
  70.  
  71.    Do While (Date.Now - startTime) < retryTimeout
  72.        Dim protonVpnInterface As NetworkInterface = NetworkInterface.GetAllNetworkInterfaces().
  73.            SingleOrDefault(Function(ni) ni.Name.Equals(ProtonVPN_NetworkInterfaceName, StringComparison.OrdinalIgnoreCase))
  74.  
  75.        If (protonVpnInterface IsNot Nothing) AndAlso
  76.           (protonVpnInterface.OperationalStatus = OperationalStatus.Up) Then
  77.  
  78.            Dim stats As IPv4InterfaceStatistics = protonVpnInterface.GetIPv4Statistics()
  79.  
  80.            ' On first detection of 'Up' status, capture baseline.
  81.            If initialBytes = -1 Then
  82.                initialBytes = stats.BytesReceived
  83.            End If
  84.  
  85.            ' Check if we have received new data since being 'Up'.
  86.            If stats.BytesReceived > initialBytes Then
  87.                isDataFlowEstablished = True
  88.                Exit Do
  89.            End If
  90.        End If
  91.  
  92.        Thread.Sleep(500)
  93.    Loop
  94.  
  95.    If Not isDataFlowEstablished Then
  96.        Throw New Exception(
  97.            $"The {ProtonVPN_WindowsServiceName} Windows service restarted " & Environment.NewLine &
  98.            $"and {ProtonVPN_NetworkInterfaceName} network interface is UP, " & Environment.NewLine &
  99.            $"but no data flow (bytes received) was detected within {retryTimeout.TotalSeconds} seconds.")
  100.    End If
  101.  
  102.    Thread.Sleep(1000) ' Additional delay to ensure connection has established with a VPN server before return.
  103. End Sub

Simplemente llamar al método ChangeProtonVpnServerViaWindowsService, esperar a que termine su ejecución, y listo.



« Última modificación: 22 Abril 2026, 16:26 pm por Eleкtro » En línea



Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.992



Ver Perfil
Re: Librería de Snippets para VB.NET !! (Compartan aquí sus snippets)
« Respuesta #621 en: 7 Mayo 2026, 17:55 pm »

Comparto con ustedes una solución completa que sirve, entre otros posibles fines, para poder calcular la luminosidad promedio perceptible de una imagen, mediante el estándar del espacio de color CIE L*A*B*. Para quien desee detalles más técnicos al respecto, está todo documentado en el código fuente, incluyendo URLs a Wikipedia.

El siguiente código fuente contiene dos métodos de extensión con nombre ComputeAverageLightness para los tipos System.Drawing.Bitmap y System.Drawing.Image, así como una sobrecarga adicional, o mejor dicho, el mismo método pero definido en otra clase diferente con nombre UtilImage, al que poder pasarle un archivo de imagen como parámetro.

El código fuente además provee dos funciones reutilizables, ComputeLabF y BuildLinearSRGBLookupTable, en la clase con nombre UtilColor.



Casos de uso real

 - Clasificar un set de imágenes, por ejemplo, fondos de escritorio, según el porcentaje de luminosidad de cada imagen, separando imágenes oscuras, equilibradas y sobreexpuestas.
 - Selección de "keyframes" en video para extraer una miniatura que lo represente, descartando aquellos frames que sean extremadamente oscuros.
 - Automatizar el proceso de revisión y selección de imágenes tras una sesión fotográfica para conservar solo las mejores y descartar las fallidas (a esto se le llama "Culling").
 - Determinar si una imagen necesita ecualización de histograma o normalización tonal antes de post-procesamiento.
 - Analizar páginas escaneadas para distinguir documentos limpios frente a documentos oscuros o degradados.
 - Determinar qué imágenes requieren corrección gamma.
 - Otras 80 sugerencias que cualquier IA te podrá mencionar si le muestras este código fuente...



El código fuente

Código
  1. Imports System.Buffers
  2. Imports System.Drawing
  3. Imports System.Drawing.Drawing2D
  4. Imports System.Drawing.Imaging
  5. Imports System.IO
  6. Imports System.Runtime.CompilerServices
  7. Imports System.Runtime.InteropServices
  8.  
  9. Public Module BitmapExtensions
  10.  
  11.    ''' <summary>
  12.    ''' Precomputed look-up table that maps each sRGB encoded byte value [0, 255]
  13.    ''' to its corresponding linear-light (physical) value in the range [0.0, 1.0].
  14.    ''' </summary>
  15.    '''
  16.    ''' <remarks>
  17.    ''' Used by function: <see cref="BitmapExtensions.ComputeAverageLightness(Bitmap, Byte)"/>
  18.    ''' to convert sRGB pixel values to linear light for accurate luminance calculations.
  19.    ''' </remarks>
  20.    Private LinearSrgbLut As IReadOnlyList(Of Double)
  21.  
  22.    ''' <summary>
  23.    ''' Computes the average CIE L* lightness of the specified <see cref="Bitmap"/>.
  24.    ''' <para></para>
  25.    ''' The bitmap must use <see cref="PixelFormat.Format32bppArgb"/> layout;
  26.    ''' any other format will raise a <see cref="NotSupportedException"/>.
  27.    ''' <para></para>
  28.    ''' For arbitrary bitmaps, prefer <see cref="ImageExtensions.ComputeAverageLightness"/> function,
  29.    ''' which handles proper <see cref="Bitmap"/> format conversion automatically.
  30.    ''' </summary>
  31.    '''
  32.    ''' <param name="bitmap">
  33.    ''' Source bitmap in <see cref="PixelFormat.Format32bppArgb"/> format.
  34.    ''' </param>
  35.    '''
  36.    ''' <param name="alphaThreshold">
  37.    ''' Optional alpha threshold below which a pixel is considered fully transparent and excluded from the lightness average.
  38.    ''' <para></para>
  39.    ''' Range: 0–255. A value of 0 includes all pixels; 255 includes only fully opaque pixels.
  40.    ''' <para></para>
  41.    ''' Default value is 8.
  42.    ''' </param>
  43.    '''
  44.    ''' <returns>
  45.    ''' Average CIE L* value in the range [0.0, 100.0], or 0.0 if all pixels are below the alpha threshold.
  46.    ''' </returns>
  47.    <Extension>
  48.    Public Function ComputeAverageLightness(sourceBitmap As Bitmap, Optional alphaThreshold As Byte = 8) As Double
  49.  
  50. #If NETCOREAPP Then
  51.        ArgumentNullException.ThrowIfNull(sourceBitmap)
  52. #Else
  53.        If bitmap Is Nothing Then
  54.            Throw New ArgumentNullException(NameOf(bitmap))
  55.        End If
  56. #End If
  57.  
  58.        If sourceBitmap.PixelFormat <> PixelFormat.Format32bppArgb Then
  59.            Throw New NotSupportedException(
  60.                $"Expected PixelFormat {NameOf(PixelFormat.Format32bppArgb)}, got {sourceBitmap.PixelFormat}.")
  61.        End If
  62.  
  63.        ' ITU-R BT.709 standard coefficients for relative luminance (Y)
  64.        ' These weights reflect human visual sensitivity to RGB channels.
  65.        Const LumaR As Double = 0.2126R
  66.        Const LumaG As Double = 0.7152R
  67.        Const LumaB As Double = 0.0722R
  68.  
  69.        ' CIE L* (Lightness) formula constants
  70.        ' Used to transform relative luminance (Y) into perceptual lightness (L*).
  71.        Const LStarScale As Double = 116.0R
  72.        Const LStarOffset As Double = 16.0R
  73.  
  74.        If BitmapExtensions.LinearSrgbLut Is Nothing Then
  75.            BitmapExtensions.LinearSrgbLut = UtilColor.BuildLinearSRGBLookupTable()
  76.        End If
  77.  
  78.        Dim rect As New Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height)
  79.        Dim bmpData As BitmapData = sourceBitmap.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
  80.        Dim pixels As Byte() = Nothing
  81.  
  82.        Try
  83.            Dim stride As Integer = bmpData.Stride
  84.            If stride <= 0 Then
  85.                Throw New NotSupportedException("Negative or zero stride is not supported.")
  86.            End If
  87.  
  88.            Dim bufferSize As Integer = stride * sourceBitmap.Height
  89.            pixels = ArrayPool(Of Byte).Shared.Rent(bufferSize)
  90.            Marshal.Copy(bmpData.Scan0, pixels, 0, bufferSize)
  91.  
  92.            Dim totalL As Double = 0.0R
  93.            Dim pixelCount As Long = 0L
  94.  
  95.            Dim y As Integer
  96.            For y = 0 To sourceBitmap.Height - 1
  97.                Dim rowOffset As Integer = y * stride
  98.  
  99.                Dim x As Integer
  100.                For x = 0 To sourceBitmap.Width - 1
  101.                    Dim idx As Integer = rowOffset + (x * 4)
  102.  
  103.                    ' Format32bppArgb byte layout: B=idx+0, G=idx+1, R=idx+2, A=idx+3
  104.                    Dim alpha As Byte = pixels(idx + 3)
  105.                    If alpha <= alphaThreshold Then
  106.                        Continue For
  107.                    End If
  108.  
  109.                    Dim r As Byte = pixels(idx + 2)
  110.                    Dim g As Byte = pixels(idx + 1)
  111.                    Dim b As Byte = pixels(idx)
  112.  
  113.                    ' Step 1: Calculate CIE Relative Luminance (Y) using the LUT
  114.                    ' This represents the physical light intensity.
  115.                    Dim relY As Double =
  116.                        (BitmapExtensions.LinearSrgbLut(r) * LumaR) +
  117.                        (BitmapExtensions.LinearSrgbLut(g) * LumaG) +
  118.                        (BitmapExtensions.LinearSrgbLut(b) * LumaB)
  119.  
  120.                    ' Step 2: Calculate Perceptual Lightness (L*)
  121.                    ' This transforms physical light into human perceived brightness [0-100].
  122.                    ' Note: ComputeLabF FUNCTION applies the cube root or linear slope per CIE specs.
  123.                    totalL += (LStarScale * UtilColor.ComputeLabF(relY)) - LStarOffset
  124.                    pixelCount += 1L
  125.                Next
  126.            Next
  127.  
  128.            Return If(pixelCount = 0L, 0.0R, totalL / pixelCount)
  129.  
  130.        Finally
  131.            sourceBitmap.UnlockBits(bmpData)
  132.            If pixels IsNot Nothing Then
  133.                ArrayPool(Of Byte).Shared.Return(pixels, clearArray:=False)
  134.            End If
  135.        End Try
  136.    End Function
  137.  
  138. End Module
  139.  
  140. Public Module ImageExtensions
  141.  
  142.    ''' <summary>
  143.    ''' Computes the average CIE L* lightness of the specified <see cref="Image"/>.
  144.    ''' </summary>
  145.    '''
  146.    ''' <param name="bitmap">
  147.    ''' Source bitmap in <see cref="PixelFormat.Format32bppArgb"/> format.
  148.    ''' </param>
  149.    '''
  150.    ''' <param name="alphaThreshold">
  151.    ''' Optional alpha threshold below which a pixel is considered fully transparent and excluded from the lightness average.
  152.    ''' <para></para>
  153.    ''' Range: 0–255. A value of 0 includes all pixels; 255 includes only fully opaque pixels.
  154.    ''' <para></para>
  155.    ''' Default value is 8.
  156.    ''' </param>
  157.    '''
  158.    ''' <returns>
  159.    ''' Average CIE L* value in the range [0.0, 100.0], or 0.0 if all pixels are below the alpha threshold.
  160.    ''' </returns>
  161.    <Extension>
  162.    Public Function ComputeAverageLightness(sourceImage As Image, Optional alphaThreshold As Byte = 8) As Double
  163.  
  164. #If NETCOREAPP Then
  165.        ArgumentNullException.ThrowIfNull(sourceImage)
  166. #Else
  167.        If sourceImage Is Nothing Then
  168.            Throw New ArgumentNullException(NameOf(sourceImage))
  169.        End If
  170. #End If
  171.  
  172.        Dim bmp As Bitmap = TryCast(sourceImage, Bitmap)
  173.        Dim mustDispose As Boolean = False
  174.  
  175.        If bmp Is Nothing OrElse bmp.PixelFormat <> PixelFormat.Format32bppArgb Then
  176.            bmp = New Bitmap(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb)
  177.            Using gfx As Graphics = Graphics.FromImage(bmp)
  178.                gfx.CompositingMode = CompositingMode.SourceCopy
  179.                gfx.InterpolationMode = InterpolationMode.NearestNeighbor
  180.                gfx.PixelOffsetMode = PixelOffsetMode.HighSpeed
  181.                gfx.CompositingQuality = CompositingQuality.HighSpeed
  182.  
  183.                gfx.DrawImage(sourceImage, 0, 0, sourceImage.Width, sourceImage.Height)
  184.            End Using
  185.            mustDispose = True
  186.        End If
  187.  
  188.        Try
  189.            Return BitmapExtensions.ComputeAverageLightness(bmp, alphaThreshold)
  190.        Finally
  191.            If mustDispose Then
  192.                bmp.Dispose()
  193.            End If
  194.        End Try
  195.  
  196.    End Function
  197.  
  198. End Module
  199.  
  200. Public Class UtilImage
  201.  
  202.    ''' <summary>
  203.    ''' Computes the average CIE L* lightness of the specified image file.
  204.    ''' </summary>
  205.    '''
  206.    ''' <example> This is a code example.
  207.    ''' <code language="VB">
  208.    ''' Dim imageFile As String = "C:\Wallpaper.jpg"
  209.    ''' Dim avgLightness As Double = UtilImage.ComputeAverageLightness(imageFile)
  210.    '''
  211.    ''' Console.WriteLine($"Image File: {Path.GetFileName(imageFile)}")
  212.    ''' Console.WriteLine($"Average Lightness (CIE L*): {(avgLightness / 100.0R):P2}")
  213.    ''' </code>
  214.    ''' </example>
  215.    '''
  216.    ''' <param name="filePath">
  217.    ''' Full path to the source image file.
  218.    ''' </param>
  219.    '''
  220.    ''' <param name="alphaThreshold">
  221.    ''' Optional alpha threshold below which a pixel is considered fully transparent and excluded from the lightness average.
  222.    ''' <para></para>
  223.    ''' Range: 0–255. A value of 0 includes all pixels; 255 includes only fully opaque pixels.
  224.    ''' <para></para>
  225.    ''' Default value is 8.
  226.    ''' </param>
  227.    '''
  228.    ''' <returns>
  229.    ''' Average CIE L* value in the range [0.0, 100.0], or 0.0 if all pixels are below the alpha threshold.
  230.    ''' </returns>
  231.    Public Shared Function ComputeAverageLightness(filePath As String, Optional alphaThreshold As Byte = 8) As Double
  232.  
  233.        Using sourceStream As New FileStream(filePath, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
  234.  
  235.            Using sourceImage As Image = Image.FromStream(sourceStream, useEmbeddedColorManagement:=False, validateImageData:=True)
  236.                Return ImageExtensions.ComputeAverageLightness(sourceImage, alphaThreshold)
  237.            End Using
  238.        End Using
  239.    End Function
  240.  
  241. End Class
  242.  
  243. Public Class UtilColor
  244.  
  245.    ''' <summary>
  246.    ''' Applies the CIE XYZn to L*a*b* (CIELAB) transfer function (also known as the f-function)
  247.    ''' used in the conversion from relative luminance (<c>Y</c>) to perceptual lightness (<c>L*</c>).
  248.    ''' </summary>
  249.    '''
  250.    ''' <example> This example shows how to calculate the Perceptual Lightness (<c>L*</c>) of a 18% "Middle Gray" value:
  251.    ''' <code language="VB">
  252.    ''' Dim relativeY As Double = 0.18R ' 18% reflectance
  253.    ''' Dim fValue As Double = ComputeLabF(relativeY)
  254.    '''
  255.    ''' ' Formula for L* (Lightness): 116 * f(Y) - 16
  256.    ''' Dim lightness As Double = (116.0R * fValue) - 16.0R
  257.    '''
  258.    ''' Console.WriteLine($"Relative Luminance (Y): {relativeY}")
  259.    ''' Console.WriteLine($"Perceptual Lightness (L*): {lightness:F2}")
  260.    ''' </code>
  261.    ''' </example>
  262.    '''
  263.    ''' <param name="value">
  264.    ''' The relative luminance (<c>Y</c>) or color component, expected in the normalized range <c>[0.0, 1.0]</c>.
  265.    ''' </param>
  266.    '''
  267.    ''' <returns>
  268.    ''' The transformed value <c>f(Y)</c> to be used in <c>L*</c>, <c>a*</c>, or <c>b*</c> calculations.
  269.    ''' </returns>
  270.    '''
  271.    ''' <remarks>
  272.    ''' This function is part of the CIE L*a*b* (CIELAB) color space definition.
  273.    ''' It models how humans perceive brightness differences.
  274.    ''' <para></para>
  275.    '''
  276.    ''' The constants used by this function are defined by the CIE (International Commission on Illumination):
  277.    ''' <para></para>
  278.    ''' - <b>Epsilon (&#949;)</b>: The transition point between linear and non-linear behavior, calculated as <c>(6/29)^3 &#8776; 0.008856</c>.
  279.    ''' <para></para>
  280.    ''' - <b>Kappa (&#954;)</b>: The slope of the linear segment near black, calculated as <c>(29/3)^3 &#8776; 903.3</c>.
  281.    ''' <para></para>
  282.    '''
  283.    ''' For more technical details, see:
  284.    ''' <para></para>
  285.    ''' <see href="https://en.wikipedia.org/wiki/CIELAB_color_space#Forward_transformation"/>
  286.    ''' <para></para>
  287.    ''' <see href="https://en.wikipedia.org/wiki/Relative_luminance"/>
  288.    ''' </remarks>
  289.    <DebuggerStepThrough>
  290.    Public Shared Function ComputeLabF(value As Double) As Double
  291.  
  292.        Const Epsilon As Double = 0.008856R
  293.        Const Kappa As Double = 903.3R
  294.  
  295.        ' CIE L* (L-Star / Lightness) formula constants
  296.        Const LStarScale As Double = 116.0R
  297.        Const LStarOffset As Double = 16.0R
  298.  
  299.        ' If the value is above Epsilon, we use the power function (cube root).
  300.        ' If the value is below Epsilon, we use a linear slope (Kappa) to avoid
  301.        ' infinite gradients at the zero point, and handle image noise better.
  302.        Return If(value > Epsilon,
  303.                  Math.Pow(value, 1.0R / 3.0R),
  304.                  (Kappa * value + LStarOffset) / LStarScale)
  305.    End Function
  306.  
  307.    ''' <summary>
  308.    ''' Precomputes a look-up table (LUT) containing the linearized sRGB values [0.0, 1.0] for each possible 8-bit channel value [0, 255].
  309.    ''' </summary>
  310.    '''
  311.    ''' <example>
  312.    ''' This example demonstrates how to use the look-up table (LUT) to linearize a standard 8-bit RGB channel value:
  313.    ''' <code language="VB">
  314.    ''' Dim lut As IReadOnlyList(Of Double) = BuildLinearSRGBLookupTable()
  315.    '''
  316.    ''' ' Get the linear intensity of a middle-gray byte (127)
  317.    ''' Dim grayByte As Integer = 127
  318.    ''' Dim linearLight As Double = lut(grayByte)
  319.    '''
  320.    ''' ' Output the result (approx. 0.21 or 21% light intensity)
  321.    ''' Console.WriteLine($"Byte Value: {grayByte}")
  322.    ''' Console.WriteLine($"Linear Light Intensity: {linearLight:P2}")
  323.    ''' </code>
  324.    ''' </example>
  325.    '''
  326.    ''' <returns>
  327.    ''' A readon-only list of 256 <see cref="Double"/> values representing normalized linear light intensities in the range [0.0, 1.0].
  328.    ''' </returns>
  329.    '''
  330.    ''' <remarks>
  331.    ''' Standard digital images (sRGB) are gamma-corrected to optimize bit depth for human perception.
  332.    ''' Before performing any physical light calculations (like luminosity), we must "undo" this correction.
  333.    ''' This process is known as <b>Gamma Expansion</b>.
  334.    ''' <para></para>
  335.    ''' For more technical details, see: <see href="https://en.wikipedia.org/wiki/SRGB#The_forward_transformation_(gamma_compression)"/>
  336.    ''' </remarks>
  337.    <DebuggerStepThrough>
  338.    Public Shared Function BuildLinearSRGBLookupTable() As IReadOnlyList(Of Double)
  339.  
  340.        ' sRGB Standard Constants
  341.  
  342.        ' The exponent used for the power-law (gamma) section.
  343.        ' While often simplified to 2.2, the official sRGB standard uses 2.4.
  344.        Const GammaExponent As Double = 2.4R
  345.  
  346.        ' The threshold that separates the linear slope from the curve.
  347.        Const GammaThreshold As Double = 0.04045R
  348.  
  349.        ' The divisor used for the linear segment near black.
  350.        Const LinearSlope As Double = 12.92R
  351.  
  352.        ' The offset (magic number) used to align the linear and curve segments.
  353.        Const Offset As Double = 0.055R
  354.  
  355.        Dim lut As Double() = New Double(255) {}
  356.  
  357.        For i As Integer = 0 To 255
  358.            ' Normalize the 8-bit integer [0, 255] to a double [0.0, 1.0]
  359.            Dim v As Double = i / 255.0R
  360.  
  361.            ' The "Piecewise" function:
  362.            ' If the value is very dark (below threshold), use a simple linear division;
  363.            ' Otherwise, use the exponential formula (Gamma Expansion).
  364.            lut(i) = If(v <= GammaThreshold,
  365.                        v / LinearSlope,
  366.                        Math.Pow((v + Offset) / (1.0R + Offset), GammaExponent))
  367.        Next
  368.  
  369.        Return Array.AsReadOnly(lut)
  370.    End Function
  371.  
  372. End Class



Ejemplos de uso

Función ComputeAverageLightness:
Código
  1. Dim imageFile As String = "C:\Wallpaper.jpg"
  2. Dim avgLightness As Double = UtilImage.ComputeAverageLightness(imageFile)
  3.  
  4. Console.WriteLine($"Image File: {Path.GetFileName(imageFile)}")
  5. Console.WriteLine($"Average Lightness (CIE L*): {(avgLightness / 100.0R):P2}")

Función ComputeLabF:
Código
  1. Dim relativeY As Double = 0.18R ' 18% reflectance
  2. Dim fValue As Double = ComputeLabF(relativeY)
  3.  
  4. ' Formula for L* (Lightness): 116 * f(Y) - 16
  5. Dim lightness As Double = (116.0R * fValue) - 16.0R
  6.  
  7. Console.WriteLine($"Relative Luminance (Y): {relativeY}")
  8. Console.WriteLine($"Perceptual Lightness (L*): {lightness:F2}")



Comentarios adicionales

Me he apoyado en el uso de la IA para implementar de la forma más rigurosa que me ha sido posible la implementación que se pueda considerar como el estándar de la fórmula de CIELAB, y las constantes LUMA y demás constantes, consultando segunda y tercera opinión en distintas IA, todo esto por que no soy ningún experto en detalles avanzados de fotografía profesional y colorimetría, pero intento hacer las cosas bien dentro de la limitación del conocimiento inadquirido en campos en los que no estoy especializado... ni tengo la menor intención de aprender más de lo necesario al respecto para resolver un problema específico que yo tenía, y que este código fuente me lo soluciona. Vamos, que si hay algún fogógrafo o diseñador profesional de artes gráficas en la sala, que no se ofenda si algún valor constante o algún cálculo en concreto no es exactamente del todo correcto, aunque en principio debería serlo.

He comparado el uso de esta estrategia con métodos más rudimentarios o tradicionales para calcular la luminosidad promedio sobre miles de imágenes, y el uso de CIELAB produce resultados perceptualmente mucho más precisos. Dicho de forma más simple, le da mil patadas a lo tradicional, garantizado.


« Última modificación: 7 Mayo 2026, 18:26 pm por Eleкtro » En línea



Páginas: 1 ... 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 [63] Ir Arriba Respuesta Imprimir 

Ir a:  

WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines