(原文写于2013-11-6,发布于振动论坛,信号处理方法分区)
(重编辑于2017-8-27,重新生成了图片,调整了部分内容)
在这里我想继续探讨“卷积”这一数学工具。主要是为了验证网络上部分“科普”文章所讲的“……卷积甚至可以用在考试作弊中,为了让照片同时像两个人,只要把两人的图像卷积处理即可……”这一说法。不同于证明一件事情的难度,证伪一件事情总是相对简单的,尤其当所需要的所有数据和工具都可以在编程实现时。所谓实践出真知,这里我们就找两张照片,来做卷积试一试吧!
引言
这个帖子的起因是,有一和朋友闲聊到卷积,她告诉我网上有文章说“为了让照片同时像两个人,只要把两人的图像卷积处理即可”。我在网上搜了一下,果然可以找到很多帖子支持这样的说法。如图:

当然从内容来看他们相互之间“借鉴”(ctrl+C&V)的成分也很大,难以区分谁是始作俑者。姑且给出一篇吧:http://blog.csdn.net/augusdi/article/details/12438247
在这些帖子中,并未对这一论点作更深入的阐述,当然,如果从他们的角度出发,得出这个结论还是有道理的:
1,卷积满足交换律 conv(A,B) = conv(B,A);
2,conv(A,B) 可以看做是以B为滤波器,对A进行滤波,那么说得到的图象“像”A应该问题不大;
3,由于卷积的交换律,也可以将conv(A,B)看做是以A为滤波器对B进行滤波,那么我们似乎也可以说得到的图象“像”B;
4, 综上,就可以说conv(A,B)得到的图象既像A又像B了。
但是,这事在我理解起来有点怪。按照上述观点,B和自己做卷积之后得到的应该‘既像B又像B’,那他就是B了?这是不是意味着:conv(B,B) = B ?这显然是不对的,因为根据卷积的性质, B只有和DELTA函数(只在一点值为1,其余点值为零的函数)作卷积之后才可能得到B自己,即:conv(B,DELTA) = B。
这是怎么一回事呢?到底两张照片相互卷积可以得到与它们都相似的图片吗?有多相似呢?可以以假乱真到“考试作弊”的程度了吗?那咱们就找两张照片来卷积吧!
原始素材
首先介绍我们的试验图片(均来自网络搜索)
两张照片的像素都是一样的(260*220),都是灰度,导入到程序中就是两个二维数组。
算例(一):直接卷积

上图的结果中……有半点像原始图片的样子吗?也许你会说,算例(一)虽然确实是两张图的卷积,但是由于他们大小一样,补零造成的边缘效应导致其结果中只有一个点没有受到补零的影响。(关于补零效应和边缘效应,请移步:互相关(cross-correlation)及其在Python中的实现)为了回答这个问题,在测试二中将女同学的图片复制四张,再与男同学的图片做卷积。
算例(二):周期扩展一张照片的尺寸以避免边缘效应
男同学头像不变,女同学头像进行了扩展:
结果仍然很遗憾(中间较亮的区域就是所谓的有效数据)。我们暂时放下对这一结果的讨论,让我们回到文章开始时提到的问题,即:既然A和B卷积可以得到既像A又像B的图像,那么一张照片和自身卷积会发生什么呢?
算例(三):男同学照片与自己作卷积

结果……当然是得不到原始照片,原因见引言。
讨论:“卷积”到底对图片做了什么?
我们先来看看男同学的图与DELTA函数(那个唯一取值为1的白色点在图片的左上角,点开大图更明显)作卷积:
假如参与卷积图像中只有一个5*5像素的方块的取值都1,其余为0,与女同学照片作卷积,结果是:
对比可以发现,结果的图像明显变虚了。这是由于上述过程使得新图片的每一个点的值都是原图对应位置周围25个像素点的求和,相当于一个低通滤波器。
假如我们构造更复杂的图像去参与卷积,例如下面展示的有五个5*5纯白像素区黑背景图。与女同学头像卷积,结果是:
结果清晰地说明了卷积运算的过程:就是以其中的一个(矩阵/向量)图作为权重,将另一个图(矩阵/向量)的作加权求和。再平移一位(改变矩阵和权重的对齐位置),接着重复加权求和,这样遍历完所有的点。这个过程在线性系统求响应(信号滤波、结构的瞬态响应)中经常用到。
用简单的矩阵作为滤波器,得到的结果还可以称得上与原图相像,但是可以想象,如果用很复杂的图(男生的图)去与女生的图作卷积,图已经被“滤波”得面目全非了……应该已经谈不上“像”谁了吧?
再讨论:那么……要怎么做才行呢?
看来,“仅仅用卷积”是不能获得既像A又像B的图片,更不能用于考试作弊了。那么,大概应该怎样做才能得到既像你又像他的图片呢?
百度google了一下,这里有一个方案,与卷积有关:
http://cs.brown.edu/courses/cs143/2011/results/proj1/djf/

大概思路是:
C = conv(A,低通滤波器)+ conv(B,高通滤波器)
这样,大概来说,C从远处看将体现出A的特征,在近处将体现出B的特征。
各位可以移步看看,即便如此,要想做到开头那些帖子中讲的“可以用于考试作弊”的程度,恐怕还有差距,而且其中还涉及滤波器截止频率的选择等技巧。
另一些就是最近比较火的“平均脸”技术了,也就是把B和A的图片来算一个平均脸了。但是平均脸技术看上去要比直接把两张图做卷积复杂得多。比较简单的有这个:
http://blog.csdn.net/dsbatigol/article/details/9162131
其中甚至还用到了“人眼对齐算法”。稍学术点的有这个:
http://wenku.baidu.com/link?url=qrv2_oER1rsZWCuNhDoKlkeV-P9gsJzfbIJse2ETCNcbp_klMqe4UxIOSOIi0mbsw432ktlhWnkrvz14b_FdC2YppJrgqsrmhUob9Z4fPbO
其中还涉及了对面部特征点的采样。
结语
总之,卷积也许有一千个奇妙八百种功用,但说把两个人的图片直接卷积就可以拿去骗监考官,我觉得有点过了,你觉得呢?
附录(PYTHON代码)
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 01 10:31:41 2013
@author: Benben
"""
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import scipy.signal as spysg
import numpy as np
def makepic(img1,img2):
plt.figure(1)
plt.imshow(img1,cmap='gray')
plt.colorbar()
plt.figure(2)
plt.imshow(img2,cmap='gray')
plt.colorbar()
son = spysg.fftconvolve(img2,img1,mode='full')
plt.figure(3)
plt.imshow(son,cmap='gray')
plt.colorbar()
plt.show()
def extendim(img):
M,N = img.shape
big_img = np.zeros((M*2,N*2))
for i in range(0,2):
for j in range(0,2):
Mi = M*i
Ni = N*j
big_img[Mi:Mi+M,Ni:Ni+N] = img[:,:]
return big_img
def test_1():
imboy = mpimg.imread('boy.jpg')
print imboy.shape
imgirl = mpimg.imread('girl.jpg')
print imgirl.shape
#big_imgirl = extendim(imgirl)
im = np.zeros_like(imgirl)
im[10,10]=1.0
#im[10,:]=1.0
#makepic(imboy,big_imgirl)
#makepic(imboy,imgirl)
#makepic(imboy,imboy)
makepic(imboy,im)
def test_2():
imgirl = mpimg.imread('girl.jpg')
im = np.zeros_like(imgirl)
#im[10,10]=1
#im[40,40]=1
im[20:25,20:25]=1.0
#im[20:30,-30:-20]=1.0
im[40:45,40:45]=1
im[20:25,40:45]=1
im[40:45,20:25]=1
im[140:145,120:125]=1
makepic(im,imgirl)
if __name__ == '__main__':
test_1()
#test_2()
平均脸看起来好复杂……要各种对齐和放缩……
赞赞