Esteganografía en compresión con pérdida(JAVA)
image compression (1)
El JPEG utiliza un método de compresión con pérdida para lograr tamaños de archivo más pequeños. Desafortunadamente, ese mismo método afecta directamente el valor de (algunos) píxeles, destruyendo así la información de la forma en que la ha incrustado. Debe guardar el archivo en un formato sin pérdidas para evitar este problema, como bmp o png.
La esteganografía JPEG es algo más compleja de codificar, pero el concepto es sencillo. Necesitará escribir un codificador jpeg o usar uno que ya exista. El código al que se vinculó es de hecho un codificador y con algunas modificaciones menores puede usarlo para su proyecto.
Si desea comprender el código, puede leer el artículo de wikipedia sobre codificación jpeg . Resumiré brevemente algunos de sus pasos clave.
- Divide la imagen en bloques de 8x8.
- Use la transformada discreta del coseno (DCT) en cada uno para obtener los coeficientes DCT flotantes y cuantícelos en enteros.
- Almacene los coeficientes cuantizados en un archivo usando la codificación Huffman y ejecute la codificación de longitud.
La cuantización en el segundo paso es el bit con pérdida, pero todo lo que sigue después es sin pérdida. Básicamente, obtenga los coeficientes cuantificados del segundo paso, modifíquelos con su algoritmo de esteganografía y continúe con el tercer paso.
Sobre las modificaciones prácticas del código vinculado.
El método de
Compress
es lo que necesita llamar para almacenar una imagen rgb en un archivo.
Se encarga de escribir los datos del encabezado y los coeficientes comprimidos.
Solo necesita agregar un poco de código en el método
WriteCompressedData
.
Lo que hace por ahora es recorrer cada bloque de imagen de 8x8, aplicar el dct y cuantificar los coeficientes, que se almacenan en
dctArray3
.
Estos datos se comprimen y se escriben en el archivo.
Ahí es donde debe intervenir, modificando
dctArray3
antes de llamar a
Huf.HuffmanBlockEncoder
.
Por ejemplo, supongamos que tiene una matriz de bytes de su secreto, llamada
message
, y desea incrustar un bit por bloque 8x8 en el lsb de un coeficiente específico.
public void WriteCompressedData(BufferedOutputStream outStream, byte[] message) {
byte currentByte;
int nBytes = message.length;
int iByte = 0;
int iBit = 7;
if (nBytes > 0) {
currentByte = message[0];
} else {
currentByte = (byte) 0;
}
// Original method code up until the following line
dctArray3 = dct.quantizeBlock(dctArray2, JpegObj.QtableNumber[comp]);
// ******************** our stuff *******************
if (iByte < nBytes) {
int bit = (currentByte >> iBit) & 1;
iBit--;
if (iBit == -1) {
iBit = 7;
iByte++;
if (iByte < nBytes) {
currentByte = message[iByte];
}
}
dctArray3[23] = (dctArray3[23] & 0xfffffffe) | bit;
}
// **************************************************
Huf.HuffmanBlockEncoder(outStream, dctArray3, lastDCvalue[comp], JpegObj.DCtableNumber[comp], JpegObj.ACtableNumber[comp]);
...
}
La decodificación es el reverso de esto, donde lees los coeficientes DCT y extraes tu secreto de ellos con el algoritmo apropiado.
Necesitará un decodificador jpeg para esto, así que tomé prestados los archivos relevantes del proyecto
F5 Steganography
.
Específicamente, necesita los archivos en la carpeta
ortega
y luego puede usarlos así.
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import ortega.HuffmanDecode;
public class Extract {
private static byte[] deZigZag = {
0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31,
40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63 };
private static int[] extract(InputStream fis, int flength) throws IOException {
byte[] carrier = new byte[flength];
fis.read(carrier);
HuffmanDecode hd = new HuffmanDecode(carrier);
int[] coeff = hd.decode();
return coeff;
}
public static void main(String[] args) {
// run with argument the stego jpeg filename
try {
File f = new File(args[0]);
FileInputStream fis = new FileInputStream(f);
int[] coeff = extract(fis, (int) f.length());
int idx = deZigZag[23];
// The coeff array has all of the DCT coefficients in one big
// array, so that the first 64 elements are the coefficients
// from the first block, the next 64 from the second and so on.
//
// idx is the position of the embedding DCT coefficient.
// You can start with that and extract its lsb, then increment
// by 64 to extract the next bit from the next "block" and so on.
} catch (Exception e) {
e.printStackTrace();
}
}
}
Tengo esto para codificar datos en imágenes JPEG en Java. Estoy convirtiendo el texto a su forma binaria y lo inserto en el LSB (dependiendo de lo que haya elegido el usuario. 1,2,3,4) del RGB en cada píxel desde (0,0) hasta (ancho, alto) .
outer:
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
Color c = new Color(image.getRGB(j, i));
int red = binaryToInteger(insertMessage(integerToBinary((int)(c.getRed())),numLSB));
int green = binaryToInteger(insertMessage(integerToBinary((int)(c.getGreen())),numLSB));
int blue = binaryToInteger(insertMessage(integerToBinary((int)(c.getBlue())),numLSB));
Color newColor = new Color(red,green,blue);
image.setRGB(j,i,newColor.getRGB());
}
}
gui.appendStatus("Binarized message is: " + binarizedMessage);
File output = new File(gui.getOutput()+".jpg");
ImageIO.write(image, "png", output);
Actualmente, lo estoy escribiendo como un png y funciona bien, pero espero hacer esto en JPEG. Tengo éxito al obtener esos datos en png. Pero como se esperaba, fallando en JPEG.
Puedo decodificar los bits ocultos en la imagen escrita y ver el mensaje dado que se eligió el LSB correcto.
Actualmente estoy leyendo sobre la esteganografía JPEG, pero no entiendo exactamente cómo debo comenzar. He visto algoritmos, tampoco me ayudó.
Vi un código que no tiene ninguna clase principal encontrada.
¿Tengo que llamarlo en mi solicitud? Modificarlo? ¿Cómo decodificaría?
Aquí hay un enlace a un código que he visto.