算法说明
算法用灰度图作为输入输出。
- 图像加密
(1)输入密钥key与原图像I
(2)利用logistic映射生成伪随机整数序列P1对图像块进行置乱,改变图像像素在每个块内的位置。
x n + 1 = λ x n ( 1 − x n ) , x n ∈ [ 0 , 1 ] x_{n+1}= \lambda x_{n}\left ( 1-x_{n} \right ),x_{n}\in [0,1] xn+1=λxn(1−xn),xn∈[0,1]
其中 3.569946 … ≤ λ ≤ 4 , 0 < x n < 1 3.569946… \leq \lambda \leq 4 , 0 < x_{n} < 1 3.569946…≤λ≤4,0<xn<1
(3)将图像I分成b*b大小的图像块,利用P1对各图像块进行位置置换。
(4)利用logistic映射生成伪随机整数序列P2,利用P2将各块内像素进行置乱处理。 - 图像解密基本是加密的逆过程。
- 特征提取。当用户向远程服务器发出图像查询请求时,允许云服务器直接在密文图像中提取图像特征。该加密算法保证了加密前后,图像分块均值直方图保持不变。图像特征提取过程为:
(1)将图像分块。
(2)计算各图像均值。
(3)计算分块均值直方图。
效果图
大概就这么个效果。就算密码错误,特征也是一致的。
代码
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
File I=new File("C:\\Users\\Coyamo\\Desktop\\raw.bmp");
File IW=new File("C:\\Users\\Coyamo\\Desktop\\out.bmp");
File NI=new File("C:\\Users\\Coyamo\\Desktop\\new.bmp");
int pwd=96585678;
encode(pwd,I,IW);
decode(96595679,IW,NI);
int[] f1=getFeature(I);
Arrays.sort(f1);
System.out.println("I "+Arrays.toString(f1));
int[] f2=getFeature(IW);
Arrays.sort(f2);
System.out.println("IW "+Arrays.toString(f2));
int[] f3=getFeature(NI);
Arrays.sort(f3);
System.out.println("NI "+Arrays.toString(f3));
System.out.println(Arrays.equals(f1,f3)&&Arrays.equals(f2,f3));//true
}
public static int toGray(int color){
int r = (color >> 16) & 0xff;
int g = (color >> 8) & 0xff;
int b = color & 0xff;
return (r*30+g*60+b*10)/100;
}
//获取特征
public static int[] getFeature(File I){
try {
BufferedImage imageI = ImageIO.read(I);
int widthI = imageI.getWidth(),
heightI = imageI.getHeight();
int sizeI = widthI * heightI;
//gray
int[] gray =new int[sizeI];
for(int i=0;i<widthI;i++){
for(int j=0;j<heightI;j++){
int color=imageI.getRGB(i, j);
gray[j*widthI+i]=toGray(color);
}
}
int b = 20;
int col,row;
int blockCount=(widthI/b)*(heightI/b);
int[] feature=new int[blockCount];
int perRowCount=heightI/b;
int perColCount=widthI/b;
int avg;
//块循环
for (int i = 0; i <blockCount; i++){
col=(i%perColCount)*b;
row=(i/perRowCount)*b;
avg=0;
//块内循环
for (int j = 0; j <b; j++){
for (int k = 0; k <b; k++){
avg+=gray[(row+j)*widthI+col+k];
}
}
avg/=b*b;
feature[i]=avg;
}
//即求每个块灰度的平均值
return feature;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//加密
public static void encode(int pwd,File I,File IW){
float x0,x0_2;
float u = 4f;
pwd%=100000000;
x0=(pwd%10000)*0.0001f;//1~4
x0_2=(pwd/10000)*0.0001f;//5~8
try {
BufferedImage imageI = ImageIO.read(I);
int widthI = imageI.getWidth(),
heightI = imageI.getHeight();
int sizeI=widthI*heightI;
int b=20;
//gray
int[] gray =new int[sizeI];
for(int i=0;i<widthI;i++){
for(int j=0;j<heightI;j++){
int color=imageI.getRGB(i, j);
gray[j*widthI+i]= toGray(color);
}
}
int blockCount=(widthI/b)*(heightI/b);
//块置换序列
float[] x = new float[blockCount];
x[0] = x0;
for (int i = 0; i < 500; i++)
x[0] = u * x[0] * (1 - x[0]);
for (int i = 0; i < blockCount - 1; i++)
x[i + 1] = u * x[i] * (1 - x[i]);
int[] index=sort(x);
//块内置换序列
float[] x2 = new float[b*b];
x2[0] = x0_2;
for (int i = 0; i < 500; i++)
x2[0] = u * x2[0] * (1 - x2[0]);
for (int i = 0; i < b*b - 1; i++)
x2[i + 1] = u * x2[i] * (1 - x2[i]);
int[] index2=sort(x2);
int[] rgb=new int[sizeI];
int[] rgb_rep=new int[sizeI];
int col,row;
int perRowCount=heightI/b;
int perColCount=widthI/b;
for (int i = 0; i <blockCount; i++){
col=(i%perColCount)*b;
row=(i/perRowCount)*b;
int repCol=(index[i]%perRowCount)*b;
int repRow=(index[i]/perRowCount)*b;
//块置换
for (int j = 0; j <b; j++){
for (int k = 0; k <b; k++){
//复制打乱的块
rgb[(row+j)*widthI+col+k]=gray[(repRow+j)*widthI+repCol+k];
}
}
//这2个循环应该可以合并。。
//懒得管了
//块内置换
for (int j = 0; j <b; j++){
for (int k = 0; k <b; k++){
//打乱块内像素
int pos=index2[j*b+k];
int nCol=(pos%b);
int nRow=pos/b;
rgb_rep[(row+j)*widthI+col+k]=rgb[(row+nRow)*widthI+col+nCol];
}
}
}
//灰度值转颜色
for (int i = 0; i <sizeI; i++){
int g=rgb_rep[i];
rgb_rep[i]=g<<16|g<<8|g;
}
BufferedImage image = new BufferedImage(widthI, heightI, imageI.getType());
image.setRGB(0, 0, widthI, heightI, rgb_rep, 0, widthI);
String suffix = I.getName().substring(I.getName().lastIndexOf('.') + 1);
ImageIO.write(image, suffix, IW);
image.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
//加密的逆过程
public static void decode(int pwd,File IW,File I){
float x0,x0_2;
float u = 4f;
pwd%=100000000;
x0=(pwd%10000)*0.0001f;//1~4
x0_2=(pwd/10000)*0.0001f;//5~8
try {
BufferedImage imageIW = ImageIO.read(IW);
int widthIW = imageIW.getWidth(),
heightIW = imageIW.getHeight();
int sizeIW=widthIW*heightIW;
//gray
int[] gray =new int[sizeIW];
for(int i=0;i<widthIW;i++){
for(int j=0;j<heightIW;j++){
int color=imageIW.getRGB(i, j);
gray[j*widthIW+i]= toGray(color);
}
}
int b=20;
int blockCount=(widthIW/b)*(heightIW/b);
int perRowCount=heightIW/b;
int perColCount=widthIW/b;
int col,row;
//块置换序列
float[] x = new float[blockCount];
x[0] = x0;
for (int i = 0; i < 500; i++)
x[0] = u * x[0] * (1 - x[0]);
for (int i = 0; i < blockCount - 1; i++)
x[i + 1] = u * x[i] * (1 - x[i]);
int[] index=sort(x);
//块内置换序列
float[] x2 = new float[b*b];
x2[0] = x0_2;
for (int i = 0; i < 500; i++)
x2[0] = u * x2[0] * (1 - x2[0]);
for (int i = 0; i < b*b - 1; i++)
x2[i + 1] = u * x2[i] * (1 - x2[i]);
int[] index2=sort(x2);
int[] rgb=new int[sizeIW];
int[] rgb_rep=new int[sizeIW];
for (int i = 0; i <blockCount; i++){
col=(i%perRowCount)*b;
row=(i/perRowCount)*b;
for (int j = 0; j <b; j++){
for (int k = 0; k <b; k++){
int pos=index2[j*b+k];
int ncol=(pos%b);
int nrow=pos/b;
rgb_rep[(row+nrow)*widthIW+col+ncol]=gray[(row+j)*widthIW+col+k];
}
}
int repCol=(index[i]%perRowCount)*b;
int repRow=(index[i]/perRowCount)*b;
for (int j = 0; j <b; j++){
for (int k = 0; k <b; k++){
rgb[(repRow+j)*widthIW+repCol+k]=rgb_rep[(row+j)*widthIW+col+k];
}
}
}
for (int i = 0; i <sizeIW; i++){
int g=rgb[i];
rgb[i]=g<<16|g<<8|g;
}
BufferedImage image = new BufferedImage(widthIW, heightIW, imageIW.getType());
image.setRGB(0, 0, widthIW, heightIW, rgb, 0, widthIW);
String suffix = IW.getName().substring(IW.getName().lastIndexOf('.') + 1);
ImageIO.write(image, suffix, I);
image.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
//根据混沌序列生成置换顺序
private static int[] sort(float[] x){
int size=x.length;
int[] index=new int[size];
for (int i = 0; i <size; i++){
index[i]=i;
}
for (int i = 0; i <size-1; i++) {
int min = i;
for (int j = i + 1; j < size; j++) {
if (x[min] > x[j]) {
min = j;
}
}
float temp = x[min];
x[min] = x[i];
x[i] = temp;
int temp2 = index[min];
index[min] = index[i];
index[i] = temp2;
}
return index;
}
}
总结
这个算法主要以学习为目的,限制图像大小为分块大小的整数倍,且是正方形。里面一些实现也许还可以简化…
这种实现无非是保持像素不变,打乱顺序,保持统计特征一致。关键在于打乱顺序的算法。