如何从.pgm文件提取并显示隐藏文本?求代码实现建议
问题描述
需要读取P5格式的灰度PGM文件,提取每个像素的最低有效位(LSB),每8个像素的LSB组合成1字节隐藏文本,转换为ASCII字符后显示隐藏消息。目前已编写部分C语言代码,但在以下环节存在困惑:
- 正确提取单个像素的LSB
- 将LSB分组转换为ASCII字符
- 实现读取到特定标识后停止的逻辑
- 正确显示提取出的字符
现有代码
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> //Clear PGM (XV) comments. void pgmCommentClear(FILE *disk){ unsigned char ch; fread(&ch, 1, 1, disk); if (ch != '#') { fseek(disk, -1, SEEK_CUR); return; } do { while (ch != '\n') fread(&ch, 1, 1, disk); } while (ch == '#'); pgmCommentClear(disk); } //Read PGM formatted image (1D array). unsigned char *PGM_FILE_READ_1D(char *FileName, int *Width, int *Height, int *color) { int pmax; char ch; char type[3]; unsigned char *Image; FILE *disk; if ((disk = fopen(FileName, "rb")) == NULL) { return NULL; } fscanf(disk, "%s", type); if (!strcmp(type, "P6")) *color = 1; else *color = 0; fread(&ch, 1, 1, disk); pgmCommentClear(disk); fscanf(disk, "%d", Width); fscanf(disk, "%d", Height); fscanf(disk, "%d", &pmax); fread(&ch, 1, 1, disk); if (*color == 1) { Image = (unsigned char *)calloc(*Height * *Width * 3, sizeof(unsigned char)); fread(Image, 1, (*Height * *Width * 3), disk); } else { Image = (unsigned char *)calloc(*Height * *Width, sizeof(unsigned char)); fread(Image, 1, (*Height * *Width), disk); } fclose(disk); return Image; } //function to convert binary to decimal int BinaryToDec(int array[]) { int decimal=0; int m=1; for(int i=7; i>=0; i--){ decimal+= array[i]*m; m *= 2; } if(array[0] == 1) decimal *= -1; printf("decimal: %d\n", decimal); return decimal; } void DecToBinary(int dec){ int binary[8]; for(int i=0; i<=8; i++){ if(dec & (1 << i)) binary[i] = 1; else binary[i] = 0; } printf("binaryform:"); for(int i=7; i>=0; i--){ printf("%d", binary[i]); } printf("\n"); } int main(void){ char *fileName = "hw10.pgm"; int Width, Height, color; unsigned char *Image; Image = PGM_FILE_READ_1D(fileName, &Width, &Height, &color); //int size = Width*Height; int size = 50; int array[8]; int xCount =0; for(int i=0; i<size; i++){ int mask=1; for(int k=0; k<8; k++){ array[k] = Image[i]&mask; mask<<1; } printf("Image: %d\n", Image[i]); DecToBinary(Image[i]); BinaryToDec(array); } }
部分运行输出
Image: 214 binaryform:11010110 decimal: 0 Image: 213 binaryform:11010101 decimal: -255 Image: 208 binaryform:11010000 decimal: 0 Image: 214 binaryform:11010110 decimal: 0 Image: 215
实现建议
1. 正确提取单个像素的LSB
每个像素仅需提取最低位,无需读取8位。单个像素的LSB提取方式为:
// 取当前像素的最低有效位,结果为0或1 int lsb = Image[i] & 0x01;
你当前代码中对每个像素循环取8位是错误的,这是读取单个像素的所有二进制位,而非逐个像素取LSB。
2. 分组LSB生成ASCII字符
需要维护计数器和字符变量,每收集8个LSB就组合成一个完整的ASCII字符:
- 初始化
char hidden_char = 0,int bit_count = 0 - 每获取一个LSB,将
hidden_char左移1位,再与LSB进行按位或操作 - 当
bit_count达到8时,输出该字符,然后重置计数器和字符变量
示例代码片段:
char hidden_char = 0; int bit_count = 0; int total_pixels = Width * Height; for (int i = 0; i < total_pixels; i++) { int lsb = Image[i] & 0x01; hidden_char = (hidden_char << 1) | lsb; bit_count++; if (bit_count == 8) { // 输出字符,可添加停止判断 printf("%c", hidden_char); // 重置状态 hidden_char = 0; bit_count = 0; } }
3. 实现停止逻辑
常见的停止方式有两种:
- 空字符终止:当生成的
hidden_char为'\0'时,立即停止提取 - 自定义标识终止:比如检测到连续的"END"字符,需要维护缓冲区记录最近生成的几个字符,匹配时停止
空字符终止的示例:
if (bit_count == 8) { if (hidden_char == '\0') { break; // 遇到结束符停止 } printf("%c", hidden_char); hidden_char = 0; bit_count = 0; }
4. 修复现有代码的错误
BinaryToDec函数中的符号位处理多余:ASCII字符是无符号的,不需要将最高位作为符号位计算负数DecToBinary函数的循环条件错误:i<=8应改为i<8,避免越界访问数组mask<<1未赋值:应改为mask <<= 1,否则mask值不会更新- 主函数中
size硬编码为50:应改为Width * Height以遍历所有像素
5. 完整提取逻辑示例
修改后的main函数核心部分:
int main(void){ char *fileName = "hw10.pgm"; int Width, Height, color; unsigned char *Image = PGM_FILE_READ_1D(fileName, &Width, &Height, &color); if (!Image) { printf("Failed to read image\n"); return 1; } char hidden_char = 0; int bit_count = 0; int total_pixels = Width * Height; printf("Hidden message: "); for (int i = 0; i < total_pixels; i++) { int lsb = Image[i] & 0x01; hidden_char = (hidden_char << 1) | lsb; bit_count++; if (bit_count == 8) { if (hidden_char == '\0') { break; } printf("%c", hidden_char); hidden_char = 0; bit_count = 0; } } printf("\n"); free(Image); // 记得释放内存 return 0; }
内容的提问来源于stack exchange,提问作者brocoli
相关产品推荐
相关产品推荐

