图像处理之颜色检测分类标记(Python OpenCV实现)

最近开始接触图像处理,接到的首个任务就是将实验室用颜色标记好的数据再在原图上按不同颜色框出来,以在模型预测阶段检查预测效果。下面使用一张摇滚乐队Halestrom的图片进行说明。

首先,我拿到的原图如下图所示:

图1

我们将原始图片按照人、地板、墙三种元素进行标记,得到下图:

图2

将上述两张图片输入我们的模型,那么模型能够做到给出一张新的图片它就能够输出一张按颜色分类标记元素的图片。我们这里要做的是,将按颜色分类标记元素的区块转换为线框绘制在原始图片上,便于检测预测效果,具体需要得到的图片如下图所示:

图3

这就需要我们根据颜色分辨出图2中的区域,然后在原图上对其描框,具体实现可以参考以下代码:

"""功能:检测图像颜色按区块划分边框"""

import numpy as np
import cv2
import os


def detect_color(img_path, mark_img_path):
    """检测一张图片中的不同颜色区域"""
    image = cv2.imread(img_path)  # 加载图片
    # 定义颜色范围,这里可以根据自己的需求定义,注意这里颜色定义的顺序是BGR
    boundaries = [
        ([0, 0, 255], [0, 0, 255]),  # 红色
        ([0, 255, 0], [0, 255, 0]),  # 绿色
        ([255, 0, 0], [255, 0, 0])  # 蓝色
    ]

    # 遍历颜色范围
    for (lower, upper) in boundaries:
        # 由颜色范围创建NumPy数组
        lower = np.array(lower, dtype="uint8")
        upper = np.array(upper, dtype="uint8")

        # 根据特定颜色范围创建mask
        mask = cv2.inRange(image, lower, upper)
        output = cv2.bitwise_and(image, image, mask=mask)

        mark_zone_with_color(output, mark_img_path, lower)


def mark_zone_with_color(src_img, mark_img, mark_color):
    """根据颜色在原始图像上标记区域"""
    # 转灰度图片
    gray = cv2.cvtColor(src_img, cv2.COLOR_BGR2GRAY)

    ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)  # ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) 则只能绘制出平地轮廓

    # 轮廓检测
    _, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

    newImg = cv2.imread(mark_img)
    newImg = cv2.resize(newImg, (512, 512))
    # 画图
    for i in range(len(contours)-1):
        cv2.drawContours(image=newImg, contours=contours[i+1], contourIdx=-1, color=tuple(mark_color.tolist()), thickness=2, maxLevel=1, lineType=8)

    cv2.imwrite(mark_img, newImg)


def batch_marker(src_img_dir, draw_contours_img_dir):
    """
    批处理需要标记的图像,注意这里默认原始图像和标记了颜色区块的图像
    是同名的,但是放在不同的文件夹里。
    """
    src_imgs = get_filenames_in_dir(src_img_dir)
    dc_imgs = get_filenames_in_dir(draw_contours_img_dir)

    for src in src_imgs:
        for dc in dc_imgs:
            if src == dc:
                detect_color(os.path.join(src_img_dir, src), os.path.join(draw_contours_img_dir, dc))


def get_filenames_in_dir(dir):
    """获取一个目录下所有文件的文件名"""
    for root, dirs, files in os.walk(dir):
        return files

使用时直接调用detect_color函数就可以了,第一个参数是像图2一样的图片路径,第二个参数是像图1一样的原始图片的路径。

opencv是经常会被使用到的图像处理库,下面介绍一下上述代码中使用到的几个关键函数。

inRange(src, lowerb, upperb[, dst]) -> dst

参数含义:src:输入源图像。

         lowrb:需要检测图像像素范围的下阈值。

         upperb:需要检测图像像素范围的上阈值。

         dst:输出图像数组,形状和输入图像src一致,CV_8U类型。

功能:将图像中的像素在lowrb和upperrb范围内的设置成255,范围之外的设置成0以输出。
cvtColor(src, code[, dst[, dstCn]]) -> dst

参数含义:src:输入源图像。

         code:图像编码方式或者是颜色空间。

功能:将图像从一个颜色空间转换到另外一个颜色空间,通常是将BGR图像转换为灰度图。
threshold(src, thresh, maxval, type[, dst]) -> retval, dst

参数含义:src:输入源图像。

         thresh:阈值。

         maxval:用于THRESH_BINARY和THRESH_BINARY_INV阈值处理的最大值。

         type:阈值类型。

         dst:与输入源图像src具有相同大小、类型、通道数的图像。

功能:通常用于从灰度图中获取二进制图像或者用于去除图像噪声(即去除太大或太小的像素)。
findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> image, contours, hierarchy

参数含义:image:输入源图像,一个8位单通道图像,非零像素被视为1,零像素保持为0,即二进制图像。如果 
         mode等于RETR_CCOMP或RETR_FLOODFILL,则输入也可以是32位整数图像标签。

         mode:轮廓检索模式。

         method:轮廓逼近方法。

         contours:检测到的轮廓,每个轮廓被存为点的矢量。

         hierarchy:可选的输出矢量。包含图像的拓扑信息,具有和轮廓相同数量的元素。

         offset:每个轮廓点的可选偏移量。

功能:从二进制图像中检索轮廓。轮廓是用于形状分析和物体检测识别的常用工具。
drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]) -> image

参数含义:image:目标图片。

         contours:所有输入轮廓,每个轮廓都被存储为点的矢量。

         contourIdx:要绘制的轮廓的索引,如果为-1则绘制所有的轮廓。

         color:轮廓绘制采用的颜色。

         thickness:要绘制轮廓的线条的粗细,如果是负的,则绘制轮廓内线条。

         lineType:线路连接。

         hierarchy:需要绘制的轮廓的层次信息。仅当只需要绘制部分轮廓时这个参数才有用。

         maxLevel:绘制轮廓的最大级别。如果为0,则绘制指定的轮廓;如果为1,则绘制所有轮廓(含嵌套 
         轮廓);如果是2,那么绘制所有轮廓,包括嵌套轮廓,嵌套的嵌套轮廓等等,但只有当有可用的层次 
         结构时参数才有效。

         offset:可选的轮廓位移参数。

功能:绘制轮廓或填充轮廓。

好了,整个代码其实比较简单,主要是调用了上面几个函数。如果朋友们还有什么函数不清楚的可以自行查看文档。若对上述代码有疑问或建议的可以评论留言给我,以使我不断给大家提供更好的代码及博文。在此提前谢过,感谢朋友们的阅读!

猜你喜欢

转载自blog.csdn.net/hfutdog/article/details/82146089