在python中如何在主图像的顶部覆盖分割图像

在python中如何在主图像的顶部覆盖分割图像,python,opencv,image-segmentation,imaging,Python,Opencv,Image Segmentation,Imaging,我有一个RGB图像和另一个分割图像,其中像素有3个值(分割图像)。我想在主图像的顶部覆盖分割图像,因为分割区域在主图像上形成轮廓,如下图。这里,分割图像像素的值为0、1和2。红色轮廓表示值为1的像素轮廓,黄色轮廓表示值为2的像素轮廓,背景像素值为0 该图像来自“扩张起始网络:心脏右心室分割的多尺度特征聚合”一文 下面是一个分割图像的示例 背景图像可以是任何图像。我只需要这些矩形计数器作为两个轮廓出现在背景图像上,类似于上面的红色和黄色线条。因此,输出将类似于下图 对不起,我用手画矩形,它们

我有一个RGB图像和另一个分割图像,其中像素有3个值(分割图像)。我想在主图像的顶部覆盖分割图像,因为分割区域在主图像上形成轮廓,如下图。这里,分割图像像素的值为0、1和2。红色轮廓表示值为1的像素轮廓,黄色轮廓表示值为2的像素轮廓,背景像素值为0

该图像来自“扩张起始网络:心脏右心室分割的多尺度特征聚合”一文

下面是一个分割图像的示例


背景图像可以是任何图像。我只需要这些矩形计数器作为两个轮廓出现在背景图像上,类似于上面的红色和黄色线条。因此,输出将类似于下图


对不起,我用手画矩形,它们不精确。我只想给你一个关于输出的见解

我尝试了四种不同的方法:

  • OpenCV
  • PIL/枕头Numpy
  • 带有ImageMagick的命令行
  • 撇渣形态学

方法1-OpenCV

  • 打开分割图像作为灰度
  • 将主图像打开为灰度,并使用颜色进行标注
  • 使用
    cv2.findContours()
  • 迭代轮廓并使用
    cv2.drawContours()
    根据分割图像中的标签将每个轮廓以彩色绘制到主图像上
文件是

因此,从这张图片开始:

这张分割图像:

当对比度拉伸时,三明治标记为灰色(1),鼻子标记为灰色(2):

代码如下:

#!/usr/bin/env python3

import numpy as np
import cv2

# Load images as greyscale but make main RGB so we can annotate in colour
seg  = cv2.imread('segmented.png',cv2.IMREAD_GRAYSCALE)
main = cv2.imread('main.png',cv2.IMREAD_GRAYSCALE)
main = cv2.cvtColor(main,cv2.COLOR_GRAY2BGR)

# Dictionary giving RGB colour for label (segment label) - label 1 in red, label 2 in yellow
RGBforLabel = { 1:(0,0,255), 2:(0,255,255) }

# Find external contours
_,contours,_ = cv2.findContours(seg,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)

# Iterate over all contours
for i,c in enumerate(contours):
    # Find mean colour inside this contour by doing a masked mean
    mask = np.zeros(seg.shape, np.uint8)
    cv2.drawContours(mask,[c],-1,255, -1)
    # DEBUG: cv2.imwrite(f"mask-{i}.png",mask)
    mean,_,_,_ = cv2.mean(seg, mask=mask)
    # DEBUG: print(f"i: {i}, mean: {mean}")

    # Get appropriate colour for this label
    label = 2 if mean > 1.0 else 1
    colour = RGBforLabel.get(label)
    # DEBUG: print(f"Colour: {colour}")

    # Outline contour in that colour on main image, line thickness=1
    cv2.drawContours(main,[c],-1,colour,1)

# Save result
cv2.imwrite('result.png',main) 
#!/usr/bin/env python3

from PIL import Image, ImageFilter
import numpy as np

def drawContour(m,s,c,RGB):
    """Draw edges of contour 'c' from segmented image 's' onto 'm' in colour 'RGB'"""
    # Fill contour "c" with white, make all else black
    thisContour = s.point(lambda p:p==c and 255)
    # DEBUG: thisContour.save(f"interim{c}.png")

    # Find edges of this contour and make into Numpy array
    thisEdges   = thisContour.filter(ImageFilter.FIND_EDGES)
    thisEdgesN  = np.array(thisEdges)

    # Paint locations of found edges in color "RGB" onto "main"
    m[np.nonzero(thisEdgesN)] = RGB
    return m

# Load segmented image as greyscale
seg = Image.open('segmented.png').convert('L')

# Load main image - desaturate and revert to RGB so we can draw on it in colour
main = Image.open('main.png').convert('L').convert('RGB')
mainN = np.array(main)

mainN = drawContour(mainN,seg,1,(255,0,0))   # draw contour 1 in red
mainN = drawContour(mainN,seg,2,(255,255,0)) # draw contour 2 in yellow

# Save result
Image.fromarray(mainN).save('result.png')
结果:


方法2-PIL/枕头和Numpy

  • 打开分割图像,找到独特的颜色
  • 打开主映像并去饱和
  • 迭代列表中的每个唯一颜色
  • 。。。使所有像素颜色为白色,所有其他像素颜色为黑色
  • 。。。找到边并使用边作为遮罩在主图像上绘制颜色
代码如下:

#!/usr/bin/env python3

import numpy as np
import cv2

# Load images as greyscale but make main RGB so we can annotate in colour
seg  = cv2.imread('segmented.png',cv2.IMREAD_GRAYSCALE)
main = cv2.imread('main.png',cv2.IMREAD_GRAYSCALE)
main = cv2.cvtColor(main,cv2.COLOR_GRAY2BGR)

# Dictionary giving RGB colour for label (segment label) - label 1 in red, label 2 in yellow
RGBforLabel = { 1:(0,0,255), 2:(0,255,255) }

# Find external contours
_,contours,_ = cv2.findContours(seg,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)

# Iterate over all contours
for i,c in enumerate(contours):
    # Find mean colour inside this contour by doing a masked mean
    mask = np.zeros(seg.shape, np.uint8)
    cv2.drawContours(mask,[c],-1,255, -1)
    # DEBUG: cv2.imwrite(f"mask-{i}.png",mask)
    mean,_,_,_ = cv2.mean(seg, mask=mask)
    # DEBUG: print(f"i: {i}, mean: {mean}")

    # Get appropriate colour for this label
    label = 2 if mean > 1.0 else 1
    colour = RGBforLabel.get(label)
    # DEBUG: print(f"Colour: {colour}")

    # Outline contour in that colour on main image, line thickness=1
    cv2.drawContours(main,[c],-1,colour,1)

# Save result
cv2.imwrite('result.png',main) 
#!/usr/bin/env python3

from PIL import Image, ImageFilter
import numpy as np

def drawContour(m,s,c,RGB):
    """Draw edges of contour 'c' from segmented image 's' onto 'm' in colour 'RGB'"""
    # Fill contour "c" with white, make all else black
    thisContour = s.point(lambda p:p==c and 255)
    # DEBUG: thisContour.save(f"interim{c}.png")

    # Find edges of this contour and make into Numpy array
    thisEdges   = thisContour.filter(ImageFilter.FIND_EDGES)
    thisEdgesN  = np.array(thisEdges)

    # Paint locations of found edges in color "RGB" onto "main"
    m[np.nonzero(thisEdgesN)] = RGB
    return m

# Load segmented image as greyscale
seg = Image.open('segmented.png').convert('L')

# Load main image - desaturate and revert to RGB so we can draw on it in colour
main = Image.open('main.png').convert('L').convert('RGB')
mainN = np.array(main)

mainN = drawContour(mainN,seg,1,(255,0,0))   # draw contour 1 in red
mainN = drawContour(mainN,seg,2,(255,255,0)) # draw contour 2 in yellow

# Save result
Image.fromarray(mainN).save('result.png')
您将得到以下结果:


方法3-ImageMagick

您也可以在命令行中执行相同的操作,而无需编写任何Python,只需使用安装在大多数Linux发行版上并可用于macOS和Windows的ImageMagick

#!/bin/bash

# Make red overlay for "1" labels
convert segmented.png -colorspace gray -fill black +opaque "gray(1)" -fill white -opaque "gray(1)" -edge 1 -transparent black -fill red     -colorize 100% m1.gif
# Make yellow overlay for "2" labels
convert segmented.png -colorspace gray -fill black +opaque "gray(2)" -fill white -opaque "gray(2)" -edge 1 -transparent black -fill yellow  -colorize 100% m2.gif
# Overlay both "m1.gif" and "m2.gif" onto main image
convert main.png -colorspace gray -colorspace rgb m1.gif -composite m2.gif -composite result.png


方法4-撇渣形态学

在这里,我使用形态学查找
1
像素附近的黑色像素和
2
像素附近的黑色像素

#!/usr/bin/env python3

import skimage.filters.rank
import skimage.morphology
import numpy as np
import cv2

# Load images as greyscale but make main RGB so we can annotate in colour
seg  = cv2.imread('segmented.png',cv2.IMREAD_GRAYSCALE)
main = cv2.imread('main.png',cv2.IMREAD_GRAYSCALE)
main = cv2.cvtColor(main,cv2.COLOR_GRAY2BGR)

# Create structuring element that defines the neighbourhood for morphology
selem = skimage.morphology.disk(1)

# Mask for edges of segment 1 and segment 2
# We are basically looking for pixels with value 1 in the segmented image within a radius of 1 pixel of a black pixel...
# ... then the same again but for pixels with a vaue of 2 in the segmented image within a radius of 1 pixel of a black pixel
seg1 = (skimage.filters.rank.minimum(seg,selem) == 0) & (skimage.filters.rank.maximum(seg, selem) == 1)
seg2 = (skimage.filters.rank.minimum(seg,selem) == 0) & (skimage.filters.rank.maximum(seg, selem) == 2)

main[seg1,:] = np.asarray([0, 0,   255]) # Make segment 1 pixels red in main image
main[seg2,:] = np.asarray([0, 255, 255]) # Make segment 2 pixels yellow in main image

# Save result
cv2.imwrite('result.png',main) 

注意:JPEG是有损的-不要将分割图像保存为JPEG,请使用PNG或GIF


关键词:Python、PIL、Pill、OpenCV、分割、分割、标记、图像、图像处理、边缘、轮廓、略读、ImageMagick、scikit图像、形态学、等级、排序过滤器、像素邻接。

如果半透明分割遮罩显示在图像顶部,
skimage
有一个内置的
label2rgb()
函数,通过标签通道着色:

输入图像


这些是快速的一行程序,可以自动为类别/类整数值选择颜色,并在原始图像上执行覆盖

整个分割区域的颜色:

from skimage import color
result_image = color.label2rgb(segmentation_results, input_image)
分割区域的颜色轮廓:

from skimage import segmentation
result_image = segmentation.mark_boundaries(input_image, segmentation_results, mode='thick')

你的问题,如果有的话,是相当不清楚的。你说你有一个RGB图像和另一个分割图像。我们两个都要好吗?那个么,你们想对最终图像中的矩形做些什么呢?谢谢。背景图像可以是任何RGB图像,这就是为什么我没有添加任何作为主图像。我只需要第二张图像中矩形的计数器覆盖在背景(主)图像上,第一张图像中的红黄线覆盖在心脏CT图像的背景图像上。我如何知道分割图像的外观?您是否有红线的SVG路径?你有一个透明的背景和一条红线勾勒出一个形状吗?你有没有一张每个组织/物体都有纯色的立体图像?如果你需要帮助,一般来说,让人们更容易地帮助你是个好主意。还有,请问什么是“矩形计数器”?我已经添加了我需要的输出。感谢帮助。矩形轮廓是输出图像中的红色和黄色线条。它们应该画在分割图像中矩形占据的像素上。这很好,但它将输入图像转换为灰度。如果所讨论的图像是RGB,您知道有什么替代方案吗?参数
img
同时接受RGB和灰度图像。所以它应该可以正常工作。文档证实了这一点——它确实接受RGB图像,但在应用分割覆盖之前,它会转换为灰度。我希望看到原始,彩色图像背后的覆盖。编辑:我认为这个
saturation
arg可能是关键,但它只出现在skimage的dev分支上,它似乎没有安装在colab上。