前言
本篇文章要使用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
💫点击直接数据领取💫
各种学习资料,有趣好玩的编程项目,各种难得一见的资源。
文章出处登录后可见!