草庐IT

语义分割中图片和mask的可视化

深度科研 2023-04-11 原文

其实取标题一直以来都是一件麻烦的事,但是如果你要看下去,我想你得有一点语义分割的见解。

用平常的语言描述该问题就是:语义分割出我们感兴趣的目标物,然后输出该目标物的轮廓点做语义分割其实有很多种方法,你可以用不同的模型去train你的dataset,但是刚接触语义分割的朋友们可能会说,我该怎么分割出我想要的目标物,而不对其它部分的像素做修改?其实这件事并不复杂,一个很直觉的想法是:修改像素对应的RGB值。那怎么修改对应像素的RGB值呢?举例来说,假设你在mmsegmentation框架下用的是SegFormer模型,你应该在class_names.py文件中修改cityscapespalette(如上图)

问题的前半段很好解决,那如何解决输出目标物轮廓点这件事情呢?一种比较直觉的想法是:将分割好的图片转为二值图,然后在转为灰度图,之后利用cv2.findContours()函数寻找轮廓点,为了验证轮廓点的准确性,再用cv2.drawContours()函数做可视化处理。这样的处理对一些简单的图片是一种策略,但是对于已经语义分割好的图片,再这样处理会不会有一些“脱裤子放屁——多此一举”呢?答案是肯定的。你想想看,你是怎么将一幅图片语义分割出来的呢?是根据每个像素的类别逐一“上色”的对吧。那这么说来,其实要做输出轮廓点这件事,只要让模型输出一张分割后的二maskt图片,然后转为灰度图,再重复上面说的cv2.findContours()cv2.drawContours()的操作就可以了吧?

那这里有一个前提,什么前提呢?前提是你已经知道如何修改像素值这件事情。所以在SegFormer里面,你可以在base.py文件中进行修改,一些注释我已经写在代码里面:

    def show_result(self,
                    img,
                    result,  # 这里的result就是每个像素的类别,它的大小应该与你的图片大小有关
                    palette=None,
                    win_name='',
                    show=False,
                    wait_time=0,
                    out_file=None):
        """Draw `result` over `img`.

        Args:
            img (str or Tensor): The image to be displayed.
            result (Tensor): The semantic segmentation results to draw over
                `img`.
            palette (list[list[int]]] | np.ndarray | None): The palette of
                segmentation map. If None is given, random palette will be
                generated. Default: None
            win_name (str): The window name.
            wait_time (int): Value of waitKey param.
                Default: 0.
            show (bool): Whether to show the image.
                Default: False.
            out_file (str or None): The filename to write the image.
                Default: None.

        Returns:
            img (Tensor): Only if not `show` or `out_file`
        """
        img = mmcv.imread(img)
        img = img.copy()
        seg = result[0]
        if palette is None:
            if self.PALETTE is None:
                palette = np.random.randint(
                    0, 255, size=(len(self.CLASSES), 3))
            else:
                palette = self.PALETTE
        palette = np.array(palette)
        assert palette.shape[0] == len(self.CLASSES)
        assert palette.shape[1] == 3
        assert len(palette.shape) == 2
        color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8)
        for label, color in enumerate(palette):
            color_seg[seg == label, :] = color  # 注意数组的特别用法
        # convert to BGR
        color_seg = color_seg[..., ::-1]

        # from IPython import embed; embed(header='debug vis')
        # img = img * 0.5 + color_seg * 0.5  # 这两步是修改的地方
        # img = img.astype(np.uint8)


        # if out_file specified, do not show image in window
        if out_file is not None:
            show = False

        if show:
            mmcv.imshow(img, win_name, wait_time)
        if out_file is not None:
            mmcv.imwrite(img, out_file)

        if not (show or out_file):
            warnings.warn('show==False and out_file is not specified, only '
                          'result image will be returned')
            return color_seg  #这一步也是修改的地方,原来是return img,这样就是分割出输出,为不是mask后的图输出

 有了输出的mask图像(与原始图片的一一对应),就可以很方便的在原图进行可视化操作:

result = inference_segmentor(model_seg, p)  # 前向推理,得到每个像素的类别
img_seg = show_result_pyplot(model_seg, p, result, get_palette(opt.palette))  # 得到分割后的mask
img = cv2.imread(p)  # 这一步是为了轮廓在原图中显示,我这里的p实际上是我的图片路径
gray_img = cv2.cvtColor(img_seg, cv2.COLOR_BGR2GRAY)  # 转为灰度图
contours, _ = cv2.findContours(gray_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)  # 寻找轮廓点
res = cv2.drawContours(img, contours, -1, (0, 0, 255), 1)  # 绘制轮廓点

可视化后的效果(mask——原图——轮廓):

当然,轮廓检测的方法还有很多,希望你不会因此局限。 

有关语义分割中图片和mask的可视化的更多相关文章

  1. ruby - Ruby 中的波形可视化 - 2

    我即将开始一个将录制和编辑音频文件的项目,我正在寻找一个好的库(最好是Ruby,但会考虑Java或.NET以外的任何库)以进行实时可视化波形。有人知道我应该从哪里开始搜索吗? 最佳答案 要流入浏览器的数据量很大。Flash或Flex图表可能是唯一能提高内存效率的解决方案。Javascript图表往往会因大型数据集而崩溃。 关于ruby-Ruby中的波形可视化,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c

  2. ruby - 使用 cedet 语义 wisent-ruby - 2

    我刚刚开始按照包括AlexOtt在内的各种指南设置cedet。这是我的init文件中目前的内容。(require'cedet)(semantic-load-enable-code-helpers);;imenubreaksifIdon'tenablethis(global-semantic-highlight-func-mode1)(global-semantic-tag-folding-mode)我非常喜欢代码折叠,因为语义比hideshow等包更了解代码我想对ruby​​进行相同的折叠。我知道cedet还可以做其他事情,但我现在只是试一试。所以我在contrib/文件夹中看到了wi

  3. ruby - 为什么 Gemfile 语义版本控制运算符 (~>) 会产生与一个数字不一致的结果? - 2

    gemspec语义版本控制运算符~>(又名twiddle-wakka,又名pessimistic运算符)允许限制gem版本但允许进行一些升级。我经常看到它可以读作:"~>3.1"=>"Anyversion3.x,butatleast3.1""~>3.1.1"=>"Anyversion3.1.x,butatleast3.1.1"但是有了一个数字,这条规则就失效了:"~>3"=>"Anyversionx,butatleast3"*NOTTRUE!*"~>3"=>"Anyversion3.x"*True.Butwhy?*如果我想要“任何版本3.x”,我可以只使用“~>3.0”,这是一致的。就

  4. Unity数据可视化图表插件XCharts3.0发布 - 2

    Unity数据可视化图表插件XCharts3.0发布历时8个多月,业余时间,断断续续,XCharts3.0总算发布了。如果要打个满意度,我给3.0版本来个80分。对于代码框架结构设计的调整改动,基本符合预期,甚是满意。相比之前的1.0和2.0版本,我认为3.0才是一个拿得出手给广大开发者使用的版本。1.0发布的时候,很兴奋,从0.1到1.0,也磨了一年,真的等不及想给大家试用了,还特地写过一篇文章以示庆祝。那个时候,1.0虽然还还不够完善,功能也不够丰富,但它是XCharts的开始,没有1.0,也就没有后面的2.0和3.0。后面的2.0发布,做了很多改进和优化,随着版本迭代,慢慢的发现有不少硬

  5. micropython复现经典单片机项目(二)可视化音频 频谱解析(基本搞定) - 2

    本人是音乐爱好者,从小就特别喜欢那个随着音乐跳动的方框效果,就是这个:arduino上一大把对,我忍你很久了,我就想用mpy做,全网没有,行我自己研究。果然兴趣是最好的老师,我之前有篇博客专门讲音频,有兴趣的可以回顾一下。提到可视化频谱,必然绕不开fft,大学学过这玩意,当时一心玩,老师讲的一个字都么听进去,网上教程简略扫了一下,大该就是把时域转频域的工具,我大mpy居然没有fft函数,奶奶的,先放着。音频信息如何收集?第一种傻瓜式的ADC,模拟转数字,原始粗暴,第二种,I2S库,我之前博客有讲过,数据是PCM编码。然后又去学PCM编码,一学豁然开朗,舒服,以代码为例:audio_in=I2S

  6. ruby - 用逗号将字符串分割成数组,除非逗号在引号内 - 2

    给定一个Ruby数组字符串,其中一些项目在引号中包含逗号:my_string.inspect#=>"\"hey,you\",21"我怎样才能得到一个数组:["hey,you","21"] 最佳答案 Ruby标准CSV库的.parse_csv就是这样做的。require'csv'"\"hey,you\",21".parse_csv#=>["hey,you","21"] 关于ruby-用逗号将字符串分割成数组,除非逗号在引号内,我们在StackOverflow上找到一个类似的问题:

  7. ruby-on-rails - Ruby On Rails 4.2 的可视化日志查看器 - 2

    我以前在Laravel4上工作过,它有一个很棒的日志查看器工具laravellogviewer查看demo我正在寻找与Rubyonrails4.2非常相似的东西,如果你们知道Rails4.2的任何好的可视化日志记录GEM,请告诉我..从代码我需要记录不同的日志级别,这个工具应该直观地组织我的日志,谢谢.. 最佳答案 这应该可以帮助您入门https://github.com/shadabahmed/logstasher如其所说Thisgemisheavilyinspiredfromlograge,butit'sfocusedonone

  8. 接受对象或其 id 作为参数的 Ruby 语义 - 2

    我在这里尝试按照最小意外原则工作...假设您有一个接受两个对象的方法。该方法需要这些是对象实例,但在您初始化类的地方,您可能只有引用ID。例如,这在网络服务的路由器/Controller中很常见。设置可能看起来像这样:post"/:foo_id/add_bar/:bar_id"doAddFooToBar.call(...)end有许多不同的方法可以解决这个问题。对我来说,这里最“惯用的”是这样的:defAddFooToBar.call(foo:nil,foo_id:nil,bar:nil,bar_id:nil)@foo=foo||Foo[foo_id]@bar=bar||Bar[bar

  9. ruby - 由两个不同的分隔符分割的字符串 - 2

    我有这样的字符串'some-dasd\dasd-dasdas\dasdas-dasd-das\dsad'。我需要通过两个不同的符号'\'和'-'将字符串拆分为数组,所以我想得到数组['some','dasd','dasd','dasdas','dasdas','dasd','das','dsad']。最好的方法是什么? 最佳答案 "ome-dasd\dasd-dasdas\dasdas-dasd-das\dsad".split(/\\|-/)应该可以解决问题。 关于ruby-由两个不同的

  10. 通过可视化运维配置,实现故障秒级自愈 - 2

    急促的告警铃声响彻寂静的夜晚。对运维人来说,晚间值守耗费更大的精力,往往一个简单的磁盘使用率告警通知,就不得不爬起来进行处理,毕竟告警无小事,对于小问题,运维人也不能心存侥幸心理。虽然有着值班人员和团队的支撑,但频繁的告警还是让运维人员精疲力竭,如何让系统的稳定性提高,减轻一线人员的工作量,减轻一线人员的压力?通过智能运维,实现故障自愈将成为不可避免的选择。故障自愈是提升企业网络系统可用性和降低故障处理的人力投入,实现故障自愈从"人工处理"到"无人值守"的变革。通过实时发现告警,进行预诊断分析,判断告警类型和级别,如果是一般告警,平台进行自动恢复,如果是严重复杂告警则通过告警通知、运维工单等形

随机推荐