3.Java实现Arnold置乱算法

416 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


一、设计思想

  1. 概述 利用Arnold变换(又称猫脸变换)可以对图像进行置乱,使得原本有意义的图像变成一张无意义的图像。该变换可以在其它图像处理前对图像做预处理,例如在数字水印嵌入前对水印进行置乱。也可以用于普通的图像加密。 通常一次Arnold变换达不到理想效果,需要对图像进行连续多次的变换。Arnold变换具有周期性,即对图像连续进行Arnold变换,最终又能得到原图像。变换的周期和图像的尺寸有关。 当图像是一张方形的图像时,Arnold变换存在逆变换。经过N次Arnold变换后的数据可以通过N次逆变换恢复数据。 Arnold变换不仅可以用于图像置乱,也可以用于其它数据的置乱和加密。

  2. 狭义的猫脸变换 2.1 公式 狭义的猫脸变换即最简单的一种变换。网络上绝大部分关于Arnold变换的博客都是狭义Arnold变换。 其矩阵运算公式为:

在这里插入图片描述

转化为多项式为: 在这里插入图片描述

其中mod()是取模运算,N是正方形图像的边长,(x', y')是像素点(x, y)变换后的坐标。

注意求模运算(mod) ≠ 求余运算(%) 。在被除数是负数时两者存在差别,例如: -5 mod(6) = 1, 但 -5 % 6 = -5。 /**

  • 求模运算 */ private int mod(int number, int mod) { return (number % mod + mod) % mod; }

2.2 物理意义和示意图 置乱的实质是新位置与旧位置的映射,且该映射是一一对应的。下图是一次猫脸变换的示意图: 在这里插入图片描述 (a)是原图 (b)是先做水平方向的错切 (c)是在(b)的基础上再做一次竖直方向的错切 (d)是对图像求模,即切割回填操作,得到变换后的图像。 如果你想知道为什么要这样变换,为什么是水平错切一个单位,竖直错切两个单位: 实际上这里水平错切的长度是一倍图像的高度,竖直错切的长度是一倍图像的高度加一倍图像的宽度。由于图像的宽高相等,所以这里看起来是水平错切一个单位,竖直两个单位。 为什么这样子错切,是因为置乱的实质是新位置与旧位置的映射,且该映射是一一对应的。 也就是说,其它错切形式可能造成多个点移动到同一个位置,导致图像信息的丢失。例如下面两种错切方式:

其他错切情形 在这里插入图片描述 第一种是水平和竖直方向都错切一个单位,第二种是水平一个单位,竖直三个单位。可以看出,取模后两种错切方式都有部分区域重叠了。因此错切的单位是有一定要求的,详见广义的Arnold变换。

  1. 狭义猫脸变换的逆变换 当一张图片的宽度和高度相同时,Arnold变换具有逆变换。虽然Arnold变换具有周期性,可以通过一直变换下去得到原图,但是周期越长,恢复原图也越长。通过逆变换可以较为方便地把变换后的图像恢复。 3.1 逆变换公式 在这里插入图片描述

二、程序清单

狭义猫脸正变换:

ArnoldDisorder.java

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

/**
 * @author ArtemisKhryso
 * @time 2021/4/21
 */
public class ArnoldDisorder {
    public static void main(String[] args) throws IOException {
        //变换500次
        for (int i = 0; i<500; i++){
            anod("img/hui/"+i+".png","img/hui/"+(i+1)+".png");
        }
    }

    public static void anod(String src,String res) throws IOException {
        System.out.println(src);

        //图像操纵对象
        BufferedImage imgSrc = ImageIO.read(new File(src));
        //图片类型
        int imgType = 5;
        int imgHight = imgSrc.getHeight();
        int imgWidth = imgSrc.getWidth();
        //猫脸矩阵
//        int[][] arnold = {{1,1},{1,2}};

        //原像素矩阵
        int[][] source = new int[imgWidth][imgHight];
        //置乱后像素矩阵
        int[][] result = new int[imgWidth][imgHight];

        //记录原像素信息到source矩阵里
        for (int i = 0; i < imgWidth; i++) {
            for (int j = 0; j < imgHight; j++) {
                source[i][j] = imgSrc.getRGB(i, j);
            }
        }

        //将原像素点移动到猫脸变换后的位置构造新矩阵
        for (int i = 0; i < imgWidth; i++) {
            for (int j = 0; j < imgHight; j++) {
                int x1 = mod((i + j),imgWidth);
                int y1 = mod((i + 2*j),imgWidth);
                result[x1][y1] = source[i][j];
            }
        }

        //按新矩阵取像素,添加像素点到图片对象相应位置生成新图片
        BufferedImage imgRes = new BufferedImage(imgWidth, imgHight, imgType);
        for (int i = 0; i < imgWidth; i++) {
            for (int j = 0; j < imgHight; j++) {
                imgRes.setRGB(i,j,result[i][j]);
            }
        }
        File imgOut = new File(res);
        //输出文件
        try {
            ImageIO.write(imgRes, "png", imgOut);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 求模运算
     */
    private static int mod(int number, int mod) {
        return (number % mod + mod) % mod;
    }
}

狭义猫脸逆变换: ArnoldDisorderNi.java

主体与正变换相同,仅需修改猫脸矩阵部分代码既可,改为狭义猫脸逆变换矩阵

//将原像素点移动到猫脸变换后的位置构造新矩阵
for (int i = 0; i < imgWidth; i++) {
    for (int j = 0; j < imgHight; j++) {
        int x1 = mod((2*i - j),imgWidth);
        int y1 = mod((j - i),imgWidth);
        result[x1][y1] = source[i][j];
    }
}

三、调试记录

1.首先测试灰度级图片Arnold变换 图片像素为125x125 //变换1次 for (int i = 0; i<1; i++){ anod("img/hui/"+i+".png","img/hui/"+(i+1)+".png"); } 在这里插入图片描述 //变换300次 for (int i = 0; i<300; i++){ anod("img/hui/"+i+".png","img/hui/"+(i+1)+".png"); } 在这里插入图片描述 测试证明矩阵边长N=125的猫脸变换周期T为250。

2.再测试彩色图片Arnold变换 图片像素为220x220 //变换1次 for (int i = 0; i<1; i++){ anod("img/qid/"+i+".png","img/qid/"+(i+1)+".png"); } 在这里插入图片描述 //变换300次 for (int i = 0; i<300; i++){ anod("img/qid/"+i+".png","img/qid/"+(i+1)+".png"); } 在这里插入图片描述 测试证明矩阵边长N=220的猫脸变换周期T为30。

3.再测试彩色图片Arnold逆变换 图片像素为220x220 以正变换的随机一张置乱图片为源图 //变换100次 for (int i = 0; i<100; i++){ anod("img/ni/"+i+".png","img/ni/"+(i+1)+".png"); } 在这里插入图片描述 测试证明已置乱图片也可通过逆矩阵变换一定次数恢复初始状态。

四、结果及其分析

在这里插入图片描述 Arnold变换具有周期性,即经过若干次变换后,矩阵回到最初状态,且周期T与N的大小有关。 Arnold变换直观、简单、具有周期性,使用非常方便。