Java_最不重要位替換(LSB)基於24位BMP圖片

向BMP圖片中隱藏一段文字並保存,從保存的圖片中提取文字.

Java代碼:


import java.awt.BorderLayout;  
import java.awt.Color;  
import java.awt.Dimension;
import java.awt.Graphics;  
import java.awt.event.ActionEvent;  
import java.awt.event.ActionListener;  
import java.io.BufferedInputStream;  
import java.io.BufferedOutputStream;  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;
import java.util.Enumeration;

import javax.swing.JFileChooser;  
import javax.swing.JFrame;
import javax.swing.JMenu;  
import javax.swing.JMenuBar;  
import javax.swing.JMenuItem;  
import javax.swing.JPanel;  
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.FontUIResource;  
  
/**
 * 
 * @ClassName: 信息隱藏_LSB技術_於BMP位圖 
 * @Description: 基於24位BMP圖片,運用LSB技術對文本信息進行隱藏
 * @author: 
 * @date: 2018年4月23日 下午8:11:52
 */

public class 信息隱藏_LSB技術_於BMP位圖  extends JFrame{ 
	
	private static final long serialVersionUID = 1L;
	int map[][];//保存像素顏色的數組  
    MyPanel center;//繪圖面板  
    File selectFile;//讀取的文件  
    int width;//圖像寬度  
    int height;//圖像高度  
    byte temp1[];//讀取BMP文件的前18個字節信息  
    byte temp2[];//讀取BMP文件的後28個字節信息  
    JScrollPane scrollpane;//滾動面板
    JTextArea infoJt;//信息文本區
    JMenuItem open;
    JMenuItem save;
    JMenuItem hide;
    JMenuItem show;
    int disWidth=1000;
    int disHeight=700;
    public 信息隱藏_LSB技術_於BMP位圖() {  
    	setUIFont(new FontUIResource("微軟雅黑", 0, 20));
        this.setLayout(new BorderLayout());//最好先設置佈局再添加組件  
        //初始化畫圖面板  
        center=new MyPanel();  
        center.setBackground(Color.WHITE);  
        center.setBackground(Color.GRAY);
      //  center.setPreferredSize(new Dimension(200, 300));
        scrollpane=new JScrollPane(center);//用center初始化滾動面板  
      //  scrollpane.setPreferredSize(new Dimension(200,100)); 
        infoJt=new JTextArea();
        infoJt.setLineWrap(true);
        infoJt.setWrapStyleWord(true);
       JScrollPane infoJs=new JScrollPane(infoJt);
        infoJs.setPreferredSize(new Dimension(disWidth, 300));
        MyListener lis=new MyListener();  
          
        //初始化菜單欄  
        JMenuBar menuBar=new JMenuBar();  
        JMenu fileMenu=new JMenu("file");  
         open=new JMenuItem("open");  
         save=new JMenuItem("save");
        JMenu LSBMenu=new JMenu("LSB");  
         hide=new JMenuItem("hide");  
         show=new JMenuItem("show");
        open.addActionListener(lis);  
        save.addActionListener(lis);
        hide.addActionListener(lis);
        show.addActionListener(lis);
        fileMenu.add(open);  
        fileMenu.add(save);  
        menuBar.add(fileMenu);
        LSBMenu.add(hide);
        LSBMenu.add(show);
        menuBar.add(LSBMenu);
        this.setJMenuBar(menuBar);  
        this.add(scrollpane,BorderLayout.CENTER);//加入的是滾動面板
        this.add(infoJs,BorderLayout.SOUTH);
        this.setTitle("LSB隱藏器");  
        this.setSize(disWidth, disHeight); 
        this.setLocationRelativeTo(null);//設置窗體出現在屏幕中間
        this.setDefaultCloseOperation(3);  
        this.setVisible(true);
       
    }  
    /** 
     * 讀取bmp文件 
     */  
    public void readBMP()  
    {  
        try {  
            FileInputStream fis=new FileInputStream(selectFile);  
            BufferedInputStream bis=new BufferedInputStream(fis);  
            byte[] wb=new byte[4];//讀取寬度的字節數組  
            byte[] hb=new byte[4];//讀取高度的字節數組  
            temp1=new byte[18];  
            bis.read(temp1);//bis.skip(18);//跳過前18個byte  
            bis.read(wb);//讀取寬度  
            bis.read(hb);//讀取高度  
            
            width=byteToint(wb);  
            height=byteToint(hb);  
            map=new int[height][width];
            int skip=4-width*3%4;//得到每行要跳過的數字(與windows 系統機制有關)  
            temp2=new byte[28];  
            bis.read(temp2);//bis.skip(28); 
            if(temp1[10]==55)//偏移量爲55,此位圖已經隱入了信息數據
            	bis.read();//跳過1字節
            for(int i=height-1;i>0;i--)  
            {  
                for(int j=0;j<width;j++)  
                {  
                    int blue=bis.read();  
                    int green=bis.read();  
                    int red=bis.read();
                    Color c=new Color(red,green,blue);  
                    map[i][j]=c.getRGB();  
                }  
                if(skip!=4)  
                bis.skip(skip);  
            }  
            bis.close();  
            center.setPreferredSize(new Dimension(width,height));  
            javax.swing.SwingUtilities.updateComponentTreeUI(center);//這裏必須要用updateComponentTreeUI(center)函數  
        //不能用repaint()函數  
          
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
      
      
    public void writeBMP()  
    {  
        try {  
            FileOutputStream fos=new FileOutputStream(selectFile);  
            BufferedOutputStream bos=new BufferedOutputStream(fos);  
            bos.write(temp1);//  
            bos.write(intTobyte(width,4));//寫入寬度  
            bos.write(intTobyte(height,4));//寫入高度  
            bos.write(temp2);
            int skip=0;  
            if(width*3/4!=0)  
            {  
                skip=4-width*3%4;  
            }
            if(temp1[10]==55)
            	bos.write(new byte[1]);
            for(int i=height-1;i>=0;i--)  
            {  
                for(int j=0;j<width;j++)  
                {  
                    Color c=new Color(map[i][j]);  
                    int blue=c.getBlue();  
                    int green=c.getGreen();  
                    int red=c.getRed();  
                    bos.write(blue);  
                    bos.write(green);  
                    bos.write(red);  
                }  
                if(skip!=0)  
                    bos.write(new byte[skip]);  
            }  
            bos.flush();  
            fos.close();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
    //字節轉int  
    public static  int byteToint(byte b[])  
    {  
        int t1=(b[3]&0xff)<<24;  
        int t2=(b[2]&0xff)<<16;  
        int t3=(b[1]&0xff)<<8;  
        int t4=b[0]&0xff;  
        //System.out.println(b[1]&0xff);//輸出的是一個整形數據  
        //在java中,設計int和比int位數來的小的類型b,如byte,char等,都是先把小類型擴展成int再來運算,  
        //return( t1<<24)+(t2<<16)+(t3<<8)+t4;//必須加括號  
        return t1+t2+t3+t4;  
    }  
    //int 轉byte  
    public static byte[] intTobyte(int a,int len)  
    {  
        byte []t=new byte[len];  
            t[0]=(byte) ((a&0xff));  
            if(len>1)  
            t[1]=(byte)((a&0xff00)>>8);  
            if(len>2)  
            t[2]=(byte)((a&0xff0000)>>16);  
            if(len>3)  
            t[3]=(byte)((a&0xff000000)>>24);  
        return t;  
    }  
    
    private static void setUIFont(javax.swing.plaf.FontUIResource f) {
		Enumeration<Object> keys = UIManager.getDefaults().keys();
		while (keys.hasMoreElements()) {
			Object key = keys.nextElement();
			Object value = UIManager.get(key);
			if (value instanceof javax.swing.plaf.FontUIResource) {
				UIManager.put(key, f);
			}
		}
	}
    
  
    /**
     * LSB處理
     * @author 
     * @date 2018年4月22日
     */
    class LSB{
    	public  boolean hide() {
    			temp1[10]+=1;//將位圖數據偏移量增1,作爲是否隱入數據的標識,此時temp1[10]=55;
    			String info=infoJt.getText();
    			char[] infoChs=info.toCharArray();
    			int index=0,endIndex=0;
    			String str;
    			byte[] infoBins=new byte[infoChs.length*16];
    			char[] wordBins=new char[16];
    			for(int i=0;i<infoChs.length;i++) {
    				str=intToWordBinaryString((int)infoChs[i]);
    				str=new StringBuilder(str).reverse().toString();
    				wordBins=str.toCharArray();
    				for(int j=0;j<16;j++) {
    					infoBins[index++]=(byte)(wordBins[j]-'0');
    				}
    			}
    			index=0;
    			for(int i=height-1;i>0;i--)  
                {  
                    for(int j=0;j<width;j++)  
                    { 
                    	if(index<infoBins.length) {
                    		map[i][j]=map[i][j]&0xfffffffe;
                    		if(infoBins[index]==1) {
                    			map[i][j]+=1;
                    		}
                    		index++;
                    	}
                    	else 
                    	{
                    		map[i][j]=map[i][j]&0xfffffffe;//結束標記,一個字的最低位爲0
                    		endIndex++;
                    		if(endIndex>=16)
                    		return true;
                    	}
                    }
                }
    			return false;
    	}
    	
    	public String show() {
    		String info="";
    		byte[] wordBins=new byte[16];
    		int index=0;
    		String wordStr="";
    		int wordInt;
    		outer:for(int i=height-1;i>=0;i--) {
    			for(int j=0;j<width;j++) {
    				wordBins[index++]=(byte)(map[i][j]&0x00000001);
    				if(index>=16) {
    					for(int k=15;k>=0;k--) {
    						wordStr+=wordBins[k];
    					}
    					wordInt=Integer.parseInt(wordStr,2);
    					if(wordInt!=0) {
    						info+=(char)wordInt;
    					}else {
    						break outer;
    					}
    					index=0;wordStr="";
    				}
    			}
    		}
    		return info;
    	}
    	
    	private String intToWordBinaryString(int i) {
			StringBuilder sb = new StringBuilder(Integer.toBinaryString(i));
			while (sb.length() < 16) {
				sb.insert(0, "0");
			}
			return sb.toString();
		}
    }
    /** 
     * 繪圖面板 
     * 
     * 
     */  
    class MyPanel extends JPanel{  
		private static final long serialVersionUID = 1L;

		public void paint(Graphics g) {  
            super.paint(g);  
            if(map!=null)  
            {  
                for(int i=0;i<map.length;i++)  
                {  
                    for(int j=0;j<map[i].length;j++)  
                    {  
                        g.setColor(new Color(map[i][j]));  
                        g.drawLine(j+200, i, j+200, i);
                    }
                }  
            }  
        }  
    }  
      
    class MyListener implements ActionListener{  
        JFileChooser fileChosser;  
        MyListener()  
        {  
            FileNameExtensionFilter filter=new FileNameExtensionFilter("24位位圖(*.bmp)", "bmp");  
            fileChosser=new JFileChooser();  
            fileChosser.setFileFilter(filter);  
            fileChosser.setCurrentDirectory(new File("\\"));  
        }  
        public void actionPerformed(ActionEvent e) { 
        	JMenuItem jmi = (JMenuItem) e.getSource();
            if(jmi==open)//選擇的是打開  
            {  
                int choose=fileChosser.showOpenDialog(null);  
                if(choose==JFileChooser.APPROVE_OPTION)//點擊的是確定按鈕  
                {  
                    selectFile=fileChosser.getSelectedFile();  
                    readBMP();
                    infoJt.setText("圖片打開成功!\n");
                    if(temp1[10]==54) {
                    infoJt.append("最多可嵌入"+height*width/8+"個字節的信息!\n");
                    }else if(temp1[10]==55) {
                    	infoJt.append("此圖片已經隱入有信息!");
                    }
                } 
            }  
            else if(jmi==save)//選擇的是保存
            {  
                int choose=fileChosser.showSaveDialog(null);  
                if(choose==JFileChooser.APPROVE_OPTION)  
                {  
                    selectFile=fileChosser.getSelectedFile();  
                    writeBMP();  
                    infoJt.setText("保存成功!"); 
                }  
            }
            else if(jmi==hide) {
            	if(new LSB().hide()) {
            		infoJt.setText("信息隱入成功!");
            	}else{
            		infoJt.setText("信息隱入出現不確定問題!");
            	}
            	
            }
            else if(jmi==show) {
            	String info=new LSB().show();
            	infoJt.setText("隱入的信息內容:\n");
            	infoJt.append(info);
            }
        }  
          
    }  
      
      
    public static void main(String[] args) {  
       new 信息隱藏_LSB技術_於BMP位圖();
    }  
}   

效果圖:



程序操作步驟:

file-->open,LSB-->hide,file-->save,file-->open,LSB-->show.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章