简体   繁体   English

不使用 OpenCV 获取图像掩码

[英]Get mask of image without using OpenCV

I'm trying the following to get the mask out of this image, but unfortunately I fail.我正在尝试以下方法从这张图片中取出面具,但不幸的是我失败了。

import numpy as np
import skimage.color
import skimage.filters
import skimage.io

# get filename, sigma, and threshold value from command line
filename = 'pathToImage'

# read and display the original image
image = skimage.io.imread(fname=filename)
skimage.io.imshow(image)
# blur and grayscale before thresholding
blur = skimage.color.rgb2gray(image)
blur = skimage.filters.gaussian(blur, sigma=2)
# perform inverse binary thresholding
mask = blur < 0.8
# use the mask to select the "interesting" part of the image
sel = np.ones_like(image)
sel[mask] = image[mask]

# display the result
skimage.io.imshow(sel)

How can I obtain the mask?我怎样才能获得面具?

在此处输入图像描述 在此处输入图像描述

Is there a general approach that would work for this image as well.是否有一种通用方法也适用于此图像。 without custom fine-tuning and changing parameters?没有自定义微调和更改参数? 在此处输入图像描述

  1. Apply high contrast (maximum possible value)应用高对比度(最大可能值)

s1

  1. convert to black & white image using high threshold (I've used 250)使用高阈值转换为黑白图像(我使用了 250)

s2

  1. min filter (value=8)最小过滤器(值=8)

s3

  1. max filter (value=8)最大过滤器(值=8)

s4

Here is how you can get a rough mask using only the skimage library methods:以下是仅使用skimage库方法获得粗略蒙版的方法:

import numpy as np
from skimage.io import imread, imsave
from skimage.feature import canny
from skimage.color import rgb2gray
from skimage.filters import gaussian
from skimage.morphology import dilation, erosion, selem
from skimage.measure import find_contours
from skimage.draw import polygon

def get_mask(img):
    kernel = selem.rectangle(7, 6)
    dilate = dilation(canny(rgb2gray(img), 0), kernel)
    dilate = dilation(dilate, kernel)
    dilate = dilation(dilate, kernel)
    erode = erosion(dilate, kernel)
    mask = np.zeros_like(erode)
    rr, cc = polygon(*find_contours(erode)[0].T)
    mask[rr, cc] = 1
    return gaussian(mask, 7) > 0.74

def save_img_masked(file):
    img = imread(file)[..., :3]
    mask = get_mask(img)
    result = np.zeros_like(img)
    result[mask] = img[mask]
    imsave("masked_" + file, result)

save_img_masked('belt.png')
save_img_masked('bottle.jpg')

Resulting masked_belt.png :结果masked_belt.png

在此处输入图像描述

Resulting masked_bottle.jpg :结果masked_bottle.jpg

在此处输入图像描述

One approach uses the fact that the background changes color only very slowly.一种方法是利用背景颜色变化非常缓慢的事实。 Here I apply the gradient magnitude to each of the channels and compute the norm of the result, giving me an image highlighting the quicker changes in color.在这里,我将梯度幅度应用于每个通道并计算结果的范数,给我一个突出显示颜色变化更快的图像。 The watershed of this (with sufficient tolerance) should have one or more regions covering the background and touching the image edge.这一分水岭(具有足够的公差)应该有一个或多个区域覆盖背景并接触图像边缘。 After identifying those regions, and doing a bit of cleanup we get these results (red line is the edge of the mask, overlaid on the input image):在识别出这些区域并进行一些清理后,我们得到了这些结果(红线是遮罩的边缘,覆盖在输入图像上):

案例 1 输出

案例 2 输出

I did have to adjust the tolerance, with a lower tolerance in the first case, more of the shadow is seen as object. I think it should be possible to find a way to set the tolerance based on the statistics of the gradient image, I have not tried.我确实必须调整公差,在第一种情况下公差较低,更多的阴影被视为object。我认为应该可以找到一种基于渐变图像统计设置公差的方法,我没试过。

There are no other parameters to tweak here, the minimum object area, 300, is quite safe;这里没有其他参数可以调整,最小的object区域,300,是比较安全的; an alternative would be to keep only the one largest object.另一种方法是只保留最大的一个 object。

This is the code, using DIPlib (disclaimer: I'm an author).这是代码,使用DIPlib (免责声明:我是作者)。 out is the mask image, not the outline as displayed above. out是蒙版图像,而不是上面显示的轮廓。

import diplib as dip
import numpy as np

# Case 1:
img = dip.ImageRead('Pa9DO.png')
img = img[362:915, 45:877]  # cut out actual image
img = img(slice(0,2))       # remove alpha channel
tol = 7

# Case 2:
#img = dip.ImageRead('jTnVr.jpg')
#tol = 1

# Compute gradient
gm = dip.Norm(dip.GradientMagnitude(img))
# Compute watershed with tolerance
lab = dip.Watershed(gm, connectivity=1, maxDepth=tol, flags={'correct','labels'})
# Identify regions touching the image edge
ll = np.unique(np.concatenate((
      np.unique(lab[:,0]),
      np.unique(lab[:,-1]),
      np.unique(lab[0,:]),
      np.unique(lab[-1,:]))))
# Remove regions touching the image edge
out = dip.Image(lab.Sizes(), dt='BIN')
out.Fill(1)
for l in ll:
   if l != 0:  # label zero is for the watershed lines
      out = out - (lab == l)
# Remove watershed lines
out = dip.Opening(out, dip.SE(3, 'rectangular'))
# Remove small regions
out = dip.AreaOpening(out, filterSize=300)
# Display
dip.Overlay(img, dip.Dilation(out, 3) - out).Show()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM