← 返回首页
绘制图像轮廓
发表时间:2025-02-22 15:51:45
绘制图像轮廓

轮廓是图像中具有相同颜色或强度的连续点组成的曲线。轮廓通常是闭合的,用来表示一个物体的完整边界。简单来说,轮廓就是物体的“外框”或者“边界线”。

1.图像轮廓

在图像处理中,轮廓也差不多是这个意思。它是一个物体或者形状的边缘线,用来区分物体和背景,或者物体之间的边界。简单来说,轮廓就是物体的“外框”或者“边界线”。 例如: 1. 一张照片里有一只猫:猫的身体边缘就是轮廓,它把猫和周围的背景分开了。 2. 一幅画里有一个三角形:三角形的三条边就是它的轮廓。 3. 一张黑白照片里有一些文字:文字的边缘就是轮廓,它把文字和空白的背景区分开。

在计算机视觉和图像处理中,我们通过一些算法(比如OpenCV中的findContours函数)来自动找到这些轮廓,然后可以用它来识别物体的形状、大小,或者对物体进行分析和处理。

图像轮廓和图像边缘是图像处理中两个非常重要的概念,它们虽然密切相关,但并不完全相同。下面用通俗的方式来解释它们的区别:

图像边缘(Edge)

图像轮廓(Contour)

二者的区别与联系

概念上的区别: - 边缘是图像中亮度或颜色变化的地方,通常是单个像素级别的特征。 - 轮廓是由多个边缘点组成的闭合曲线,表示一个物体的完整边界。

直观上的区别: - 边缘更像是“线条”,可能不完整,也可能不闭合。 - 轮廓更像是“框”,是一个完整的边界,通常可以用来表示一个物体的形状。

用途上的区别: - 边缘检测主要用于提取图像中的纹理、形状变化等信息,常用于图像分割、特征提取等。 - 轮廓检测主要用于识别物体的形状、计算物体的面积、周长等,常用于目标检测和形状分析。

简单类比:边缘就像是一幅画中的“线条”,用来勾勒形状。轮廓就像是一幅画中的“框”,用来完整地表示一个物体的边界。

实际例子:假设你有一张照片,照片里有一个杯子:边缘可能是杯子的把手、杯口、杯身和桌面之间的分界线。这些是亮度变化的地方。轮廓则是杯子的外边界,是一个完整的曲线,用来区分杯子和背景。

2.OpenCV绘制图像轮廓

在OpenCV中使用Python处理图像轮廓,通常涉及以下几个步骤:读取图像、转换为灰度图、应用边缘检测、寻找轮廓,最后绘制轮廓。下面是一个完整的例子,展示了如何使用OpenCV库在Python中检测图像的轮廓。

import cv2
import numpy as np

# 1. 读取图像
image = cv2.imread('./images/jpg/apples01.jpg')  # 替换为你的图像路径
if image is None:
    print("无法加载图像,请检查路径!")
    exit()

# 2. 转换为灰度图像
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 3. 应用高斯模糊(可选,用于减少噪声)
blurred_image = cv2.GaussianBlur(gray_image, (5, 5), 0)

# 4. 二值化处理
_, binary_image = cv2.threshold(blurred_image, 20, 255, cv2.THRESH_BINARY)

# 5. 查找轮廓
# 使用cv2.RETR_EXTERNAL提取最外层轮廓,cv2.CHAIN_APPROX_SIMPLE压缩轮廓点
contours, hierarchy = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 6. 绘制轮廓
# 参数说明:
# - image: 原始图像
# - contours: 轮廓列表
# - -1: 绘制所有轮廓
# - (0, 255, 0): 轮廓颜色(BGR格式,这里是绿色)
# - 2: 轮廓线条粗细
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)

# 7. 显示结果
cv2.imshow('Original Image', image)
cv2.imshow('Gray Image', gray_image)
cv2.imshow('Binary Image', binary_image)
cv2.imshow('Contours', image)

# 8. 等待用户按键并关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

轮廓极点实例

import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont

font = cv2.FONT_HERSHEY_SIMPLEX


def putText_Chinese(img, text, pt, textColor=(0, 255, 0), textSize=20):
    left, top = pt[0], pt[1]
    if (isinstance(img, np.ndarray)):
        # 判断是否OpenCV图片类型
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(img)
        fontText = ImageFont.truetype("simhei.ttf", textSize, encoding="utf-8")
        draw.text((left, top), text, textColor, font=fontText)
        return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)


img = cv2.imread('./images/png/china.png')
cv2.imshow('src', img)

temp = img.copy()

gray = cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY)

ret, thresh = cv2.threshold(gray, 130, 250, cv2.THRESH_BINARY)
cv2.imshow("thres", thresh)

contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

for cnt in contours:
    (x, y, w, h) = cv2.boundingRect(cnt)
    if (w > 300 and h > 300):
        left_most = tuple(cnt[cnt[:, :, 0].argmin()][0])
        right_most = tuple(cnt[cnt[:, :, 0].argmax()][0])
        top_most = tuple(cnt[cnt[:, :, 1].argmin()][0])
        bottom_most = tuple(cnt[cnt[:, :, 1].argmax()][0])
        cv2.drawContours(img, cnt, -1, (255, 0, 0), 2)
        cv2.circle(img, left_most, 5, (0, 255, 0), -1, cv2.LINE_AA)
        cv2.circle(img, right_most, 5, (0, 255, 0), -1, cv2.LINE_AA)
        cv2.circle(img, top_most, 5, (0, 255, 0), -1, cv2.LINE_AA)
        cv2.circle(img, bottom_most, 5, (0, 255, 0), -1, cv2.LINE_AA)

        img = putText_Chinese(img, "西", left_most, (0, 255, 255), 30)
        img = putText_Chinese(img, "东", right_most, (0, 255, 255), 30)
        img = putText_Chinese(img, "北", top_most, (0, 255, 255), 30)
        img = putText_Chinese(img, "南", bottom_most, (0, 255, 255), 30)
        img = putText_Chinese(img, "中 国", (int(x + w / 2), int(y + h / 2)), (255, 255, 0), 70)

cv2.imshow('contours', img)
cv2.imwrite('result.bmp', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

原图如下:

阈值处理分割出地图轮廓如下:

计算对应轮廓的极点坐标并标注,效果如下: