【Python CUDA版】河北工业大学计算机图像处理实验三:空间域平滑与锐化

本文探讨了在图像处理实验中如何使用GPU并行运算加速图像平滑和锐化处理。实验涉及MATLAB工具箱中的滤波函数、自定义算法以及CUDA实现的GPU加速。对比了不同平滑方法对椒盐噪声和高斯噪声的处理效果,以及梯度算子、Roberts、Prewitt和Sobel算子在图像锐化上的应用,分析了各种方法的优缺点。实验表明,GPU加速能显著提升处理效率。
摘要由CSDN通过智能技术生成

我是不是在河工带历史上做数字图像处理实验的时候第一个启用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、filter2conv2均是基于卷积的图像滤波函数,都可用于图像滤波,用法类似,如:

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

六、实验总结与思考

1、分析不同方法对不同噪声的处理效果及其优缺点。
**超限像素平滑法**
对椒盐噪声处理效果较好,对保护仅有微小灰度差的细节及纹理也有效对高
斯噪声处理效果不明显。与领域平均法相比,处理后的图像较为清晰。随着领域
的增大,该算法去噪能力增强,但是模糊程度也加大。
**灰度最相近的 K 个邻点平均法**
较小的 K 值使噪声方差下降较小,但保持细节效果较好,而较大的 K 值平滑
噪声较好,但会使图像边缘模糊。
2 、分析不同方法对锐化效果及其优缺点
**Grad 算子**
对于一副图像的突出边缘区,其梯度值较大;对于平滑区,梯度值较小;对
于灰度级为常数的区域,梯度为零。
**Roberts 算子**
在边缘检测的效果中由于没有经过图像平滑处理的,因此图像噪声一般得不
到很好的抑制,也因此影响到计算定位时容易丢失一部分的边缘。但是
Roberts 算子的定位精度较高,对具有陡峭的低噪声图像响应较好。
**Sobel 算子**
邻域的像素对当前像素产生的影响不是等价的,所以距离不同的像素具有不
同的权值检测方法对灰度渐变和噪声较多的图像处理效果较好。sobel 算子对边
缘定位不是很准确,图像的边缘不止一个像素;当对精度要求不是很高时,是一
种较为常用的边缘检测方法。
**Prewitt 算子**
模板对噪声有抑制作用,抑制噪声的原理是通过像素平均。但是像素平均相
当于对图像的低通滤波,并且只能对水平和垂直方向的边缘进行检测,无法检测
斜侧方向,使边缘检测不完整。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ace2NoU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值