分割环,即 R 中的非完整对象(在 EBIimage 或其他中)

Segmenting rings i.e. non-full objects in R (in EBIimage or other)

我依靠边缘检测(而不是颜色检测)从血细胞中提取特征。原始图像看起来像:

我正在使用 R EBImage 包 运行 一个 sobel + 低通滤波器来得到这样的东西:

library(EBImage)
library(data.table)
img <- readImage("6hr-007-DIC.tif")

#plot(img)


#print(img, short = T)

# 1. define filter for edge detection
hfilt <- matrix(c(1, 2, 1, 0, 0, 0, -1, -2, -1), nrow = 3) # sobel

# rotate horizontal filter to obtain vertical filter
vfilt <- t(hfilt)

# get horizontal and vertical edges
imgH <- filter2(img, hfilt, boundary="replicate")
imgV <- filter2(img, vfilt, boundary="replicate")

# combine edge pixel data to get overall edge data
hdata <- imageData(imgH)
vdata <- imageData(imgV)
edata <- sqrt(hdata^2 + vdata^2)

# transform edge data to image
imgE <- Image(edata)
#print(display(combine(img, imgH, imgV, imgE), method = "raster", all = T))

display(imgE, method = "raster", all = T)


# 2. Enhance edges with low pass filter

hfilt <- matrix(c(1, 1, 1, 1, 1, 1, 1, 1, 1), nrow = 3) # low pass

# rotate horizontal filter to obtain vertical filter
vfilt <- t(hfilt)

# get horizontal and vertical edges
imgH <- filter2(imgE, hfilt, boundary="replicate")
imgV <- filter2(imgE, vfilt, boundary="replicate")

# combine edge pixel data to get overall edge data
hdata <- imageData(imgH)
vdata <- imageData(imgV)
edata <- sqrt(hdata^2 + vdata^2)


# transform edge data to image
imgE <- Image(edata)
plot(imgE)

我想知道是否有任何方法可以填充大环(血细胞)中的孔,使其成为有点像实心体:

(显然这不是同一张图片,但想象一下最后一张图片只是从边缘开始的。)

然后我想使用 EBImage 包中的 computeFeatures() 方法(据我所知,它只适用于实体)

EDIT 多一点代码来提取带有 "connections" 对象的内部边界。附加代码包括定义分段单元格的凸包和创建填充掩码。

简短的回答是 fillHullfloodFill 可能有助于填充具有明确边界的单元格。

下面较长的(已编辑)答案提出了一种可能有用的 floodFill 方法。您从低对比度 DIC 图像中提取信息做得很好,但更多的图像处理可能会有所帮助,例如 "flat-field correction" 用于嘈杂的 DIC 图像。这个 Wikipedia page 中描述了原理,但是一个简单的实现会产生奇迹。此处建议的编码解决方案需要用户与 select 个单元格进行交互。这不是一个强有力的方法。尽管如此,也许更多的图像处理与定位细胞的代码相结合是可行的。最后,细胞内部被分割并可用于 computeFeatures.

的分析

代码从经过阈值处理的图像开始(已修剪边缘并转换为二进制)。

# Set up plots for 96 dpi images
  library(EBImage)
  dm <- dim(img2)/96
  dev.new(width = dm[1], height = dm[2])
# Low pass filter with gblur and make binary
  xb <- gblur(img2, 3)
  xt <- thresh(xb, offset = 0.0001)
  plot(xt) # thresh.jpg
# dev.print(jpeg, "thresh.jpg", width = dm[1], unit = "in", res = 96) 

# Keep only "large" objects
  xm <- bwlabel(xt)
  FS <- computeFeatures.shape(xm)
  sel <- which(FS[,"s.area"] < 800)
  xe <- rmObjects(xm, sel)

# Make binary again and plot
  xe <- thresh(xe)
  plot(xe) # trimmed.jpg
#  dev.print(jpeg, "trimmed.jpg", width = dm[1], unit = "in", res = 96)

# Choose cells with intact interiors
# This is done by hand here but with more pre-processing, it may be
# possible to have the image suitable for more automated analysis...
  pp <- locator(type = "p", pch = 3, col = 2) # marked.jpg
#  dev.print(jpeg, "marked.jpg", width = dm[1], unit = "in", res = 96)

# Fill interior of each cell with a unique integer
  myCol <- seq_along(pp$x) + 1
  xf1 <- floodFill(xe, do.call(rbind, pp), col = myCol)

# Discard original objects from threshold (value = 1) and see
  cells1 <- rmObjects(xf1, 1)
  plot(colorLabels(cells1))
# dev.print(jpeg, "cells1.jpg", width = dm[1], unit = "in", res = 96)

我需要引入算法来连接顶点之间的整数点并填充 多边形。此处的代码实现了 Bresenham 算法,并使用仅适用于凸(简单)多边形的简单多边形填充例程。

#
# Bresenham's balanced integer line drawing algorithm
#
bresenham <- function(x, y = NULL, close = TRUE)
{
# accept any coordinate structure
  v <- xy.coords(x = x, y = y, recycle = TRUE, setLab = FALSE)
  if (!all(is.finite(v$x), is.finite(v$y)))
    stop("finite coordinates required")

  v[1:2] <- lapply(v[1:2], round) # Bresenham's algorithm IS for integers
  nx <- length(v$x)
  if (nx == 1) return(list(x = v$x, y = v$y)) # just one point
  if (nx > 2 && close == TRUE) { # close polygon by replicating 1st point
    v$x <- c(v$x, v$x[1])
    v$y <- c(v$y, v$y[1])
    nx <- nx + 1
  }
# collect result in 'ans, staring with 1st point
  ans <- lapply(v[1:2], "[", 1)

# process all vertices in pairs
  for (i in seq.int(nx - 1)) {
    x <- v$x[i] # coordinates updated in x, y
    y <- v$y[i]
    x.end <- v$x[i + 1]
    y.end <- v$y[i + 1]

    dx <- abs(x.end - x); dy <- -abs(y.end - y)
    sx <- ifelse(x < x.end, 1, -1)
    sy <- ifelse(y < y.end, 1, -1)
    err <- dx + dy

  # process one segment
    while(!(isTRUE(all.equal(x, x.end)) && isTRUE(all.equal(y, y.end)))) {
      e2 <- 2 * err
      if (e2 >= dy) { # increment x
        err <- err + dy
        x <- x + sx
      }
      if (e2 <= dx) { # increment y
        err <- err + dx
        y <- y + sy
      }
      ans$x <- c(ans$x, x)
      ans$y <- c(ans$y, y)
    }
  }
# remove duplicated points (typically 1st and last)
  dups <- duplicated(do.call(cbind, ans), MARGIN = 1) 
  return(lapply(ans, "[", !dups))
}

以及查找简单多边形内点的简单例程。

#
# Return x,y integer coordinates of the interior of a CONVEX polygon
#
cPolyFill <- function(x, y = NULL) 
{
  p <- xy.coords(x, y = y, recycle = TRUE, setLab = FALSE)
  p[1:2] <- lapply(p[1:2], round)
  nx <- length(p$x)
  if (any(!is.finite(p$x), !is.finite(p$y)))
    stop("finite coordinates are needed")

  yc <- seq.int(min(p$y), max(p$y))
  xlist <- lapply(yc, function(y) sort(seq.int(min(p$x[p$y == y]), max(p$x[p$y == y]))))
  ylist <- Map(rep, yc, lengths(xlist))
  ans <- cbind(x = unlist(xlist), y = unlist(ylist))
  return(ans)
}

现在这些可以与 ocontour()chull() 一起使用来创建和填充每个分段单元格的凸包。这"fixes"那些被入侵的小区

# Create convex hull mask
  oc <- ocontour(cells1) # for all points along perimeter
  oc <- lapply(oc, function(v) v + 1) # off-by-one flaw in ocontour
  sel <- lapply(oc, chull) # find points that define convex hull
  xh <- Map(function(v, i) rbind(v[i,]), oc, sel) # new vertices for convex hull
  oc2 <- lapply(xh, bresenham) # perimeter points along convex hull

# Collect interior coordinates and fill
  coords <- lapply(oc2, cPolyFill)
  cells2 <- Image(0, dim = dim(cells1))
  for(i in seq_along(coords))
    cells2[coords[[i]]] <- i # blank image for mask
  xf2 <- xe
  for (i in seq_along(coords))
    xf2[coords[[i]]] <- i # early binary mask

# Compare before and after
  img <- combine(colorLabels(xf1), colorLabels(cells1),
    colorLabels(xf2), colorLabels(cells2))
  plot(img, all = T, nx = 2)
  labs <- c("xf1", "cells1", "xf2", "cells2")
  ix <- c(0, 1, 0, 1)
  iy <- c(0, 0, 1, 1)
  text(dm[1]*96*(ix + 0.05), 96*dm[2]*(iy + 0.05), labels = labs,
    col = "white", adj = c(0.05,1))
 # dev.print(jpeg, "final.jpg", width = dm[1], unit = "in", res = 96)