faibuuk claud, convirtiendo archivos a imágenes

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Nota: inconcluso

Supongo que es bien tirado de las mechas. Pero el tema plantea un ejercicio interesante y lo suficientemente complejo como para pasar el tiempo libre (tambien sirve como analgésico mental). No tiene porqué ser faisbiuk, pero me anima la idea de aprovecharme de meta :).

Idea
Almacenar cualquier archivo en formato de imágen dentro de fb.

En principio parecía bastante sencillo, se tomaría un archivo, convertiría su contenido a hexadecimal y de ahí de acuerdo a una tabla, se convertiría cada caracter hex del par, en un valor de 0 a 255, el que luego sería guardado secuencialmente en un canal (vector) (en escala de grises), lo que conformaría los pixeles de la imágen. Con ésto, visualmente, se vería una imagen de 1xN píxels siendo N el largo del archivo en bytes multiplicado por 2 (archivo -> hex).
Para darle formato a la imagen, mejor dicho para formar una imagen de un ancho determinado, bastaría con dividir el vector en n piezas según el ancho deseado y rellenar la pieza -1 (la del final), con un color de fondo en el extremo derecho. Finalmente todo el contenido se volcaría en un archivo PNG con compresión nivel 9.

Y fué bastante sencillo, de hecho la codificación/decodificación no es muy compleja, pero es exacta, lo que representa un problema mayúsculo, porque no es tolerante a variaciones de luminocidad, lo que a su vez se anexa a otro problema, el relleno del fondo no está lo suficientemente alejado de uno de los extremos del rango 0-255.

Para ejemplificar, usaré el siguiente texto:

What is Lorem Ipsum?

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Why do we use it?

It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).

Where does it come from?

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

Luego de aplicar el proceso, se tiene la siguiente imagen resultante. La que si bien puede ser descargada desde el foro y decodificada con éxito, no tiene el mismo resultado desde una plataforma que aplica una conversión de formato o un procesado de cualquier tipo a la imagen subiente (la que se está subiendo a fb).

sample.txt.png


Como se puede ver a simple vista, no hay una profundidad de color de 1 bit, ya que aun no he calado el asunto lo suficiente, lo que me llevó a los 8bits por defecto. Tambien se aprecia el background blanco de relleno abajo a la derecha de la imagen, que es méramente una forma de reconocer cuando se acaba la data y empieza la basura descartable que hace de relleno.

Profundidad de 1 bit
Pudiera ahorrarme todos los problemas leyendo valores tendientes a los extremos 0 y 255, claramente si, pero entonces tendría 2 colores posibles para encodear la data + toda la información que da soporte al formato (en éste caso PNG), lo que en términos simples volvería el archivo resultante al menos 8 veces mas pesado que el original.

Pero ésto es demostrativo, así que hay que analizarlo.

El texto del ejemplo da un total de 2316 bytes, para de una forma simple, guardar toda la data en 2 colores (blanco y negro), se tendría que almacenar la data a nivel de bits y no de bytes, danto así un total de 18528 bits, lo que daría una resolución de aprox 137x137px. Aquí aplicaría el mismo algoritmo de mas abajo, de los valores proximales pero con una distancia considerablemente mas grande, pudiéndome saltar así las conversiones de formato con diferenciales en perfiles de color, paletas y luminocidad (hasta cierto punto), pero no así reducciones/ampliaciones.

Prosiguiendo con los 8bits

Acá es donde se complica la cosa.
Como mencioné antes, con la profundidad de color tengo el rango 0-255 en una escala de grises, (donde 0 es mas oscuro y 255 mas blanco), lo que me permitió entre otras cosas, crear un alfabeto equivalente al hexadecimal. Algo como lo siguiente:

Código:
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7:7
8:8
9: 9
a: 10
b: 11
c: 12
d: 13
e: 14
f: 15

Cuando se lee la data un archivo se obtienen datos binarios, lo que puede fácilmente ser traducido a hexadecimal. Lo pueden observar ustedes mismos con (en una terminal bash):

Bash:
xxd -p algún-archivo | tr -d '\n'
# Resultado
57686174206973204c6f72656d20497073756d3f0a4c6f72656d20497073

De ahí si toman el 5 y el 7, obtendrán 57, el primer par hex, con lo que formando los siguientes pares tendrían 57 68 61 74 20... cada par es un byte (8 bits). Y si se traduce con la tabla alfabeto, caracter a caracter (sin tener en cuenta el par), se obtendría lo siguiente:

Código:
5 7 6 8 6 1 7 4 2 0 6 9 7 3 2 0 4 13 6 15 7 2 6 5 6 13 2 0 4 9 7 0 7 3 7 5 6 13 3 15 0 10 4 12 ...

Lo que llevaría a una imagen totalmente negra con variaciones imperceptibles de color, efecto que para el proceso es transparente, hasta que ingresa fb a la ecuación. Y lo que en un principio a nivel de vectores de colores sería una equivalencia simple, se transforma en el problema principal. Provocando que la tabla alfabeto mute a lo siguiente:

Código:
0: 0
1: 15
2: 30
3: 45
4: 60
5: 75
6: 90
7: 105
8: 120
9: 135
a: 150
b: 165
c: 180
d: 195
e: 210
f: 225
BG FILL: 255

La idea era dejar brechas entremedio, lo posiblemente grandes como para sortear la variación de la luminocidad, que no es tanta como para ser indetectable, sin caer en el por menor de la teoría profunda de JPEG y el color RGB, ni sus ecuaciones relacionadas y/o conversiones raras (aún).

Ahora sí, la siguiente conversión pretende simular la operación upload/download desde fb y la respectiva conversión para estudiar la variación en la luminocidad. PNG -> JPG -> PNG.

Código:
Original:Convertida
(Comienzo del archivo)
30:31
75:72
75:76
0:0
60:60
60:56
60:64
90:91
30:32
195:193
45:46
15:13
30:32
210:212
45:38
105:109
0:0
150:151
45:48
45:41
30:36
0:0
45:40
0:4
30:28
0:0
90:92
225:228
90:85
30:35
90:91
150:147
0:0
150:148
45:47
180:184
45:44
180:177
30:31
225:226
75:75
60:59
...
255:255
255:255
255:255
255:254
255:253
255:255
255:255
255:254
(Final del archivo)

Se aprecia la variación de la luminocidad en la data, y creo que tambien ya se deja entrever la razón para seccionar en tramos la tabla alfabeto, tambien se ve al final como se aleja del valor de relleno. Para solucionar esto igual podría estirar un poco mas las secciones dependiendo de análisis futuros en relación a la distancia máxima observada con algunas muestras adicionales reales, así se podría tener un borde a cada lado del valor principal de la siguiente manera:

Código:
min en rango  <----- valor ----> max en rango (contiguo ->) | min en rango <----- valor ----> max en rango

Lo que se pudiera traducir en

Código:
... 187 <- 195 -> 202 | 203 <- 210 -> 217 ...

Pero dado que la corrupción estaría a la vuelta de la esquina como mucho, se me vino a la mente mover dicha solución con valores mas distanciados en base al modelo HSL. Con lo que pudiera perfectamente (o no tanto) definir la tabla en el espacio RGB y de ahí convertir a HSL cada elemento, situándome en cada color distinto, partiendo su luminocidad a la mitad y situando valores intercalados con un margen desde ambos extremos.

O mas gráficamente:

hsl-color-wheel.png


Para entender un poco mas el modelo HSL del color en el círuclo 2D. En el centro se tiene el negro y en los extremos el blanco. Si se traza una línea recta desde el centro hasta algún extremo (radio), entonces se tendrá la luminocidad, o mejor dicho, el porcentaje de luminocidad del color escogido, el que por cierto, se obtiene en base a los grados del círculo.

De ahí que y continuando con la idea anterior, pudiéndome situar al 20% de cada margen en el radio (dentro del espacio del color, justo en donde se vuelve claro y oscuro), tendría una diferenciación clara y mas de 16 puntos desde donde escoger para alimentar a la tabla alfabeto, pudiéndo situar la línea divisoria perfectamente en el 50% del radio del círculo. Pero esto supondría agrandar x3 la información contenida en el archivo PNG resultante (debido a que almacenaría 3 canales, R, G y B).

¿Por qué PNG?

Es menos complejo internamente que JPEG, e igualmente soportado por muchas plataformas, tambien tiene el canal en escala de grises que simplifica bastante las operaciones y soporta compresión, lo que transformó los 2.3kb del texto de ejemplo, en 1.5kb. Pero no necesariamente debe hacer todo el trabajo. Perfectamente se puede usar brotli en un nivel entre 3 y 5 para obtener mejores resultados, con una capa posterior de cifrado, y dejarlo en un álbum privado en fb.

Tambien existen otros formatos interesantes que se pueden usar para subir imágenes a fb, como GIF, lo que cae en la categoría de vídeos dentro de fb, situando el límite de subida en 10GB :), permitiendo así guardar una que otra imagen ISO.

Lo importante acá es sobrevivir a la conversión de formato, que bien podría ahorrármela sacando directamente de la estufa un archivo JPEG en pocas líneas (pero sería aburrido). Y otra ventaja, es que el archivo resultante en PNG es mas liviano, con lo que una vez tuviera la biblioteca armada, solo entraría a descargar en formato puntual.

A ver si cuando tenga mas tiempo, me animo a terminar ésto.

pd: en mi foto de perfil está el programa en una versión anterior, para quien se anime a decodificarlo :siii
 
Última modificación:

luis.3w

Miembro Regular
Se incorporó
4 Agosto 2019
Mensajes
82
interesante, por favor si sigues anda dejando los update para ir viendo el avance :zippy
 
Upvote 0

Carlos E. Flores

Zombie
Miembro del Equipo
MOD
Se incorporó
17 Marzo 2005
Mensajes
28.525
¿Por qué no con colores?

Te agrega variables y reduces la cantidad de datos en cada espectro.
 
Upvote 0

doncoyote

The ignored one
Se incorporó
23 Mayo 2006
Mensajes
2.723
alguna vez vi un documental en el que mostraban como usaban estos métodos para enviar mensajes ocultos en las ím;agenes, me acuerdo muy poco pero era muy similar.
 
Upvote 0

Carlos E. Flores

Zombie
Miembro del Equipo
MOD
Se incorporó
17 Marzo 2005
Mensajes
28.525
alguna vez vi un documental en el que mostraban como usaban estos métodos para enviar mensajes ocultos en las ím;agenes, me acuerdo muy poco pero era muy similar.

Por ahí por el 2000 recuerdo que habían ya programas para ocultar texto en imágenes. Funcionaban bien, pero es diferrente a esto que es como encriptar sólamente en un fondo sin información.
 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
¿Por qué no con colores?

Te agrega variables y reduces la cantidad de datos en cada espectro.
En principio no me quize calentar la cabeza, porque suponía una prueba de concepto, y me dió una paja tremenda leerme los papiros con la información relativa al color RGB (y peor todavía en JPEG). Usar nibbles en vez de bytes me daba un conjunto de 4bits perfecto para calzar la tabla en 16 valores distintos + 1 relleno. Aunque todo indica que sí me debería mover al espacio HSL por el diferencial en la distancia. Pero va a acarrear una triplicación de la información gracias a los canales RGB que de momento no se están almacenando en la imágen. Aún con JPEG, tengo aprox entre un 10 y 30% de incremento en el tamaño. Aunque no probé la variación entre conversiones de RGB, eso tambien lo tengo en la lista para mas adelante.

Por ahí por el 2000 recuerdo que habían ya programas para ocultar texto en imágenes. Funcionaban bien, pero es diferrente a esto que es como encriptar sólamente en un fondo sin información.
Mientras dormía pensaba en eso, pero no es tan sencillo, tendría que fabricarme una especie de tabla con los valores y añadirla en la meta data de la imágen o algo por el estilo.

Tampoco es que suponga algo taaan complejo, perfectamente se podría calzar la tabla alfabeto actual en 16 colores random, distanciados lo suficiente dentro del espacio RGB, generando todo algoritmicamente, pero volvería a tener que añadir la tabla con valores de referencia para mapear a partir de la imagen original la información escondida. Y habrían limitancias en el tamaño, ya que no se podría por ninguna parte almacenar una ISO en un wallpaper, levantaría enormes sospechas.

Pero me da curiosidad el como los nibbles, representados en ascii como elementos contiguos se pudieran comprimir, lo voy a probar mas rato, ya que el texto es siempre bien desinflado por algoritmos como zip.
edit: lo acabo de probar, ni brotli se la puede...

Acá lo que se ve, no es un fondo sin información, es literalmente "la información", porque el proceso convierte nibbles a 16 colores distintos, definidos en escala de grises. Por eso dividí los pares hex en 2 elementos distintos, cada par representa 8 bits y cada elemento del par 4 bits, lo que me devuelve al conjunto de 16 elementos + 1 relleno para el fondo que es completamente opcional.

La idea la tomé de un proyecto que vi en un reel, en donde usaban a yt como cloud aprovechando la compresión que ofrecen ciertos formatos de vídeo.
 
Última modificación:
Upvote 0

jarayama

Capo
Se incorporó
30 Julio 2005
Mensajes
119
Yo creo que un ejemplo práctico se encuentra en los métodos para almacenar datos en los VHS, alcanzaban a almacenar bastante data, para la epoca, con procesos parecidos para codificar, añadiendo verificaciones de redundancia y todas esas cosas.
 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
He estado explorando el comportamiento de fb al subir archivos PNG. Aún no salgo de la escala de grises, pero encontré como no triplicar la información en el espacio RGB, era tan obvio que lo había pasado por alto, así que antes de comentar sobre fb me gustaría describir como en el upgrade del código que estoy haciendo, deseché temporalmente la tabla alfabeto.

Básicamente si tuvieramos un archivo cualquiera, y lo leyeramos en modo binario con algún lenguaje como python, obtendríamos bytes, o grupos de 8 bits, los cuales pueden representar un rango entero de entre 0~255 en su formato unsigned y de -127~127 con signo. Entonces, por si alguien que no entiende mucho lee, si toma la calculadora y multiplica 127*2 obtendrá 254, no 255, ahí hay que considerar tambien el 0 como elemento representable, así que sumándole 1 ya se obtiene el 255.

Los mas capos ya se imaginarán a donde voy :)

El RGB consta de 3 valores distintos, que dan un total de 24 bits (3 * 8 = 24, 3 bytes). De los cuales cada byte representa uno de los colores de los leds (RED = Rojo, GREEN = Verde, BLUE = Azúl), cada valor como ya supondrán, puede tomar un valor unsigned (sin signo negativo) entero de 0~255. Sip, era muy obvia la cosa.

Entonces para componer una imagen a groso modo, vamos con un ejemplo teórico. Por ejemplo un tablero de ajedréz, el que se compone de 8 filas horizontales y 8 columnas verticales, osea una matriz de 8x8.

Código:
// Tablero de ajedréz de 8x8
m = [
 [0, 255, 0, 255, 0, 255, 0, 255],
 [255, 0, 255, 0, 255, 0, 255, 0],
 [0, 255, 0, 255, 0, 255, 0, 255],
 [255, 0, 255, 0, 255, 0, 255, 0],
 [0, 255, 0, 255, 0, 255, 0, 255],
 [255, 0, 255, 0, 255, 0, 255, 0],
 [0, 255, 0, 255, 0, 255, 0, 255],
 [255, 0, 255, 0, 255, 0, 255, 0],
]
alto = largo( m )
ancho = largo( m[0] )

De ahí podemos sacar en claro, que el largo de la matriz es 8, lo que vendría siendo el alto de la imagen, y el largo del primer elemento de la matriz, que corresponde a un vector es 8, lo que sería el ancho de la imagen. En lenguajes distintos a Python una matriz no tiene necesariamente que contener vectores dentro de un vector general, pero ya que Python la lleva en la modernidad para éstas cosas, seguro que la juventud lo entiende mas fácil.

Por si no se dieron cuenta, la matriz está compuesta por colores en escala de grises, por lo que ahora tocaría mover todo a un espacio RGB, o lo que comúnmente se ve en algunas librerías/módulos: modo L a modo RGB.

Código:
// Conversión a RGB
negro = ( 0, 0, 0 )
blanco = ( 255, 255, 255 )
m = [
 [negro, blanco, negro, blanco, negro, blanco, negro, blanco],
 [blanco, negro, blanco, negro, blanco, negro, blanco, negro],
 [negro, blanco, negro, blanco, negro, blanco, negro, blanco],
 [blanco, negro, blanco, negro, blanco, negro, blanco, negro],
 [negro, blanco, negro, blanco, negro, blanco, negro, blanco],
 [blanco, negro, blanco, negro, blanco, negro, blanco, negro],
 [negro, blanco, negro, blanco, negro, blanco, negro, blanco],
 [blanco, negro, blanco, negro, blanco, negro, blanco, negro],
]

Si, en efecto, no me pensaba dar la paja de escribir las tuplas de valores RGB, supongo que se entiende la conversión. Igual pueden usar algúna herramienta online como color-hex.com para ver colores y esas cosas.

Prosiguiendo con la explicación, ya tenemos una imágen de 8x8 pixeles que representa un tablero de ajedréz, algo así como, pero sin todo el añadido de la imagen que pillé en Google.

tablero-ajedrez-de-icono-del-juego-ejemplo-vector-dise%C3%B1o-plano-131916161.jpg


Ahora de donde viene la parte obvia que al parecer no era tan obvia.

De la lectura del flujo binario, desde el archivo que se está leyendo para ser convertido a una imágen, se obtienen en efecto bytes, osea, valores que representan el rango entero 0~255, así que si quisiéramos convertir algún texto para representarlo en la tabla solo tendríamos que ir apilando uno detrás de otro los bytes. Veámoslo en un ejemplo para que sea un poco mas sencillo.

El texto a tomar debería ser de 8x8 bytes, es decir de 64 bytes máximo. Nos cabe poco mas de una estrofa de una canción en escala de grises.

Código:
Eran las diez de la noche, piloteaba mi nave
Era mi taxi un Volk

Lo que traducido a enteros carácter a carácter se vería como:

Código:
v = [69, 114, 97, 110, 32, 108, 97, 115, 32, 100, 105, 101, 122, 32, 100, 101, 32, 108, 97, 32, 110, 111, 99, 104, 101, 44, 32, 112, 105, 108, 111, 116, 101, 97, 98, 97, 32, 109, 105, 32, 110, 97, 118, 101, 10, 69, 114, 97, 32, 109, 105, 32, 116, 97, 120, 105, 32, 117, 110, 32, 86, 111, 108, 107]

Ahora lo único que tendríamos que hacer sería dividir el vector v en pedazos iguales de 8 elementos de la siguiente manera:

Código:
m = [
 [69, 114, 97, 110, 32, 108, 97, 115],
 [32, 100, 105, 101, 122, 32, 100, 101],
 [32, 108, 97, 32, 110, 111, 99, 104],
 [101, 44, 32, 112, 105, 108, 111, 116],
 [101, 97, 98, 97, 32, 109, 105, 32],
 [110, 97, 118, 101, 10, 69, 114, 97],
 [32, 109, 105, 32, 116, 97, 120, 105],
 [32, 117, 110, 32, 86, 111, 108, 107]
]

Y listop, ya tendríamos nuestra imágen de 8x8px y 64 pixeles en total.

1716697102658.png


Ahora si nos pasamos del espacio en escala de grises al RGB, podríamos almacenar la misma cantidad de datos en un formato ligeramente diferente, ajustando la resolución a los datos de tal forma que pudieramos almacenar la canción entera que consta de 2316 bytes (incluyendo saltos de línea) en una imágen de 28x28 pixeles y nos sobraría espacio, propiamente 12 bytes. Lo que matemáticamente sería:

Código:
largo_data = 2316
// 772 pixeles
pixeles_requeridos = largo_data / 3
// 27,7849 -> redondeo -> 28
ancho_imagen = redondeo_arriba( raíz cuadrada( pixeles_requeridos ) )
largo_imagen = ancho_imagen
// bytes sobrantes: pixeles_requeridos - ( ancho_imagen ^ 2 ) = 784
// 784 - 772 = 12
// bytes sobrantes: 12 bytes

Ahora se están preguntando si un caracter equivale a 1 byte, y tenemos 2316 caracteres, entonces tenemos 1,14kb o 2316 bytes, ¿cierto?. Para entender ésto tenemos que volver al pixel RGB donde 1 pixel se compone por 3 valores, de modo que podemos dividir el total de caracteres entre 3 para de ésta forma evitar triplicar la data. Lo que entre otras cosas elimina por completo el uso de imágenes JPEG com pérdida y nos mueve de nuevo a GIF y PNG.

Solo por darles el gusto voy a meter la letra entera para que vean como quedaría (y ojo que pensaba meter el padre nuestro :uy ).

Código:
Eran las diez de la noche, piloteaba mi nave
Era mi taxi un VolksWagen del año 68
Era un dia de esos malos donde no hubo pasaje
Las lentejuelas de un traje me hicieron la parada
Era una rubia preciosa
Llevaba minifalda
El escote en su espalda
Llegaba justo a la gloria
Una lágrima negra rodaba en su mejilla
Mientras que el retrovisor decía: "ve, qué pantorillas"
Yo vi un poco más, ah-ah-ah
Eran las diez con cuarenta, sigzagueaba en reforma
Me dijo: "me llamo Norma" mientras cruzaba la pierna
Sacó un cigarro algo extraño, de esos que te dan risa
Le ofrecí fuego de prisa y me temblaba la mano
Le pregunté: "¿por quién llora?"
Y me dijo: "por un tipo
Que se cree que por rico
Puede venir a engañarme"
No caiga usted por amores
Debe de levantarse (Le dije)
Cuente con un servidor
Si lo que quiere es vengarse
Y me sonrió, oh-oh-oh, oh-oh-oh
¿Qué es lo que hace un taxista seduciendo a la vida?
¿Qué es lo que hace un taxista construyendo una herida?
¿Qué es lo que hace un taxista en frente de una dama?
¿Qué es lo que hace un taxista con sus sueños de cama?
Me pregunté, eh-eh-eh, eh-eh-eh
Lo vi abrazando y besando a una humilde muchacha
Es de clase muy sencilla, lo sé por su facha
Me sonreía en el espejo y se sentaba de lado
Yo estaba idiotizado con el espejo empañado
Me dijo: "doble en la esquina
Iremos hasta mi casa
Después de un par de tequilas
Veremos qué es lo que pasa"
Para qué describir lo que hicimos en la alfombra
Si basta con resumir que le besé hasta la sombra
Y un poco más, ah-ah-ah-ah
No se sienta usted tan sola
Sufro, aunque no es lo mismo
Mi mujer y mi horario
Han abierto un abismo
Cómo se sufre ambos lados de las clases sociales
Usted sufre en su mansión, yo sufro en los arrabales
Me dijo: "vente conmigo, que sepa, no estoy sola"
Se hizo, en el pelo, una cola, fuimos al bar donde estaba
Entramos, precisamente
Él abrazaba una chica
Mira si es grande el destino
Y esta ciudad es chica
Era mi mujer, eh-eh-eh, eh-eh-eh
¿Qué es lo que hace un taxista seduciendo a la vida?
¿Qué es lo que hace un taxista construyendo una herida?
¿Qué es lo que hace un taxista cuando un caballero
Coincide con su mujer en horario y esmero?
Me pregunté, eh-eh-eh, eh-eh-eh
Desde aquella noche, ellos juegan a engañarnos
Se ven en el mismo bar
Y la rubia para el taxi siempre a las diez en el mismo
Lugar

Matriz con datos RGB
Código:
m = [
[[69, 114, 97], [110, 32, 108], [97, 115, 32], [100, 105, 101], [122, 32, 100], [101, 32, 108], [97, 32, 110], [111, 99, 104], [101, 44, 32], [112, 105, 108], [111, 116, 101], [97, 98, 97], [32, 109, 105], [32, 110, 97], [118, 101, 10], [69, 114, 97], [32, 109, 105], [32, 116, 97], [120, 105, 32], [117, 110, 32], [86, 111, 108], [107, 115, 87], [97, 103, 101], [110, 32, 100], [101, 108, 32], [97, 241, 111], [32, 54, 56], [10, 69, 114]]
[[97, 32, 117], [110, 32, 100], [105, 97, 32], [100, 101, 32], [101, 115, 111], [115, 32, 109], [97, 108, 111], [115, 32, 100], [111, 110, 100], [101, 32, 110], [111, 32, 104], [117, 98, 111], [32, 112, 97], [115, 97, 106], [101, 10, 76], [97, 115, 32], [108, 101, 110], [116, 101, 106], [117, 101, 108], [97, 115, 32], [100, 101, 32], [117, 110, 32], [116, 114, 97], [106, 101, 32], [109, 101, 32], [104, 105, 99], [105, 101, 114], [111, 110, 32]]
[[108, 97, 32], [112, 97, 114], [97, 100, 97], [10, 69, 114], [97, 32, 117], [110, 97, 32], [114, 117, 98], [105, 97, 32], [112, 114, 101], [99, 105, 111], [115, 97, 10], [76, 108, 101], [118, 97, 98], [97, 32, 109], [105, 110, 105], [102, 97, 108], [100, 97, 10], [69, 108, 32], [101, 115, 99], [111, 116, 101], [32, 101, 110], [32, 115, 117], [32, 101, 115], [112, 97, 108], [100, 97, 10], [76, 108, 101], [103, 97, 98], [97, 32, 106]]
[[117, 115, 116], [111, 32, 97], [32, 108, 97], [32, 103, 108], [111, 114, 105], [97, 10, 85], [110, 97, 32], [108, 225, 103], [114, 105, 109], [97, 32, 110], [101, 103, 114], [97, 32, 114], [111, 100, 97], [98, 97, 32], [101, 110, 32], [115, 117, 32], [109, 101, 106], [105, 108, 108], [97, 10, 77], [105, 101, 110], [116, 114, 97], [115, 32, 113], [117, 101, 32], [101, 108, 32], [114, 101, 116], [114, 111, 118], [105, 115, 111], [114, 32, 100]]
[[101, 99, 237], [97, 58, 32], [34, 118, 101], [44, 32, 113], [117, 233, 32], [112, 97, 110], [116, 111, 114], [105, 108, 108], [97, 115, 34], [10, 89, 111], [32, 118, 105], [32, 117, 110], [32, 112, 111], [99, 111, 32], [109, 225, 115], [44, 32, 97], [104, 45, 97], [104, 45, 97], [104, 10, 69], [114, 97, 110], [32, 108, 97], [115, 32, 100], [105, 101, 122], [32, 99, 111], [110, 32, 99], [117, 97, 114], [101, 110, 116], [97, 44, 32]]
[[115, 105, 103], [122, 97, 103], [117, 101, 97], [98, 97, 32], [101, 110, 32], [114, 101, 102], [111, 114, 109], [97, 10, 77], [101, 32, 100], [105, 106, 111], [58, 32, 34], [109, 101, 32], [108, 108, 97], [109, 111, 32], [78, 111, 114], [109, 97, 34], [32, 109, 105], [101, 110, 116], [114, 97, 115], [32, 99, 114], [117, 122, 97], [98, 97, 32], [108, 97, 32], [112, 105, 101], [114, 110, 97], [10, 83, 97], [99, 243, 32], [117, 110, 32]]
[[99, 105, 103], [97, 114, 114], [111, 32, 97], [108, 103, 111], [32, 101, 120], [116, 114, 97], [241, 111, 44], [32, 100, 101], [32, 101, 115], [111, 115, 32], [113, 117, 101], [32, 116, 101], [32, 100, 97], [110, 32, 114], [105, 115, 97], [10, 76, 101], [32, 111, 102], [114, 101, 99], [237, 32, 102], [117, 101, 103], [111, 32, 100], [101, 32, 112], [114, 105, 115], [97, 32, 121], [32, 109, 101], [32, 116, 101], [109, 98, 108], [97, 98, 97]]
[[32, 108, 97], [32, 109, 97], [110, 111, 10], [76, 101, 32], [112, 114, 101], [103, 117, 110], [116, 233, 58], [32, 34, 191], [112, 111, 114], [32, 113, 117], [105, 233, 110], [32, 108, 108], [111, 114, 97], [63, 34, 10], [89, 32, 109], [101, 32, 100], [105, 106, 111], [58, 32, 34], [112, 111, 114], [32, 117, 110], [32, 116, 105], [112, 111, 10], [81, 117, 101], [32, 115, 101], [32, 99, 114], [101, 101, 32], [113, 117, 101], [32, 112, 111]]
[[114, 32, 114], [105, 99, 111], [10, 80, 117], [101, 100, 101], [32, 118, 101], [110, 105, 114], [32, 97, 32], [101, 110, 103], [97, 241, 97], [114, 109, 101], [34, 10, 78], [111, 32, 99], [97, 105, 103], [97, 32, 117], [115, 116, 101], [100, 32, 112], [111, 114, 32], [97, 109, 111], [114, 101, 115], [10, 68, 101], [98, 101, 32], [100, 101, 32], [108, 101, 118], [97, 110, 116], [97, 114, 115], [101, 32, 40], [76, 101, 32], [100, 105, 106]]
[[101, 41, 10], [67, 117, 101], [110, 116, 101], [32, 99, 111], [110, 32, 117], [110, 32, 115], [101, 114, 118], [105, 100, 111], [114, 10, 83], [105, 32, 108], [111, 32, 113], [117, 101, 32], [113, 117, 105], [101, 114, 101], [32, 101, 115], [32, 118, 101], [110, 103, 97], [114, 115, 101], [10, 89, 32], [109, 101, 32], [115, 111, 110], [114, 105, 243], [44, 32, 111], [104, 45, 111], [104, 45, 111], [104, 44, 32], [111, 104, 45], [111, 104, 45]]
[[111, 104, 10], [191, 81, 117], [233, 32, 101], [115, 32, 108], [111, 32, 113], [117, 101, 32], [104, 97, 99], [101, 32, 117], [110, 32, 116], [97, 120, 105], [115, 116, 97], [32, 115, 101], [100, 117, 99], [105, 101, 110], [100, 111, 32], [97, 32, 108], [97, 32, 118], [105, 100, 97], [63, 10, 191], [81, 117, 233], [32, 101, 115], [32, 108, 111], [32, 113, 117], [101, 32, 104], [97, 99, 101], [32, 117, 110], [32, 116, 97], [120, 105, 115]]
[[116, 97, 32], [99, 111, 110], [115, 116, 114], [117, 121, 101], [110, 100, 111], [32, 117, 110], [97, 32, 104], [101, 114, 105], [100, 97, 63], [10, 191, 81], [117, 233, 32], [101, 115, 32], [108, 111, 32], [113, 117, 101], [32, 104, 97], [99, 101, 32], [117, 110, 32], [116, 97, 120], [105, 115, 116], [97, 32, 101], [110, 32, 102], [114, 101, 110], [116, 101, 32], [100, 101, 32], [117, 110, 97], [32, 100, 97], [109, 97, 63], [10, 191, 81]]
[[117, 233, 32], [101, 115, 32], [108, 111, 32], [113, 117, 101], [32, 104, 97], [99, 101, 32], [117, 110, 32], [116, 97, 120], [105, 115, 116], [97, 32, 99], [111, 110, 32], [115, 117, 115], [32, 115, 117], [101, 241, 111], [115, 32, 100], [101, 32, 99], [97, 109, 97], [63, 10, 77], [101, 32, 112], [114, 101, 103], [117, 110, 116], [233, 44, 32], [101, 104, 45], [101, 104, 45], [101, 104, 44], [32, 101, 104], [45, 101, 104], [45, 101, 104]]
[[10, 76, 111], [32, 118, 105], [32, 97, 98], [114, 97, 122], [97, 110, 100], [111, 32, 121], [32, 98, 101], [115, 97, 110], [100, 111, 32], [97, 32, 117], [110, 97, 32], [104, 117, 109], [105, 108, 100], [101, 32, 109], [117, 99, 104], [97, 99, 104], [97, 10, 69], [115, 32, 100], [101, 32, 99], [108, 97, 115], [101, 32, 109], [117, 121, 32], [115, 101, 110], [99, 105, 108], [108, 97, 44], [32, 108, 111], [32, 115, 233], [32, 112, 111]]
[[114, 32, 115], [117, 32, 102], [97, 99, 104], [97, 10, 77], [101, 32, 115], [111, 110, 114], [101, 237, 97], [32, 101, 110], [32, 101, 108], [32, 101, 115], [112, 101, 106], [111, 32, 121], [32, 115, 101], [32, 115, 101], [110, 116, 97], [98, 97, 32], [100, 101, 32], [108, 97, 100], [111, 10, 89], [111, 32, 101], [115, 116, 97], [98, 97, 32], [105, 100, 105], [111, 116, 105], [122, 97, 100], [111, 32, 99], [111, 110, 32], [101, 108, 32]]
[[101, 115, 112], [101, 106, 111], [32, 101, 109], [112, 97, 241], [97, 100, 111], [10, 77, 101], [32, 100, 105], [106, 111, 58], [32, 34, 100], [111, 98, 108], [101, 32, 101], [110, 32, 108], [97, 32, 101], [115, 113, 117], [105, 110, 97], [10, 73, 114], [101, 109, 111], [115, 32, 104], [97, 115, 116], [97, 32, 109], [105, 32, 99], [97, 115, 97], [10, 68, 101], [115, 112, 117], [233, 115, 32], [100, 101, 32], [117, 110, 32], [112, 97, 114]]
[[32, 100, 101], [32, 116, 101], [113, 117, 105], [108, 97, 115], [10, 86, 101], [114, 101, 109], [111, 115, 32], [113, 117, 233], [32, 101, 115], [32, 108, 111], [32, 113, 117], [101, 32, 112], [97, 115, 97], [34, 10, 80], [97, 114, 97], [32, 113, 117], [233, 32, 100], [101, 115, 99], [114, 105, 98], [105, 114, 32], [108, 111, 32], [113, 117, 101], [32, 104, 105], [99, 105, 109], [111, 115, 32], [101, 110, 32], [108, 97, 32], [97, 108, 102]]
[[111, 109, 98], [114, 97, 10], [83, 105, 32], [98, 97, 115], [116, 97, 32], [99, 111, 110], [32, 114, 101], [115, 117, 109], [105, 114, 32], [113, 117, 101], [32, 108, 101], [32, 98, 101], [115, 233, 32], [104, 97, 115], [116, 97, 32], [108, 97, 32], [115, 111, 109], [98, 114, 97], [10, 89, 32], [117, 110, 32], [112, 111, 99], [111, 32, 109], [225, 115, 44], [32, 97, 104], [45, 97, 104], [45, 97, 104], [45, 97, 104], [10, 78, 111]]
[[32, 115, 101], [32, 115, 105], [101, 110, 116], [97, 32, 117], [115, 116, 101], [100, 32, 116], [97, 110, 32], [115, 111, 108], [97, 10, 83], [117, 102, 114], [111, 44, 32], [97, 117, 110], [113, 117, 101], [32, 110, 111], [32, 101, 115], [32, 108, 111], [32, 109, 105], [115, 109, 111], [10, 77, 105], [32, 109, 117], [106, 101, 114], [32, 121, 32], [109, 105, 32], [104, 111, 114], [97, 114, 105], [111, 10, 72], [97, 110, 32], [97, 98, 105]]
[[101, 114, 116], [111, 32, 117], [110, 32, 97], [98, 105, 115], [109, 111, 10], [67, 243, 109], [111, 32, 115], [101, 32, 115], [117, 102, 114], [101, 32, 97], [109, 98, 111], [115, 32, 108], [97, 100, 111], [115, 32, 100], [101, 32, 108], [97, 115, 32], [99, 108, 97], [115, 101, 115], [32, 115, 111], [99, 105, 97], [108, 101, 115], [10, 85, 115], [116, 101, 100], [32, 115, 117], [102, 114, 101], [32, 101, 110], [32, 115, 117], [32, 109, 97]]
[[110, 115, 105], [243, 110, 44], [32, 121, 111], [32, 115, 117], [102, 114, 111], [32, 101, 110], [32, 108, 111], [115, 32, 97], [114, 114, 97], [98, 97, 108], [101, 115, 10], [77, 101, 32], [100, 105, 106], [111, 58, 32], [34, 118, 101], [110, 116, 101], [32, 99, 111], [110, 109, 105], [103, 111, 44], [32, 113, 117], [101, 32, 115], [101, 112, 97], [44, 32, 110], [111, 32, 101], [115, 116, 111], [121, 32, 115], [111, 108, 97], [34, 10, 83]]
[[101, 32, 104], [105, 122, 111], [44, 32, 101], [110, 32, 101], [108, 32, 112], [101, 108, 111], [44, 32, 117], [110, 97, 32], [99, 111, 108], [97, 44, 32], [102, 117, 105], [109, 111, 115], [32, 97, 108], [32, 98, 97], [114, 32, 100], [111, 110, 100], [101, 32, 101], [115, 116, 97], [98, 97, 10], [69, 110, 116], [114, 97, 109], [111, 115, 44], [32, 112, 114], [101, 99, 105], [115, 97, 109], [101, 110, 116], [101, 10, 201], [108, 32, 97]]
[[98, 114, 97], [122, 97, 98], [97, 32, 117], [110, 97, 32], [99, 104, 105], [99, 97, 10], [77, 105, 114], [97, 32, 115], [105, 32, 101], [115, 32, 103], [114, 97, 110], [100, 101, 32], [101, 108, 32], [100, 101, 115], [116, 105, 110], [111, 10, 89], [32, 101, 115], [116, 97, 32], [99, 105, 117], [100, 97, 100], [32, 101, 115], [32, 99, 104], [105, 99, 97], [10, 69, 114], [97, 32, 109], [105, 32, 109], [117, 106, 101], [114, 44, 32]]
[[101, 104, 45], [101, 104, 45], [101, 104, 44], [32, 101, 104], [45, 101, 104], [45, 101, 104], [10, 191, 81], [117, 233, 32], [101, 115, 32], [108, 111, 32], [113, 117, 101], [32, 104, 97], [99, 101, 32], [117, 110, 32], [116, 97, 120], [105, 115, 116], [97, 32, 115], [101, 100, 117], [99, 105, 101], [110, 100, 111], [32, 97, 32], [108, 97, 32], [118, 105, 100], [97, 63, 10], [191, 81, 117], [233, 32, 101], [115, 32, 108], [111, 32, 113]]
[[117, 101, 32], [104, 97, 99], [101, 32, 117], [110, 32, 116], [97, 120, 105], [115, 116, 97], [32, 99, 111], [110, 115, 116], [114, 117, 121], [101, 110, 100], [111, 32, 117], [110, 97, 32], [104, 101, 114], [105, 100, 97], [63, 10, 191], [81, 117, 233], [32, 101, 115], [32, 108, 111], [32, 113, 117], [101, 32, 104], [97, 99, 101], [32, 117, 110], [32, 116, 97], [120, 105, 115], [116, 97, 32], [99, 117, 97], [110, 100, 111], [32, 117, 110]]
[[32, 99, 97], [98, 97, 108], [108, 101, 114], [111, 10, 67], [111, 105, 110], [99, 105, 100], [101, 32, 99], [111, 110, 32], [115, 117, 32], [109, 117, 106], [101, 114, 32], [101, 110, 32], [104, 111, 114], [97, 114, 105], [111, 32, 121], [32, 101, 115], [109, 101, 114], [111, 63, 10], [77, 101, 32], [112, 114, 101], [103, 117, 110], [116, 233, 44], [32, 101, 104], [45, 101, 104], [45, 101, 104], [44, 32, 101], [104, 45, 101], [104, 45, 101]]
[[104, 10, 68], [101, 115, 100], [101, 32, 97], [113, 117, 101], [108, 108, 97], [32, 110, 111], [99, 104, 101], [44, 32, 101], [108, 108, 111], [115, 32, 106], [117, 101, 103], [97, 110, 32], [97, 32, 101], [110, 103, 97], [241, 97, 114], [110, 111, 115], [10, 83, 101], [32, 118, 101], [110, 32, 101], [110, 32, 101], [108, 32, 109], [105, 115, 109], [111, 32, 98], [97, 114, 10], [89, 32, 108], [97, 32, 114], [117, 98, 105], [97, 32, 112]]
[[97, 114, 97], [32, 101, 108], [32, 116, 97], [120, 105, 32], [115, 105, 101], [109, 112, 114], [101, 32, 97], [32, 108, 97], [115, 32, 100], [105, 101, 122], [32, 101, 110], [32, 101, 108], [32, 109, 105], [115, 109, 111], [10, 76, 117], [103, 97, 114], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0]]
]

Nótece que al final en la última fila, se aprecian 12 elementos repletos de ceros ( [0,0,0] ) (negro en RGB). Ésto corresponde al relleno de los 12 pixeles que sobraban.

Acá me quisiera detener, porque de alguna manera hay que separar la mierda de relleno de la data válida, y en mas de una ocasión el último pixel de la sección de datos de la imagen, será o negro o blanco, 0x000000 y 0xffffff respectivamente (0,0,0); (255, 255, 255).

Para ésto se me ocurrió usar valores opuestos al último pixel, como de momento estamos operando con blanco y negro, voy a ejemplificarlo así.

Código:
// Valor desconocido del último pixel
ul = ?

// Valor por defecto
r = 0

// Al estilo de PSInt para los jóvenes :)
Si ul es 0 Entonces
  r = 255
SiNo Si ul es 255 Entonces
  r = 0
// ul > 0 && ul <= 127
SiNo Si ul entre 1 y 127 Entonces
  r = 255
// ul > 127 && ul <= 255
SiNo Si ul entre 128 y 254 Entonces
  r = 0
Fin Si

De ésta forma podría separar la mierda de una forma útil. Igual se podría agregar un identificador de 4 bytes si el espacio alcanza, y si se está muy justo, dejar solo el identificador (marcador), algo como 0xfa 0xfb 0xfc 0xfd, algo que difícilmente se encuentre al interior de un archivo (aunque tengo mis dudas al respecto).

Ahora que ya terminé con la explicación inicial volvamos a fb, pero en otro comentario para separar las cosas.
 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
¿Se recuerdan de mi técnica de valores proximales, que describí al principio?

Código:
... 187 <- 195 -> 202 | 203 <- 210 -> 217 ...

Fb me mando a la b, porque entre otras cosas las diferencias son mucho mas grandes de lo que puede soportar el rango 0~255 dividido en 16 secciones. Realicé una coomparación y tenía distancias entre valores de +40, así que me di la paja de hacer algunas pruebas que me arrojaron valores interesantes.

Primero que todo, probé algunos módulos en Python como PIL para juguetear con JPEG, lo que resultó en un rotundo fracaso, porque no maneja JPEG sin pérdida (hasta donde leí), y en la práctica, la imágen cambiaba la información en cada generación, lo que hacía imposible la recuperación con valores proximales tan cercanos. Pero sin embargo, abre la posibilidad a probar con valores proximales distanciados como lo describí anteriormente, haciendo uso del espacio HSV, mediante la elección de puntos arbitrarios con la variación de la luminocidad como parametro de corte para de esa manera recuperar la información. Cosa que voy a avanzar mas adelante.

Lo segundo que hice fué investigar como fb procesaba la imagen internamente, así que escarbando en internet encontré una nota de ingeniería de meta que explicaba mas menos como incrustaban el perfil de color que usan, y otras cosas. Así que lo siguiente fué subir imágenes para revisar si desde un mismo origen (archivo PNG), en subidas sucesivas, las imágenes resultantes sufrían cambios significativos (a diferencia de la prueba anterior).

Acá los resultados me dejaron en la duda, tienen un proceso interno que se encarga de realizar la conversión de las imágenes a JPEG, el creo que reutiliza las imágenes en base a algún hash. Lo sé porque subí la misma imágen varias veces y en ningún caso tuvo un cambio en la data. Pero el proceso no calcula en base a un hash completo, porque en una de las subidas modifiqué el último pixel y no hubo cambios, en cambio, modifiqué el primer pixel, y todo el contenido de la imagen cambio considerablemente. Seguramente por temas de optimización solo lee el comienzo de cada imagen.

Tambien probé subir las imágenes desde distintas cuentas de fb y obtuve el mismo resultado, sin cambios, no sé si tomará en cuenta la IP o reconocerá la imágen de otra manera en base a algún tipo de caché o algo por el estilo.

Finalmente probé bajarme imágenes random desde fb, volverlas a subir, y luego a descargar. Y no obtuve cambios, la imagen fuente era idéntica a la subida y descargada.

Donde si obtuve cambios significativos fué cuando subí una imagen JPEG cualquiera y la descargué, fb realiza una reducción en el tamaño de aprox del 50%, la imagen original tenía 1.3MB y quedó en aprox 650kb. Así que en teoría, los valores proximales en un espacio de color deberían dar alguna esperanza.

Tambien había pensado reducir el alfabeto de 16 elementos a 8 a ver como me va, pero será para otro día.

Terminando el upgrade del programilla, voy a ponerme manos a la obra con GIF a ver si puedo subir alguna ISO o archivos grandes aprovechando los 10GB que ofrece fb por vídeo. De momento ya podría tirar mano de servicios como imgbb o imgur, aprovechando que muchos de esos ofrecen API.
 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Me paso a dejar unas muestras de un pdf con el que estoy haciendo pruebas en PNG para color y escala de grises. Una curiosidad de la que no me había dado cuenta en la generación, es que como el byn (blanco y negro) solo tiene 1 canal, la resolución resultante es mas grande que cuando se usa color.

Dejé el PDF que utilicé como archivo de origen para quien se anime a realizar la codificación/decodificación. Utilicé el último método que describí acá.


 

Archivo adjunto

  • test.pdf
    1 MB · Visitas: 75
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Pensando en que las imágenes pudiéran ser fácilmente decodificables, escribí un pequeño algoritmo para hacer algo parecido al cifrado del Cesar, pero basado en /dev/urandom.

La idea es poder hacer una sustitución de un byte en particular en base a un diccionario que contiene de 256 elementos (0~255), teniendo como clave el byte original y el byte a sustituir, algo así:

Código:
{
  0: 178,
  1: 234,
  3: 23,
  4: 89,
 ...
}

Así los datos en la imágen estarían sustituidos obteniéndose de esa manera, basura ilegible si algún curioso worker en alguna plataforma se preguntara porque tantas imágenes con "estática". Igual la sustitución es fácilmente sorteable, así que para eso pensé en urandom.

Generación de una lista de 256 elementos aleatóriamente reordenada.

El algoritmo es bien sencillo, primero se necesita un vector con los 256 elementos, cada uno en rango 0~255. De ahí se harán pasadas en un loop de acuerdo a N realizando intercambios de un elemento a por b, lo ideal es que N sea mayor o igual a 10000, de ésta manera al menos se tendrá una gran posibilidad de que el reordenamiento sea efectivamente a lo largo del vector y no queden valores contiguos. Algo así:

Código:
v = [0,1,2,3...253,254,255]

// leer_urandom(n)
// n: cantidad de bytes a leer desde urandom

// entero( b )
// b: byte a a convertir a entero

N = 10000 
para i = 0 hasta N hacer
  // leer datos desde urandom y convertirlos a entero
  pos_a = entero( leer_urandom( 1 ) )
  pos_b = entero( leer_urandom( 1 ) )
  
  // almacenar datos para el intercambio
  a = v[pos_a]
  b = v[pos_b]

  // intercambio
  v[pos_a] = b
  v[pos_b] = a
  
  // incrementar i
  i++
fin para

Con el vector reordenado 10 mil veces solo quedaría formar el diccionario de datos:

Código:
d = {}

para i = 0 hasta 255 hacer
  // agregar elementos al diccionario
  d[i] = v[i]
  
  // incrementar i
  i++
fin para

Obtendríamos algo como ésto:

Código:
{0: 27, 1: 101, 2: 11, 3: 14, 4: 252, 5: 119, 6: 213, 7: 182, 8: 138, 9: 85, 10: 87, 11: 75, 12: 161, 13: 198, 14: 39, 15: 235, 16: 253, 17: 95, 18: 240, 19: 109, 20: 17, 21: 120, 22: 64, 23: 91, 24: 57, 25: 20, 26: 32, 27: 214, 28: 181, 29: 190, 30: 52, 31: 88, 32: 204, 33: 187, 34: 234, 35: 231, 36: 154, 37: 215, 38: 159, 39: 0, 40: 222, 41: 136, 42: 28, 43: 48, 44: 143, 45: 162, 46: 93, 47: 115, 48: 31, 49: 135, 50: 248, 51: 203, 52: 127, 53: 205, 54: 239, 55: 176, 56: 149, 57: 177, 58: 168, 59: 83, 60: 47, 61: 144, 62: 219, 63: 90, 64: 232, 65: 165, 66: 208, 67: 107, 68: 192, 69: 12, 70: 130, 71: 40, 72: 128, 73: 16, 74: 196, 75: 183, 76: 43, 77: 50, 78: 137, 79: 19, 80: 189, 81: 197, 82: 37, 83: 23, 84: 140, 85: 199, 86: 220, 87: 147, 88: 54, 89: 78, 90: 223, 91: 245, 92: 169, 93: 201, 94: 238, 95: 61, 96: 157, 97: 156, 98: 113, 99: 24, 100: 4, 101: 152, 102: 228, 103: 42, 104: 110, 105: 123, 106: 62, 107: 134, 108: 63, 109: 210, 110: 51, 111: 74, 112: 170, 113: 151, 114: 99, 115: 211, 116: 89, 117: 53, 118: 81, 119: 26, 120: 22, 121: 206, 122: 73, 123: 59, 124: 76, 125: 15, 126: 180, 127: 86, 128: 79, 129: 133, 130: 179, 131: 224, 132: 132, 133: 3, 134: 186, 135: 236, 136: 212, 137: 94, 138: 66, 139: 65, 140: 103, 141: 251, 142: 126, 143: 125, 144: 9, 145: 145, 146: 250, 147: 225, 148: 247, 149: 46, 150: 163, 151: 5, 152: 237, 153: 44, 154: 216, 155: 102, 156: 8, 157: 72, 158: 111, 159: 141, 160: 191, 161: 246, 162: 108, 163: 244, 164: 139, 165: 10, 166: 45, 167: 21, 168: 155, 169: 33, 170: 60, 171: 114, 172: 116, 173: 13, 174: 82, 175: 142, 176: 146, 177: 129, 178: 160, 179: 218, 180: 153, 181: 58, 182: 77, 183: 106, 184: 195, 185: 18, 186: 69, 187: 92, 188: 221, 189: 158, 190: 150, 191: 172, 192: 185, 193: 2, 194: 49, 195: 173, 196: 207, 197: 226, 198: 38, 199: 112, 200: 241, 201: 243, 202: 121, 203: 184, 204: 70, 205: 34, 206: 71, 207: 174, 208: 233, 209: 255, 210: 30, 211: 80, 212: 36, 213: 254, 214: 227, 215: 242, 216: 167, 217: 56, 218: 1, 219: 96, 220: 202, 221: 217, 222: 29, 223: 209, 224: 188, 225: 171, 226: 164, 227: 200, 228: 100, 229: 25, 230: 6, 231: 124, 232: 67, 233: 249, 234: 122, 235: 41, 236: 35, 237: 7, 238: 98, 239: 118, 240: 117, 241: 105, 242: 84, 243: 148, 244: 97, 245: 104, 246: 229, 247: 68, 248: 178, 249: 230, 250: 131, 251: 55, 252: 193, 253: 166, 254: 175, 255: 194}

Y conforme los datos se van reordenando desde /dev/urandom los diccionarios tendrían valores distintos en cada ejecución

Código:
{0: 20, 1: 104, 2: 22, 3: 27, 4: 221, 5: 18, 6: 42, 7: 142, 8: 41, 9: 141, 10: 77, 11: 162, 12: 15, 13: 83, 14: 239, 15: 190, 16: 78, 17: 233, 18: 252, 19: 242, 20: 97, 21: 105, 22: 50, 23: 45, 24: 4, 25: 26, 26: 224, 27: 53, 28: 254, 29: 96, 30: 39, 31: 229, 32: 6, 33: 80, 34: 14, 35: 71, 36: 91, 37: 44, 38: 194, 39: 210, 40: 136, 41: 243, 42: 193, 43: 13, 44: 0, 45: 110, 46: 207, 47: 145, 48: 206, 49: 203, 50: 143, 51: 199, 52: 179, 53: 154, 54: 92, 55: 28, 56: 79, 57: 216, 58: 24, 59: 12, 60: 149, 61: 122, 62: 173, 63: 177, 64: 166, 65: 182, 66: 86, 67: 121, 68: 111, 69: 81, 70: 215, 71: 29, 72: 214, 73: 144, 74: 66, 75: 1, 76: 84, 77: 112, 78: 98, 79: 43, 80: 234, 81: 89, 82: 171, 83: 137, 84: 227, 85: 69, 86: 116, 87: 70, 88: 208, 89: 197, 90: 147, 91: 63, 92: 99, 93: 202, 94: 101, 95: 238, 96: 174, 97: 168, 98: 60, 99: 184, 100: 138, 101: 253, 102: 94, 103: 195, 104: 198, 105: 183, 106: 151, 107: 189, 108: 213, 109: 232, 110: 225, 111: 222, 112: 178, 113: 170, 114: 200, 115: 181, 116: 249, 117: 146, 118: 11, 119: 25, 120: 165, 121: 175, 122: 134, 123: 2, 124: 100, 125: 185, 126: 85, 127: 169, 128: 192, 129: 93, 130: 153, 131: 48, 132: 251, 133: 3, 134: 130, 135: 135, 136: 180, 137: 230, 138: 140, 139: 228, 140: 235, 141: 127, 142: 56, 143: 23, 144: 204, 145: 103, 146: 119, 147: 139, 148: 163, 149: 108, 150: 186, 151: 17, 152: 33, 153: 161, 154: 73, 155: 57, 156: 21, 157: 87, 158: 75, 159: 125, 160: 218, 161: 247, 162: 164, 163: 65, 164: 237, 165: 246, 166: 187, 167: 188, 168: 148, 169: 32, 170: 74, 171: 132, 172: 47, 173: 231, 174: 131, 175: 220, 176: 120, 177: 126, 178: 5, 179: 68, 180: 212, 181: 16, 182: 209, 183: 250, 184: 61, 185: 107, 186: 211, 187: 62, 188: 35, 189: 106, 190: 219, 191: 191, 192: 64, 193: 201, 194: 76, 195: 248, 196: 58, 197: 223, 198: 129, 199: 102, 200: 196, 201: 117, 202: 118, 203: 109, 204: 226, 205: 217, 206: 19, 207: 244, 208: 34, 209: 176, 210: 82, 211: 31, 212: 255, 213: 72, 214: 59, 215: 150, 216: 49, 217: 51, 218: 152, 219: 240, 220: 38, 221: 90, 222: 40, 223: 113, 224: 157, 225: 37, 226: 124, 227: 245, 228: 55, 229: 241, 230: 114, 231: 167, 232: 236, 233: 172, 234: 95, 235: 52, 236: 9, 237: 88, 238: 160, 239: 133, 240: 46, 241: 123, 242: 115, 243: 156, 244: 155, 245: 30, 246: 67, 247: 205, 248: 159, 249: 36, 250: 8, 251: 54, 252: 128, 253: 158, 254: 7, 255: 10}

Código:
{0: 242, 1: 119, 2: 74, 3: 87, 4: 229, 5: 23, 6: 60, 7: 86, 8: 114, 9: 14, 10: 146, 11: 207, 12: 157, 13: 29, 14: 164, 15: 78, 16: 152, 17: 133, 18: 15, 19: 51, 20: 136, 21: 163, 22: 68, 23: 153, 24: 17, 25: 182, 26: 4, 27: 92, 28: 227, 29: 56, 30: 46, 31: 129, 32: 231, 33: 222, 34: 120, 35: 72, 36: 230, 37: 233, 38: 243, 39: 52, 40: 191, 41: 150, 42: 218, 43: 142, 44: 21, 45: 117, 46: 36, 47: 33, 48: 131, 49: 217, 50: 5, 51: 110, 52: 97, 53: 91, 54: 9, 55: 31, 56: 125, 57: 126, 58: 172, 59: 214, 60: 103, 61: 47, 62: 181, 63: 197, 64: 185, 65: 247, 66: 170, 67: 18, 68: 252, 69: 99, 70: 196, 71: 77, 72: 127, 73: 141, 74: 156, 75: 195, 76: 245, 77: 161, 78: 59, 79: 76, 80: 53, 81: 116, 82: 199, 83: 128, 84: 41, 85: 42, 86: 168, 87: 98, 88: 154, 89: 49, 90: 176, 91: 113, 92: 234, 93: 194, 94: 45, 95: 94, 96: 238, 97: 210, 98: 145, 99: 228, 100: 237, 101: 209, 102: 180, 103: 70, 104: 221, 105: 22, 106: 26, 107: 105, 108: 169, 109: 8, 110: 100, 111: 251, 112: 246, 113: 198, 114: 65, 115: 235, 116: 83, 117: 73, 118: 148, 119: 253, 120: 249, 121: 32, 122: 55, 123: 28, 124: 240, 125: 61, 126: 232, 127: 158, 128: 123, 129: 89, 130: 175, 131: 190, 132: 187, 133: 106, 134: 62, 135: 109, 136: 241, 137: 54, 138: 12, 139: 1, 140: 111, 141: 112, 142: 248, 143: 121, 144: 67, 145: 80, 146: 149, 147: 179, 148: 96, 149: 24, 150: 166, 151: 69, 152: 27, 153: 236, 154: 102, 155: 220, 156: 93, 157: 140, 158: 11, 159: 204, 160: 43, 161: 216, 162: 138, 163: 212, 164: 208, 165: 147, 166: 101, 167: 215, 168: 192, 169: 193, 170: 205, 171: 201, 172: 226, 173: 130, 174: 167, 175: 143, 176: 171, 177: 244, 178: 108, 179: 118, 180: 115, 181: 178, 182: 2, 183: 58, 184: 160, 185: 225, 186: 71, 187: 135, 188: 203, 189: 155, 190: 177, 191: 151, 192: 50, 193: 122, 194: 19, 195: 13, 196: 188, 197: 10, 198: 184, 199: 40, 200: 213, 201: 134, 202: 39, 203: 90, 204: 174, 205: 144, 206: 224, 207: 48, 208: 0, 209: 202, 210: 137, 211: 95, 212: 107, 213: 139, 214: 81, 215: 63, 216: 64, 217: 173, 218: 104, 219: 132, 220: 25, 221: 211, 222: 85, 223: 6, 224: 162, 225: 206, 226: 16, 227: 189, 228: 159, 229: 3, 230: 38, 231: 34, 232: 44, 233: 239, 234: 57, 235: 79, 236: 219, 237: 124, 238: 82, 239: 223, 240: 75, 241: 20, 242: 165, 243: 254, 244: 255, 245: 183, 246: 200, 247: 37, 248: 35, 249: 250, 250: 84, 251: 7, 252: 186, 253: 30, 254: 66, 255: 88}

Código:
{0: 221, 1: 104, 2: 6, 3: 144, 4: 36, 5: 156, 6: 67, 7: 54, 8: 182, 9: 43, 10: 218, 11: 83, 12: 34, 13: 253, 14: 178, 15: 47, 16: 209, 17: 93, 18: 235, 19: 127, 20: 225, 21: 88, 22: 135, 23: 246, 24: 189, 25: 193, 26: 103, 27: 202, 28: 19, 29: 44, 30: 242, 31: 117, 32: 231, 33: 154, 34: 118, 35: 241, 36: 15, 37: 188, 38: 63, 39: 40, 40: 251, 41: 243, 42: 24, 43: 1, 44: 75, 45: 122, 46: 108, 47: 58, 48: 53, 49: 237, 50: 2, 51: 91, 52: 38, 53: 226, 54: 149, 55: 145, 56: 65, 57: 33, 58: 27, 59: 208, 60: 245, 61: 159, 62: 162, 63: 85, 64: 98, 65: 238, 66: 199, 67: 181, 68: 5, 69: 79, 70: 254, 71: 198, 72: 11, 73: 66, 74: 92, 75: 136, 76: 110, 77: 109, 78: 172, 79: 60, 80: 129, 81: 13, 82: 206, 83: 239, 84: 105, 85: 29, 86: 223, 87: 166, 88: 141, 89: 161, 90: 152, 91: 248, 92: 51, 93: 46, 94: 81, 95: 175, 96: 167, 97: 106, 98: 155, 99: 179, 100: 71, 101: 197, 102: 26, 103: 116, 104: 101, 105: 185, 106: 21, 107: 128, 108: 252, 109: 8, 110: 247, 111: 14, 112: 228, 113: 134, 114: 214, 115: 120, 116: 39, 117: 23, 118: 114, 119: 176, 120: 174, 121: 22, 122: 96, 123: 102, 124: 204, 125: 210, 126: 25, 127: 200, 128: 234, 129: 77, 130: 139, 131: 41, 132: 0, 133: 131, 134: 20, 135: 177, 136: 151, 137: 138, 138: 229, 139: 52, 140: 133, 141: 18, 142: 94, 143: 191, 144: 230, 145: 42, 146: 157, 147: 227, 148: 3, 149: 130, 150: 35, 151: 61, 152: 64, 153: 132, 154: 240, 155: 10, 156: 148, 157: 68, 158: 233, 159: 160, 160: 250, 161: 124, 162: 95, 163: 207, 164: 211, 165: 158, 166: 184, 167: 216, 168: 232, 169: 4, 170: 78, 171: 57, 172: 89, 173: 72, 174: 194, 175: 112, 176: 140, 177: 190, 178: 147, 179: 180, 180: 7, 181: 169, 182: 150, 183: 48, 184: 45, 185: 16, 186: 173, 187: 119, 188: 146, 189: 99, 190: 37, 191: 213, 192: 255, 193: 97, 194: 224, 195: 32, 196: 192, 197: 217, 198: 196, 199: 244, 200: 17, 201: 82, 202: 59, 203: 163, 204: 164, 205: 183, 206: 220, 207: 121, 208: 90, 209: 126, 210: 236, 211: 50, 212: 70, 213: 186, 214: 125, 215: 30, 216: 62, 217: 76, 218: 142, 219: 73, 220: 165, 221: 111, 222: 168, 223: 87, 224: 107, 225: 195, 226: 100, 227: 80, 228: 12, 229: 123, 230: 137, 231: 84, 232: 215, 233: 28, 234: 222, 235: 9, 236: 86, 237: 55, 238: 171, 239: 203, 240: 205, 241: 31, 242: 219, 243: 170, 244: 143, 245: 115, 246: 74, 247: 201, 248: 249, 249: 56, 250: 212, 251: 49, 252: 69, 253: 113, 254: 187, 255: 153}

Luego tocaría aplicar la sustitución según la lectura de los archivos antes de meter la data en el modo L o RGB según la elección.

Código:
// un vector con los bytes leídos desde el archivo
vdatos = [0xfa, 0xfb, 0xfc...0xfe, 0xfe, 0xff]

// byte( x )
// convierte un entero a byte
// x: entero a convertir
para i = 0 hasta largo( bytes ) hacer
  b = entero( vdatos[i] )
  // sustitución
  s = bytes( d[b] )
  vdatos[i] = s
fin para

El diccionario se tendría que guardar en un archivo para el uso posterior respectivo, lo que se podría realizar en un archivo con una extensión cualquiera en texto plano por ej:

Código:
000002001070002065003220004193005161006189007096008158009132010133011125012005013059014122015108016239017124018115019240020153021191022175023243024145025151026003027010028171029248030177031159032016033255034017035217036079037168038176039100040187041178042013043015044140045165046041047091048033049227050071051058052112053032054007055204056118057082058042059121060192061225062031063218064103065139066252067162068195069019070011071223072155073236074111075221076024077143078150079097080166081062082028083208084174085087086038087063088235089066090098091230092014093110094242095018096126097116098051099106100001101080102073103072104130105085106173107196108198109219110075111047112246113074114200115207116167117034118251119212120092121083122114123188124215125226126134127135128232129201130094131119132040133214134101135137136164137203138241139099140147141157142039143078144199145146146089147056148053149170150012151104152213153090154006155008156054157154158182159086160238161004162123163211164249165030166025167027168142169231170172171247172069173149174185175046176190177202178129179120180237181254182021183109184136185036186205187043188068189169190102191057192023193060194035195127196184197148198197199209200244201253202088203020204250205179206163207156208095209107210152211206212045213022214064215093216144217052218216219160220210221105222245223050224180225067226044227077228113229084230181231138232228233131234009235061236037237141238000239076240117241186242222243055244029245183246229247026248233249048250081251049252194253224254128255234

Ésto se leería en grupos de a 6 bytes, para luego separar cada elemento en 2 y rearmar de nuevo el diccionario. Y una vez recuperados los valores de 8bits en rango 0~255 de la imagen decodificada, se tendríá que aplicar el proceso de sustitución inverso, intercambiando las claves del diccionario por los valores para repetir el loop de sustitución.

Igual las permutaciones que requeriría romper por fuerza bruta el diccionario reordenado no son pocas, y aún cuando se lograra dar con la correcta combinación del orden, pudiera haber un rar con clave al interior (cueck xD)...

Bien interesante el ejercicio, y los diccionarios ofrecen un tiempo de acceso batante bueno como para no ralentizar el proceso de lectura del archivo.
 
Upvote 0

sndestroy

Digital Detox
Miembro del Equipo
MOD
Se incorporó
8 Abril 2009
Mensajes
1.873
Buen proyecto de esteganografía :thumbup

En su tiempo quise hacer algo similar, que al final "evolucionó" a un algoritmo de compresión, que al final quedó en nada porque entré a estudiar y el tiempo libre se me acabó. Creo que aún tengo guardados los papeles con las ecuaciones y snippets de código, pero ni idea dónde...
 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Buen proyecto de esteganografía :thumbup

En su tiempo quise hacer algo similar, que al final "evolucionó" a un algoritmo de compresión, que al final quedó en nada porque entré a estudiar y el tiempo libre se me acabó. Creo que aún tengo guardados los papeles con las ecuaciones y snippets de código, pero ni idea dónde...
Había pensado mas adelante darle una vuelta a la compresión, porque de tanto ver los mismos valores repetidos uno se empieza a preguntar si a caso no se podrá comprimir algoritmicamente, pero lo voy a dejar por ahí en el tintero mientras.

Para el que quiera leer sobre eso, un buen punto de partida es LZW, del que tambien hay material en vídeo.

LZW - Compresión
LZW - Descompresión

Pero mas interesante que la compresión, hoy mientras dormía en la madrugada, estuve pensando en que podría realizar dibujos en la estática, aunque no le dí muchas vueltas porque justo me desperté :daleoh, pero la idea era básicamente la siguiente (antes que se me olvide).

Supongamos una matriz random de 5x5 a la que le quisiéramos dibujar una L por ej (los ceros a la izquierda son relleno para que se vea todo ordenado) y los datos son random, ni L ni RGB, así se simplifica para el ejemplo.

Código:
// Matriz con estática producto de la conversión del archivo a colores
m = [
 00, 01, 02, 03, 04,
 05, 06, 07, 08, 09,
 10, 11, 12, 13, 14,
 15, 16, 17, 18, 19,
 20, 21, 22, 23, 24
]

// La L a representar en 2D delimitada por el patrón formado por las X
dibujo = [
  0, X, 0, 0, 0,
  0, X, 0, 0, 0,
  0, X, 0, 0, 0,
  0, X, X, X, 0,
  0, 0, 0, 0, 0
]

Para representar el dibujo como algo visible de entre los pixeles o elementos de la matriz, se tendría que hacer una operación que pudiera describir como superposición en donde la matriz dibujo se suporpone a la matriz m, y como resultado obtenemos un vector unidimensional secuencial desde 0,0 a N,N, representando el dibujo.

Para ejemplificar en algo mas concreto, sería como tener 2 capas, para realizar la superposición y luego una rasterización (si no me equivoco), de forma que en el eje z del plano obtendríamos algo así:

Código:
eje z 0 --- capa M --- capa dibujo --- N

Y con la edición hecha en un editor, tendríamos que tener la forma seleccionada, copiarla en la imagen de origen para luego pegarla en la imágen de destino (algo trivial en photoshop).

Concretamente

Código:
// superposición de dibujo sobre m
m = [
 00, X, 02, 03, 04,
 05, X, 07, 08, 09,
 10, X, 12, 13, 14,
 15, X, X, X, 19,
 20, 21, 22, 23, 24
]

// vector que almacena los datos secuencialmente con tupla de 3 elementos, siendo el elemento 2 desde 0, el que almacena el color en modo L o RGB.
v = [ (0,1,1), (1,1, 6), (2,1, 11), (3,1, 16), (3,2,17), (3,3, 18) ];

Los colores con que se escribiera el espacio 2D del dibujo serían completamente indiferentes en la medida que en el vector v tuviéramos las coordenadas para la reconstrucción posterior.

Entonces si por ejemplo, tuviéramos el logo de capa 9, y se lo quisiéramos poner a una imagen resultante, lo único que tendríamos que hacer sería encargarnos de las alíneaciones y una que otra reducción de tamaño, para llevar todo al formato de coordenadas. Eso y para facilitar aún mas las cosas, ocupar un formato sin pérdida con un canal Alfa o un color que hiciera de contraste a la paleta de color del archivo. Con lo que habría que idear una especie de formato interno para representar por una parte la data útil separada de las coordenadas de reconstrucción, algo que se podría hacer con un marcador binario de 4 bytes separando las capas secuenciales de bytes.

Algo igual de interesante sería representar texto a partir de texto plano y darle algún formato en base a un archivo fuente TTF o que se yo.

Código:
imagen.write( '¡hola mundo!', 'Verdana Bold', '12' )
 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Me pasaba a dejar algunos archivos adjuntos para quien se anime en un futuro a llevar toda la teoría a la práctica y quiera comprobar resultados.

- "sin cifrar.png" es la imagen en RGB normal, del mismo archivo test.pdf que subí mas arriba.
- "cifrado.png" es la imagen cifrada en RGB
- "cifrado.png.bin" es la clave binaria

Para que chequeen el archivo
d34af9a9cf73dad19bfe7fbaa4fdb9774ecea2d15ba953611ebdf3f7cc197608 test.zip

Respecto a ésto, estoy costumbrado a pensar en ascii, así que olvidé que podía solo apilar los bytes de forma contigua en una tira de bytes. Por lo tanto lo que verán en el archivo clave no será texto plano, si nó data binaria en el siguiente formato.

Código:
[clave][valor de sustitución][clave][valor de sustitución]...

Por lo tanto habrán 512 bytes, lo que corresponde a la unión de clave valor del diccionario de 256 elementos que expliqué anteriormente. El que se deberá invertir, osea, se deben cambiar las claves por los valores y los valores por las claves, y luego pasar el loop por el stream para realizar el descifrado. Todo ésto después de haber retirado el relleno, para el cual usé un marcador de 4 bytes "0xfa 0xfb 0xfc 0xfd", y luego es eso viene solo relleno (0xff -> 255).

Entonces si corren xxd sobre el archivo.bin verán ésto

Código:
// xxd cifrado.png.bin
00000000: 007b 0196 02b6 030c 048f 0587 0610 0728  .{.............(
00000010: 0824 09b5 0a7f 0bf8 0ca0 0dda 0e97 0fc4  .$..............
00000020: 1021 11f9 1280 13d3 140d 154f 1611 173d  .!.........O...=
00000030: 18a7 1977 1a62 1b13 1cd5 1d26 1e6e 1f7d  ...w.b.....&.n.}
00000040: 20c1 217a 22c3 23fa 24cd 2552 265c 2735   .!z".#.$.%R&\'5
00000050: 28d7 2944 2ab7 2be2 2c70 2dc9 2e67 2fa4  (.)D*.+.,p-..g/.
00000060: 30c8 31bb 32a1 336b 3423 35db 36ca 375d  0.1.2.3k4#5.6.7]
00000070: 3801 3927 3a4b 3b0b 3cb0 3d4e 3ec0 3f86  8.9':K;.<.=N>.?.
00000080: 400a 4149 426f 43c5 44ee 4589 4636 4781  @.AIBoC.D.E.F6G.
00000090: 48ea 499e 4a5f 4b48 4c1c 4d39 4ed6 4f5e  H.I.J_KHL.M9N.O^
000000a0: 508d 51f0 5295 5338 548c 552a 56ef 5702  P.Q.R.S8T.U*V.W.
000000b0: 5820 59e5 5a16 5b8a 5c3b 5dbd 5e53 5f5b  X Y.Z.[.\;].^S_[
000000c0: 60ac 6157 6275 634a 6419 65fd 663e 67cc  `.aWbucJd.e.f>g.
000000d0: 687e 6925 6aa8 6b71 6c60 6db4 6ebc 6fb2  h~i%j.kql`m.n.o.
000000e0: 703c 7112 7293 736a 74f7 7509 7618 77d9  p<q.r.sjt.u.v.w.
000000f0: 7885 7998 7af6 7bb8 7c82 7d47 7eff 7fd1  x.y.z.{.|.}G~...
00000100: 8000 81df 8261 8365 84e8 8506 86b9 87a2  .....a.e........
00000110: 8854 8955 8a9d 8bf2 8cf3 8d6c 8ed0 8fe7  .T.U.......l....
00000120: 9029 91ae 9231 9303 9433 95d4 9643 9741  .)...1...3...C.A
00000130: 989a 99a6 9a69 9b63 9c91 9d45 9e83 9f32  .....i.c...E...2
00000140: a0a3 a12d a2f5 a31e a44d a5bf a6af a7d2  ...-.....M......
00000150: a8aa a9dd aa9c ab92 ace1 ad56 ae42 af79  ...........V.B.y
00000160: b00e b150 b276 b304 b4e3 b558 b6a9 b7c6  ...P.v.....X....
00000170: b8b3 b978 ba37 bbdc bce9 bd3f bebe bff4  ...x.7.....?....
00000180: c0cb c117 c2c2 c308 c49b c530 c69f c7a5  ...........0....
00000190: c88e c9d8 ca2f cb1f cceb cd05 ceba cf94  ...../..........
000001a0: d02e d184 d2ec d3ad d40f d534 d64c d7cf  ...........4.L..
000001b0: d87c d91d dae0 db73 dc66 dde6 dec7 dfce  .|.....s.f......
000001c0: e0fe e12c e26d e314 e472 e551 e6de e73a  ...,.m...r.Q...:
000001d0: e874 e999 eaed eb46 ec59 ede4 ee64 ef2b  .t.....F.Y...d.+
000001e0: f022 f107 f215 f38b f488 f5fb f668 f7fc  ."...........h..
000001f0: f8f1 f940 fa1a fbb1 fc1b fdab fe5a ff90  [email protected]..

En cada grupo de 2 bytes (por ej al inicio 007b), verán a la izquierda la clave y a la derecha el valor de sustitución, por lo que en éste caso puntual todos los 0x00 se habrán sustituido por 0x7b.
 

Archivo adjunto

  • test.zip
    2,8 MB · Visitas: 65
Upvote 0

MetalCOB

Miembro Regular
Se incorporó
2 Enero 2010
Mensajes
97
Pillé esto


 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Pillé esto


Buena gracias, el 2do repo convierte a BMP, ayer estaba leyendo sobre eso, es bastante complejo el formato, el 1ro es parecido a lo que hice.

Yo creo que un ejemplo práctico se encuentra en los métodos para almacenar datos en los VHS, alcanzaban a almacenar bastante data, para la epoca, con procesos parecidos para codificar, añadiendo verificaciones de redundancia y todas esas cosas.
Ayer me di el tiempo de leer sobre el VHS en wikipedia y llegué a los algoritmos de reducción de ruido, con eso ya tuve material suficiente para entretenerme.

Quizá ya sea tiempo de compartir la herramienta... pero me da una paja tremenda subir todo a un github
 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Logré pasar de PNG a JPEG, lo implementé por pura curiosidad el modo de encoding bit a bit (0 a 0 y 1 a 255), y con PNG no se me disparó taaanto el tamaño del archivo resultante como había pensado. Propiamente el aumento fué de 964kb a 1.3MB (para el archivo PDF de pruebas), ahora bien, de PNG a JPEG el tamaño aumentó con crees a 6.9MB, todo ésto convirtiendo sin perder calidad con:

Bash:
convert -quality 0 in.png out.jpg

El único problema es que como son mas datos (unos y ceros), el guardado se demora más tiempo. Por lo tanto con lo que tendría que lidiar ahora, es con la resolución máxima de meta (2048x2048), cosa que la pude resolver usando la herarmienta split. Por lo pronto, por cada imagen subida con éste modo podría almacenar un máximo de 512 bytes (una real cagada, pero vamos que estoy mas cerca de convertir a fb en un cloud bien cutre :zippysconf).

Mientras tanto quiero exponer como quedan los desplazamientos en los valores de los pixeles (en blanco y negro, ojo que no es escala de grises) despues de subir el archivo a fb, descargarlo y leerlo en crudo (sin procesar), es bastante prometedora la reconstrucción. Los siguientes son los primeros 100 pixeles del archivo descargado.

Código:
19 0 255 18 0 255 7 255 0 255 0 255 7 0 0 8 0 255 0 0 0 255 0 0 4 255 0 17 8 255 243 0 0 0 255 0 255 255 0 255 0 1 255 252 0 0 20 244 6 0 255 4 252 247 255 0 2 3 253 255 0 243 8 248 0 7 0 0 255 0 255 0 0 0 255 0 5 255 0 247 255 255 227 255 0 250 255 0 255 251 249 4 3 250 0 11 255 255 239 255

Acá implementaría la misma técnica de los valores proximales poniendo la varilla en 127 byte a byte, convirtiendo todo lo que sea menor o igual a 127 en un 0 y todo lo que sea mayor a 127 en un 1. Entonces los bytes de arriba se transformarían en (como originalmente estaban):

Código:
0 0 1 0 0 1 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 1 1 0 1 0 0 1 1 0 0 0 1 0 0 1 0 1 1 1 0 0 0 1 1 0 1 0 1 0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 1 1 1 1 1 0 1 1 0 1 1 1 0 0 1 0 0 1 1 1 1

Luego del primer procesado, tocaría agrupar todo en grupos de bytes (8-bits) para leerlo posteriormente como una tira de bytes.
Código:
[0 0 1 0 0 1 0 1]
[0 1 0 1 0 0 0 0]
[0 1 0 0 0 1 0 0]
[0 1 0 0 0 1 1 0]
[0 0 1 0 1 1 0 1]
[0 0 1 1 0 0 0 1]
[0 0 1 0 1 1 1 0]
[0 0 1 1 0 1 0 1]
[0 0 0 0 1 0 1 0]
[0 0 1 0 0 1 0 1]
[1 1 1 1 0 1 1 0]
[1 1 1 0 0 1 0 0]
[1 1 1 1 . . . .]

Hasta aquí me parece que parcialmente el objetivo de almacenar en fb ya se cumple. Igual hay algo mas interesante.

Los archivos a fb se pueden subir en PNG, luego internamente son covertidos a JPEG, por lo que un PNG de 584.4kb se transforma en un JPEG de 2.4MB al descargarlo, y en un PNG de 2.1MB al convertirlo, cosa que me ahorraré cuando agregue una librería para leer JPEG directamente en la decodificación.

Lo penca de ésto es que de momento no puedo cifrar nada, al menos no directamente desde la herramienta... aunque si podría cifrar directamente al leer el archivo, pero en fin es harina de otro costal.

A ver si mas adelante puedo implementar el tema en colores comprimiendo mejor la información en el proceso. Lo que requeriría volver a trabajar en un espacio menor a 8-bits para seccionar a discreción el modelo HSL.
 
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Dejo por acá algunos recursos que pudieran ser de interés.

Understanding and Decoding a JPEG Image using Python
RGB to HSL conversion
HSL and HSV
Explicación del sistema de compresión JPEG (*)
LZW - Python (**)
LZW compression is brilliant (**)
Basic lzw compression help in python (**)
Python - Package LZW (**)
Algoritmo LZSS (**)
La librería Matplotlib
Librería Pillow (python)
Portable Network Graphics (PNG) Specification (Third Edition)
Reducción de ruido
BMP file format
Modulo pylibjpeg (Python)
Python bit manipulation
Image Magic - Color modifications (***)
Creating PNG file in Python
Pure PNG lib (Python)
Crominancia (#)

* Éste creo que es la base para entender el resto
** Ésto es un añadido para juguetear con un alfabeto pequeño a ver que resulta, si ven los vídeos, y se vuelven al primer post a mirar la tabla de hexadecimal con los nibbles se darán cuenta de a que me refiero.
*** Ésto ayuda a entender las conversiones que se realizan entre formatos en relación a la luminancia, si quieren entender la crominancia vallan a * y a #.

Y para finalizar, el proyecto que vi en el reel de fb, me costó encontrarlo de nuevo Infinite Storage Glitch
 
Última modificación:
Upvote 0

freishner

Capo
Se incorporó
16 Noviembre 2021
Mensajes
436
Voy a describir un poco mas el modo de trabajo que se haría con HSL, tambien sirve como resumen.

Ahora se que las variaciones que realiza JPEG son en relación a la crominancia de la foto, y que la luminancia tiende a verse no tan afectada, pero los colores si lo hacen debido a que el ojo humano persibe los cambios de luz en vez de los de color (eso a grandes rasgos), de ahí que un color menos o un color mas en una imagen 2K pasa mas o menos desapercibido, esto es lo que se conoce como algoritmo de compresión con pérdida en el formato mencionado, y es el talón de aquiles de la idea de encodear datos en fb usando imágenes.

Entonces, volviendo al planteamiento inicial, como la información que se va reconstruir debe ser fidedigna a la original, el reto principal era sobrevivir a la conversión de formato realizada por las plataformas (con la correspondiente pérdida de calidad) (ARCHIVO->PNG->JPEG->ARCHIVO) (da igual el generar un PNG o un JPEG inicialmente desde un archivo, yo generé el primero, así que no voy a generar el segundo (por paja), pero se puede, lo que si hice fué leer el segundo).

Para todos los efectos se debe cumplir lo siguiente:
Código:
hash(archivo a subir) == hash(archivo a descargar)
Donde hash es cualquier función que permita obtener un checksum de un archivo para efectos coomparativos.

De acá ya habrán deducido que no es posible utilizar todos los píxeles como celdas para almacenar información dentro del formato mencionado, pero si es posible ubicarse dentro del modelo HSL de forma estratégica tomando ventaja de que el formato no intercambia colores, es decir que una imagen cualquiera nunca va a tener un cambio tan drástico como se ve a continuación:

1717389270181.png


En condiciones normales (conversión de una plataforma) a menos que se aplicare algún filtro o un recorte (cosa que no nos interesa) tendríamos lo siguiente conforme la calidad fuera disminuyendo de 10 en 10 de 100 a 10 (ignorar las líneas blancas, son del recorte a mano :/).

100% (foto original)
1717389443374.png


90%
1717389475745.png


80%
1717389507125.png


70%
1717389535403.png


60%
1717389589828.png


50%
1717389626291.png


40%
1717389652010.png


30%
1717389674560.png


20%
1717389701809.png


10%
1717389724558.png

Seguramente en la medida que fueron mirando hacia abajo, no se dieron cuenta de la pérdida de calidad debido a que las imágenes no quedaron guardadas en su memoria de largo plazo, pero veamos una coomparación de la original con respecto a la que redujo un 90% su calidad.

100% - 10%
1717389443374.png
1717389724558.png


Nótece que el color está ahí, con menos información, pero sigue ahí, el color no se movió de ángulo (hablando en términos de HSL).

Veamos un poco mas la teoría de la técnica a usar.

Lo que se ve acá es una sección de un pétalo amarillo, pero el amarillo que se percibe está compuesto por 4 colores distintos, factor que para el ojo humano es impersectible a menos que tengamos una variación brusca de la luminocidad.

Código:
hsl(44.5, 100%, 44.1%) hsl(44.3, 100%, 44.3%)
hsl(44.8, 100%, 44.1%) hsl(44.6, 100%, 44.3%)

1717392656810.png


HSL
Como resúmen rápido, mencionaré que el espacio de color HSL tiene 3 componentes, el tono, la saturación y la luminocidad.

Para que ejemplificar, en HSL todos los colores que se acercan al 0 en luminocidad tienden al blanco y en contra parte, cuando se acercan al 100 tienden al negro. En cambio, la saturación hace que un color se acerque al blanco, gris o negro cuando está en 0 y al color cuando está en 100, para valores de 100, 50 y 0 respectivamente en función de la luminocidad. Por lo que para H: 0, L: 0
y S: 0 tendríamos blanco, H: 0: L, 50 y S: 0, gris y H: 0, L:0 y S: 0, negro, y finalmente para representar el color (en este caso el rojo) tendríamos que variar la luminocidad entre un 4 y un 95%, el resto del rango es casi imperseptible (al menos para mi).


Ya con la teoría de HSL podemos tirar mano de detalles mas técnicos. Volvamos a la imagen anterior del color:

1717392656810.png


Sabemos que el tono de la imagen es de 44° y que está al 44% de luminocidad, con ésto en mente ya podemos re-elaborar un mapa de 28 posiciones estratégicas para almacenar datos.

El círculo interior y el exterior representan extremos y la línea blanca el uso de la luminancia.

Como ya había ejemplificado anteriormente, la torta HSL es divisible en al menos 26 secciones teóricas, los 13 colores principales y sus diferencias lumínicas, esto en base al comportamiento del formato, de no tocar el color y preservar la luminancia.

De acá podemos obtener la siguiente tabla:
Código:
0: (1) L->25, L->80 (2)
30: (3) L->25, L->80 (4)
60: (5) L->25, L->80 (6)
90: (7) L->25, L->80 (8)
120: (9) L->25, L->80 (10)
130: (11) L->25, L->80 (12)
150: (13) L->25, L->80 (14)
180: (15) L->25, L->80 (16)
210: (17) L->25, L->80 (18)
240: (19) L->25, L->80 (20)
270: (21) L->25, L->80 (22)
300: (23) L->25, L->80 (24)
330: (25) L->25, L->80 (26)

Con lo que nuestro color amarillo automáticamente dejaría de ser amarillo y se convertiría en su extremo mas cercano, el naranja, lo que nos daría la coordenada el color, luego como su luminocidad tiende al negro nos moveríamos a L: 25%, ignorando la saturación, que si podría ser afectada por el formato.
De ésta manera obtenemos las 26 posiciones diferentes por pixel dentro de HSL. Lo que nos lleva a un alfabeto de 26 elementos distintos para almacenar información que sobreviviría a la pérdida de información, o mejor dicho el descarte controlado de ella.
Pudieramos agregar 2 elementos mas con las secciones del exterior y el centro del espacio, el negro y el blanco, con lo que terminaríamos con 28 elementos distintos (a un paso de los 5 bits, me imagino que igual es estirable).

Como no tenemos un alfabeto de 28 elementos (y a esta hora no me voy a poner a fabricar uno), lo que si tenemos es uno de 16 que se puede trabajar a 4 bits, el hexadecimal (no voy a pecar de ignorante, y sin probarlo, no aseguraría los 5 bits).

Así que la tabla en vez de estár dividida por 26, quedaría dividida en 14, mas el negro y el blanco, obteniendo así los 16 elementos.

Código:
Negro: (0, 0) (H: cualquiera: S: cualquiera: L -> 100)
0: (1,1) L->25, L->80 (2, 2)
60: (3, 3) L->25, L->80 (4, 4)
120: (5, 5) L->25, L->80 (6, 6)
180: (7, 7) L->25, L->80 (8, 8)
240: (9, 9) L->25, L->80 (10, a)
300: (11, b) L->25, L->80 (12, c)
360: (13, d) L->25, L->80 (14, e)
Blanco: (16, f) (H: cualquiera: S: cualquiera: L -> 0)

Ahora si, ya podríamos encodear información:

Código:
// Texto a codificar
Texto: hola
Ascii: 104 111 108 97
Hex: 68 6f 6c 61
Binario 8bits: 01101000 01101000 01101100 01101100
Binario 4bits: 0110 1000 0110 1000 0110 1100 0110 1100

// Pixels HSL
// ? representa cualquier valor
1) 6: H: 120, S: ?, L: 80 
2) 8: H: 180, S: ?, L: 65
3) 6: H: 120, S: ?, L: 78 
4) f: H: ?, S: ?, L: 12
5) 6: H: 120, S: ?, L:75 
6) c: H: 300, S: ?, L: 70
7) 6: H: 120, S: ?, L: 67
8) 1: H: 0, S: ?, L: 30

Si son observadores se habrán fijado que para el valor 0x6 la luminocidad ha variado constantemente, y esa es la ventaja que nos permite la tendencia entre los extremos del rango definido por la línea blanca en el modelo HSL de la foto, tambien notarán que el valor f (blanco) no difierencia el color (lo mismo que el blanco).

Tambien se preguntarán, porqué ubicar el negro en el centro y el blanco en el anillo exterior. Porque a parte de ser muy intuitivo que el negro va en el negro y el blanco en el blanco (para evitar confusiones), la tendencia (aunque pudiera variar), de los colores oscuros y blancos, es que todos cuando se acercan al negro tienden a negro y cuando se acercan al blanco tienden al blanco, en ese punto diferenciarlos dentro del formato se hace una tarea imposible.

Finalmente, me quedaría llevar ésto a la práctica, pero antes de irme, comentar que la técnica del seccionado con valores extremos entre cortes, se puede utilizar para encodear archivos en audio , con lo que teóricamente, se pudiera almacenar una película en spotify :siii, pero eso ya es harina de otro costal.
 

Archivo adjunto

  • 1717389418042.png
    1717389418042.png
    128,6 KB · Visitas: 50
  • HSL.jpg
    HSL.jpg
    88,6 KB · Visitas: 51
Upvote 0
Subir