Java_最不重要位替换(LSB)基于24位BMP图片
程序员文章站
2024-01-24 17:12:34
...
向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.