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;
    }
  • 相关阅读:
    Kinect 开发 —— 硬件设备解剖
    Kinect 开发 —— 引言
    (转)OpenCV 基本知识框架
    OpenCV —— 摄像机模型与标定
    OpenCV —— 跟踪与运动
    OpenCV —— 图像局部与分割(二)
    OpenCV —— 图像局部与部分分割(一)
    OpenCV —— 轮廓
    OpenCV —— 直方图与匹配
    OpenCV —— 图像变换
  • 原文地址:https://www.cnblogs.com/mydrizzle/p/12602275.html
Copyright © 2011-2022 走看看