图像处理-007形态变换(二)

本文介绍了图像处理中的形态学变换,重点讲解了Hit-and-Miss变换及其在寻找图像特征,如角点检测中的应用。同时,阐述了细化操作,包括细化原理和OpenCV中的实现,用于减少图像线条宽度至单位像素,保留图像骨架。通过代码示例展示了C++和Python两种实现方式,并给出了细化效果。
摘要由CSDN通过智能技术生成

图像处理-007形态变换(二)

Hit-and-Miss Transform(击中击不中)

Hit-and-Miss从二值图像中查找前景色/背景色的特定模式非常有用,与腐蚀,膨胀使用的kernel仅存在0,1不同,hit-and-Misss的kernel中除0,1外还存在空白。腐蚀,膨胀,开操作,闭操作,形态学梯度,顶帽,黑帽运算使用的kernel中1用来表示前景色,0表示不关注, 在hit-and-miss运算中的1表示前景色,0表示背景色,空白表示不关注。

以图006-14中展示的kernel为例,该kernel可用来找到二值图像中的角点,运算时将kernel置于图像之上,锚点依次与图像中的坐标P对应,P的邻域元素依次与kernel的邻域元素比较,除去kernel中的空白元素外,其余元素完全匹配时则P标记为1,否则为0.

在这里插入图片描述

图007-1 查找图像角点的kernel

使用图007-2中的kernel找出图像中的四个角点如图007-3所示

在这里插入图片描述

图007-2 寻找图像四角点的kernel

在这里插入图片描述

图007-3 二值图像Hit-And-Miss运算结果图

实现代码: C++

/**
 * hit and miss
 * @param img
 * @return
 */
int Morphological::hit_and_miss_transform(const Mat &img) {
  logger_info("======hit_and_miss_transform=====================");
  //  kernel size为7*7
  Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(7, 7));

  Mat dst;
  morphologyEx(img, dst, MORPH_HITMISS, kernel);
  imshow("hit_and_miss", dst);
  return EXIT_SUCCESS;
}

实现代码:python

# hit and miss
def hit_and_miss_transform(origin_img):
    # 非灰度图像转化维灰度图像
    if origin_img.shape[2] == 3:
        gray_image = cv.cvtColor(origin_img, cv.COLOR_BGR2GRAY)
    else:
        gray_image = origin_img
    titles = ["origin_image"]
    images = [cv.cvtColor(gray_image, cv.COLOR_BGR2RGB)]
    for i in range(3, 13, 2):
        kernel = cv.getStructuringElement(cv.MORPH_RECT, (i, i))
        dst = cv.morphologyEx(gray_image, cv.MORPH_HITMISS, kernel)
        titles.append("hit_and_miss_kernel_" + str(i))
        images.append(cv.cvtColor(dst, cv.COLOR_BGR2RGB))
    for i in range(6):
        plt.subplot(2, 3, i + 1)
        plt.imshow(images[i], 'gray', vmin=0, vmax=255)
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    plt.show()

在这里插入图片描述

图007-4 二值图像不同kernel大小下hit-and-miss效果图

Thining(细化)

细化会删除二值图像中选定的前景(白色)像素,类似于腐蚀、开操作,删除不必要的像素点,仅保留图像的轮廓。

dst = t h i n i n g ( src , element ) = src − hit-and-miss ( src , element ) \texttt{dst} = \mathrm{thining} ( \texttt{src} , \texttt{element} )= \texttt{src} - \texttt{hit-and-miss} ( \texttt{src} , \texttt{element} ) dst=thining(src,element)=srchit-and-miss(src,element)

图像细化后依然保持图像的骨架且细小部分的连通性不变,细化将图像线条从多像素宽度减少至单位像素宽度。

opencv提供函数thinning() 用于图像的细化。其详细描述如下:

void cv::ximgproc::thinning(InputArray     src,    #输入图像 8bit单通道灰度图像
                            OutputArray     dst, #输出图像 与输入图像同类型,同大小
                            int     thinningType = THINNING_ZHANGSUEN #细化类型
                            )        
Python:
cv.ximgproc.thinning(src[, dst[, thinningType]]    )->dst

实现代码: C++

/**
 * 图像细化 THINNING_ZHANGSUEN
 * @param img
 * @return
 */
int Morphological::thinning_ZHANGSUEN_transform(const Mat &img) {
  logger_info("======hit_and_miss_transform=====================");

  Mat dst;
  ximgproc::thinning(img, dst, ximgproc::ThinningTypes::THINNING_ZHANGSUEN);
  imshow("thinning zhangsuen", dst);
  return EXIT_SUCCESS;
}

/**
 * 图像细化 THINNING_GUOHALL
 * @param img
 * @return
 */
int Morphological::thinning_GUOHALL_transform(const Mat &img) {
  logger_info("======thinning_GUOHALL_transform=====================");
  Mat dst;
  ximgproc::thinning(img, dst, ximgproc::ThinningTypes::THINNING_GUOHALL);
  imshow("thinning guohall", dst);
  return EXIT_SUCCESS;
}

实现代码: python

# thinning
def thinning_transform(origin_img):
    # 非灰度图像转化维灰度图像
    if origin_img.shape[2] == 3:
        gray_image = cv.cvtColor(origin_img, cv.COLOR_BGR2GRAY)
    else:
        gray_image = origin_img
    titles = ["origin_image"]
    # images = [cv.cvtColor(gray_image, cv.COLOR_BGR2RGB)]
    images = [gray_image]
    dst = cv.ximgproc.thinning(gray_image, cv.ximgproc.THINNING_ZHANGSUEN)
    images.append(dst)
    titles.append("thinning_zhangsuen")
    dst = cv.ximgproc.thinning(gray_image, cv.ximgproc.THINNING_GUOHALL)
    images.append(dst)
    titles.append("thinning_gauhall")
    for i in range(3):
        plt.subplot(1, 3, i + 1)
        plt.imshow(images[i], 'gray', vmin=0, vmax=255)
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    plt.show()

细化效果图如图007-5所示
在这里插入图片描述

图007-5 图像细化效果图

Thickening(粗化)

粗化是形态学的相对操作,细化前景色相当于粗化背景色。粗化操作由源图像与hit-and-miss处理后的并集组成。

dst = t h i n i n g ( src , element ) = src ∪ hit-and-miss ( src , element ) \texttt{dst} = \mathrm{thining} ( \texttt{src} , \texttt{element} )= \texttt{src} \cup \texttt{hit-and-miss} ( \texttt{src} , \texttt{element} ) dst=thining(src,element)=srchit-and-miss(src,element)

Skeletonization/Medial Axis Transform

骨架化指将二值图像的前景色(高亮部分)减少直至骨架的过程,该过程丢弃原始图像中大量的前景像素,但保留了图像的原始区域范围与连通性。

骨架化后的图像既然保留了原始图像的骨架及连通性,则在视觉处理中可大幅减少数据量的计算和对内存的占用。

实现代码: C++

/**
 * 骨架
 * @param img
 * @return
 */
int Morphological::skeleton_transform(const Mat &img) {
  logger_info("======skeleton_transform=====================");
  imshow("image", img);
  Mat dst;
//  图像阈值转化  灰度图像转二值图像
  threshold(img, dst, 127, 255, THRESH_BINARY);
// 卷积核
  Mat kernel = getStructuringElement(MORPH_CROSS, Size(7, 7));

  Mat skel(dst.size(), CV_8UC1, cv::Scalar(0));
  Mat temp(dst.size(), CV_8UC1);

  bool done;
  do {
    cv::morphologyEx(dst, temp, MORPH_OPEN, kernel);
    cv::bitwise_not(temp, temp);
    cv::bitwise_and(dst, temp, temp);
    cv::bitwise_or(skel, temp, skel);
    cv::erode(dst, dst, kernel);

    double max;
    cv::minMaxLoc(dst, 0, &max);
    done = (max == 0);
  } while (!done);

  imshow("skeleton", skel);
  return EXIT_SUCCESS;
}

实现代码:python


# 骨架化
def skeleton_transform(origin_img):
    # 非灰度图像转化维灰度图像
    if origin_img.shape[2] == 3:
        gray_image = cv.cvtColor(origin_img, cv.COLOR_BGR2GRAY)
    else:
        gray_image = origin_img
    titles = ["origin_image"]
    images = [gray_image]
    ret, binary_img = cv.threshold(gray_image, 127, 255, cv.THRESH_BINARY)
    titles.append("binary_image")
    images.append(binary_img)

    # 获取卷积核
    kernel = cv.getStructuringElement(cv.MORPH_CROSS, (7, 7))
    skeleton_img = np.zeros(binary_img.shape, np.uint8)
    # tmp_img = np.zeros(binary_img.shape, np.uint8)

    while True:
        # 开运算
        tmp_img = cv.morphologyEx(binary_img, cv.MORPH_OPEN, kernel)
        tmp_img = cv.bitwise_not(tmp_img)
        tmp_img = cv.bitwise_and(binary_img, tmp_img)
        skeleton_img = cv.bitwise_or(skeleton_img, binary_img)
        binary_img = cv.erode(binary_img, kernel)
        min, max, _, _ = cv.minMaxLoc(binary_img)
        if max == 0:
            break
    titles.append("skeleton_image")
    images.append(skeleton_img)

    for i in range(3):
        plt.subplot(1, 3, i + 1)
        plt.imshow(images[i], 'gray', vmin=0, vmax=255)
        plt.title(titles[i])
        plt.xticks(), plt.yticks([])
    plt.show()

参考文献:

  1. https://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm

  2. http://www.cs.cmu.edu/~cil/vision.html

  3. https://docs.opencv.org/4.6.0/db/d06/tutorial_hitOrMiss.html

  4. https://felix.abecassis.me/2011/09/opencv-morphological-skeleton/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值