LSB 全称为 least significant bit,是最低有效位的意思。Lsb 图片隐写是基于 lsb 算法的一种图片隐写术,以下统称为 lsb 隐写,这是一种常见的信息隐藏方法。通常是把src图像写入dst图像,但我们肉眼去看却并不能分辨被写入src图像的dst图像与没有被写入的dst图像有任何区别。
bmp图像是位图,比如 800 600大小的一张32位bmp图像,除了bmp文件头54字节之外,后面就是存储的位图信息,位图信息通常由rgba来排列,24位的bmp图像就是按照rgb来排列,每个像素点分别由argb分量,每个分量都是一个char来表示,所以bmp的大小应该是 800 * 600 * 4 + 54 = 1920054个字节。
而每个分量由4字节,32位来表示,比如颜色 a分量数值是101,也就是 0110 0101,咱们把这个数的最低位从1改成0,那么a变成100,也就是 0110 0100,我们肉眼是察觉不出来的。常用的隐写术是将后两位都设置成0,也就是说,哪怕103(01100111)被改成100(01100100),我们肉眼也看不出来。
所以文字盲水印的原理就是,把文字字符串转成二进制,然后依次替换bmp位图的末bit位,这样就达到了效果
那试试把文字串通过隐写加入到bmp中,看看图像有什么变化,直接上代码
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <pixman.h>
#define MAX_WATERMARK_LEN 100
#pragma pack(2)
typedef struct BITMAPFILEHEADER
{
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
}BITMAPFILEHEADER;
typedef struct BITMAPINFOHEADER
{
uint32_t biSize;
uint32_t biWidth;
uint32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
uint32_t biXPelsPerMeter;
uint32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
}BITMAPINFOHEADER;
void add_watermark(pixman_image_t *dst_image, uint8_t *src_data, int src_width, int src_height, int dst_x, int dst_y, int w, int h)
{
int reverse_watermark = 1;
uint8_t *reverse_data = NULL;
pixman_image_t *src_image = NULL;
int stride = (src_width* 4 + 3) / 4 * 4;
if(reverse_watermark)
{
uint8_t *p = src_data + (src_height - 1) * stride;
reverse_data = malloc(src_width * stride);
for(int i = 0; i < src_height; i++)
{
memcpy(reverse_data + i * stride, p, stride);
p -= stride;
}
src_image = pixman_image_create_bits(PIXMAN_b8g8r8a8, src_width, src_height, (uint32_t *)reverse_data, stride);
}
else
{
src_image = pixman_image_create_bits(PIXMAN_b8g8r8a8, src_width, src_height, (uint32_t *)src_data, stride);
}
pixman_image_composite32(PIXMAN_OP_ADD,
src_image, NULL, dst_image,
0, 0, // src
0, 0, // mask
dst_x, dst_y, // dst
w,
h);
if(reverse_data)
free(reverse_data);
}
// set the lowest order to bit
// x
// bit 0 or 1
unsigned int set_low_bit(unsigned int x, int bit)
{
return x & 0xFE | bit;
}
// order 0 ~ 7
// set order to bit
unsigned int set_bit(unsigned int x, int order, int bit)
{
return x | (bit << order);
}
unsigned int get_low_bit(unsigned int x)
{
return x & 0x1;
}
char *get_text(uint8_t *data, int width, int height)
{
char *str = malloc(MAX_WATERMARK_LEN + 1);
memset(str, 0, MAX_WATERMARK_LEN + 1);
for(int i = 0; i < MAX_WATERMARK_LEN; i++)
{
int a = 0;
uint8_t *p = data + i * 8;
for(int j = 0; j < 8; j++)
{
unsigned int bit = get_low_bit(p[j]);
printf("get_bit-[%d]\n", bit);
a = set_bit(a, 8 - 1 - j, bit);
printf("set bit over a-[%d]\n", a);
}
printf("\n");
str[i] = a;
}
str[MAX_WATERMARK_LEN] = '\0';
return str;
}
void add_lsb_watermark(uint8_t *rgba, char *text)
{
int index = 0;
for(int i = 0; i < MAX_WATERMARK_LEN; i++)
{
if(i > 0 && i % (strlen(text)) == 0)
index = 0;
uint8_t *p = rgba + i * 8;
for(int j = 0; j < 8; j++)
{
int bit = (text[index] & (1 << 7 - j)) >> (7 - j);
printf("bit=[%d]", bit);
p[j] = set_low_bit(p[j], bit);
}
printf("\n");
index++;
}
}
int main(int argc, char *argv[])
{
BITMAPFILEHEADER bmph2;
BITMAPINFOHEADER infoh2;
// printf("InfoHead=[%d]\n", sizeof(BITMAPFILEHEADER));
// printf("InfoHead=[%d]\n", sizeof(BITMAPINFOHEADER));
FILE *fp2 = fopen("aaa.bmp", "rb");
fseek(fp2, 0, SEEK_END);
int len2 = ftell(fp2);
rewind(fp2);
int file_head_len2 = fread(&bmph2, 1, sizeof(BITMAPFILEHEADER), fp2);
// printf("read file head len=[%d]\n", file_head_len2);
int info_head_len2 = fread(&infoh2, 1, sizeof(BITMAPINFOHEADER), fp2);
// printf("read info head len=[%d]\n", info_head_len2);
int width2 = infoh2.biWidth;
int height2 = infoh2.biHeight;
int data_len2 = len2 - file_head_len2 - info_head_len2;
uint8_t *data2 = malloc(sizeof(uint8_t) *(data_len2));
data_len2 = fread(data2, 1, len2 - 54, fp2);
printf("read dst bitmap width-height--[%d][%d]=[%d]-bitcount=[%d]\n", data_len2, width2, height2, infoh2.biBitCount);
add_lsb_watermark(data2, "luobo-oboul");
FILE *ffp2 = fopen("aaa-with-watermark.bmp", "wb+");
infoh2.biHeight = height2;
fwrite(&bmph2, 1, file_head_len2, ffp2);
fwrite(&infoh2, 1, info_head_len2, ffp2);
fwrite(data2, 1, data_len2, ffp2);
fclose(ffp2);
char *str = get_text(data2, width2, height2);
printf("get-text-[%s]\n", str);
free(str);
return 0;
}
gcc.exe -o lsb-bitmap lsb-bitmap.c -g `pkg-config --cflags --libs pixman-1`
将aaa.bmp里加上了luobo-oboul的文字水印,图片对比图如下