1. 概述
利用Arnold變換(又稱(chēng)貓臉變換)可以對(duì)圖像進(jìn)行置亂,使得原本有意義的圖像變成一張無(wú)意義的圖像。該變換可以在其它圖像處理前對(duì)圖像做預(yù)處理,例如在數(shù)字盲水印嵌入前對(duì)水印進(jìn)行置亂。也可以用于普通的圖像加密。
通常一次Arnold變換達(dá)不到理想效果,需要對(duì)圖像進(jìn)行連續(xù)多次的變換。Arnold變換具有周期性,即對(duì)圖像連續(xù)進(jìn)行Arnold變換,最終又能得到原圖像。變換的周期和圖像的尺寸有關(guān)。
當(dāng)圖像是一張方形的圖像時(shí),Arnold變換存在逆變換。經(jīng)過(guò)N次Arnold變換后的數(shù)據(jù)可以通過(guò)N次逆變換恢復(fù)數(shù)據(jù)。
Arnold變換不僅可以用于圖像置亂,也可以用于其它數(shù)據(jù)的置亂和加密。
2. 狹義的貓臉變換
2.1 公式
狹義的貓臉變換即最簡(jiǎn)單的一種變換。網(wǎng)絡(luò)上絕大部分關(guān)于Arnold變換的博客都是狹義Arnold變換。
其矩陣運(yùn)算公式為:

轉(zhuǎn)化為多項(xiàng)式為:

其中mod()是取模運(yùn)算,N是正方形圖像的邊長(zhǎng),(x', y')是像素點(diǎn)(x, y)變換后的坐標(biāo)。
注意求模運(yùn)算(mod) ≠ 求余運(yùn)算(%) 。在被除數(shù)是負(fù)數(shù)時(shí)兩者存在差別,例如: -5 mod(6) = 1, 但 -5 % 6 = -5。
/**
* 求模運(yùn)算
*/
private int mod(int number, int mod) {
return (number % mod + mod) % mod;
}
2.2 物理意義和示意圖
置亂的實(shí)質(zhì)是新位置與舊位置的映射,且該映射是一一對(duì)應(yīng)的。下圖是一次貓臉變換的示意圖:

- (a)是原圖
- (b)是先做水平方向的錯(cuò)切
- (c)是在(b)的基礎(chǔ)上再做一次豎直方向的錯(cuò)切
- (d)是對(duì)圖像求模,即切割回填操作,得到變換后的圖像。
如果你想知道為什么要這樣變換,為什么是水平錯(cuò)切一個(gè)單位,豎直錯(cuò)切兩個(gè)單位:
實(shí)際上這里水平錯(cuò)切的長(zhǎng)度是一倍圖像的高度,豎直錯(cuò)切的長(zhǎng)度是一倍圖像的高度加一倍圖像的寬度。由于圖像的寬高相等,所以這里看起來(lái)是水平錯(cuò)切一個(gè)單位,豎直兩個(gè)單位。
為什么這樣子錯(cuò)切,是因?yàn)?strong>置亂的實(shí)質(zhì)是新位置與舊位置的映射,且該映射是一一對(duì)應(yīng)的。
也就是說(shuō),其它錯(cuò)切形式可能造成多個(gè)點(diǎn)移動(dòng)到同一個(gè)位置,導(dǎo)致圖像信息的丟失。例如下面兩種錯(cuò)切方式:
其他錯(cuò)切情形
第一種是水平和豎直方向都錯(cuò)切一個(gè)單位,第二種是水平一個(gè)單位,豎直三個(gè)單位。可以看出,取模后兩種錯(cuò)切方式都有部分區(qū)域重疊了。因此錯(cuò)切的單位是有一定要求的,詳見(jiàn)后文廣義的Arnold變換。
2.3 代碼實(shí)現(xiàn)
2.3.1 Java泛型的實(shí)現(xiàn)
此處寬高需要相等是方便后續(xù)的逆變換。
public class Arnold<T> {
/**
* 貓臉變換
* @param origin 原始圖像
* @param dest 用于保存變換后的圖像
* @param count 變換的次數(shù)
*/
public void arnold(T[][] origin, T[][] dest, int count) {
int newY, newX;
while (count > 0) {
for (int row = 0; row < origin.length; row++) {
for (int col = 0; col < origin[0].length; col++) {
newX = (col + row) % origin.length;
newY = (col + 2 * row) % origin.length;
dest[newY][newX] = origin[row][col];
}
}
count--;
origin = Arrays.copyOf(dest, dest.length);
}
}
}
2.3.2 適用于Android的一維數(shù)組形式
/**
* 貓臉變換
* @param origin 原始圖像,寬高必須一致
* @param dest 用于保存輸出
* @param SIZE 寬和高
* @param count 變換的次數(shù)
*/
public void arnold(int[] origin, int[] dest, int SIZE, int count) {
int oldY, oldX, newY, newX;
while (count > 0) {
for (int index = 0; index < origin.length; index++) {
oldX = index % SIZE;
oldY = index / SIZE;
newX = (oldX + oldY) % SIZE;
newY = (oldX + 2 * oldY) % SIZE;
dest[newY * SIZE + newX] = origin[index];
}
count--;
origin = Arrays.copyOf(dest, dest.length);
}
}
2.3.3 實(shí)際運(yùn)行結(jié)果
如圖所示,一次變換后,原圖得到了一定程度的置亂,但還能分辨出原始圖像的信息,6次變換后圖像已看不出原始圖像的信息。

3. 狹義貓臉變換的逆變換
當(dāng)一張圖片的寬度和高度相同時(shí),Arnold變換具有逆變換。雖然Arnold變換具有周期性,可以通過(guò)一直變換下去得到原圖,但是周期越長(zhǎng),恢復(fù)原圖也越長(zhǎng)。通過(guò)逆變換可以較為方便地把變換后的圖像恢復(fù)。
3.1 逆變換公式

轉(zhuǎn)換為多項(xiàng)式為:

3.2 代碼實(shí)現(xiàn)
3.2.1 泛型形式的實(shí)現(xiàn)
/**
* 貓臉逆變換
* @param origin 原始圖像
* @param dest 用于保存變換后的圖像
* @param count 變換的次數(shù)
*/
public void inverseArnold(T[][] origin, T[][] dest, int count) {
int newY, newX;
while (count > 0) {
for (int row = 0; row < origin.length; row++) {
for (int col = 0; col < origin[0].length; col++) {
newX = (col + row) % origin.length;
newY = (col + 2 * row) % origin.length;
dest[newY][newX] = origin[row][col];
}
}
count--;
origin = Arrays.copyOf(dest, dest.length);
}
}
3.2.2 適用于Android的一維數(shù)組形式
/**
* 貓臉逆變換
* @param origin 原圖
* @param dest 保存變換后的圖像
* @param SIZE 寬高
* @param count 變換次數(shù)
*/
public void inverseArnold(int[] origin, int[] dest, int SIZE, int count) {
int oldY, oldX, newY, newX;
while (count > 0) {
for (int index = 0; index < origin.length; index++) {
oldX = index % SIZE;
oldY = index / SIZE;
newY = mod((oldY - oldX), SIZE);
newX = mod((2 * oldX - oldY), SIZE);
dest[newY * SIZE + newX] = origin[index];
}
count--;
origin = Arrays.copyOf(dest, dest.length);
}
}
逆變換的效果當(dāng)然就是把圖像復(fù)原了。此處就不在貼效果圖了。
4. 廣義的貓臉變換
4.1 公式
如前文所述,只要錯(cuò)切的單位滿足取?;靥詈?,原圖與變換后的圖能夠一一對(duì)應(yīng),那么該變換就是有效的。滿足這個(gè)條件的公式是:


其逆變換公式為:


4.2 代碼實(shí)現(xiàn)
這里只列出了用于Android的一維數(shù)組形式:
4.2.1 廣義正變換
/**
* 廣義貓臉變換
* @param origin 原圖
* @param dest 變換后的圖
* @param SIZE 圖像寬度和高度
* @param count 變換次數(shù)
*/
public void arnold(int[] origin, int[] dest, int SIZE, int count, int a, int b) {
final int ab_plus_1 = a * b + 1;
int oldY, oldX, newY, newX;
while (count > 0) {
for (int index = 0; index < origin.length; index++) {
oldX = index % SIZE;
oldY = index / SIZE;
newX = (oldX + a * oldY) % SIZE;
newY = (b * oldX + ab_plus_1 *oldY) % SIZE;
dest[newY * SIZE + newX] = origin[index];
}
count--;
origin = Arrays.copyOf(dest, dest.length);
}
}
4.2.2 廣義逆變換
/**
* 廣義貓臉逆變換
* @param origin 原圖
* @param dest 變換后的圖
* @param SIZE 圖像寬度和高度
* @param count 變換次數(shù)
*/
public void inverseArnold(int[] origin, int[] dest, int SIZE, int count, int a, int b) {
final int ab_plus_1 = a * b + 1;
int oldY, oldX, newY, newX;
while (count > 0) {
for (int index = 0; index < origin.length; index++) {
oldX = index % SIZE;
oldY = index / SIZE;
newX = mod(ab_plus_1 * oldX - a * oldY, SIZE);
newY = mod(oldY - b * oldX, SIZE);
dest[newY * SIZE + newX] = origin[index];
}
count--;
origin = Arrays.copyOf(dest, dest.length);
}
}
5. 利用Arnold變換進(jìn)行加密
對(duì)于廣義Arnold變換,當(dāng)a、b、count任何一個(gè)值不同時(shí),變換后圖像也是不相同的。因此,可以把(a、b、count)作為加密參數(shù)對(duì)圖像進(jìn)行加密。此外,還可以對(duì)圖像的不同部分進(jìn)行不同的加密,使得更難破解。例如,可以把圖像分為四份(甚至可以有交集),分別對(duì)每一份子圖進(jìn)行加密,這樣又增大了破解的難度。
Arnold加密后,如果圖像被破壞了,例如壓縮、涂改等。解密后的圖像依然能恢復(fù)一部分?jǐn)?shù)據(jù)。
下圖是以參數(shù)(7,11,4)加密的圖像,以及對(duì)加密后的圖像進(jìn)行涂抹后再解密的結(jié)果。

- (a) 原圖
- (b) 加密后的圖像
- (c) 涂抹
- (d) 解謎后的圖像
可以看出Arnold變換有較高的魯棒性,即使添加了多個(gè)較大的圓也能恢復(fù)出原圖的大致信息。
根據(jù)Arnold變換的原理,我們還可以用來(lái)加密其它數(shù)據(jù),而不僅僅是圖像。
轉(zhuǎn)載須注明出處:http://www.itdecent.cn/p/39727dbaffd9
