電子科技大學 格拉斯哥學院 2017級歐陽韜

線性濾波/卷積

(注:這部分摘自這篇博客
線性濾波可以說是圖像處理最基本的方法,它可以允許我們對圖像進行處理,產生很多不同的效果。做法很簡單。首先,我們有一個二維的濾波器矩陣(有個高大上的名字叫卷積核)和一個要處理的二維圖像。然後,對於圖像的每一個像素點,計算它的鄰域像素和濾波器矩陣的對應元素的乘積,然後加起來,作爲該像素位置的值。這樣就完成了濾波過程。
線性濾波過程
對圖像和濾波矩陣進行逐個元素相乘再求和的操作就相當於將一個二維的函數移動到另一個二維函數的所有位置,這個操作就叫卷積或者協相關。卷積和協相關的差別是,卷積需要先對濾波矩陣進行180的翻轉,但如果矩陣是對稱的,那麼兩者就沒有什麼差別了。

實際應用——加密

很多我們熟知的圖像處理其實都可以由由濾波和卷積簡單做到。比如銳化,軟化,浮雕及高斯模糊,具體效果及原理可看這篇文章
加密是社會中一項很重要的應用,也是信息傳播中的一項重要安全措施。對於這個問題,本人思考了下濾波在圖像加密中的應用。濾波和卷積都存在對應的逆操作,要想對圖像加密的話,選定一個甚至多個卷積覈對圖像進行變換從而可以得到一個隱含內容的無規則圖樣。解密的時候需要按照對應的順序進行對應的逆操作便能得到原圖樣。但是逆濾波和逆卷積涉及的知識面都比較深在下還沒怎麼弄懂orz。想要更深入學習的可以參考逆濾波反捲積
這裏介紹一種在下自己思考的一種比較簡單的加密算法。

二維前綴和思想

假如我們選定卷積核爲中心及中心的左上區域都爲1的矩陣對圖像進行操作,可以想見加密圖樣的每個像素點都是對應位置的原圖像及其左上方一定區域內的信息之和,我們稱之爲前綴和,需要注意的是這個前綴和不是某位置及左上部分所有信息之和而是有一定範圍內的。下圖展示了4*4的圖像矩陣的濾波過程。
濾波1
可以看到左上第一個像素點由於左上沒有其他信息了,所以加密後的對應點信息就是其本身。它右邊面第一個像素點加密後則有其本身和最左上像素點信息之和。減去最左上信息則可得到第二個點的原信息。依次類推,我們可以還原圖像的全部信息。參考代碼如下:

#include<stdio.h>
#include<algorithm>
using namespace std;
int m;//原圖像大小m*m 
int s1[200][200];//原圖像
int s2[200][200]={	{0,1,3,5},
					{4,10,14,18},
					{12,26,30,34},
					{20,42,46,50}};//加密圖像 
int n;//卷積核大小n*n
int r;//卷積核半徑 
int c[200][200]={	{1,1,0},
					{1,1,0},
					{0,0,0}};//convolution kernel 卷積核 
void read()
{
	scanf("%d",&m);
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<m;j++)
		{
			scanf("%d",&s2[i][j]);
		}
	}
}
void read_kernel()
{
	scanf("%d",&n);
	r=(n+1)/2;//默認卷積核邊長爲奇數 
	for(int i=0;i<r;i++)
	{
		for(int j=0;j<r;j++)
		{
			c[i][j]=1;
		}
	}
}
int main()
{
	//read();
	//read_kernel();
	m=4;
	n=3; r=2;
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<m;j++)
		{
			s1[i][j]=s2[i][j];
			for(int x=max(i-r+1,0);x<=i;x++)
			{
				for(int y=max(j-r+1,0);y<=j;y++)
				{
					if(x==i&&y==j) continue;
					s1[i][j]-=s1[x][y];
				}
			}
		}
	}
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<m;j++)
		{
			printf("%d\t",s1[i][j]);
		}
		printf("\n");
	}
	return 0;
}

這份代碼的時間複雜度爲O(m2r2)的,其實還有很多可優化的地方,有興趣可以自行優化一下。

更復雜的卷積核——更好的加密效果

形如上述所說的卷積核會使一部分的信息直接相加,其實在實際中加密效果可能並不理想,甚至還是可以看得出原圖像的大致內容。所以我們考慮將卷積核中的參數變化來使變換更無規則可循。如果理解了上述代碼,其實可以發現前綴和這個性質其實沒有得到太大的利用。每次還原(i,j)像素點的時候,其左上所有像素點都已成功還原,而我們則是用對應加密信息一個個減去左上像素點的原本信息來還原該像素點原本信息。如果這個卷積核的左上部分不是1,是不是也能用同樣的方法來消除其他點對待處理像素點信息的影響呢。其實我們可以把卷積核中的數值視作權值,再用待處理像素點加密信息來加上其他點乘以對應權值的相反數,最後再乘上它本身對應權值的倒數就可以得到其原本信息了。
濾波2
參考代碼如下

#include<stdio.h>
#include<iostream>
#include<time.h> 
#include<algorithm>
using namespace std;
int m;//原圖像大小m*m 
int s1[200][200];//原圖像
int s2[200][200]={	{0,3,11,19},
					{12,29,30,31},
					{0,33,34,35},
					{-12,37,38,39}};//加密圖像 
int n;//卷積核大小n*n
int r;//卷積核半徑 
int c[200][200]={	{-1,-6,0},
					{5,3,0},
					{0,0,0}};//convolution kernel 卷積核 
void read()
{
	scanf("%d",&m);
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<m;j++)
		{
			scanf("%d",&s2[i][j]);
		}
	}
}
void read_kernel()
{
	scanf("%d",&n);
	r=(n+1)/2;//默認卷積核邊長爲奇數 
	for(int i=0;i<r;i++)
	{
		for(int j=0;j<r;j++)
		{
			c[i][j]=1;
		}
	}
}
int main()
{
	//read();
	//read_kernel();
	m=4;
	n=3; r=2;
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<m;j++)
		{
			s1[i][j]=s2[i][j];
			for(int x=0;x<r;x++)
			{
				for(int y=0;y<r;y++)
				{
					if(i-r+1+x<0||j-r+1+y<0) continue;
					if(x==r-1&&y==r-1) continue;
					s1[i][j]-=c[x][y]*s1[i-r+1+x][j-r+1+y];
				}
			}
			if(c[r-1][r-1]!=0) s1[i][j]/=c[r-1][r-1]; 
		}
	}
	for(int i=0;i<m;i++)
	{
		for(int j=0;j<m;j++)
		{
			printf("%d\t",s1[i][j]);
		}
		printf("\n");
	}
	return 0;
}

補充

可以觀察到其實卷積核中非零部分不一定要在左上,在另外三個方向也是沒有關係的。我們也可以用多種不同的卷積覈對圖像進行多方向多重加密使加密效果更加優秀。解碼時也僅需反過來按順序進行多次逆運算就能還原圖像了。

參考資料

[1]https://blog.csdn.net/zouxy09/article/details/49080029
[2]https://blog.csdn.net/haoji007/article/details/53911940
[3]https://blog.csdn.net/qq_16234613/article/details/79387345)
[4]https://blog.csdn.net/mmmmmk_/article/details/82942783
[5]https://baike.baidu.com/item/圖像處理/294902?fr=aladdin#2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章