图片LSB隐写(java)

简介: 图片LSB隐写(java)

一、隐写原理

       LSB隐写原理就是图片中的像素一般是由三种颜色组成,即三原色(红绿蓝)。由这三种原色可以组成其他各种颜色,在png图片的存储中,每个颜色占有8bit,即有256种颜色,一共包含256的三次方颜色,即16777216中颜色。人类的眼睛可以区分约1,000万种不同的颜色,剩下无法区分的颜色就有6777216。


       LSB隐写就是修改了像素中的最低位,把一些信息隐藏起来。png图片是一种无损压缩,也只有在无损压缩或无压缩的图片(bmp图片是一种无压缩)上实现LSB隐写,


       举例:观察图一、图二,没有发现区别,图二是经过图一加特定的信息(测试123aaa)生成新的图片,使用解密函数,可以重新恢复图片中的信息。

 

                           图一

                         图二

二、实现代码

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
 
/**
 * 图像掩蔽 RGB依次取Bit
 */
public class ImageMasker {
    public boolean[] content = null;
    private BufferedImage image = null;
 
    private void setContent(Color color, int start) {
        content[start] = (1 == color.getRed() % 2);
        content[start + 1] = (1 == color.getGreen() % 2);
        content[start + 2] = (1 == color.getBlue() % 2);
    }
 
    private int changeToColor(int rgb, int start) {
        if (content[start]) rgb = rgb | 0x00010000;
        else rgb = rgb & 0xFFFEFFFF;
        if (content[start + 1]) rgb = rgb | 0x00000100;
        else rgb = rgb & 0xFFFFFEFF;
        if (content[start + 2]) rgb = rgb | 0x00000001;
        else rgb = rgb & 0xFFFFFFFE;
        return rgb;
    }
 
    /**
     * 读取文件
     * @param file
     * @return
     */
    public boolean read(String file) {
        try {
            image = ImageIO.read(new File(file));
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        int height = image.getHeight();
        int width = image.getWidth();
        content = new boolean[height * width * 3];
        for (int h = 0; h < height; h++) {
            for (int w = 0; w < width; w++) {
                int color = image.getRGB(w, h);
                Color c = new Color(color);
                setContent(c, (h * width + w) * 3);
            }
        }
        return true;
    }
 
    public boolean write(String file) {
        if (null == image) return false;
 
        int height = image.getHeight();
        int width = image.getWidth();
 
        for (int h = 0; h < height; h++)
            for (int w = 0; w < width; w++)
                image.setRGB(w, h, changeToColor(image.getRGB(w, h), (h * width + w) * 3));
        try {
            String format = file.substring(file.lastIndexOf(".") + 1);
            ImageIO.write(image, format, new File(file));
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
 
}
public class StringEncoder {
 
    public static boolean[] encodeString(String s) {
        try {
            byte[] b = s.getBytes("GBK");
            boolean[] bl = new boolean[b.length * 8];
            for (int i = 0; i < b.length; i++) {
                bl[8 * i] = (((b[i]) & 0x1) == 1);
                bl[8 * i + 1] = (((b[i] >> 1) & 0x1) == 1);
                bl[8 * i + 2] = (((b[i] >> 2) & 0x1) == 1);
                bl[8 * i + 3] = (((b[i] >> 3) & 0x1) == 1);
                bl[8 * i + 4] = (((b[i] >> 4) & 0x1) == 1);
                bl[8 * i + 5] = (((b[i] >> 5) & 0x1) == 1);
                bl[8 * i + 6] = (((b[i] >> 6) & 0x1) == 1);
                bl[8 * i + 7] = (((b[i] >> 7) & 0x1) == 1);
            }
            return bl;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 根据隐写的图片,还原写入的数据
     * @param bl
     * @return
     */
    public static String decodeString( boolean[] bl) {
        try {
            byte[] b = new byte[bl.length / 8];
            for (int i = 0; i < b.length; i++) {
                b[i] = 0;
                if (bl[8 * i]) b[i] = (byte) (b[i] | 0x1);
                if (bl[8 * i + 1]) b[i] = (byte) (b[i] | 0x2);
                if (bl[8 * i + 2]) b[i] = (byte) (b[i] | 0x4);
                if (bl[8 * i + 3]) b[i] = (byte) (b[i] | 0x8);
                if (bl[8 * i + 4]) b[i] = (byte) (b[i] | 0x10);
                if (bl[8 * i + 5]) b[i] = (byte) (b[i] | 0x20);
                if (bl[8 * i + 6]) b[i] = (byte) (b[i] | 0x40);
                if (bl[8 * i + 7]) b[i] = (byte) (b[i] | 0x80);
            }
            return new String(b, "GBK");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "Failed to decode string!";
    }
 
}

三、测试

 public static void main(String[] args) {
 
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        ImageMasker imageMasker = new ImageMasker();
        try {
            System.out.print("请输入文件路径:");
            String s = bufferedReader.readLine();
            imageMasker.read(s);
            System.out.print("写入内容还是读取(r/w):");
            s = bufferedReader.readLine();
            if (s.equals("r")) {
                System.out.print(StringEncoder.decodeString(imageMasker.content).trim());
            } else if (s.equals("w")) {
                System.out.print("输入数据(<=" + imageMasker.content.length / 8 + "):");
                s = bufferedReader.readLine();
 
                boolean[] value = StringEncoder.encodeString(s);
                for (int i = 0; i < value.length; i++)
                    imageMasker.content[i] = value[i];
                for (int i = value.length; i < imageMasker.content.length; i++) {
                    imageMasker.content[i] = false;
                }
 
                System.out.print("输出文件路径:");
                s = bufferedReader.readLine();
                imageMasker.write(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

图一:C:\Users\Administrator\Desktop\sorce.png

图二:C:\Users\Administrator\Desktop\sorce1.png


中文乱码,大家帮我看看原因。

来自网络,可以试试

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
 
public class LSB {
    public static void main(String[] args) throws IOException {
        Scanner scan = new Scanner(System.in);
        System.out.println("***************LSB图像隐写编码程序****************");
        System.out.println("*********请选择想要使用的功能(输入数字)*************");
        System.out.println();
        System.out.println("******************1.LSB编码********************");
        System.out.println("******************2.LSB解码********************");
        System.out.println();
        System.out.print("请输入你想选择的功能:");
        String choice = scan.next();
        switch (choice) {
            case "1":
                System.out.print("请输入需要加密的文件的路径:");
                String textPath = scan.next();
                System.out.print("请输入png图像辅助文件的路径:");
                String imagePath = scan.next();
                System.out.print("最后再输入一下生成的png图片的保存路径:");
                String imageOutputPath = scan.next();
                LSBEncoder(textPath, imagePath, imageOutputPath);
                scan.close();
                break;
            case "2":
                System.out.print("请输入待解码的png图片的路径:");
                String imageInputPath = scan.next();
                System.out.print("请输入解码后,存放数据的文件名称");
                String textFilePath = scan.next();
                LSBDecoder(imageInputPath, textFilePath);
                scan.close();
                break;
            default:
                System.out.print("谁叫你乱按键盘的啊!!!活该不能执行,哼?!");
                scan.close();
                break;
        }
    }
 
    public static void LSBEncoder(String textPath, String imagePath, String imageOutputPath) throws IOException {
 
        //读取png图像
        BufferedImage image = ImageIO.read(new File(imagePath));
        int width = image.getWidth();
        int height = image.getHeight();
        int[][][] rgb = new int[width][height][3];
        //将图像每个点的像素(R,G,B)存储在数组中
        for (int w = 0; w < width; w++) {
            for (int h = 0; h < height; h++) {
                int pixel = image.getRGB(w, h);//读取的是一个24位的数据
                //数据三个字节分别代表R、B、G
                rgb[w][h][0] = (pixel & 0xff0000) >> 16;//R
                rgb[w][h][1] = (pixel & 0xff00) >> 8;//B
                rgb[w][h][2] = (pixel & 0xff);//G
            }
        }
 
        //导入待加密机密文件
        FileInputStream fis = new FileInputStream(textPath);
        int byteLen = fis.available();
        byte[] buf = new byte[byteLen];
        fis.read(buf);
 
 
        //我用两个字节(16位)表示数据部分的长度,也就是说,图像转像素点后,前16个像素点的R对应的字节的最低有效位都是默认表示数据字节数组的长度
        //规定,数据的长度最大为2^16-1
        int[] bufLen = new int[2];
        bufLen[0] = (byteLen & 0xff00) >> 8;
        bufLen[1] = (byteLen & 0xff);
        for (int i = 0; i < 2; i++) {
            for (int j = 7; j >= 0; j--) {
                int h = (i * 8 + (7 - j)) / width;
                int w = (i * 8 + (7 - j)) % width;
                //只取每个像素点的R,的字节的最低位
                if ((bufLen[i] >> j & 1) == 1) {
                    rgb[w][h][0] = rgb[w][h][0] | 1;
                } else {
                    rgb[w][h][0] = rgb[w][h][0] & 0xe;
                }
            }
        }
 
        //按照规则将数据的二进制序列全部放到每一个像素点的第一个字节的最后一位上
        for (int i = 2; i < byteLen + 2; i++) {
            for (int j = 7; j >= 0; j--) {
                //高
                int h = (i * 8 + (7 - j)) / width;
                //宽
                int w = (i * 8 + (7 - j)) % width;
                if ((buf[i - 2] >> j & 1) == 1) {
                    rgb[w][h][0] = rgb[w][h][0] | 1;//变1
                } else {
                    rgb[w][h][0] = rgb[w][h][0] & 0xe;
                }
            }
        }
        //        构建通过编码生成的png图片的类型
        BufferedImage imageOutput = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int w = 0; w < width; w++) {
            for (int h = 0; h < height; h++) {
                int[] color = new int[3];
                color[0] = rgb[w][h][0] << 16;
                color[1] = rgb[w][h][1] << 8;
                color[2] = rgb[w][h][2];
                int pixel = 0xff000000 | color[0] | color[1] | color[2];
                imageOutput.setRGB(w, h, pixel);
            }
        }
        ImageIO.write(imageOutput, "png", new File(imageOutputPath));
    }
 
    public static void LSBDecoder(String imageInputPath, String textFilePath) throws IOException {
        BufferedImage imageInput = ImageIO.read(new File(imageInputPath));
        int width = imageInput.getWidth();
        int height = imageInput.getHeight();
        int[] bufLen = new int[2];
        //将图像每个点的像素(R,G,B)存储在数组中
        for (int i = 0; i < 2; i++) {
            int[] bits = new int[8];
            for (int j = 7; j >= 0; j--) {
                int h = (i * 8 + (7 - j)) / width;
                int w = (i * 8 + (7 - j)) % width;
                int pixel = imageInput.getRGB(w, h);
                int r = (pixel & 0xff0000) >> 16;
                bits[j] = (r & 1) << j;
            }
            bufLen[i] = bits[7] | bits[6] | bits[5] | bits[4] | bits[3] | bits[2] | bits[1] | bits[0];
        }
        int byteLen = ((bufLen[0] << 7) | bufLen[1]);
        byte[] buf = new byte[byteLen];
        for (int i = 2; i < byteLen + 2; i++) {
            int[] bits = new int[8];
            for (int j = 7; j >= 0; j--) {
                int h = (i * 8 + (7 - j)) / width;
                int w = (i * 8 + (7 - j)) % width;
                int pixel = imageInput.getRGB(w, h);
                int r = (pixel & 0xff0000) >> 16;
                bits[j] = (r & 0x1) << j;
            }
            buf[i - 2] = (byte) (bits[7] | bits[6] | bits[5] | bits[4] | bits[3] | bits[2] | bits[1] | bits[0]);
        }
 
        FileOutputStream fos = new FileOutputStream(textFilePath);
        fos.write(buf);
        fos.close();
    }
}

相关文章
|
3月前
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
106 4
|
3月前
|
Java
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
76 2
|
3月前
|
算法 Java Linux
java制作海报二:java使用Graphics2D 在图片上合成另一个照片,并将照片切割成头像,头像切割成圆形方法详解
这篇文章介绍了如何使用Java的Graphics2D类在图片上合成另一个照片,并将照片切割成圆形头像的方法。
62 1
java制作海报二:java使用Graphics2D 在图片上合成另一个照片,并将照片切割成头像,头像切割成圆形方法详解
|
3月前
|
Java 数据安全/隐私保护
Java ffmpeg 实现视频加文字/图片水印功能
【10月更文挑战第22天】在 Java 中使用 FFmpeg 实现视频加文字或图片水印功能,需先安装 FFmpeg 并添加依赖(如 JavaCV)。通过构建 FFmpeg 命令行参数,使用 `drawtext` 滤镜添加文字水印,或使用 `overlay` 滤镜添加图片水印。示例代码展示了如何使用 JavaCV 实现文字水印。
220 1
|
3月前
|
前端开发 小程序 Java
java基础:map遍历使用;java使用 Patten 和Matches 进行正则匹配;后端传到前端展示图片三种情况,并保存到手机
这篇文章介绍了Java中Map的遍历方法、使用Pattern和matches进行正则表达式匹配,以及后端向前端传输图片并保存到手机的三种情况。
33 1
|
3月前
|
算法 Java Linux
java制作海报四:java BufferedImage 转 InputStream 上传至OSS。png 图片合成到模板(另一个图片)上时,透明部分变成了黑色
这篇文章主要介绍了如何将Java中的BufferedImage对象转换为InputStream以上传至OSS,并解决了png图片合成时透明部分变黑的问题。
134 1
|
3月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
165 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
4月前
|
Java
Java-FileInputStream和FileOutputStream的使用,txt文件及图片文件的拷贝
这篇文章介绍了Java中FileInputStream和FileOutputStream的使用,包括如何读取和写入txt文件以及如何拷贝图片文件。
Java-FileInputStream和FileOutputStream的使用,txt文件及图片文件的拷贝
|
3月前
|
算法 小程序 Java
java制作海报三:获取微信二维码详情,并改变大小,合成到海报(另一张图片)上
这篇文章介绍了如何使用Java获取微信小程序的二维码,并将其调整大小后合成到海报(另一张图片)上。
63 0
|
3月前
|
算法 Java Linux
java制作海报一:java使用Graphics2D 在图片上写字,文字换行算法详解
这篇文章介绍了如何在Java中使用Graphics2D在图片上绘制文字,并实现自动换行的功能。
176 0