前言
本篇文章要使用OpenCV、Numpy 和Math这3个工具包实现一个简单的滤镜编辑器。在这个滤镜编辑器中,包含了3种滤镜效果,它们分别是浮雕滤镜、雕刻滤镜和凸透镜滤镜。本篇文章将对目标图像(如图1所示)进行处理,使得目标图像分别呈现浮雕滤镜(如图2所示)、雕刻滤镜(如图3所示)和凸透镜滤镜(如图4所示)的视觉效果。
浮雕滤镜效果
为了实现浮雕滤镜效果,首先要把实现浮雕滤镜效果的原理搞清楚、弄明白。实现浮雕滤镜效果的原理如下所示:
-
根据灰度图像中的某一个像素的像素值与其周围像素的像素值之间的差值,确定这个像素经过卷积处理后的像素值;
-
由于边缘点的像素值与其周围像素的像素值之间的差值较大,经卷积处理后,导致这些边缘点较亮,从而达到凸显边缘的目的,进而形成浮雕状;
-
为经卷积处理后的每一个像素加上一个灰度偏移值128,作为呈现浮雕滤镜效果的图像的底色。
明确了实现浮雕滤镜效果的原理后,再来学习一下实现浮雕滤镜效果的算法。实现浮雕滤镜效果的算法如下所示:
-
对灰度图像中的每一个像素进行卷积处理;
-
实现浮雕滤镜效果的卷积核算子需采用如下矩阵:
[[1, 0], [0, -1]]
掌握了实现浮雕滤镜效果的原理和算法后,下面开始编写用于实现浮雕滤镜效果的方法,即fuDiao()
方法。fuDiao()
方法是一个自定义的、有参且有返回值的方法,fuDiao()
方法的语法格式如下所示:
def fuDiao(img):
……# 省略方法体中的代码
return canvas
参数说明:
img:与目标图像对应的灰度图像。
返回值说明:
canvas:画布,用于呈现浮雕滤镜效果的图像。
那么,fuDiao()方法
被省略的代码各自发挥怎样的作用?此外,被省略的代码又是那些呢?
先要明确fuDiao()方法中被省略的代码各自发挥的作用是什么:
因为用于实现浮雕滤镜效果的卷积核算子是一个二维矩阵,所以需要使用Numpy工具包
中的array()方法
创建这个二维矩阵。关键代码如下所示:
kernel = np.array([[1, 0], [0, -1]])
分别获取灰度图像中像素的行数和列数。关键代码如下所示:
row = img.shape[0]
col = img.shape[1]
根据灰度图像中像素的行数和列数,创建一个等高、等宽的,纯黑色的画布。关键代码如下所示:
canvas = np.zeros([row, col], dtype=np.uint8)
根据横、纵坐标,使用嵌套的for循环得到灰度图像中的每一个像素。关键代码如下所示:
for i in range(row - 1):
for j in range(col - 1):
使用实现浮雕滤镜效果的卷积核算子,对灰度图像中的每一个像素进行卷积处理,并且为经卷积处理后的每一个像素加上一个灰度偏移值128。关键代码如下所示:
new_value = np.sum(img[i:i + 2, j:j + 2] * kernel) + 128
对于经卷积处理且加上一个灰度偏移值后的每一个像素的像素值,如果大于255,那么等于255;如果小于0,那么等于0。关键代码如下所示:
if new_value > 255:
new_value = 255
elif new_value < 0:
new_value = 0
else:
pass
把经过比较后的每一个像素的像素值根据坐标赋值给画布对应位置上的像素。关键代码如下所示:
canvas[i, j] = new_value
结合fuDiao()方法
中被省略的代码和它们各自发挥的作用,就能够迅速完成fuDiao()
方法的编写。fuDiao()
方法的代码如下所示:
def fuDiao(img):
kernel = np.array([[1, 0], [0, -1]])
row = img.shape[0]
col = img.shape[1]
canvas = np.zeros([row, col], dtype=np.uint8)
for i in range(row - 1):
for j in range(col - 1):
new_value = np.sum(img[i:i + 2, j:j + 2] * kernel) + 128
if new_value > 255:
new_value = 255
elif new_value < 0:
new_value = 0
else:
pass
canvas[i, j] = new_value
return canvas
雕刻滤镜效果
实现雕刻滤镜效果的原理与实现浮雕滤镜效果的原理大致相同,实现雕刻滤镜效果的原理如下所示:
-
根据灰度图像中的某一个像素的像素值与其周围像素的像素值之间的差值,确定这个像素经过卷积处理后的像素值;
-
由于边缘点的像素值与其周围像素的像素值之间的差值较大,经卷积处理后,导致这些边缘点较暗,从而达到凹陷边缘的目的,进而形成雕刻状;
-
为经卷积处理后的每一个像素加上一个灰度偏移值128,作为呈现雕刻滤镜效果的图像的底色。
虽然实现雕刻滤镜效果的原理与实现浮雕滤镜效果的原理大致相同,但是实现雕刻滤镜效果的算法与实现浮雕滤镜效果的算法大不相同。实现雕刻滤镜效果的算法如下所示:
-
对灰度图像中的每一个像素进行卷积处理;
-
实现雕刻滤镜效果的卷积核算子需采用如下矩阵:
[[-1, 0],
[0, 1]]
掌握了实现雕刻滤镜效果的原理和算法后,下面开始编写用于实现雕刻滤镜效果的方法,即diaoKe()
方法。与fuDiao()
方法相同,diaoKe()
方法也是一个自定义的、有参且有返回值的方法,diaoKe()
方法的语法格式如下所示:
def diaoKe(img):
……# 省略方法体中的代码
return canvas
参数说明:
img:与目标图像对应的灰度图像。
返回值说明:
canvas:画布,用于呈现雕刻滤镜效果的图像。
diaoKe()方法被省略的代码与fuDiao()方法被省略的代码大同小异,首先明确下diaoKe()方法中被省略的代码各自发挥的作用是什么:
用于实现雕刻滤镜效果的卷积核算子与用于实现浮雕滤镜效果的卷积核算子虽然不同,但也是一个二维矩阵,因此需要使用Numpy工具包中的array()方法创建这个二维矩阵。关键代码如下所示:
kernel = np.array([[-1, 0], [0, 1]])
分别获取灰度图像中像素的行数和列数。关键代码如下所示:
row = img.shape[0]
col = img.shape[1]
根据灰度图像中像素的行数和列数,创建一个等高、等宽的,纯黑色的画布。关键代码如下所示:
canvas = np.zeros([row, col], dtype=np.uint8)
根据横、纵坐标,使用嵌套的for循环得到灰度图像中的每一个像素。关键代码如下所示:
for i in range(row - 1):
for j in range(col - 1):
使用实现雕刻滤镜效果的卷积核算子,对灰度图像中的每一个像素进行卷积处理,并且为经卷积处理后的每一个像素加上一个灰度偏移值128。关键代码如下所示:
new_value = np.sum(img[i:i + 2, j:j + 2] * kernel) + 128
对于经卷积处理且加上一个灰度偏移值后的每一个像素的像素值,如果大于255,那么等于255;如果小于0,那么等于0。关键代码如下所示:
if new_value > 255:
new_value = 255
elif new_value < 0:
new_value = 0
else:
pass
把经过比较后的每一个像素的像素值根据坐标赋值给画布对应位置上的像素。关键代码如下所示:
canvas[i, j] = new_value
结合diaoKe()
方法中被省略的代码和它们各自发挥的作用,就能够迅速完成diaoKe()
方法的编写。diaoKe()
方法的代码如下所示:
def diaoKe(img):
kernel = np.array([[-1, 0], [0, 1]])
row = img.shape[0]
col = img.shape[1]
canvas = np.zeros([row, col], dtype=np.uint8)
for i in range(row - 1):
for j in range(col - 1):
new_value = np.sum(img[i:i + 2, j:j + 2] * kernel) + 128
if new_value > 255:
new_value = 255
elif new_value < 0:
new_value = 0
else:
pass
canvas[i, j] = new_value
return canvas
凸透镜滤镜效果
所谓凸透镜滤镜效果,相当于用户使用凸透镜观察一幅图像而成的视觉效果。实现凸透镜滤镜效果的原理与实现浮雕滤镜效果的原理和实现雕刻滤镜效果的原理大不相同。下面将着重对实现凸透镜滤镜效果的原理进行讲解:
-
当使用凸透镜中心观察一幅图像时,被观察的图像区域将按照一定比例进行放大;相应地,这个区域的周围区域将被压缩;
-
为了让放大后的图像区域看起来和谐自然,这些被压缩的周围区域要保持连续性。
明确了实现凸透镜滤镜效果的原理后,再来学习一下实现凸透镜滤镜效果的算法。实现凸透镜滤镜效果的算法如下所示:
-
根据目标图像的宽、高确定凸透镜的半径;
-
选择一个凸函数作为映射函数;
如果目标图像中的某一个像素与目标图像中心之间的距离的平方不大于凸透镜的半径的平方(两个整数进行比较,保证比较结果的精确度),就使用映射函数对这个像素的横、纵坐标进行映射处理。
掌握了实现凸透镜滤镜效果的原理和算法后,下面开始编写用于实现凸透镜滤镜效果的方法,即tuTouJing()
方法。与fuDiao()
方法和diaoKe()
方法相同,tuTouJing()
方法也是一个自定义的、有参且有返回值的方法,tuTouJing()
方法的语法格式如下所示:
def tuTouJing(img):
……# 省略方法体中的代码
return canvas
参数说明:
img:目标图像。
返回值说明:
canvas:画布,用于呈现凸透镜滤镜效果的图像。
先要明确tuTouJing()
方法中被省略的代码各自发挥的作用是什么:
分别获取目标图像中像素的行数和列数以及目标图像的通道数。关键代码如下所示:
row = img.shape[0]
col = img.shape[1]
channel = img.shape[2]
根据目标图像中像素的行数和列数以及目标图像的通道数,创建一个等高、等宽、等通道数的,纯黑色的画布。关键代码如下所示:
canvas = np.zeros([row, col, channel], dtype=np.uint8)
根据目标图像中像素的行数和列数,分别获取目标图像中心的横、纵坐标。关键代码如下所示:
center_x = row/2
center_y = col/2
比较目标图像中心的横、纵坐标的大小,把较小的数值作为凸透镜的半径。关键代码如下所示:
radius = min(center_x, center_y)
根据横、纵坐标,使用嵌套的for循环得到目标图像中的每一个像素。关键代码如下所示:
for i in range(row):
for j in range(col):
计算目标图像中的每一个像素与目标图像中心之间的距离的平方和距离。关键代码如下所示:
distance = ((i-center_x) * (i-center_x) + (j-center_y) * (j-center_y))
new_dist = math.sqrt(distance)
把目标图像中的每一个像素的像素值根据坐标赋值给画布对应位置上的像素。关键代码如下所示:
canvas[i,j,:] = img[i, j, :]
如果目标图像中的某一个像素与目标图像中心之间的距离的平方不大于凸透镜的半径的平方,就使用映射函数对这个像素的横、纵坐标进行映射处理。关键代码如下所示:
if distance <= radius**2:
new_i = np.int(np.floor(new_dist * (i-center_x) / radius + center_x))
new_j = np.int(np.floor(new_dist * (j-center_y) / radius + center_y))
把经过映射处理后的每一个像素的像素值根据坐标赋值给画布对应位置上的像素。关键代码如下所示:
canvas[i,j,:] = img[new_i, new_j, :]
结合tuTouJing()方法中被省略的代码和它们各自发挥的作用,就能够迅速完成tuTouJing()方法的编写。tuTouJing()方法的代码如下所示:
def tuTouJing(img):
row = img.shape[0]
col = img.shape[1]
channel = img.shape[2]
canvas = np.zeros([row, col, channel], dtype=np.uint8)
center_x = row/2
center_y = col/2
radius = min(center_x, center_y)
for i in range(row):
for j in range(col):
distance = ((i-center_x) * (i-center_x) + (j-center_y) * (j-center_y))
new_dist = math.sqrt(distance)
canvas[i,j,:] = img[i, j, :]
if distance <= radius**2:
new_i = np.int(np.floor(new_dist * (i-center_x) / radius + center_x))
new_j = np.int(np.floor(new_dist * (j-center_y) / radius + center_y))
canvas[i,j,:] = img[new_i, new_j, :]
return canvas
浅析Python OpenCV三种滤镜效果
- Author -
苏州程序大白- Original Sources -
声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Reply on: @reply_date@
@reply_contents@