Python计算机视觉:9种图像特效处理实战与原理解析
本文深入解析Python计算机视觉中的9种图像特效处理技术。通过OpenCV与NumPy实现毛玻璃、浮雕、素描、怀旧、流年、水波及卡通等特效。详细阐述各算法原理,如像素邻域随机替换、灰度差分、高斯模糊反色及RGB通道转换公式,并提供完整可运行的代码示例与参数调整建议,帮助开发者掌握图像像素级操作技巧。
前言
本文参考的相关文章及论文链接均附于原文,论文已上传至资源区供免费下载学习。若资源区暂未显示,请耐心等待审核通过。欢迎大家共同学习,共同进步,一起加油!
图像特效处理的核心在于对图像像素点的通道、灰度值等数据进行操作,以实现预期的视觉效果。下文将逐一展示几种常见特效的实现原理及对应的Python代码,旨在为读者提供实用的技术参考。
首先介绍本系列文章的基础操作:读取图像信息。
"""
Author:XiaoMa
date:2021/11/16
"""
import cv2
import numpy as np
import math
import matplotlib.pyplot as plt
img0 = cv2.imread('E:\From Zhihu\For the desk\cvfifteen1.jpg')
img1 = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
h, w = img0.shape[:2]
print(h, w)
cv2.imshow("W0", img0)
cv2.imshow("W1", img1)
cv2.waitKey(delay = 0)
执行上述代码后,获取的图像尺寸信息如下:


386 686
1. 毛玻璃特效
毛玻璃特效的原理是在当前像素点的邻域内随机选取一个像素点来替代原像素,从而产生模糊效果。代码实现如下,已添加详细注释,如有疑问欢迎在评论区讨论。
#毛玻璃特效
img2 = np.zeros((h, w, 3), np.uint8) #生成与原图像等大的全零矩阵
for i in range(0, h - 6): #防止下面的随机数超出边缘
for j in range(0, w - 6):
index = int(np.random.random()*6) #0~6的随机数
(b, g, r) = img0[i + index, j + index]
img2[i, j] = (b, g, r)
cv2.imshow("W2", img2)
cv2.waitKey(delay = 0)
处理后的效果如图所示:

由于在生成随机数时需避开图像边缘像素,导致图像右边界和下边界出现未填充区域。
编写博客时产生新思路:若创建全零矩阵时直接限定边界范围,即可避免该问题。经测试,该方法可行,具体实现如下:
#毛玻璃特效
img2 = np.zeros((h - 6, w - 6, 3), np.uint8) #生成的全零矩阵考虑到了随机数范围,变小了
for i in range(0, h - 6): #防止下面的随机数超出边缘
for j in range(0, w - 6):
index = int(np.random.random()*6) #0~6的随机数
(b, g, r) = img0[i + index, j + index]
img2[i, j] = (b, g, r)
cv2.imshow("W2", img2)
cv2.waitKey(delay = 0)

采用上述方式生成的毛玻璃特效虽消除了未填充区域,但图像尺寸会相应缩小。
2. 浮雕特效
Python实现浮雕特效的原理是先提取图像边界曲线,随后降低边界周围像素值,从而构建出具有立体感的浮雕图像。
#浮雕特效(需要对灰度图像进行操作)
img3 = np.zeros((h, w, 3), np.uint8)
for i in range(0, h):
for j in range(0, w - 2): #减2的效果和上面一样
grayP0 = int(img1[i, j])
grayP1 = int(img1[i, j + 2]) #取与前一个像素点相邻的点
newP = grayP0 - grayP1 + 150 #得到差值,加一个常数可以增加浮雕立体感
if newP > 255:
newP = 255
if newP < 0:
newP = 0
img3[i, j] = newP
cv2.imshow("W3", img3)
cv2.waitKey(delay = 0)
最终效果如图所示:

3. 素描特效
该特效实现逻辑较为直观,代码中已包含详细注释:
#素描特效
img4 = 255 - img1 #对原灰度图像的像素点进行反转
blurred = cv2.GaussianBlur(img4, (21, 21), 0) #进行高斯模糊
inverted_blurred = 255 - blurred #反转
img4 = cv2.divide(img1, inverted_blurred, scale = 127.0) #灰度图像除以倒置的模糊图像得到铅笔素描画
cv2.imshow("W4", img4)
cv2.waitKey(delay = 0)

4. 怀旧特效
怀旧特效需利用特定的R、G、B通道转换公式对图像三通道进行处理,转换公式如下:

具体实现代码如下:
#怀旧特效
img5 = np.zeros((h, w, 3), np.uint8)
for i in range(0, h):
for j in range(0, w):
B = 0.272 * img0[i, j][2] + 0.534 * img0[i, j][1] + 0.131 * img0[i, j][0]
G = 0.349 * img0[i, j][2] + 0.686 * img0[i, j][1] + 0.168 * img0[i, j][0]
R = 0.393 * img0[i, j][2] + 0.769 * img0[i, j][1] + 0.189 * img0[i, j][0]
if B > 255:
B = 255
if G > 255:
G = 255
if R > 255:
R = 255
img5[i, j] = np.uint8((B, G, R))
cv2.imshow("W5", img5)
cv2.waitKey(delay = 0)
处理效果如图所示:

相较于前几种特效,怀旧特效的计算量较大,需遍历每个像素点并计算其三个通道的数值。
5. 流年特效
与怀旧特效不同,流年特效仅需对图像每个像素点的蓝色通道(B)进行简单调整:先开根号,再乘以特定参数。实现代码如下:
#流年特效
img6 = np.zeros((h, w, 3), np.uint8)
for i in range(0, h):
for j in range(0, w):
B = math.sqrt(img0[i, j][0]) *14 # B通道的数值开平方乘以参数14
G = img0[i, j][1]
R = img0[i, j][2]
if B > 255:
B = 255
img6[i, j] = np.uint8((B, G, R))
cv2.imshow("W6", img6)
cv2.waitKey(delay = 0)

建议读者多修改参数进行调试,以观察不同参数下的视觉效果。
6. 水波特效
水波特效利用三角函数生成波纹传递函数,并根据设定的中心点生成特效。该算法具有一定难度,建议多尝试几次。博主后续若有时间,将专门撰写水波特效的详细介绍文章。实现代码如下:
#水波特效
img7 = np.zeros((h, w, 3), np.uint8)
wavelength = 20 #定义水波特效波长
amplitude = 30 #幅度
phase = math.pi / 4 #相位
centreX = 0.5 #水波中心点X
centreY = 0.5 #水波中心点Y
radius = min(h, w) / 2
icentreX = w*centreX #水波覆盖宽度
icentreY = h*centreY #水波覆盖高度
for i in range(0, h):
for j in range(0, w):
dx = j - icentreX
dy = i - icentreY
distance = dx * dx + dy * dy
if distance > radius * radius:
x = j
y = i
else:
# 计算水波区域
distance = math.sqrt(distance)
amount = amplitude * math.sin(distance / wavelength * 2 * math.pi - phase)
amount = amount * (radius - distance) / radius
amount = amount * wavelength / (distance + 0.0001)
x = j + dx * amount
y = i + dy * amount
# 边界判断
if x < 0:
x = 0
if x >= w - 1:
x = w - 2
if y < 0:
y = 0
if y >= h - 1:
y = h - 2
p = x - int(x)
q = y - int(y)
# 图像水波赋值
img7[i, j, :] = (1 - p) * (1 - q) * img0[int(y), int(x), :] + p * (1 - q) * img0[int(y), int(x), :]
+ (1 - p) * q * img0[int(y), int(x), :] + p * q * img0[int(y), int(x), :]
cv2.imshow("W7", img7)
cv2.waitKey(delay = 0)
处理效果如图所示:

由于示例图片中波纹区域恰好涉及头发部分,导致波纹效果不够清晰。建议更换其他图片尝试,以更好地观察算法效果。
7. 卡通特效
该特效主要原理是提取图像边界轮廓,并与原图像进行按位与运算,从而获得卡通化效果。代码如下,已添加注释:
#卡通特效
num_bilateral = 7 #定义双边滤波的数目
for i in range(num_bilateral): #双边滤波处理,除去噪声,保留边界
img_color = cv2.bilateralFilter(img0, d = 9, sigmaColor = 5, sigmaSpace = 3)
img_blur = cv2.medianBlur(img1, 7) # 中值滤波处理
img_edge = cv2.adaptiveThreshold(img_blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, blockSize = 5, C = 2) #边缘检测及自适应阈值化处理,提取边界
img_edge = cv2.cvtColor(img_edge, cv2.COLOR_GRAY2RGB) #转换回彩色图像
img8 = cv2.bitwise_and(img0, img_edge) #图像的与运算
cv2.imshow('W8', img8)
cv2.waitKey(delay = 0)
处理效果如图所示:

若对效果不满意,可不断调整双边滤波和中值滤波的参数,直至获得满意结果。
8. 将图像汇总至一张图中
此为基于上述特效的基础操作,代码无需额外注释,直接展示如下:
#将所有图像保存到一张图中
plt.rcParams['font.family'] = 'SimHei'
imgs = [img0, img1, img2, img3, img4, img5, img6, img7, img8]
titles = ['原图', '灰度图', '毛玻璃特效', '浮雕特效', '素描特效', '怀旧特效', '流年特效', '水波特效', '卡通特效']
for i in range(9):
imgs[i] = cv2.cvtColor(imgs[i], cv2.COLOR_BGR2RGB)
plt.subplot(3, 3, i + 1)
plt.imshow(imgs[i])
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.suptitle('图像特效处理')
plt.savefig('E:\From Zhihu\For the desk\cvfifteenresult.jpg', dpi = 1080)
plt.show()
最终生成的汇总图像如下:

9. 总体代码
只需修改图像读取路径即可直接使用。再次强调,建议从头到尾逐步运行代码;若仅需为亲友生成素描或浮雕效果,则可直接调用相关部分。
"""
Author:XiaoMa
date:2021/11/16
"""
import cv2
import numpy as np
import math
import matplotlib.pyplot as plt
img0 = cv2.imread('E:\From Zhihu\For the desk\cvfifteen1.jpg')
img1 = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
h, w = img0.shape[:2]
print(h, w)
cv2.imshow("W0", img0)
cv2.imshow("W1", img1)
cv2.waitKey(delay = 0)
#毛玻璃特效
img2 = np.zeros((h - 6, w - 6, 3), np.uint8) #生成的全零矩阵考虑到了随机数范围,变小了
for i in range(0, h - 6): #防止下面的随机数超出边缘
for j in range(0, w - 6):
index = int(np.random.random()*6) #0~6的随机数
(b, g, r) = img0[i + index, j + index]
img2[i, j] = (b, g, r)
cv2.imshow("W2", img2)
cv2.waitKey(delay = 0)
#浮雕特效(需要对灰度图像进行操作)
img3 = np.zeros((h, w, 3), np.uint8)
for i in range(0, h):
for j in range(0, w - 2): #减2的效果和上面一样
grayP0 = int(img1[i, j])
grayP1 = int(img1[i, j + 2]) #取与前一个像素点相邻的点
newP = grayP0 - grayP1 + 150 #得到差值,加一个常数可以增加浮雕立体感
if newP > 255:
newP = 255
if newP < 0:
newP = 0
img3[i, j] = newP
cv2.imshow("W3", img3)
cv2.waitKey(delay = 0)
#素描特效
img4 = 255 - img1 #对原灰度图像的像素点进行反转
blurred = cv2.GaussianBlur(img4, (21, 21), 0) #进行高斯模糊
inverted_blurred = 255 - blurred #反转
img4 = cv2.divide(img1, inverted_blurred, scale = 127.0) #灰度图像除以倒置的模糊图像得到铅笔素描画
cv2.imshow("W4", img4)
cv2.waitKey(delay = 0)
#怀旧特效
img5 = np.zeros((h, w, 3), np.uint8)
for i in range(0, h):
for j in range(0, w):
B = 0.272 * img0[i, j][2] + 0.534 * img0[i, j][1] + 0.131 * img0[i, j][0]
G = 0.349 * img0[i, j][2] + 0.686 * img0[i, j][1] + 0.168 * img0[i, j][0]
R = 0.393 * img0[i, j][2] + 0.769 * img0[i, j][1] + 0.189 * img0[i, j][0]
if B > 255:
B = 255
if G > 255:
G = 255
if R > 255:
R = 255
img5[i, j] = np.uint8((B, G, R))
cv2.imshow("W5", img5)
cv2.waitKey(delay = 0)
#流年特效
img6 = np.zeros((h, w, 3), np.uint8)
for i in range(0, h):
for j in range(0, w):
B = math.sqrt(img0[i, j][0]) *14 # B通道的数值开平方乘以参数14
G = img0[i, j][1]
R = img0[i, j][2]
if B > 255:
B = 255
img6[i, j] = np.uint8((B, G, R))
cv2.imshow("W6", img6)
cv2.waitKey(delay = 0)
#水波特效
img7 = np.zeros((h, w, 3), np.uint8)
wavelength = 20 #定义水波特效波长
amplitude = 30 #幅度
phase = math.pi / 4 #相位
centreX = 0.5 #水波中心点X
centreY = 0.5 #水波中心点Y
radius = min(h, w) / 2
icentreX = w*centreX #水波覆盖宽度
icentreY = h*centreY #水波覆盖高度
for i in range(0, h):
for j in range(0, w):
dx = j - icentreX
dy = i - icentreY
distance = dx * dx + dy * dy
if distance > radius * radius:
x = j
y = i
else:
# 计算水波区域
distance = math.sqrt(distance)
amount = amplitude * math.sin(distance / wavelength * 2 * math.pi - phase)
amount = amount * (radius - distance) / radius
amount = amount * wavelength / (distance + 0.0001)
x = j + dx * amount
y = i + dy * amount
# 边界判断
if x < 0:
x = 0
if x >= w - 1:
x = w - 2
if y < 0:
y = 0
if y >= h - 1:
y = h - 2
p = x - int(x)
q = y - int(y)
# 图像水波赋值
img7[i, j, :] = (1 - p) * (1 - q) * img0[int(y), int(x), :] + p * (1 - q) * img0[int(y), int(x), :]
+ (1 - p) * q * img0[int(y), int(x), :] + p * q * img0[int(y), int(x), :]
cv2.imshow("W7", img7)
cv2.waitKey(delay = 0)
#卡通特效
num_bilateral = 7 #定义双边滤波的数目
for i in range(num_bilateral): #双边滤波处理
img_color = cv2.bilateralFilter(img0, d = 9, sigmaColor = 5, sigmaSpace = 3)
img_blur = cv2.medianBlur(img1, 7) # 中值滤波处理
img_edge = cv2.adaptiveThreshold(img_blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, blockSize = 5, C = 2) #边缘检测及自适应阈值化处理
img_edge = cv2.cvtColor(img_edge, cv2.COLOR_GRAY2RGB) #转换回彩色图像
img8 = cv2.bitwise_and(img0, img_edge) #图像的与运算
cv2.imshow('W8', img8)
cv2.waitKey(delay = 0)
#将所有图像保存到一张图中
plt.rcParams['font.family'] = 'SimHei'
imgs = [img0, img1, img2, img3, img4, img5, img6, img7, img8]
titles = ['原图', '灰度图', '毛玻璃特效', '浮雕特效', '素描特效', '怀旧特效', '流年特效', '水波特效', '卡通特效']
for i in range(9):
imgs[i] = cv2.cvtColor(imgs[i], cv2.COLOR_BGR2RGB)
plt.subplot(3, 3, i + 1)
plt.imshow(imgs[i])
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.suptitle('图像特效处理')
plt.savefig('E:\From Zhihu\For the desk\cvfifteenresult.jpg', dpi = 1080)
plt.show()
结束语
本文总结了图像特效处理中毛玻璃、浮雕、素描等操作的原理及简单实现。对于较复杂的算法(如水波特效),计划后续单独撰文介绍。此外,油画特效因算法难度与水波特效相当,暂未涵盖,待本系列更新完毕后再行补充。
至此,关于Python计算机视觉编程进阶之图像特效处理篇的介绍暂告一段落。更多Python图像特效处理相关内容,欢迎搜索本站历史文章或继续阅读后续文章。感谢大家的支持!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













