基于LSB算法的水印的嵌入与提取(含攻击)--Java
- 一、图像处理基础
- 二、水印嵌入与提取算法
- 三、LSB算法实现
- 四、结果与分析
一、图像处理基础
图像分为三种图像:彩色图像、灰度图像、黑白图像
彩色图像得到信息表示为 像素* 3 * 8
3:因为有三种颜色(RGB),所以用三个通道表示
8:每个颜色的信息为0-255,可用8个二进制数表示
灰度图像的信息可表示为 像素 * 8
因为灰度图像只有一种颜色:灰色 所以只需要表示一种颜色
黑白图像的颜色信息表示为 像素
黑白图像的颜色信息可以只用0和1来表示黑白
二、水印嵌入与提取算法
按照空间域来分
空间域上,经典的 LSB( Least Significant Bits),支持的水印信息量大,对原图影响小。但是抗干扰能力比较差,不能抵抗图像的裁剪、缩放和jpg压缩。
频域上,不支持盲提取算法,例如:DCT,DWT,抗干扰能力强。但提取时需要原图。
频域上,支持盲提取算法,例如:DWT+SVD,主要是使用图像中的稳定特征作为作为提取的指引,一般使用图像矩阵的特征值。相比DWT,抗干扰能力差一些,可附加的水印信息量小一些
提取时不需要原图像,缺点是能嵌入的信息非常有限。
提取时需要原图像,优点是嵌入信息比较多,实现上是把水印的信息分成两部分。
我们的课程要求了LSB和DCT算法,这里先介绍LSB算法
- LSB简介:空域图像水印技术是指在图像的空间域中嵌入水印的技术。 最简单和有代表性的方案就是用秘密信息代替图像的最低有效位(LSB)或者多个位平面的所有比特的算法。 LSB(Least Significant Bits)算法:将秘密信息嵌入到载体图像像素值的最低有效位。如下图,我们可以将其每八位的最后一位替换成我们的秘密信息,也就完成了信息隐写的过程。
- LSB原理 :LSB隐写原理源于图片中的像素一般是由三种颜色组成,即三原色(红绿蓝),由这三种原色可以组成其他各种颜色,在bmp图片的存储中,每个颜色占有8bit,即有256种颜色,一共包含256的三次方颜色,即16777216中颜色,人类的眼睛可以区分约1000万种不同的颜色,剩下无法区分的颜色就有6777216,当我们把其中一些信息改变,图片只发生位变化(取最低位,权值最小)如红色从255变为254,而人是觉察不到这种变化的,当时里面的信息却发生了变化,这样就实现了我们的数字隐写功能。PNG图片格式和BMP图片格式都是一种无损压缩,LSB隐写就是修改了像素中的最低位缩的图片上实现LSB隐写,如果是JPG图片,就没办法使用LSB隐写了,因为其是有损压缩。
- LSB特点:
- 非常简单
- 非常快
- 易于实现
- 很高的容量和嵌入效率
- LSB位是随机的,与高位和其他点无关
三、LSB算法实现
- 这里引用了opencv的库–用于计算自相关,所以创建一个Maven工程,并引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>dct</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
<version>4.6.0-0</version>
</dependency>
<dependency>
<groupId>opencv</groupId>
<artifactId>opencv</artifactId>
<version>460</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/lib/opencv-460.jar</systemPath>
</dependency>
</dependencies>
</project>
- 因为我报错,所以我重新引入了opencv的jar包,并把dll文件放在系统文件目录(C:\Windows\System32)下
- 嵌入水印
LSB算法嵌入流程如下:
(1)对原始图像中每个像素点的灰度值进行变换,由十进制转换为二进制;
(2)将秘密信息转换为二进制序列,并将原始图像中像素点的最低比特位替换为二进制序列中的每一比特信息;
(3)在替换过程结束后,将像素点的二进制数据转换回十进制数据,保存为载密图像。
package lsb.lsb_main;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class LSBEmbed2{
public static void main(String[] args) {
// 加载原图和水印图
BufferedImage coverImage = null;
BufferedImage watermarkImage = null;
try {
coverImage = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\lena.bmp")); // 原图路径
watermarkImage = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png")); // 水印图路径
} catch (IOException e) {
e.printStackTrace();
}
int watermarkImage_width = watermarkImage.getWidth();
int watermarkImage_height = watermarkImage.getHeight();
StringBuilder sb = new StringBuilder();
for (int y = 0; y <watermarkImage_height; y++) {
for (int x = 0; x < watermarkImage_width; x++) {
int watermarkPixel = watermarkImage.getRGB(x,y);
int watermarkRed = (watermarkPixel >> 16) & 0xff;
int watermarkGreen = (watermarkPixel >> 8) & 0xff;
int watermarkBlue = watermarkPixel & 0xff;
String s1 = Integer.toBinaryString(watermarkRed);
String s2 = Integer.toBinaryString(watermarkGreen);
String s3 = Integer.toBinaryString(watermarkBlue);
for (int i = s1.length(); i < 8; i++) {
s1 = "0"+ s1;
}
for (int i = s2.length(); i < 8; i++) {
s2 = "0"+ s2;
}
for (int i = s3.length(); i < 8; i++) {
s3 = "0"+ s3;
}
sb.append(s1);
sb.append(s2);
sb.append(s3);
}
}
System.out.println(sb.length());
String pixelBits = sb.toString().substring(0, watermarkImage_height*watermarkImage_width*3 *8);
// 在原图中嵌入水印
if(coverImage!=null){
int width = coverImage.getWidth();
int height = coverImage.getHeight();
int x =0;
int y = 0;
int i = 0;
for ( y = 0; y < height; y++) {
for ( x = 0; x < width; x++) {
if((width*y+x)*3 >=watermarkImage_height*watermarkImage_width*3*8){
break;
}
// 获取原图和水印图像素值
int coverPixel = coverImage.getRGB(x, y);
if(watermarkImage!=null){
int startIndex = i * 3;
int endIndex = Math.min(startIndex + 3, pixelBits.length());
String bits = pixelBits.substring(startIndex, endIndex);
i++;
// 提取原图像素和水印像素的RGB值
int coverRed = (coverPixel >> 16) & 0xff;
int coverGreen = (coverPixel >> 8) & 0xff;
int coverBlue = coverPixel & 0xff;
// 使用LSB算法嵌入水印到原图像素
String s1 = Integer.toBinaryString(coverRed);
String s2 = Integer.toBinaryString(coverGreen);
String s3 = Integer.toBinaryString(coverBlue);
for (int a = s1.length(); a < 8; a++) {
s1 = "0"+ s1;
}
for (int a = s2.length(); a < 8; a++) {
s2 = "0"+ s2;
}
for (int a = s3.length(); a < 8; a++) {
s3 = "0"+ s3;
}
if( !bits.substring(0,1).equals(s1.substring(7,8))){
if( "1".equals(s1.substring(7,8))){
coverRed = coverRed-1;
}else{
coverRed = coverRed+1;
}
}
if( !bits.substring(1,2).equals(s2.substring(7,8))){
if( "1".equals(s2.substring(7,8))){
coverGreen = coverGreen-1;
}else{
coverGreen = coverGreen+1;
}
}
if( !bits.substring(2,3).equals(s3.substring(7,8))){
if( "1".equals(s3.substring(7,8))){
coverBlue = coverBlue-1;
}else{
coverBlue = coverBlue+1;
}
}
// coverRed = (coverRed & 0xfe) | (Integer.parseInt(bits.substring(0,1))& 0x01) ;
// coverGreen = (coverGreen & 0xfe) | (Integer.parseInt(bits.substring(1,2))& 0x01) ;
// coverBlue = (coverBlue & 0xfe) | (Integer.parseInt(bits.substring(2,3))& 0x01) ;
// 将新的像素值设置到原图像素中
int newPixel = (coverRed << 16) | (coverGreen << 8) | coverBlue;
coverImage.setRGB(x, y, newPixel);
}
}
}
StringBuilder sb1 = new StringBuilder();
int x1 = 0;
int y1 = 0;
for ( y1 = 0; y1 < coverImage.getHeight(); y1++) {
for (x1 = 0; x1 < coverImage.getWidth(); x1++) {
if((width*y1+x1)*3 >=watermarkImage_height*watermarkImage_width*3*8){
break;
}
int rgb =coverImage.getRGB(x1, y1);
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
String s1 = Integer.toBinaryString(r);
String s2 = Integer.toBinaryString(g);
String s3 = Integer.toBinaryString(b);
for (int c = s1.length(); c < 8; c++) {
s1 = "0"+ s1;
}
for (int c = s2.length(); c < 8; c++) {
s2 = "0"+ s2;
}
for (int c = s3.length(); c < 8; c++) {
s3 = "0"+ s3;
}
s1 = s1.substring(7,8);
s2 = s2.substring(7,8);
s3 = s3.substring(7,8);
sb1.append(s1);
sb1.append(s2);
sb1.append(s3);
}
}
if(sb1.equals(sb)){
System.out.println("相等");
}else{
System.out.println("不相等");
System.out.println(sb.length());
System.out.println(sb1.length());
}
// 保存水印嵌入后的图像
try {
ImageIO.write(coverImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\watermarked.bmp")); // 保存路径和格式
ImageIO.write(coverImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\interference\\photo\\watermarked.bmp"));
ImageIO.write(coverImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\rotate\\photo\\watermarked.bmp"));
ImageIO.write(coverImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\guass\\photo\\watermarked.bmp"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 提取水印
提取算法
与嵌入算法对应,LSB算法提取流程如下:
(1)对载密图像中每个像素点的灰度值进行变换,由十进制转换为二进制;
(2)提取图像像素点中最低有效位的数据,根据嵌入顺序进行组合得到原秘密序列
package lsb.lsb_main;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class WatermarkExtractor2 {
public static void main(String[] args) throws IOException {
// try {
// 读取含有水印的图像
BufferedImage image = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\watermarked.bmp"));
// 读取嵌入图像的大小
BufferedImage watermarkImage = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png"));
int watermarkWidth = watermarkImage.getWidth();
int watermarkHeight = watermarkImage.getHeight();
// 计算需要读取的像素数量
int pixelCount = watermarkWidth * watermarkHeight;
int bitCount = 3 * pixelCount * 8; // 每个像素有 3 个通道,每个通道嵌入 1 bit
// 遍历图像像素,取出每个像素的最低位,并组成一个二进制串
StringBuilder sb = new StringBuilder();
for (int y = 0; y < image.getHeight(); y++) {
if (sb.length() >= bitCount) {
break;
}
for (int x = 0; x < image.getWidth(); x++) {
if (sb.length() >= bitCount) {
break;
}
int rgb = image.getRGB(x, y);
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
sb.append(r & 1);
sb.append(g & 1);
sb.append(b & 1);
}
}
// for (int y = 0; y < image.getHeight(); y++) {
// for (int x = 0; x < image.getWidth(); x++) {
// int rgb = image.getRGB(x, y);
// int r = (rgb >> 16) & 0xff;
// int g = (rgb >> 8) & 0xff;
// int b = rgb & 0xff;
// String s1 = Integer.toBinaryString(r);
// String s2 = Integer.toBinaryString(g);
// String s3 = Integer.toBinaryString(b);
// for (int i = s1.length(); i < 8; i++) {
// s1 = "0"+ s1;
// }
// for (int i = s2.length(); i < 8; i++) {
// s2 = "0"+ s2;
// }
// for (int i = s3.length(); i < 8; i++) {
// s3 = "0"+ s3;
// }
// s1 = s1.substring(7,8);
// s2 = s2.substring(7,8);
// s3 = s3.substring(7,8);
// sb.append(s1);
// sb.append(s2);
// sb.append(s3);
// if (sb.length() >= bitCount) {
// break;
// }
// }
// if (sb.length() >= bitCount) {
// break;
// }
// }
// 从二进制串中取出嵌入的图像像素信息
System.out.println(sb.length());
String pixelBits = sb.toString().substring(0, bitCount);
int[] pixels = new int[pixelCount];
for (int i = 0; i < pixels.length; i++) {
int startIndex = i * 24;
int endIndex = Math.min(startIndex + 24, pixelBits.length());
String bits = pixelBits.substring(startIndex, endIndex);
int r = Integer.parseInt(bits.substring(0, 8), 2);
int g = Integer.parseInt(bits.substring(8, 16), 2);
int b = Integer.parseInt(bits.substring(16, 24), 2);
pixels[i] = (r << 16) | (g << 8) | b;
}
// 创建提取出来的水印图像
BufferedImage extractedImage = new BufferedImage(watermarkWidth, watermarkHeight, BufferedImage.TYPE_INT_RGB);
extractedImage.setRGB(0, 0, watermarkWidth, watermarkHeight, pixels, 0, watermarkWidth);
// 保存提取出来的水印图像
ImageIO.write(extractedImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\extracted.bmp"));
System.out.println("水印提取成功!");
// } catch (Exception e) {
// System.out.println("水印提取失败:" + e.getMessage());
// }
}
}
- 对图像进行剪切,高斯,旋转攻击
剪切:
package lsb.interference;
import org.opencv.core.Core;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class Watermarked {
static{
//加载opencv动态库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) throws IOException {
// try {
// 读取含有水印的图像
cutImage("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\watermarked.bmp",
"C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\interference\\photo\\cutWater.bmp",
0,0,400,400);
BufferedImage image = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\interference\\photo\\cutWater.bmp"));
// 读取嵌入图像的大小
BufferedImage watermarkImage = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png"));
int watermarkWidth = watermarkImage.getWidth();
int watermarkHeight = watermarkImage.getHeight();
// 计算需要读取的像素数量
int pixelCount = watermarkWidth * watermarkHeight;
int bitCount = 3 * pixelCount * 8; // 每个像素有 3 个通道,每个通道嵌入 1 bit
// 遍历图像像素,取出每个像素的最低位,并组成一个二进制串
StringBuilder sb = new StringBuilder();
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int rgb = image.getRGB(x, y);
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
sb.append(r & 1);
sb.append(g & 1);
sb.append(b & 1);
if (sb.length() >= bitCount) {
break;
}
}
if (sb.length() >= bitCount) {
break;
}
}
// for (int y = 0; y < image.getHeight(); y++) {
// for (int x = 0; x < image.getWidth(); x++) {
// int rgb = image.getRGB(x, y);
// int r = (rgb >> 16) & 0xff;
// int g = (rgb >> 8) & 0xff;
// int b = rgb & 0xff;
// String s1 = Integer.toBinaryString(r);
// String s2 = Integer.toBinaryString(g);
// String s3 = Integer.toBinaryString(b);
// for (int i = s1.length(); i < 8; i++) {
// s1 = "0"+ s1;
// }
// for (int i = s2.length(); i < 8; i++) {
// s2 = "0"+ s2;
// }
// for (int i = s3.length(); i < 8; i++) {
// s3 = "0"+ s3;
// }
// s1 = s1.substring(7,8);
// s2 = s2.substring(7,8);
// s3 = s3.substring(7,8);
// sb.append(s1);
// sb.append(s2);
// sb.append(s3);
// if (sb.length() >= bitCount) {
// break;
// }
// }
// if (sb.length() >= bitCount) {
// break;
// }
// }
// 从二进制串中取出嵌入的图像像素信息
System.out.println(sb.length());
String pixelBits = sb.toString().substring(0,480000);
int[] pixels = new int[pixelCount];
for (int i = 0; i < 20000; i++) {
int startIndex = i * 24;
int endIndex = Math.min(startIndex + 24, pixelBits.length());
String bits = pixelBits.substring(startIndex, endIndex);
int r = Integer.parseInt(bits.substring(0, 8), 2);
int g = Integer.parseInt(bits.substring(8, 16), 2);
int b = Integer.parseInt(bits.substring(16, 24), 2);
pixels[i] = (r << 16) | (g << 8) | b;
}
// 创建提取出来的水印图像
BufferedImage extractedImage = new BufferedImage(watermarkWidth, watermarkHeight, BufferedImage.TYPE_INT_RGB);
extractedImage.setRGB(0, 0, watermarkWidth, watermarkHeight, pixels, 0, watermarkWidth);
// 保存提取出来的水印图像
ImageIO.write(extractedImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\interference\\photo\\extracted.bmp"));
System.out.println("水印提取成功!");
// } catch (Exception e) {
// System.out.println("水印提取失败:" + e.getMessage());
// }
}
/**
* 剪切图片,没有处理图片后缀名是否正确,还有gif动态图片
* @param sourcePath 源路径(包含图片)
* @param targetPath 目标路径 null则默认为源路径
* @param x 起点x坐标
* @param y 起点y左边
* @param width 剪切宽度
* @param height 剪切高度
* @return 目标路径
* @throws IOException if sourcePath image doesn't exist
*/
public static void cutImage(String sourcePath,String targetPath,int x,int y,int width,int height) throws IOException{
File imageFile = new File(sourcePath);
if(!imageFile.exists()){
throw new IOException("Not found the images:"+sourcePath);
}
if(targetPath==null || targetPath.isEmpty()) targetPath = sourcePath;
String format = sourcePath.substring(sourcePath.lastIndexOf(".")+1,sourcePath.length());
BufferedImage image = ImageIO.read(imageFile);
image = image.getSubimage(x, y, width, height);
ImageIO.write(image, format, new File(targetPath));
}
}
旋转:
package lsb.rotate;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import static dct.dct_main.dct.getImageWatermarkWithText;
import static dct.dct_main.dct.matrixToBinaryPhoto;
public class Watermarked {
static{
//加载opencv动态库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) throws IOException {
// try {
// 读取含有水印的图像
rotatingImage("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\rotate\\photo\\watermarked.bmp","C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\rotate\\photo\\rotateWatermarked.bmp");
BufferedImage image = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\rotate\\photo\\rotateWatermarked.bmp"));
// 读取嵌入图像的大小
BufferedImage watermarkImage = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png"));
int watermarkWidth = watermarkImage.getWidth();
int watermarkHeight = watermarkImage.getHeight();
// 计算需要读取的像素数量
int pixelCount = watermarkWidth * watermarkHeight;
int bitCount = 3 * pixelCount * 8; // 每个像素有 3 个通道,每个通道嵌入 1 bit
// 遍历图像像素,取出每个像素的最低位,并组成一个二进制串
StringBuilder sb = new StringBuilder();
for (int y = 0; y < image.getHeight(); y++) {
if (sb.length() >= bitCount) {
break;
}
for (int x = 0; x < image.getWidth(); x++) {
if (sb.length() >= bitCount) {
break;
}
int rgb = image.getRGB(x, y);
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
sb.append(r & 1);
sb.append(g & 1);
sb.append(b & 1);
}
}
// for (int y = 0; y < image.getHeight(); y++) {
// for (int x = 0; x < image.getWidth(); x++) {
// int rgb = image.getRGB(x, y);
// int r = (rgb >> 16) & 0xff;
// int g = (rgb >> 8) & 0xff;
// int b = rgb & 0xff;
// String s1 = Integer.toBinaryString(r);
// String s2 = Integer.toBinaryString(g);
// String s3 = Integer.toBinaryString(b);
// for (int i = s1.length(); i < 8; i++) {
// s1 = "0"+ s1;
// }
// for (int i = s2.length(); i < 8; i++) {
// s2 = "0"+ s2;
// }
// for (int i = s3.length(); i < 8; i++) {
// s3 = "0"+ s3;
// }
// s1 = s1.substring(7,8);
// s2 = s2.substring(7,8);
// s3 = s3.substring(7,8);
// sb.append(s1);
// sb.append(s2);
// sb.append(s3);
// if (sb.length() >= bitCount) {
// break;
// }
// }
// if (sb.length() >= bitCount) {
// break;
// }
// }
// 从二进制串中取出嵌入的图像像素信息
System.out.println(sb.length());
String pixelBits = sb.toString().substring(0, bitCount);
int[] pixels = new int[pixelCount];
for (int i = 0; i < pixels.length; i++) {
int startIndex = i * 24;
int endIndex = Math.min(startIndex + 24, pixelBits.length());
String bits = pixelBits.substring(startIndex, endIndex);
int r = Integer.parseInt(bits.substring(0, 8), 2);
int g = Integer.parseInt(bits.substring(8, 16), 2);
int b = Integer.parseInt(bits.substring(16, 24), 2);
pixels[i] = (r << 16) | (g << 8) | b;
}
// 创建提取出来的水印图像
BufferedImage extractedImage = new BufferedImage(watermarkWidth, watermarkHeight, BufferedImage.TYPE_INT_RGB);
extractedImage.setRGB(0, 0, watermarkWidth, watermarkHeight, pixels, 0, watermarkWidth);
// 保存提取出来的水印图像
ImageIO.write(extractedImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\rotate\\photo\\extracted.bmp"));
System.out.println("水印提取成功!");
// } catch (Exception e) {
// System.out.println("水印提取失败:" + e.getMessage());
// }
}
public static void rotatingImage(String file,String receivePath){
Mat src = Imgcodecs.imread(file);
Mat dst = new Mat();//用来接收结果
Mat rotationMatrix = Imgproc.getRotationMatrix2D(new Point(75, 75), 10, 1);//
//旋转
Imgproc.warpAffine(src, dst,rotationMatrix, new Size(src.cols(), src.cols()));
//提取水印
int[][] imageWatermarkWithText = getImageWatermarkWithText(dst);
matrixToBinaryPhoto(imageWatermarkWithText,receivePath);
}
}
高斯:
package lsb.guass;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import static dct.dct_main.dct.*;
public class WaterCorrelation {
static{
//加载opencv动态库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) throws IOException {
// try {
// 读取含有水印的图像
//addGaussianNoise(new File(""))
BufferedImage image1 = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\guass\\photo\\watermarked.bmp"));
BufferedImage bufferedImage = addGaussianNoise(image1);
ImageIO.write(bufferedImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\guass\\photo\\guassWater.bmp"));
BufferedImage image = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\guass\\photo\\guassWater.bmp"));
// 读取嵌入图像的大小
BufferedImage watermarkImage = ImageIO.read(new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png"));
int watermarkWidth = watermarkImage.getWidth();
int watermarkHeight = watermarkImage.getHeight();
// 计算需要读取的像素数量
int pixelCount = watermarkWidth * watermarkHeight;
int bitCount = 3 * pixelCount * 8; // 每个像素有 3 个通道,每个通道嵌入 1 bit
// 遍历图像像素,取出每个像素的最低位,并组成一个二进制串
StringBuilder sb = new StringBuilder();
for (int y = 0; y < image.getHeight(); y++) {
if (sb.length() >= bitCount) {
break;
}
for (int x = 0; x < image.getWidth(); x++) {
if (sb.length() >= bitCount) {
break;
}
int rgb = image.getRGB(x, y);
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
sb.append(r & 1);
sb.append(g & 1);
sb.append(b & 1);
}
}
// for (int y = 0; y < image.getHeight(); y++) {
// for (int x = 0; x < image.getWidth(); x++) {
// int rgb = image.getRGB(x, y);
// int r = (rgb >> 16) & 0xff;
// int g = (rgb >> 8) & 0xff;
// int b = rgb & 0xff;
// String s1 = Integer.toBinaryString(r);
// String s2 = Integer.toBinaryString(g);
// String s3 = Integer.toBinaryString(b);
// for (int i = s1.length(); i < 8; i++) {
// s1 = "0"+ s1;
// }
// for (int i = s2.length(); i < 8; i++) {
// s2 = "0"+ s2;
// }
// for (int i = s3.length(); i < 8; i++) {
// s3 = "0"+ s3;
// }
// s1 = s1.substring(7,8);
// s2 = s2.substring(7,8);
// s3 = s3.substring(7,8);
// sb.append(s1);
// sb.append(s2);
// sb.append(s3);
// if (sb.length() >= bitCount) {
// break;
// }
// }
// if (sb.length() >= bitCount) {
// break;
// }
// }
// 从二进制串中取出嵌入的图像像素信息
System.out.println(sb.length());
String pixelBits = sb.toString().substring(0, bitCount);
int[] pixels = new int[pixelCount];
for (int i = 0; i < pixels.length; i++) {
int startIndex = i * 24;
int endIndex = Math.min(startIndex + 24, pixelBits.length());
String bits = pixelBits.substring(startIndex, endIndex);
int r = Integer.parseInt(bits.substring(0, 8), 2);
int g = Integer.parseInt(bits.substring(8, 16), 2);
int b = Integer.parseInt(bits.substring(16, 24), 2);
pixels[i] = (r << 16) | (g << 8) | b;
}
// 创建提取出来的水印图像
BufferedImage extractedImage = new BufferedImage(watermarkWidth, watermarkHeight, BufferedImage.TYPE_INT_RGB);
extractedImage.setRGB(0, 0, watermarkWidth, watermarkHeight, pixels, 0, watermarkWidth);
// 保存提取出来的水印图像
ImageIO.write(extractedImage, "bmp", new File("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\guass\\photo\\extracted.bmp"));
System.out.println("水印提取成功!");
// } catch (Exception e) {
// System.out.println("水印提取失败:" + e.getMessage());
// }
}
public static void rotatingImage(String file,String receivePath){
Mat src = Imgcodecs.imread(file);
Mat dst = new Mat();//用来接收结果
Mat rotationMatrix = Imgproc.getRotationMatrix2D(new Point(75, 75), 10, 1);//
//旋转
Imgproc.warpAffine(src, dst,rotationMatrix, new Size(src.cols(), src.cols()));
//提取水印
int[][] imageWatermarkWithText = getImageWatermarkWithText(dst);
matrixToBinaryPhoto(imageWatermarkWithText,receivePath);
}
/**
* 添加高斯噪声
* @param image
* @return
*/
public static BufferedImage addGaussianNoise(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
BufferedImage noiseImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int rgb = image.getRGB(i, j);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
r = Math.min(Math.max(generateNoise(r), 0), 255);
g = Math.min(Math.max(generateNoise(g), 0), 255);
b = Math.min(Math.max(generateNoise(b), 0), 255);
noiseImage.setRGB(i, j, (r << 16) | (g << 8) | b);
}
}
return noiseImage;
}
}
这三个代码提取部分一样,造成冗余,可自行包装
- 计算自相关函数:
package lsb;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
public class ImageCorrelation {
public static void main(String[] args) {
ImageCorrelation("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png",
"C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\extracted.bmp",
"无攻击时的自相关函数:");
ImageCorrelation("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png",
"C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\rotate\\photo\\rotateWatermarked.bmp",
"旋转攻击时的自相关函数:");
ImageCorrelation("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png",
"C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\interference\\photo\\extracted.bmp",
"剪切攻击时的自相关函数:");
ImageCorrelation("C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\lsb_main\\photo\\haerbin.png",
"C:\\Users\\ck\\IdeaProjects\\laser1\\dct\\src\\main\\java\\lsb\\guass\\photo\\extracted.bmp",
"高斯攻击后的自相关:");
}
/**
* 图片自相关--评价效果
*
* @param path1
* @param path2
* @param out
*/
public static void ImageCorrelation(String path1,String path2,String out) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat img1 = Imgcodecs.imread(path1);
Mat img2 = Imgcodecs.imread(path2);
Mat result = new Mat();
Imgproc.matchTemplate(img1, img2, result, Imgproc.TM_CCORR_NORMED);
Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
System.out.println(out + mmr.maxVal);
}
}
四、结果与分析
根据图片和自相关函数可知:
LSB算法的嵌入域提取效果好,但是无法抵抗各种攻击,鲁棒性差