我是不是在河工带历史上做数字图像处理实验的时候第一个启用GPU并行运算加速的
AMD显卡用户可以跳过此文
一、实验目的与要求
1.加深对图像增强及边缘检测技术的感性认识,应用MATLAB工具箱自带的处理函数或自己编程完成相关的工作,分析处理结果,巩固所学理论知识。
2.熟练掌握空域滤波中常用的平滑和锐化滤波器,针对不同类型和强度的噪声,进行滤波处理,体会并正确评价滤波效果,了解不同滤波方式的使用场合,能够从理论上作出合理的解释。
二、实验相关知识
图像增强是指按特定的需要突出一幅图像中的某些有用信息,同时消弱或去除某些不需要的信息的处理方法,其主要目的是使处理后的图像对某些特定的应用比原来的图像更加有效。图像平滑与锐化处理是图像增强的主要研究内容。
图像分割是由图像处理过渡到图像分析的关键步骤,一般是按照一定的规则,把图像分成互不重叠的若干区域或子集,并提取出感兴趣目标的技术和过程。本实验主要研究利用边缘提取算子的分割方法。
和本实验有关的几个常用Matlab函数:
(1) imnoise:用于对图像生成模拟噪声,如:
j=imnoise(i,'gaussian',0,0.02) %在图像i上叠加均值为0、方差为0.02的高斯噪声,得到含噪图像j
j=imnoise(i,'salt & pepper',0.04) %在图像i上叠加密度为0.04的椒盐噪声,得到含噪图像j
(2) fspecial:用于产生预定义滤波器,如:
h=fspecial('average',3); %产生3×3模板的均值滤波器
h=fspecial('sobel'); %产生sobel水平边缘增强的滤波器
可选项还有:'gaussian'高斯低通滤波器、'laplacian'拉普拉斯滤波器、'log'高斯拉普拉斯滤波器等
(3) imfilter、filter2、conv2:均是基于卷积的图像滤波函数,都可用于图像滤波,用法类似,如:
i=imread('p1.tif');
h=fspecial('prewitt'); %产生prewitt算子的水平方向模板
j1=imfilter(i,h); %或者j2=filter2(h,i); 或者j3=conv2(i,h);
(4) medfilt2:用于图像的中值滤波,如:
j=medfilt2(i, [M N]); %对矩阵i进行二维中值滤波,邻域为M×N,缺省值为3×3
三、实验内容
1、图像平滑(去噪):编写超限像素平滑法,灰度最相近的K个邻点平均法(函数名称可以自定义),并对上面实验1选择的噪声图片进行处理,显示处理前后的各个图像,分析不同方法对不同噪声的处理效果及其优缺点。
2、图像锐化:编写梯度锐化算法函数my_grad(method,T);参数method可以是梯度算子、Roberts算子、Prewitt和Sobel算子,T是梯度阈值,根据参数method和T选用不同的算子和阈值进行锐化处理,用二值图像表示处理结果,小于T的用黑色表示,大于等于T的用白色表示。显示处理前后的各个图像,分析不同方法对锐化效果及其优缺点。
四、实验图像
原图为4K图片,上传不上来只能放个截图了
4K!也就是为什么我会在接下来的代码里进行GPU加速运算,CPU跑到天荒地老了
图像在空间域的平滑和锐化本质都是对每一个像素进行单独的运算和变化,所以可以采用GPU加速。
GPU加速的原理其实是为每一个像素单独分配一个线程进行运算(这多快),亲测速度比CPU提高一百倍以上
四、实验代码与结果
平滑:
import math
import time
import cv2 as cv
import numba.cuda.libdevice
import numpy as np
import random
import matplotlib.pyplot as plt
from numba import cuda
import argparse
parser = argparse.ArgumentParser(description="")
parser.add_argument("--T", default=20, type=int, help="超限像素平滑法的阈值")
parser.add_argument("--file", default="lena.png", type=str)
args = parser.parse_args()
def sp_noise(image, prob):
'''
添加椒盐噪声
image:原始图片
prob:噪声比例
'''
output = np.zeros(image.shape, np.uint8)
noise_out = np.zeros(image.shape, np.uint8)
thres = 1 - prob
for i in range(image.shape[0]):
for j in range(image.shape[1]):
rdn = random.random() # 随机生成0-1之间的数字
if rdn < prob: # 如果生成的随机数小于噪声比例则将该像素点添加黑点,即椒噪声
output[i][j] = 0
noise_out[i][j] = 0
elif rdn > thres: # 如果生成的随机数大于(1-噪声比例)则将该像素点添加白点,即盐噪声
output[i][j] = 255
noise_out[i][j] = 255
else:
output[i][j] = image[i][j] # 其他情况像素点不变
noise_out[i][j] = 100
result = [noise_out, output] # 返回椒盐噪声和加噪图像
return result
def gauss_noise(image, mean=0, var=0.001):
'''
添加高斯噪声
image:原始图像
mean : 均值
var : 方差,越大,噪声越大
'''
image = np.array(image / 255, dtype=float) # 将原始图像的像素值进行归一化,除以255使得像素值在0-1之间
noise = np.random.normal(mean, var ** 0.5, image.shape) # 创建一个均值为mean,方差为var呈高斯分布的图像矩阵
out = image + noise # 将噪声和原始图像进行相加得到加噪后的图像
if out.min() < 0:
low_clip = -1.
else:
low_clip = 0.
out = np.clip(out, low_clip, 1.0) # clip函数将元素的大小限制在了low_clip和1之间了,小于的用low_clip代替,大于1的用1代替
out = np.uint8(out * 255) # 解除归一化,乘以255将加噪后的图像的像素值恢复
noise = noise * 255
return [noise, out]
@cuda.jit()
def ultra_smooth(image, output, new, tmp, height, width, T):
i = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.x
j = cuda.blockIdx.y * cuda.blockDim.y + cuda.threadIdx.y
if i < height and j < width:
for k in range(i - 2, i + 3):
for m in range(j - 2, j + 3):
tmp[i, j] += image[k, m]
new[i, j] = round(tmp[i, j] / 25)
if numba.cuda.libdevice.abs(new[i, j] - image[i, j]) > T:
output[i, j] = new[i, j]
else:
output[i, j] = image[i, j]
@cuda.jit()
def nearest_k_means(image, output, v1, v2, height, width):
i = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.x
j = cuda.blockIdx.y * cuda.blockDim.y + cuda.threadIdx.y
if i < height and j < width:
v1[i, j, 0] = numba.cuda.libdevice.abs(image[i, j] - image[i - 1, j - 1])
v1[i, j, 1] = numba.cuda.libdevice.abs(image[i, j] - image[i - 1, j])
v1[i, j, 2] = numba.cuda.libdevice.abs(image[i, j] - image[i - 1, j + 1])
v1[i, j, 3] = numba.cuda.libdevice.abs(image[i, j] - image[i, j - 1])
v1[i, j, 4] = numba.cuda.libdevice.abs(image[i, j] - image[i, j])
v1[i, j, 5] = numba.cuda.libdevice.abs(image[i, j] - image[i, j + 1])
v1[i, j, 6] = numba.cuda.libdevice.abs(image[i, j] - image[i + 1, j - 1])
v1[i, j, 7] = numba.cuda.libdevice.abs(image[i, j] - image[i + 1, j])
v1[i, j, 8] = numba.cuda.libdevice.abs(image[i, j] - image[i + 1, j + 1])
v2[i, j, 0] = image[i - 1, j - 1]
v2[i, j, 1] = image[i - 1, j]
v2[i, j, 2] = image[i - 1, j + 1]
v2[i, j, 3] = image[i, j - 1]
v2[i, j, 4] = image[i, j]
v2[i, j, 5] = image[i, j + 1]
v2[i, j, 6] = image[i + 1, j - 1]
v2[i, j, 7] = image[i + 1, j]
v2[i, j, 8] = image[i + 1, j + 1]
for b in range(8):
for c in range(8 - b):
if v1[i, j, c] > v1[i, j, c + 1]:
temp = v1[i, j, c]
v1[i, j, c] = v1[i, j, c + 1]
v1[i, j, c + 1] = temp
temp1 = v2[i, j, c]
v2[i, j, c] = v2[i, j, c + 1]
v2[i, j, c + 1] = temp1
sum = 0
for a in range(5):
sum += v2[i, j, a]
output[i, j] = round(sum / 5)
if __name__ == "__main__":
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 显示中文
origin = cv.imread(args.file, 0)
plt.figure(dpi=200)
plt.subplot(331)
plt.imshow(origin, cmap="gray")
plt.axis(False)
plt.title("原始图像", fontsize=5)
threadsperblock = (32, 32)
blockspergrid_x = int(math.ceil(origin.shape[0] / threadsperblock[0]))
blockspergrid_y = int(math.ceil(origin.shape[1] / threadsperblock[1]))
blockspergrid = (blockspergrid_x, blockspergrid_y)
img_sp = sp_noise(origin, 0.04)[1]
plt.subplot(334)
plt.imshow(img_sp, cmap="gray")
plt.axis(False)
plt.title("椒盐噪声", fontsize=5)
img_gauss = gauss_noise(origin, 0, 0.02)[1]
plt.subplot(337)
plt.imshow(img_gauss, cmap="gray")
plt.axis(False)
plt.title("高斯噪声", fontsize=5)
cuda_sp = cuda.to_device(np.array(img_sp))
cuda_gauss = cuda.to_device(np.array(img_gauss))
tmp = np.zeros((origin.shape[0], origin.shape[1]))
new = cuda.to_device(img_sp.copy())
cuda.synchronize()
img_new = cuda.to_device(origin.copy())
ultra_smooth[blockspergrid, threadsperblock](cuda_sp, img_new, new, cuda.to_device(tmp), origin.shape[0],
origin.shape[1], args.T)
img_new = img_new.copy_to_host()
plt.subplot(335)
plt.imshow(img_new, cmap="gray")
plt.axis(False)
plt.title("超限像素平滑法", fontsize=5)
cuda.synchronize()
img_new = cuda.to_device(img_new)
ultra_smooth[blockspergrid, threadsperblock](cuda_gauss, img_new, new, cuda.to_device(tmp), origin.shape[0],
origin.shape[1], args.T)
img_new = img_new.copy_to_host()
plt.subplot(338)
plt.imshow(img_new, cmap="gray")
plt.axis(False)
plt.title("超限像素平滑法", fontsize=5)
v1 = np.zeros((img_sp.shape[0], img_sp.shape[1], 9))
v2 = np.zeros((img_sp.shape[0], img_sp.shape[1], 9))
cuda.synchronize()
start = time.time()
img_new = cuda.to_device(img_new)
nearest_k_means[blockspergrid, threadsperblock](cuda_sp, img_new, cuda.to_device(v1), cuda.to_device(v2),
img_sp.shape[0], img_sp.shape[1])
img_new = img_new.copy_to_host()
plt.subplot(336)
plt.imshow(img_new, cmap="gray")
plt.axis(False)
plt.title("K个邻点平均法", fontsize=5)
cuda.synchronize()
img_new = cuda.to_device(img_new)
nearest_k_means[blockspergrid, threadsperblock](cuda_gauss, img_new, cuda.to_device(v1), cuda.to_device(v2),
img_sp.shape[0], img_sp.shape[1])
print(time.time() - start)
img_new = img_new.copy_to_host()
plt.subplot(339)
plt.imshow(img_new, cmap="gray")
plt.axis(False)
plt.title("K个邻点平均法", fontsize=5)
plt.show()
实验结果:
python Exp3-1.py --file 22_Cage2_4k.jpg --T 20
好吧我承认好像对于4K图像这点噪声配置好像不明显
换个图像
4K用时2.56s,256*256用时0.75s,不得不说GPU真滴快
锐化:
import math
import numba.cuda.libdevice
import numpy as np
from numba import cuda
import cv2 as cv
import argparse
import matplotlib.pyplot as plt
parser = argparse.ArgumentParser("")
parser.add_argument("--T", default=20, type=int, help="阈值")
parser.add_argument("--file", default="lena.png", type=str, help="file path")
parser.add_argument("--mode", default=1, type=int, help="算子: 1:Grid\t2:Robert\t3:Prewitt\t4:Sobel")
args = parser.parse_args()
@cuda.jit()
def my_grad(image, output, method, grid, T, height, width):
i = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.x
j = cuda.blockIdx.y * cuda.blockDim.y + cuda.threadIdx.y
if i < height and j < width:
# 梯度算子
if method == 1:
f_y = numba.cuda.libdevice.abs(image[i, j + 1] - image[i, j])
f_x = numba.cuda.libdevice.abs(image[i + 1, j] - image[i, j])
grid[i, j] = f_x + f_y
if grid[i, j] >= T:
output[i, j] = 255
else:
output[i, j] = 0
# Robert
if method == 2:
f_x = numba.cuda.libdevice.abs(image[i + 1, j + 1] - image[i, j])
f_y = numba.cuda.libdevice.abs(image[i + 1, j] - image[i, j + 1])
grid[i, j] = f_x + f_y
if grid[i, j] >= T:
output[i, j] = 255
else:
output[i, j] = 0
# Prewitt
if method == 3:
f_x = numba.cuda.libdevice.abs(
image[i, j + 2] - image[i, j] + image[i + 1, j + 2] - image[i + 1, j] + image[i + 2, j + 2] - image[
i + 2, j])
f_y = numba.cuda.libdevice.abs(
image[i + 2, j] - image[i, j] + image[i + 2, j + 1] - image[i, j + 1] + image[i + 2, j + 2] - image[
i, j + 2])
grid[i, j] = f_x + f_y
if grid[i, j] >= T:
output[i, j] = 255
else:
output[i, j] = 0
if method == 4:
f_x = numba.cuda.libdevice.abs(
image[i, j + 2] - image[i, j] + 2*(image[i + 1, j + 2] - image[i + 1, j]) + image[i + 2, j + 2] - image[
i + 2, j])
f_y = numba.cuda.libdevice.abs(
image[i + 2, j] - image[i, j] + 2*(image[i + 2, j + 1] - image[i, j + 1]) + image[i + 2, j + 2] - image[
i, j + 2])
grid[i, j] = f_x + f_y
if grid[i, j] >= T:
output[i, j] = 255
else:
output[i, j] = 0
if __name__ == "__main__":
origin = cv.imread(args.file, 0)
output = cuda.to_device(origin.copy())
grid = np.zeros((origin.shape[0], origin.shape[1]))
threadsperblock = (32, 32)
blockspergrid_x = int(math.ceil(origin.shape[0] / threadsperblock[0]))
blockspergrid_y = int(math.ceil(origin.shape[1] / threadsperblock[1]))
blockspergrid = (blockspergrid_x, blockspergrid_y)
cuda.synchronize()
my_grad[blockspergrid, threadsperblock](cuda.to_device(origin), output, args.mode, cuda.to_device(grid), args.T,
origin.shape[0],
origin.shape[1])
plt.figure(dpi=500)
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 显示中文
if args.mode==1:
plt.title("梯度算子")
if args.mode==2:
plt.title("Robert算子")
if args.mode==3:
plt.title("Prewitt算子")
if args.mode==4:
plt.title("Sobel算子")
plt.imshow(output.copy_to_host(), cmap="gray")
plt.axis(False)
plt.show()
T=50
T=50
T=80
T=80