opencv — 颜色和轮廓 的 提取&筛选

注:本文使用的编程语言是python。
如果读者使用的是C++,有些代码可能需要自行变更!

前言

初学opencv的阶段,难免是从枯燥地啃文档和记函数开始。逐一而草率地”过”一遍函数用法,对于初学者而言,其实很难起到学习的进步。只有在具体的实例中,才能更好地理解函数用法和搭配 的 妙用。
笔者在视觉库cvzone和halcon的启发下,总结了些opencv实现的颜色和轮廓的提取&筛选方法,能够方便地应用在不同的项目之上。
如果读友是小白,在这里也推荐个B站上的油管搬运教程:opencv超实用实战项目,因为是手把手敲代码的,对初学者非常友好。还有,视频原作者是 巴基斯坦 的 CV工程师 Murtaza。这边附上他油管的主页 Murtaza’s Workshop,感兴趣的朋友可以看看。
言归正传,我们开始吧!

Part1. 颜色提取&筛选

颜色提取&筛选 是最直观的图像处理方式,简单粗暴但不失为有效。
其主要步骤如下:

  • 1.将原图像由 RGB模型转为 HSV模型
    (因为HSV模型有专门的 H色调 通道,更方便颜色的提取s)
  • 2.确定 目标提取颜色 HSV范围 (可以不止一个)
  • 3.使用inRange()函数,获取图像掩膜
  • 4.使用图像位操作,将掩膜进行合并
  • 5.用掩膜覆盖原图像,使其仅保留预期的部分

针对步骤2,这里给出常用的HSV范围:

颜色HSV 范围起始范围结束范围全部(0, 0, 0) (180, 255, 255)黑色(0, 0, 0) (180, 255, 46)灰色(0, 0, 46) (180, 43, 220)白色(0, 0, 211) (180, 30, 255)红色(0, 43, 46) (10, 255, 255) (156, 43, 46) (180, 255, 255)橙色(11, 43, 46) (25, 255, 255)黄色(26, 43, 46) (34, 255, 255)绿色(35, 43, 46) (77, 255, 255)青色(78, 43, 46) (99, 255, 255)蓝色(100, 43, 46) (124, 255, 255)紫色(125, 43, 46) (155, 255, 255) 从表中不难看出,区分各颜色的主要参数,就在于H通道的取值。表内颜色的取值边界比较宽泛模糊,如果有更精准的提取需求,建议还是手动调参。

接下来,就是代码部分。为了使模块更通用,笔者是把功能写成python类进行封装。调用类实例化的对象,以实现颜色提取的功能。主要是在魔术方法__call__()内实现。

import cv2
import numpy as np

def setMask(src: np.ndarray, mask: np.ndarray) -> np.ndarray:
    channels = cv2.split(src)

    result = []
    for i in range(len(channels)):
        result.append(cv2.bitwise_and(channels[i], mask))

    dest = cv2.merge(result)

    return dest

class ColorFilter(object):
    def __init__(self):
        self.colorRange = []

    def __call__(self, src: np.ndarray) -> np.ndarray:

        finalMask = np.zeros_like(src)[:, :, 0]

        hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)

        for each in self.colorRange:

            lower, upper = each

            mask = cv2.inRange(hsv, lower, upper)

            finalMask = cv2.bitwise_or(finalMask, mask)

        dest = setMask(src, finalMask)

        return dest

if __name__ == '__main__':
    src = cv2.imread(r'xxx.png')

    colorFilter = ColorFilter()
    colorFilter.colorRange = [
        ((xxx,xxx,xxx),(xxx,xxx,xxx)),
        ((xxx,xxx,xxx),(xxx,xxx,xxx))
    ]
    dest = colorFilter(src)
    cv2.imshow('dest', dest)
    cv2.waitKey(0)

笔者在这里以opencv的logo为例,对红、蓝色的区域进行提取。如下是效果图:

opencv -- 颜色和轮廓 的 提取&筛选
对于其他复杂的情形,可以修改上述代码中,对掩膜的图像位操作,以实现复杂的区域的合并、剪切、取反等效果。

Part2. 轮廓提取&筛选

相比较颜色提取,轮廓提取对于实际应用的普适性更好,也相应得更复杂。
其主要步骤如下:

  • 1.彩色图转灰度图,二值化处理。
    (如果图像噪声多,可适当使用滤波器)
  • 2.使用findContours()函数,提取轮廓
  • 3.使用arcLength(),contourArea()函数获取 轮廓周长和面积
    (也可以引入其他特征量)
  • 4.筛选轮廓,过滤掉无效轮廓
  • 5.进行 绘制轮廓/制作掩膜 等操作

针对步骤3,这里给出halcon里常用的区域特征量:

特征名称解释取值范围计算公式面积(Area)轮廓的面积(0,+∞)A周长(contlength)轮廓线总长(0,+∞)P紧密度(Compactness)相同周长的圆和当前轮廓的面积比[1,+∞)4PiA/P^2圆度(Circularity)当前轮廓和最小外接圆的面积比(0,1]A/(Pi*R外接^2)凸度(Convexity)当前轮廓和凸包的面积比(0,1]A/A凸矩形度(Rectangularity)当前轮廓和最小外接矩形的比值(0,1]A/A矩 以上是常用且在opencv中容易实现的特征量。相关的opencv函数见下:

  • 轮廓周长:arcLength()
  • 轮廓面积:contourArea()
  • 凸包检测:convexHull()
  • 最小外接圆:minEnclosingCircle()
  • 最小外接矩形:minAreaRect()

接下来,就是代码部分。同上,是封装在python类中实现。因为轮廓的筛选方式很多,这里实现的是简单的依据 轮廓面积和周长范围进行筛选。

import cv2
import numpy as np

class ContourFilter(object):
    def __init__(self):
        super(ContourFilter, self).__init__()

        self.threshold = lambda image: cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                                             cv2.THRESH_BINARY_INV, 15, 21)

        self.areaRanges = []

        self.perimeterRanges = []

        self.contourColor = (255, 127, 127)

        self.contourThickness = 3

        self.inPlace = False

        self.paint = True

    def __call__(self, src: np.ndarray) -> np.ndarray:

        if not self.areaRanges:
            self.areaRanges = [(0, float('inf'))]
        if not self.perimeterRanges:
            self.perimeterRanges = [(0, float('inf'))]

        if self.inPlace:
            dest = src.copy()
        else:
            dest = np.zeros_like(src)

        if len(src.shape) == 3 and src.shape[2] == 3:
            gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
        elif len(src.shape) == 2:
            gray = src

        binary = self.threshold(gray)

        contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        resultContours = []

        for contour in contours:

            perimeter = cv2.arcLength(contour, True)

            area = cv2.contourArea(contour)

            for perimeterRange, areaRange in zip(self.perimeterRanges, self.areaRanges):
                if perimeterRange[0] < perimeter  perimeterRange[1] and areaRange[0] < area  areaRange[1]:
                    resultContours.append(contour)

        if self.paint:
            cv2.drawContours(dest, resultContours, -1, self.contourColor, self.contourThickness)

        return dest

if __name__ == '__main__':
    src = cv2.imread(r'xxx.png')
    contourFilter = ContourFilter()
    contourFilter.areaRanges.append((minArea, maxArea))
    contourFilter.perimeterRanges.append((minPerimeter, maxPerimeter))

    dest = colorFilter(src)
    cv2.imshow('dest', dest)
    cv2.waitKey(0)

笔者在这里同样以opencv的logo为例,给出运行的效果图:

opencv -- 颜色和轮廓 的 提取&筛选
上述代码用例实现了轮廓的绘制,读者也可以自行更改代码,使其变体为生成掩膜、区域填充等功能。笔者在此就不加赘述。

结束语

笔者作为初涉计算机视觉领域的在校学生,技术水平有限。本文中倘若出现错误或者值得补充的地方,希望各位读者在评论中指出。衷心感谢每位看到本文的读者!

Original: https://blog.csdn.net/m0_50616665/article/details/124810344
Author: 墨鼠.Rat.
Title: opencv — 颜色和轮廓 的 提取&筛选

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/518457/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球