Salvar JPG con mucho menos peso sin perder calidad

Iniciado por Fran1946, 8 Mayo 2015, 19:37 PM

0 Miembros y 1 Visitante están viendo este tema.

Neocortex

Cita de: Fran1946 en 12 Junio 2015, 01:26 AM
Hola:

Todo esto ya lo se desde hace muchos años.

Pues claro, eso es el propósito para reducir el peso de los archivos y no se ni me importa la tasa de conversión que utiliza msPaint, solo me interesa el resultado que es el que ya he explicado muchas veces (conseguir reducir el peso de los archivos sin perder calidad ni resolución a efectos del ojo humano, incluso con Zoom elevados)

Buenas,

Perdón no alcancé a leer todos los posts porque ando en el trabajo pero entiendo más o menos tu punto, corrígeme si me equivoco:
- Mantener las propiedades de la imagen (EXIF data).
- que la compresión sea humanamente sin pérdidas.
- Que la resolución no cambie.


^
Esa fue mi primer versión del compresor, así como a ti me interesaba comprimir a gran escala gran cantidad de imágenes que estuvieran pesadas y reducirlas a menos de 1MB. El truco es que yo no utilicé ninguna herramienta externa, si no hice un mappeo de los colores e hice un algoritmo bastante sencillo que es muy parecido a lo que hace .jpg cada que se guarda la imagen, solo que mi algoritmo solo hace la compresión una vez, si se vuelve a guardar ya no comprime más, a menos que se guarde con jpg...
Lo de las propiedades y eso no me puse a indagar mucho en el tema cuando lo hice, pero a ver si esté fin le busco y te resuelvo eso, sirve que mejoro el código que ya tiene sus años...

La última versión que hice maneja drop de carpetas completas.



Saludos y una disculpa si se malinterpretó mi respuesta



Fran1946

Hola Neocortex:

Efectivamente lo que quiero es como dices, me extrañaba tu respuesta si habías seguido el post, o es que yo no me he explicado bien.

Yo como te comenté, ya tengo el programa funcionando empleando msPaint, pero si me gustaría poder hacerlo sin mspaint.

Veo que consigues, en la primera imagen de tu programa, una tasa de reducción del 89% en una imagen de 1920x1080, y aunque no puedo apreciar bien en tu captura detalles ampliados, parece que a simple vista no pierde calidad apreciable.

Pero no se que resolución en ppp (puntos por pulgada) tiene esa imagen.

Yo tengo cientos de imágenes muy grandes, 5184x3456 de una cámara Cannon EOS 600D 16 Mpix pero es de 72 ppp, que demuestra lo equivocada que esta la mayoría de la gente, que cree que una cámara de 16 Mpix tiene mejor calidad que una de 3.5 Mpix Cannon IXUS V3 de 180 ppp (yo tengo las dos).
Pues no... los Mpix solo significan el tamaño máximo de la imagen, y los ppp son la calidad (resolución) de la imagen.
Por que 72 ppp son 5184 pixels en una pulgada cuadrada, o sea en un cuadrado de 25.4x25.4 mm.
Y 180 ppp son 32400 pixels en la misma pulgada cuadrada, o sea un 160% mas de resolución.

Si no te importa compartir conmigo ese algoritmo, te lo agradecería mucho.

Y de todas formas gracias por tu respuesta, y no tengo nada que disculparte, solo agradecerte la información.

No se si has visto el vídeo que subí a YouTube, si lo quieres ver es este:

https://www.youtube.com/watch?v=BkmX5sx_bGw

Saludos.   


Neocortex

#32
es bastante sencillo, lo hice para una materia en la universidad en un domingo en la mañana.

a la imagen en cuestión la recorres en ancho y alto, luego le sacas el valor de los pixeles.



Luego a los arreglos generados se les asigna nuevo valor


Para no hacerla muy larga, lo que hace es generar un promedio de valores
si el pixel está en (122,152,167) lo convierte a (120,150,170), así reduce la cantidad de colores.

Si gustas pudieras dejarme alguna fotografía de esas magnitudes para ver el tiempo que dura y te paso los resultados.

*EDIT*
Ah... Y a los limites del blanco y negro le di cierto margen mayor para que no se notara tanto.


Saludos!

Fran1946

#33
Hola Neocortex:

Gracias por tu respuesta una vez más.
Veo que el código está en C++, y yo hace 40 años que no he vuelto a programar en C++, en aquella época no había Visual C, solo C++ de Borland, en puro MSDOS.

Y me resulta bastante difícil traducir este código a VB6, que es el lenguaje que utilizo para las aplicaciones que hago actualmente para mis "apaños" y por hobby.
Si pudieras traducirme el código a VB6 te lo agradecería.

Te pongo el link para que tengas 2 imágenes de las que he convertido, se llaman:
'O_MG_2356.JPG' esta es la original pesa 8071,2 Kb , y 'C_MG_2356.JPG' esta es la convertida y pesa 1235,2 Kb, un 84,7 % menos, la puedes ver con Photoshop u otro soft con el Zoom que quieras y no las puedes distinguir.
Y el tiempo que tarda con mi sistema y mspaint es 4 seg, por que hago una espera de 3 segundos por precaución y dar le tiempo a que cargue la imagen, para salvarla después.

https://dl.dropboxusercontent.com/u/51073224/Imagenes.rar

Un saludo

Neocortex

Lo pasé a Vb.net, como quiera y te da una idea... Por cierto pobré la imagen le pasó lo mismo que la versión que comprimes, y con 20kb de más jaja, ojo que mi script es prácticamente hacerlo a pie y es algo lento, tardo como 40-50 segundos en hacer todo (mappeo, conversion y guardado)



Public Sub listas()
Dim pixeles As Byte() = New Byte(50) {}
Dim pixeldos As Byte() = New Byte(254) {}
Dim sumador As Byte = 0
For i As Integer = 0 To pixeles.Length - 2
pixeles(i) = sumador
sumador += 5
Next
For i As Integer = 0 To pixeldos.Length - 2
pixeldos(i) = CByte(i)
Next
For i As Integer = 0 To intwidth - 1
For j As Integer = 0 To intheight - 1
'#Region "limites"
If arrayRed(i, j) = 0 OrElse arrayRed(i, j) < 2 Then
arrayRed(i, j) = 0
End If
If arrayGreen(i, j) = 0 OrElse arrayGreen(i, j) < 2 Then
arrayGreen(i, j) = 0
End If
If arrayBlue(i, j) = 0 OrElse arrayBlue(i, j) < 2 Then
arrayBlue(i, j) = 0
End If
If arrayRed(i, j) = 255 OrElse arrayRed(i, j) > 252 Then
arrayRed(i, j) = 255
End If
If arrayGreen(i, j) = 255 OrElse arrayGreen(i, j) > 252 Then
arrayGreen(i, j) = 255
End If
If arrayBlue(i, j) = 255 OrElse arrayBlue(i, j) > 252 Then
arrayBlue(i, j) = 255
End If
'#End Region
For m As Integer = 3 To pixeles.Length - 3
If arrayRed(i, j) >= pixeles(m) AndAlso arrayRed(i, j) < pixeles(m + 1) Then
arrayRed(i, j) = CByte(pixeles(m) + 2)
End If
If arrayBlue(i, j) >= pixeles(m) AndAlso arrayBlue(i, j) < pixeles(m + 1) Then
arrayBlue(i, j) = CByte(pixeles(m) + 2)
End If
If arrayGreen(i, j) >= pixeles(m) AndAlso arrayGreen(i, j) < pixeles(m + 1) Then
arrayGreen(i, j) = CByte(pixeles(m) + 2)
End If
Next
Next
Next
End Sub


For i As Integer = 0 To intwidth - 1
For j As Integer = 0 To intheight - 1
Dim clr As Color = img2.GetPixel(i, j)
red = clr.R
green = clr.G
blue = clr.B
arrayRed(i, j) = clr.R
arrayGreen(i, j) = clr.G
arrayBlue(i, j) = clr.B
Next
Next


Saludos

Fran1946

Gracias de nuevo.
Lo probaré para comparar los resultados a nivel de calidad y resolución, pero la diferencia de tiempo de conversión es enorme.

He hecho la conversión de una carpeta que tiene 86 imágenes del tamaño de la que te puse en el link, de 5184x3456 a 72 ppp.
Con mi sistema tarda 04:44:04 minutos en convertir las 86 imágenes.

Con tu sistema, tomando como promedio 45 seg/imagen, tardaría 64:50:00 minutos, o sea aproximadamente una hora, contra 4.5 minutos, 14.3

veces mas lento, y además según dices la imagen que te subí tiene 20 Kb mas que la misma convertida con mi sistema, mira la captura, le he

agregado un medidor de tiempo al programa.

Pero además el hecho de utilizar msPaint, tiene la ventaja de que el usuario esta viendo cada imagen que se esta convirtiendo, y cuanto

reduce el peso, en tiempo real.
Pero me gustaría saber como poder ejecutar al principio, mspaint con un tamaño de ventana pequeño, como la captura que subo.


subir fotos gratis


subir imagenes gratis

Saludos.

Neocortex

Creo que encontré la manera de mejorar el código, en la tarde que llegue a la casa le muevo al código y dejo resultados, aunque dudo llegar a la velocidad del mspaint  :laugh:

Saludos desde México

okik

#37
Hola de nuevo
Perdonad que me meta en vuestro intercambio de pareceres  :P

Repito lo que te dije Fran1946 tiempo atrás, si no quieres depender de mspaint, puedes usar las funciones GDI que se encuentran el gdi32.dll de windows, que no hace falta instalar porque va con el SO. Creo que en todas las versiones, en w98 o Me, no tengo ni idea, pero quien uso eso ya.

Como te dije no conozco mucho el tema del GDI, lo tengo pendiente. Por ahora solo tengo esto que si que he podido mirar. Como muestra que este es el camino si no me equivoco, o por lo menos uno más. A no ser que diseñes tu propio sistema de compresión.

https://mega.co.nz/#!zd8lkJZb!7tjONqVuRwIrBsp7SfbfzNDUTZIazreQXEoLP1XLvXI

Para cualquier duda sobre este código, puedes preguntarme




Ejemplo sencillo del uso de GDI para cambiar la intensidad de color.  


ca.caColorfulness = -100  'Convierte la imagen a blanco y negro


Código (vb) [Seleccionar]
Private Type COLORADJUSTMENT
       caSize As Integer
       caFlags As Integer
       caIlluminantIndex As Integer
       caRedGamma As Integer
       caGreenGamma As Integer
       caBlueGamma As Integer
       caReferenceBlack As Integer
       caReferenceWhite As Integer
       caContrast As Integer
       caBrightness As Integer
       caColorfulness As Integer
       caRedGreenTint As Integer
End Type

Private Declare Function SetColorAdjustment Lib "gdi32" _
(ByVal hdc As Long, _
lpca As COLORADJUSTMENT) As Long
Private Declare Function SetStretchBltMode Lib "gdi32" _
(ByVal hdc As Long, _
ByVal nStretchMode As Long) As Long
Private Declare Function GetColorAdjustment Lib "gdi32" _
(ByVal hdc As Long, _
lpca As COLORADJUSTMENT) As Long
Private Declare Function StretchBlt Lib "gdi32" _
(ByVal hdc As Long, _
ByVal x As Long, _
ByVal y As Long, ByVal nWidth As Long, _
ByVal nHeight As Long, _
ByVal hSrcDC As Long, _
ByVal xSrc As Long, _
ByVal ySrc As Long, _
ByVal nSrcWidth As Long, _
ByVal nSrcHeight As Long, _
ByVal dwRop As Long) As Long

Const HALFTONE = 4
Dim Imagen As StdPicture
Private Sub Form_Load()
'Valores para el HScroll
HScroll1.Max = 100
HScroll1.Min = -100
HScroll1.Value = 0

CommonDialog1.ShowOpen
CommonDialog1.Filter = "*.jpg file|*.jpg"

'Abre el cuadro de diálogo abrir y mete la imagen en la variable 'Imagen'
If Len(CommonDialog1.FileName) > 0 Then
Set Imagen = LoadPicture(CommonDialog1.FileName)
End If

'Valores para el Picture
Picture1.AutoRedraw = True
Picture1.ScaleMode = vbPixels
Picture1.Picture = Imagen

End Sub

Private Sub HScroll1_Scroll()
Dim ca As COLORADJUSTMENT
   With Picture1
      .Picture = Imagen 'Linea necesaria para actualizar la imagen
       SetStretchBltMode .hdc, HALFTONE 'No borrar
       GetColorAdjustment .hdc, ca 'No borrar
       ca.caColorfulness = HScroll1.Value 'Cambia el  valor de color de la imagen
       SetColorAdjustment .hdc, ca  'No borrar
       StretchBlt .hdc, 0, 0, .ScaleWidth, .ScaleHeight, .hdc, 0, 0, .ScaleWidth, .ScaleHeight, vbSrcCopy
       .Refresh
   End With
End Sub

Fran1946

Hola okik:

No hay nada que perdonar, te agradezco una vez más tu ayuda y aportes.
Citarsi no quieres depender de mspaint, puedes usar las funciones GDI que se encuentran el gdi32.dll de windows

A mi no me importa depender de mspaint, me es muy cómodo utilizarlo ya que me resuelve el objetivo de la aplicación, sin tener que complicarme en escribir y probar código, ya que solo lo utilizo cuando saco fotos y las almaceno en el HD con el mínimo peso posible, y esto lo hace perfecto la aplicación que ya la doy por terminada.
No obstante, tu aporte de código y el de otros posibles usuarios del foro, son importantes para aprender cuestiones que no se y quizás nunca utilice, pero el saber no ocupa lugar, y los guardo en mi colección para posibles usos futuros.
No creo que se pueda mejorar, ni siquiera igualar, el algoritmo de compresión de mspaint, a no ser que el GDI tenga una función especifica para salvar a JPG.

Por que el mspaint de Win 7 y posteriores, tiene mas herramientas, y sigue siendo muy básico, pero el algoritmo no tiene nada que ver con el mspaint de Win XP, no altera nada el peso, por eso no me sirve.
Si esta aplicación fuera solo para mí, que tengo Win XP y seguiré con el siempre, por que odio Win 7, Win 8, etc, no hubiera modificado el mspaint, pero tengo amigos y familiares que todos tienen Win 7 u 8.1, y la aplicación no funcionaría. 

Voy a probar tu código y te cuento.

Saludos.