在图像上叠加绘制分割结果的方法

简介

在进行图像分割之后,我们常常需要将分割的结果叠加绘制到原图上,并以不同的颜色显示,以便进行展示.

已有的方案

scikit-image,opencv,gluoncv, detectron2 中都提供有类似功能的函数.不过还是存在一定的缺点:

  1. scikit-image 中的 skimage.color.label2rgb 只能处理灰度图像,彩色图像也是必须转换为灰度图像之后才将分割结果绘制上去.只能说勉强能用.
  2. opencv 中的 cv2.addWeighted 实际上是用于将两个图像按照不同的比例进行融合.将其简单的用于图像与分割结果的融合,会发现在没有分割目标的区域灰度降低,即整体的图像变暗了.其实不算是很好的解决方案.
  3. detectron2 中的 detectron2.utils.visualizer.Visualizer 也提供了类似的功能,但是文档说其为了渲染质量而牺牲了速度,不易在需要实时展示的场景使用.而且为了找个功能而单独安装 detectron2 有点得不偿失.但是这部分代码却很难单独抽取出来使用.
  4. gluoncv 提供的 gluoncv.utils.viz.plot_mask 代码简洁.我们可以将其单独提取出来,并且简单修改一下,更加好用.

修改后的版本

基于 gluoncv.utils.viz.plot_mask,我将其简单的修改,代码如下:

import numpy as np

def plot_mask(img, masks, colors=None, alpha=0.5) -> np.ndarray:
    """Visualize segmentation mask.

    Parameters
    ----------
    img: numpy.ndarray
        Image with shape `(H, W, 3)`.
    masks: numpy.ndarray
        Binary images with shape `(N, H, W)`.
    colors: numpy.ndarray
        corlor for mask, shape `(N, 3)`.
        if None, generate random color for mask
    alpha: float, optional, default 0.5
        Transparency of plotted mask

    Returns
    -------
    numpy.ndarray
        The image plotted with segmentation masks, shape `(H, W, 3)`

    """
    if colors is None:
        colors = np.random.random((masks.shape[0], 3)) * 255
    else:
        if colors.shape[0] < masks.shape[0]:
            raise RuntimeError(
                f"colors count: {colors.shape[0]} is less than masks count: {masks.shape[0]}"
            )
    for mask, color in zip(masks, colors):
        mask = np.stack([mask, mask, mask], -1)
        img = np.where(mask, img * (1 - alpha) + color * alpha, img)

    return img.astype(np.uint8)

由于最近正好在看如何用 opencv C++ 写代码,我还写了一个简单的 opencv C++ 版本:

#include <opencv2/opencv.hpp>
using namespace cv;
void plot_mask(Mat image, Mat mask, float alpha = 0.5, Scalar color = Scalar(0, 255, 0))
{
    auto img_iter = image.begin<Vec3b>();
    auto mask_iter = mask.begin<uchar>();

    while (img_iter != image.end<Vec3b>())
    {
        if (*mask_iter)
        {
            (*img_iter)[0] = (*img_iter)[0] * (1 - alpha) + color[0] * alpha;
            (*img_iter)[1] = (*img_iter)[1] * (1 - alpha) + color[1] * alpha;
            (*img_iter)[2] = (*img_iter)[2] * (1 - alpha) + color[2] * alpha;
        }
        img_iter++;
        mask_iter++;
    }
}

不过很明显,这个 C++ 版本的函数只能处理 mask 中只有一类标签的,有多个类别的可以多次调用该函数处理.相信这个修改并不算太难,只是目前暂时先不更新了.


719 Words

2021-11-28 00:00 +0000