zoukankan      html  css  js  c++  java
  • 关于BMP格式文件LSB信息隐藏算法

    图像信息隐藏,通俗地讲,就是将秘密藏进普通的图片文件或者音频文件中。图片文件是由一系列(比方10000个)字节组成,秘密信息(中文,英文等)由少量(比方30个汉字,30*2*8=480比特)的字节组成,把图片中每10个字节的最后1位用秘密信息的1位来代替(事先商量好的),就把30个汉字信息藏进普通的图片文件中480个字节中,照片质量基本不受影响。

    BMP格式:BMP(位图格式)是DOS和WindowS兼容计算机系统的标准Windows图像格式。BMP格式支持RGB、索引颜色、灰度和位图颜色模式,但不支持Alpha通道。BMP格式支持1、4、24、32位的RGB位图。

    典型的BMP图像文件由四部分组成:

    1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;

    2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;

    3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;

    4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。

    信息的隐藏和提取是两个独立部分,分为把信息编码写入图片和把信息从图片提取后解码出来两部分。

    LSB 算法设计思路:将待隐藏数据信息以二进制读方式存入buffer,并将一个bitu数据存入一个字节数据的最低位,即将一个字节数据隐藏于8字节数据每字节最低位,由于仅对每一个字节最低位进行修改,图片以肉眼区分无法发现明显失真,隐藏较为隐蔽。

    实现方法见代码

    bmp_hide:

    #include<windows.h>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include<ctime>
    #include<iomanip>
    #include<sstream>
    #include<stack>
    #include<vector>
    #include <iostream>
    #define LENGTH_NAME_BMP 30//bmp图片文件名的最大长度
    using namespace std;
    //变量定义
    BITMAPFILEHEADER strHead;
    BITMAPINFOHEADER strInfo;
    typedef struct tagIMAGEDATA
    {
        //图像数据结构,一个像素
        BYTE blue;//
        BYTE green;//绿
        BYTE red;//
    } IMAGEDATA;
    
    void showBmpHead(BITMAPFILEHEADER pBmpHead)
    {
        /***function:显示BMP图片文件头**/
        cout<<"位图文件头:";
        (pBmpHead.bfType==0x4d42)?(cout<<"BM"<<endl):(cout<<endl);
        cout<<"文件大小:"<<pBmpHead.bfSize<<"字节"<<endl;
        cout<<"保留字_1:"<<pBmpHead.bfReserved1<<endl;
        cout<<"保留字_2:"<<pBmpHead.bfReserved2<<endl;
        cout<<"实际位图数据的偏移字节数:"<<pBmpHead.bfOffBits<<"字节"<<endl<<endl;
    }
    void showBmpInforHead(tagBITMAPINFOHEADER pBmpInforHead)
    {
        /***function:显示BMP图片信息头**/
        cout<<"位图信息头:"<<endl;
        cout<<"结构体的长度:"<<pBmpInforHead.biSize<<endl;
        cout<<"位图宽:"<<pBmpInforHead.biWidth<<endl;
        cout<<"位图高:"<<pBmpInforHead.biHeight<<endl;
        cout<<"平面数:"<<pBmpInforHead.biPlanes<<endl;
        cout<<"采用颜色位数:"<<pBmpInforHead.biBitCount<<endl;
        cout<<"压缩方式:"<<pBmpInforHead.biCompression<<endl;
        cout<<"实际位图数据占用的字节数:"<<pBmpInforHead.biSizeImage<<endl;
        cout<<"X方向分辨率:"<<pBmpInforHead.biXPelsPerMeter<<endl;
        cout<<"Y方向分辨率:"<<pBmpInforHead.biYPelsPerMeter<<endl;
        cout<<"使用的颜色数:"<<pBmpInforHead.biClrUsed<<endl;
        cout<<"重要颜色数:"<<pBmpInforHead.biClrImportant<<endl;
    }
    
    void ConverseBinary(const char* ch,int length,vector<int> &val)
    {
        /**
        function:把字符串的ASCII码转为二进制
        ch:输入字符串
        val:字符的二进制数组,
        **/
        stack<int> stk;
        for(int i = 0; i<length; i++)
        {
            int ascii = ch[i];/**取字符**/
            for(int j = 0; j<8; j++)/**转为二进制 **/
            {
                stk.push(ascii%2);
                ascii>>=1;
            }
            for(int k = 0; k<8; k++)/**出栈,存入数组 **/
            {
                val.push_back(stk.top());
                stk.pop();
            }
        }
    }
    
    void HideMessage(IMAGEDATA* &imagedata,vector<int> &val)
    {
        for(int i = 0,j=0; i < val.size(); j++)
        {
            imagedata[j].blue|=1;/**或1最低位置1**/
            if(val[i++]==0)
                imagedata[j].blue^=1;/**异或1最低位置0**/
            imagedata[j].green|=1;
            if(val[i++]==0)
                imagedata[j].green^=1;
            imagedata[j].red|=1;
            if(val[i++]==0)
                imagedata[j].red^=1;
        }
    }
    
    int main()
    {
        char strFile[LENGTH_NAME_BMP] ;     //bmp文件名
        IMAGEDATA *imagedata = NULL;                    //动态分配存储原图片的像素信息的二维数组
        int width,height;   //图片的宽度和高度
    
        cout<<"请输入隐藏信息载体BMP图片路径名:"<<endl;
        cin>>strFile;//输入文件名
    
        FILE *fpi;                                      //读取图片内容
    
        FILE *fpw;                                      //写入文件内容
    
        fpi=fopen(strFile,"rb");                        //只读方式打开文件
    
        /**
        //FILE * fopen(const char * path, const char * mode);
        //返回值:文件顺利打开后,指向该流的文件指针就会被返回。
        //如果文件打开失败则返回 NULL,并把错误代码存在 error 中。
        **/
    
        if(fpi != NULL)
        {
            fread(&strHead,sizeof(tagBITMAPFILEHEADER),1,fpi);        //读取文件头
            /***
            //函数原型:
            //size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
            //buffer 用于接收数据的内存地址
            //size 要读的每个数据项的字节数,单位是字节
            //count 要读count个数据项,每个数据项size个字节.
            //stream 输入流
            //返回值    返回真实读取的项数,若大于count则意味着产生了错误。
            //另外,产生错误后,文件位置指示器是无法确定的。
            //若其他stream或buffer为空指针,
            //或在unicode模式中写入的字节数为奇数,
            //此函数设置errno为EINVAL以及返回0.
            ***/
    
            if(0x4d42!=strHead.bfType)
            {
                cout<<"此文件不是bmp格式文件!"<<endl;
                system("pause");
                return 0;
            }
    
            //showBmpHead(strHead);               //显示文件头
            fread(&strInfo,sizeof(tagBITMAPINFOHEADER),1,fpi);
            //showBmpInforHead(strInfo);              //显示文件信息头
    
            width = strInfo.biWidth;
            height = strInfo.biHeight;
    
            imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));
    
            //初始化原始图片的像素数组
            for(int i = 0; i < height; ++i)
            {
                for(int j = 0; j < width; ++j)
                {
                    (*(imagedata + i * width + j)).blue = 0xff;
                    (*(imagedata + i * width + j)).green = 0xcc;
                    (*(imagedata + i *  width + j)).red = 0x66;
                }
            }
    
            //fseek(fpi,54,SEEK_SET);//定位到54字节后
    
            fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);//读出图片的像素数据
            fclose(fpi);
        }
        else
        {
            cout<<"文件打开错误!"<<endl;
            system("pause");
            return 0;
        }
    
        /*********图片隐藏信息处理*************/
        int capacity = width*height*3/8;
        string s;
        const char* ch;
        vector<int> val;
    
        cout<<"文件容量:"<<capacity<<"字节"<<endl;
        cout<<"请输入要隐藏的信息:";
        cin>>s;
        ch = s.data();
    
        ConverseBinary(ch,s.length(),val);
        HideMessage(imagedata,val);
    
        /**********************/
        //保存bmp图片
        string fname;
        cout<<"请输入要保存的文件名:"<<endl;
        cin>>fname;
        const char* filename = fname.data();
    
        //创建图片文件
        if((fpw=fopen(filename,"wb"))==NULL)
        {
            cout<<"创建BMP文件错误!"<<endl;
            system("pause");
            return 0;
        }
    
        fwrite(&strHead,sizeof(tagBITMAPFILEHEADER),1,fpw);             //写入位图文件头
        /**
        //size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
        //buffer:是一个指针,对fwrite来说,是要获取数据的地址;
        //size:要写入内容的单字节数;
        //count:要进行写入size字节的数据项的个数;
        //stream:目标文件指针;
        //返回值:返回实际写入的数据项个数count。
        **/
    
        fwrite(&strInfo,sizeof(tagBITMAPINFOHEADER),1,fpw);             //写入位图信息头
        fwrite(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpw);//写入位图数据
        fclose(fpw);
    
        cout<<endl<<"信息已隐藏在文件:"<<fname<<endl<<endl;
    
        delete[] imagedata;//释放内存
        system("pause");
        return 0;
    }

    生成的bmp图片文件隐藏的信息需要通过对应的解密程序进行提取

    bmp_extract:

    #include<windows.h>
    #include <stdio.h>
    #include <cstdlib>
    #include <cmath>
    #include<ctime>
    #include<iomanip>
    #include<sstream>
    #include<stack>
    #include<vector>
    #include <iostream>
    #define LENGTH_NAME_BMP 30//bmp图片文件名的最大长度
    using namespace std;
    //变量定义
    BITMAPFILEHEADER strHead;
    BITMAPINFOHEADER strInfo;
    typedef struct tagIMAGEDATA
    {//图像数据结构,一个像素
        BYTE blue;//
        BYTE green;//绿
        BYTE red;//
    } IMAGEDATA;
    
    void ExtractMessage(IMAGEDATA* &imagedata,int length,vector<int> &extract)
    {
        for(int i = 0; i < length*8/3; i++)
        {
            int n = ((int)imagedata[i].blue);
            n&=1;
            extract.push_back(n);
    
            n = ((int)imagedata[i].green);
            n&=1;
            extract.push_back(n);
    
            n = ((int)imagedata[i].red);
            n&=1;
            extract.push_back(n);
        }
    }
    
    string ConverseString(vector<int> &extract)
    {
        string stri;
        for(int j = 0; j<extract.size()/8; j++)
        {
            int buffer = 0;
            for (int k = 0; k<8; k++)
            {
                buffer<<=1;
                buffer+=extract[j*8+k];
            }
            stri+= buffer;
        }
        return stri;
    }
    
    int main()
    {
        char strFile[LENGTH_NAME_BMP];
        IMAGEDATA *imagedata = NULL;
        int width,height;
        cout<<"请输入已隐藏信息的载体BMP文件路径名:";
        cin>>strFile;
        FILE *fpi;
        fpi=fopen(strFile,"rb");
        if(fpi != NULL)
        {
            fread(&strHead,sizeof(tagBITMAPFILEHEADER),1,fpi);
            if(0x4d42!=strHead.bfType)
            {
                cout<<"此文件不是bmp格式文件!"<<endl;
                system("pause");
                return 0;
            }
            fread(&strInfo,sizeof(tagBITMAPINFOHEADER),1,fpi);
            width = strInfo.biWidth;
            height = strInfo.biHeight;
            imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));
            for(int i = 0; i < height; ++i)
            {
                for(int j = 0; j < width; ++j)
                {
                    (*(imagedata + i * width + j)).blue = 0xff;
                    (*(imagedata + i * width + j)).green = 0xcc;
                    (*(imagedata + i *  width + j)).red = 0x66;
                }
            }
            fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);
            fclose(fpi);
        }
        else
        {
            cout<<"文件打开错误!"<<endl;
            system("pause");
            return 0;
        }
        /**********图片提取信息处理************/
        int capacity = width*height*3/8;
        int messageLength;
        cout<<"此文件可容纳信息量:"<<capacity<<"字节"<<endl;
    
        vector<int> extract;
    
        //cout<<endl<<"文件容量:"<<capacity<<"字节"<<endl;
        cout<<"要提取的信息长度:";
        cin>>messageLength;
    
        ExtractMessage(imagedata,messageLength,extract);
        string stri = ConverseString(extract);
    
        cout<<"图片内隐藏的信息为:"<<endl<<stri<<endl;
        /**********************/
    
        //释放内存
        delete[] imagedata;
        system("pause");
        return 0;
    }
  • 相关阅读:
    关于UltraISO打开iso文件后只有部分文件问题
    CollectionViewController 集合视图
    SDWebImage, 关于第三方异步加载图片的一些方法
    NSFileManager(文件管理类)
    UIReview(UI总结)
    sqlite( 轻量级数据库)
    DataPersistence(数据持久化: 文件读写, NSUserDefault, 归档)
    第三方(SDWebImage, 网络类AFNetWorking)
    网络编程(GET, POST)
    数据解析(SAX, JSON)
  • 原文地址:https://www.cnblogs.com/mydrizzle/p/12602275.html
Copyright © 2011-2022 走看看