474 lines
12 KiB
C++
474 lines
12 KiB
C++
/*
|
||
Nome: Mario
|
||
Cognome: Montanari
|
||
Classe: 3AIN
|
||
Data: 09/05/2025
|
||
|
||
1) Leggere il primo pixel (BGR);
|
||
2) Modificarlo;
|
||
3) Elaborare un’immagine (crearla con paint con dimensioni in pixel multiple di 4):
|
||
- Rendendo i pixel casuali;
|
||
- Generando blocchi uniformi di colore;
|
||
4) Inventare un banale algoritmo di compressione e decompressione:
|
||
- stessi header poi … i prossimi 50 px sono verdi, i prossimi 20 px sono blu, …
|
||
5) Steganografia:
|
||
- Nascondere i byte corrispondenti ai caratteri di una parola sparpagliandoli
|
||
nei pixel secondo una mappa prestabilita;
|
||
- Nascondere i bit corrispondenti al byte di una o più lettere mascherandoli
|
||
come lsbit dei pixel secondo una mappa prestabilita;
|
||
- Decomprimere un file bmp compresso secondo un algoritmo banale (vedi es 4.e).
|
||
*/
|
||
|
||
#include <iostream>
|
||
#include <cstdlib>
|
||
#include <ctime>
|
||
|
||
using namespace std;
|
||
|
||
#pragma pack(1)
|
||
|
||
typedef struct {
|
||
char bfType[2];
|
||
int bfSize;
|
||
int bfReserved;
|
||
int bfOffBits;
|
||
} BITMAPFILEHEADER;
|
||
|
||
typedef struct {
|
||
unsigned char b;
|
||
unsigned char g;
|
||
unsigned char r;
|
||
} BGR;
|
||
|
||
void compressioneBMP(const char * inputFilename, const char * outputFilename, int lunghezza, int altezza, int offset);
|
||
void decompressioneBMP(const char * inputFilename, const char * outputFilename, int lunghezza, int altezza, int offset);
|
||
void nascondiByteNeiPixel(const char * inputFile, const char * outputFile, const char * messaggio, int offset);
|
||
void estraiByteDaiPixel(const char * filename, int offset, int intervallo, int lunghezzaMessaggio);
|
||
void nascondiBitNeiPixel(const char * inputFilename, const char * outputFilename, const char * messaggio, int offset);
|
||
void estraiBitDaiPixel(const char * filename, int offset, int lunghezzaMessaggio);
|
||
|
||
int main(void) {
|
||
cout << "sizeof(BITMAPFILEHEADER): " << sizeof(BITMAPFILEHEADER) << endl;
|
||
|
||
FILE * file = fopen("tuttaBianca.bmp", "rb");
|
||
|
||
if (file != NULL) {
|
||
srand(time(NULL));
|
||
|
||
int bfOffBits;
|
||
|
||
BITMAPFILEHEADER bf;
|
||
|
||
// Scrivo l'header del file .bmp
|
||
fseek(file, 0, SEEK_SET);
|
||
fread(&bf, sizeof(bf), 1, file);
|
||
cout << "bfType: " << bf.bfType[0] << bf.bfType[1] << endl;
|
||
|
||
fseek(file, 2, SEEK_CUR);
|
||
cout << "bfSize: " << bf.bfSize << endl;
|
||
|
||
fseek(file, 10, SEEK_CUR);
|
||
cout << "bfOffBits: " << bf.bfOffBits << endl;
|
||
|
||
fseek(file, bf.bfOffBits, SEEK_SET);
|
||
|
||
|
||
// --------------------------------------------------------------------
|
||
// 1. Copiare il file BMP originale in modificato.bmp
|
||
// --------------------------------------------------------------------
|
||
|
||
rewind(file);
|
||
|
||
FILE * file_mod = fopen("modificato.bmp", "wb");
|
||
|
||
if (file_mod != NULL) {
|
||
char buffer[1024];
|
||
int byte;
|
||
|
||
while ((byte = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
||
fwrite(buffer, 1, byte, file_mod);
|
||
}
|
||
|
||
fclose(file_mod);
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
|
||
|
||
// --------------------------------------------------------------------
|
||
// 2. Modificare il primo pixel in rosso nel file modificato.bmp
|
||
// --------------------------------------------------------------------
|
||
|
||
file_mod = fopen("modificato.bmp", "r+b");
|
||
|
||
if (file_mod != NULL) {
|
||
fseek(file_mod, bf.bfOffBits, SEEK_SET);
|
||
|
||
BGR bgr;
|
||
|
||
bgr.b = 255;
|
||
bgr.g = 0;
|
||
bgr.r = 0;
|
||
|
||
fwrite(&bgr, sizeof(BGR), 1, file_mod);
|
||
|
||
fclose(file_mod);
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
|
||
|
||
// --------------------------------------------------------------------
|
||
// 3. Creare il file modificato2.bmp e riempire con pixel casuali
|
||
// --------------------------------------------------------------------
|
||
|
||
rewind(file);
|
||
|
||
FILE * file_mod2 = fopen("modificato2.bmp", "wb");
|
||
|
||
if (file_mod2 != NULL) {
|
||
char buffer[1024];
|
||
int byte;
|
||
|
||
while ((byte = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
||
fwrite(buffer, 1, byte, file_mod2);
|
||
}
|
||
|
||
fclose(file_mod2);
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
|
||
|
||
// --------------------------------------------------------------------
|
||
// Sovrascrivere i pixel con valori casuali in modificato2.bmp
|
||
// --------------------------------------------------------------------
|
||
|
||
file_mod2 = fopen("modificato2.bmp", "r+b");
|
||
|
||
if (file_mod2 != NULL) {
|
||
fseek(file_mod2, bf.bfOffBits, SEEK_SET);
|
||
|
||
int lunghezza = 400;
|
||
int altezza = 400;
|
||
int padding = (4 - (lunghezza * 3) % 4) % 4;
|
||
|
||
fseek(file_mod2, bf.bfOffBits, SEEK_SET);
|
||
|
||
for (int y = 0; y < altezza; y++) {
|
||
for (int x = 0; x < lunghezza; x++) {
|
||
BGR pixel;
|
||
|
||
pixel.b = rand() % 256;
|
||
pixel.g = rand() % 256;
|
||
pixel.r = rand() % 256;
|
||
|
||
fwrite(&pixel, sizeof(BGR), 1, file_mod2);
|
||
}
|
||
|
||
// Scrivi il padding se necassario
|
||
for (int p = 0; p < padding; p++) {
|
||
fputc(0x00, file_mod2);
|
||
}
|
||
}
|
||
|
||
fclose(file_mod2);
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
|
||
cout << endl;
|
||
|
||
// --------------------------------------------------------------------
|
||
// 4A. Compressione del file modificato2.bmp
|
||
// --------------------------------------------------------------------
|
||
|
||
compressioneBMP("modificato2.bmp", "compresso.bmpc", 400, 400, bf.bfOffBits);
|
||
|
||
|
||
// --------------------------------------------------------------------
|
||
// 4B. Decompressione del file compresso.bmp
|
||
// --------------------------------------------------------------------
|
||
|
||
decompressioneBMP("compresso.bmpc", "decompresso.bmp", 400, 400, bf.bfOffBits);
|
||
|
||
cout << endl;
|
||
|
||
// Steganografia dei byte interi
|
||
nascondiByteNeiPixel("tuttaBianca.bmp", "conMessaggio_byte.bmp", "MONTANARI", bf.bfOffBits);
|
||
estraiByteDaiPixel("conMessaggio_byte.bmp", bf.bfOffBits, 50, 9);
|
||
|
||
cout << endl;
|
||
|
||
// Steganografia dei bit
|
||
nascondiBitNeiPixel("tuttaBianca.bmp", "conMessaggio_bit.bmp", "MONTANARI", bf.bfOffBits);
|
||
estraiBitDaiPixel("conMessaggio_bit.bmp", bf.bfOffBits, 9);
|
||
|
||
fclose(file);
|
||
} else {
|
||
perror("Error (source)");
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void compressioneBMP(const char * inputFilename, const char * outputFilename, int lunghezza, int altezza, int offset) {
|
||
FILE * inputFile = fopen(inputFilename, "rb");
|
||
FILE * outputFile = fopen(outputFilename, "wb");
|
||
|
||
if (inputFile != NULL && outputFile != NULL) {
|
||
|
||
// Copia l'header
|
||
char header[1024];
|
||
|
||
fread(header, 1, offset, inputFile);
|
||
fwrite(header, 1, offset, outputFile);
|
||
|
||
|
||
int padding = (4 - (lunghezza * 3) % 4) % 4;
|
||
|
||
BGR pixel;
|
||
BGR precedente;
|
||
int count = 0;
|
||
bool primo = true;
|
||
|
||
// Legge i pixel uno a uno e li comprime
|
||
for (int y = 0; y < altezza; y++) {
|
||
for (int x = 0; x < lunghezza; x++) {
|
||
fread(&pixel, sizeof(BGR), 1, inputFile);
|
||
|
||
if (primo) {
|
||
precedente = pixel;
|
||
count = 1;
|
||
primo = false;
|
||
} else if (
|
||
pixel.b == precedente.b &&
|
||
pixel.g == precedente.g &&
|
||
pixel.r == precedente.r &&
|
||
count < 255
|
||
) {
|
||
count++;
|
||
} else {
|
||
// Scrive [numero_pixel_uguali][colore]
|
||
fputc(count, outputFile);
|
||
fwrite(&precedente, sizeof(BGR), 1, outputFile);
|
||
precedente = pixel;
|
||
count = 1;
|
||
}
|
||
}
|
||
|
||
fseek(inputFile, padding, SEEK_CUR);
|
||
}
|
||
|
||
// Scrive l'ultimo blocco
|
||
if (count > 0) {
|
||
fputc(count, outputFile);
|
||
fwrite(&precedente, sizeof(BGR), 1, outputFile);
|
||
}
|
||
|
||
cout << "Compressione completata in: " << outputFilename << endl;
|
||
|
||
fclose(inputFile);
|
||
fclose(outputFile);
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
}
|
||
|
||
void decompressioneBMP(const char * inputFilename, const char * outputFilename, int lunghezza, int altezza, int offset) {
|
||
FILE * inputFile = fopen(inputFilename, "rb");
|
||
FILE * outputFile = fopen(outputFilename, "wb");
|
||
|
||
if (inputFile != NULL && outputFile != NULL) {
|
||
|
||
// Leggi e scrivi l'header BMP
|
||
char header[1024];
|
||
|
||
fread(header, 1, offset, inputFile);
|
||
fwrite(header, 1, offset, outputFile);
|
||
|
||
int padding = (4 - (lunghezza * 3) % 4) % 4;
|
||
int totalPixels = lunghezza * altezza;
|
||
int writtenPixels = 0;
|
||
|
||
while (writtenPixels < totalPixels) {
|
||
int count = fgetc(inputFile);
|
||
|
||
BGR pixel;
|
||
|
||
fread(&pixel, sizeof(BGR), 1, inputFile);
|
||
|
||
for (int i = 0; i < count; i++) {
|
||
fwrite(&pixel, sizeof(BGR), 1, outputFile);
|
||
writtenPixels++;
|
||
|
||
// Dopo ogni riga, aggiungi un padding
|
||
if (writtenPixels % lunghezza == 0) {
|
||
for (int p = 0; p < padding; p++) {
|
||
fputc(0x00, outputFile);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
fclose(inputFile);
|
||
fclose(outputFile);
|
||
|
||
cout << "Decompressione completata in: " << outputFilename << endl;
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
}
|
||
|
||
// Nasconde ogni byte di un messaggio nei colori di pixel a intervalli
|
||
void nascondiByteNeiPixel(const char * inputFilename, const char * outputFilename, const char * messaggio, int offset) {
|
||
FILE * inputFile = fopen(inputFilename, "rb");
|
||
FILE * outputFile = fopen(outputFilename, "wb");
|
||
|
||
if (inputFile != NULL && outputFile != NULL) {
|
||
char header[1024];
|
||
|
||
fread(header, 1, offset, inputFile);
|
||
fwrite(header, 1, offset, outputFile);
|
||
|
||
BGR pixel;
|
||
|
||
int i = 0;
|
||
int intervallo = 50;
|
||
int posizionePixel = 0;
|
||
|
||
int lunghezza = strlen(messaggio);
|
||
|
||
while (fread(&pixel, sizeof(BGR), 1, inputFile) == 1) {
|
||
if (posizionePixel % intervallo == 0 && i < lunghezza) {
|
||
pixel.r = messaggio[i++];
|
||
}
|
||
|
||
fwrite(&pixel, sizeof(BGR), 1, outputFile);
|
||
posizionePixel++;
|
||
}
|
||
|
||
fclose(inputFile);
|
||
fclose(outputFile);
|
||
|
||
cout << "Byte nascosti in: " << outputFilename << endl;
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
}
|
||
|
||
// Recupera il messaggio nascosto
|
||
void estraiByteDaiPixel(const char * filename, int offset, int intervallo, int lunghezzaMessaggio) {
|
||
FILE * file = fopen(filename, "rb");
|
||
|
||
if (file != NULL) {
|
||
fseek(file, offset, SEEK_SET);
|
||
|
||
BGR pixel;
|
||
|
||
int posizionePixel = 0;
|
||
int letti = 0;
|
||
|
||
cout << "Messaggio estratto: ";
|
||
while (fread(&pixel, sizeof(BGR), 1, file) == 1 && letti < lunghezzaMessaggio) {
|
||
if (posizionePixel % intervallo == 0) {
|
||
cout << (char)pixel.r;
|
||
|
||
letti++;
|
||
}
|
||
|
||
posizionePixel++;
|
||
}
|
||
|
||
cout << endl;
|
||
|
||
fclose(file);
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
}
|
||
|
||
// Nasconde bit a bit un messaggio nei pixel
|
||
void nascondiBitNeiPixel(const char * inputFilename, const char * outputFilename, const char * messaggio, int offset) {
|
||
FILE * inputFile = fopen(inputFilename, "rb");
|
||
FILE * outputFile = fopen(outputFilename, "wb");
|
||
|
||
if (inputFile != NULL && outputFile != NULL) {
|
||
char header[1024];
|
||
|
||
fread(header, 1, offset, inputFile);
|
||
fwrite(header, 1, offset, outputFile);
|
||
|
||
int posizioneBit = 0;
|
||
int posizioneChr = 0;
|
||
|
||
BGR pixel;
|
||
|
||
int len = strlen(messaggio);
|
||
|
||
while (fread(&pixel, sizeof(BGR), 1, inputFile) == 1) {
|
||
if (posizioneChr < len) {
|
||
char chr = messaggio[posizioneChr];
|
||
int bit = (chr >> (7 - posizioneBit)) & 1;
|
||
|
||
pixel.b = (pixel.b & 0xFE) | bit;
|
||
|
||
posizioneBit++;
|
||
|
||
if (posizioneBit == 8) {
|
||
posizioneBit = 0;
|
||
|
||
posizioneChr++;
|
||
}
|
||
}
|
||
|
||
fwrite(&pixel, sizeof(BGR), 1, outputFile);
|
||
}
|
||
|
||
fclose(inputFile);
|
||
fclose(outputFile);
|
||
|
||
cout << "Bit nascosti in: " << outputFilename << endl;
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
}
|
||
|
||
// Recupera i bit nascosti nei pixel
|
||
void estraiBitDaiPixel(const char * filename, int offset, int lunghezzaMessaggio) {
|
||
FILE * file = fopen(filename, "rb");
|
||
|
||
if (file != NULL) {
|
||
fseek(file, offset, SEEK_SET);
|
||
|
||
BGR pixel;
|
||
|
||
int posizioneBit = 0;
|
||
int chr = 0;
|
||
int estratti = 0;
|
||
|
||
cout << "Messaggio estratto: ";
|
||
while (fread(&pixel, sizeof(BGR), 1, file) == 1 && estratti < lunghezzaMessaggio) {
|
||
int bit = pixel.b & 1;
|
||
|
||
chr = (chr << 1) | bit;
|
||
|
||
posizioneBit++;
|
||
|
||
if (posizioneBit == 8) {
|
||
cout << (char)chr;
|
||
|
||
posizioneBit = 0;
|
||
chr = 0;
|
||
|
||
estratti++;
|
||
}
|
||
}
|
||
|
||
cout << endl;
|
||
|
||
fclose(file);
|
||
} else {
|
||
perror("Error");
|
||
}
|
||
} |