【Python CUDA版】河北工业大学计算机图像处理实验五:图像分割

一、实验目的与要求

1.了解图像分割的意义和常用方法

2.掌握常用的图像分割方法

二、实验相关知识

图像分割就是把图像分成互不重叠的区域并提取出感兴趣目标的技术,是由图像处理到图像分析的关键步骤。

和本实验有关的常用Matlab函数:

edge:检测灰度或二值图像的边缘,返回一个二值图像,1像素是检测到的边缘,0像素是非边缘

用法:BW=edge(I,'sobel',thresh,direction); %I为检测对象;

边缘检测算子可用sobel、roberts、prewitt、zerocross、log、canny;

thresh指定阈值,检测时忽略所有小于阈值的边缘,默认自动选择阈值;

direction指定方向,可选项有horizontal、vertical或both,在指定的方向上用算子进行边缘检测

三、实验内容

1、分别用Roberts、Prewitt、Sobel三种边缘检测算子,对图像wire.bmp进行水平、垂直及各个方向的边界检测,并将检测结果转化为白底黑线条的方式显示出来;

2、使用手动阈值分割法对bottle图像进行分割,显示分割结果,并分析其优缺点。

3、对下图A进行处理,得到类似图B的样子;

图A 

 图B

 

四、实验代码及结果

1、边界检测

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import argparse

parser = argparse.ArgumentParser("")
parser.add_argument("--method", type=int, default=1, choices=[1, 2, 3], help="1:Sobel\t2:Prewitt\t3:Roberts")
parser.add_argument("--file", type=str, default="img.png")
args = parser.parse_args()

if __name__ == "__main__":
    plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
    plt.subplot(221)
    origin = cv.imread(args.file, 0)
    plt.imshow(origin, cmap="gray")
    plt.title("原图像")
    plt.axis(False)
    if args.method == 1:
        # OpenCV文档中对Sobel算子的介绍:“in the case of 8-bit input images it will result in truncated derivatives”。
        # 即Sobel函数求完导数后会有负值,还有会大于255的值,所以使用16位有符号数表示梯度
        # 然后用 cv.convertScaleAbs转回uint8(8位无符号数)形式

        # 索贝尔算子
        # 边界类型选择REPLICATE,与MATLAB edge函数文档中默认方式一致
        x = cv.Sobel(origin, cv.CV_16S, 1, 0, borderType=cv.BORDER_REPLICATE)
        y = cv.Sobel(origin, cv.CV_16S, 0, 1, borderType=cv.BORDER_REPLICATE)
        absX = cv.convertScaleAbs(x)
        ret, Sobel_X = cv.threshold(absX, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
        plt.subplot(222)
        plt.imshow(255 - Sobel_X, cmap="gray")
        plt.title("垂直方向检测边缘")
        plt.axis(False)

        absY = cv.convertScaleAbs(y)
        ret, Sobel_Y = cv.threshold(absY, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
        plt.subplot(223)
        plt.imshow(255 - Sobel_Y, cmap="gray")
        plt.title("水平方向检测边缘")
        plt.axis(False)

        Sobel = cv.addWeighted(Sobel_X, 0.5, Sobel_Y, 0.5, 0)
        plt.subplot(224)
        plt.imshow(255 - Sobel, cmap="gray")
        plt.axis(False)
        plt.suptitle("Sobel算子", fontsize=20)
        plt.show()

    if args.method == 2:
        # prewitt算子
        # 自定义x, y方向卷积核,然后通过卷积实现
        prewitt_kernel_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
        prewitt_kernel_y = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]], dtype=int)

        x = cv.filter2D(origin, cv.CV_16S, prewitt_kernel_x, borderType=cv.BORDER_REPLICATE)
        y = cv.filter2D(origin, cv.CV_16S, prewitt_kernel_y, borderType=cv.BORDER_REPLICATE)

        absX = cv.convertScaleAbs(x)
        ret, Prewitt_X = cv.threshold(absX, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
        plt.subplot(222)
        plt.imshow(255 - Prewitt_X, cmap="gray")
        plt.title("垂直方向检测边缘")
        plt.axis(False)

        absY = cv.convertScaleAbs(y)
        ret, Prewitt_Y = cv.threshold(absY, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
        plt.subplot(223)
        plt.imshow(255 - Prewitt_Y, cmap="gray")
        plt.title("水平方向检测边缘")
        plt.axis(False)

        Prewitt = cv.addWeighted(absX, 0.5, absY, 0.5, 0)
        plt.subplot(224)
        plt.imshow(255 - Prewitt, cmap="gray")
        plt.axis(False)
        plt.suptitle("Prewitt算子", fontsize=20)
        plt.show()

    if args.method == 3:
        # matlab edge方法中的Roberts算子实际上有除以2,[[-1, 0], [0, 1]]/2,[[0, -1], [1, 0]]/2
        # 但是在这里加上/2效果依然比MATLAB函数要好
        roberts_kernel_x = np.array([[-1, 0], [0, 1]], dtype=int)
        roberts_kernel_y = np.array([[0, -1], [1, 0]], dtype=int)
        x = cv.filter2D(origin, cv.CV_16S, roberts_kernel_x)
        y = cv.filter2D(origin, cv.CV_16S, roberts_kernel_y)

        absX = cv.convertScaleAbs(x)
        ret, Roberts_X = cv.threshold(absX, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
        plt.subplot(222)
        plt.imshow(255 - Roberts_X, cmap="gray")
        plt.title("垂直方向检测边缘")
        plt.axis(False)

        absY = cv.convertScaleAbs(y)
        ret, Roberts_Y = cv.threshold(absY, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
        plt.subplot(223)
        plt.imshow(255 - Roberts_Y, cmap="gray")
        plt.title("水平方向检测边缘")
        plt.axis(False)

        Roberts = cv.addWeighted(absX, 0.5, absY, 0.5, 0)
        plt.subplot(224)
        plt.imshow(255 - Roberts, "gray")
        plt.title("Roberts算子")
        plt.axis(False)
        plt.show()

实验结果:

 

 

 

 2、阈值分割

import math

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
from numba import cuda


@cuda.jit()
def segment(img, height, width, low_ind):
    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 img[i, j] > low_ind:
            img[i, j] = 255
        else:
            img[i, j] = 0


if __name__ == "__main__":
    plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
    img = cv.imread("bottle.png", 0)
    plt.subplot(131)
    plt.imshow(img, cmap="gray")
    plt.title("原图像")
    plt.axis(False)
    # 使用cv库函数统计灰度直方图
    greyScale_map = cv.calcHist([img], [0], None, [256], [0, 256])
    greyScale_map = np.array(greyScale_map).reshape(256)
    plt.subplot(132)
    plt.bar(range(256), greyScale_map, width=1)
    plt.title("直方图")

    # 将灰度直方图拟合为3次曲线
    z = np.polyfit(range(256), greyScale_map, 5)
    y = np.poly1d(z)(range(256))
    # print(y)
    plt.plot(range(256), y, color="red")
    # 找到极小值点,即波谷
    low_ind = signal.argrelextrema(y, np.less)[0]
    print(low_ind)
    # 进行阈值分割
    threadsperblock = (32, 32)
    blockspergrid_x = int(math.ceil(img.shape[0] / threadsperblock[0]))
    blockspergrid_y = int(math.ceil(img.shape[1] / threadsperblock[1]))
    blockspergrid = (blockspergrid_x, blockspergrid_y)
    cuda.synchronize()
    cuda_img = cuda.to_device(img)
    segment[blockspergrid, threadsperblock](cuda_img, img.shape[0], img.shape[1], low_ind[0])
    plt.subplot(133)
    plt.imshow(cuda_img.copy_to_host(), cmap="gray")
    plt.axis(False)
    plt.title("阈值分割后")

    plt.show()

灰度图像中画面比较简单且对象物的灰度分布比较有规律,背景和对象物 在图像的灰度直方图上各形成一个波峰,由于每两个波峰间形成一个低谷,因而选择双峰间低谷处所对应的灰度值为阈值,可将两个区域分离。
因为图像较为简单,这里选择用三次曲线拟合直方图,极小值点为阈值。
【优缺点分析】
优点:
(1)操作简单方便。
(2)在物体与背景有较强的对比度的图像中,此种方法应用特别有效。比如说物体内部灰度分布均匀一致,背景在另一个灰度级上也分布均匀,这时利用阈值可以将目标与背景分割得很好。如果目标和背景的差别是某些其他特征而不是灰 度特征时,那么先将这些特征差别转化为灰度差别,然后再应用阈值分割方法进行处理,这样使用阈值分割技术也可能是有效的 。
缺点:
(1)不适合处理峰值较多,灰度级分布较密的图像。
(2)无法获取最优阈值,因此无法获得最佳结果。

3、

import math

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
from numba import cuda
from scipy import signal


@cuda.jit()
def segment(img, height, width, low_ind):
    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 img[i, j] > low_ind:
            img[i, j] = 255
        else:
            img[i, j] = 0


if __name__ == "__main__":
    plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
    img = cv.imread("particle.png", 0)
    plt.subplot(221)
    plt.imshow(img, cmap="gray")
    plt.axis(False)
    plt.title("原图像")
    greyScale_map = cv.calcHist([img], [0], None, [256], [0, 256])
    greyScale_map = np.array(greyScale_map).reshape(256)
    plt.subplot(222)
    plt.bar(range(256), greyScale_map, width=1)
    plt.title("直方图")

    # 阈值分割
    threadsperblock = (32, 32)
    blockspergrid_x = int(math.ceil(img.shape[0] / threadsperblock[0]))
    blockspergrid_y = int(math.ceil(img.shape[1] / threadsperblock[1]))
    blockspergrid = (blockspergrid_x, blockspergrid_y)
    cuda.synchronize()
    cuda_img = cuda.to_device(img)
    segment[blockspergrid, threadsperblock](cuda_img, img.shape[0], img.shape[1], 110)
    new_image = cuda_img.copy_to_host()
    del cuda_img

    plt.subplot(223)
    plt.imshow(new_image, cmap="gray")
    plt.axis(False)
    plt.title("阈值分割后")

    # Sobel算子进行边缘检测
    x = cv.Sobel(new_image, cv.CV_16S, 1, 0, borderType=cv.BORDER_REPLICATE)
    y = cv.Sobel(new_image, cv.CV_16S, 0, 1, borderType=cv.BORDER_REPLICATE)
    absX = cv.convertScaleAbs(x)
    absY = cv.convertScaleAbs(y)

    # 二值化
    ret, Sobel_X = cv.threshold(absX, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    ret, Sobel_Y = cv.threshold(absY, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)

    Sobel = cv.addWeighted(Sobel_X, 0.5, Sobel_Y, 0.5, 0)

    plt.subplot(224)
    plt.imshow(255 - Sobel, cmap="gray")
    plt.axis(False)
    plt.title("Sobel算子边缘检测")
    plt.show()

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ace2NoU

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

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

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

打赏作者

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

抵扣说明:

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

余额充值