Skip to content

Commit

Permalink
version 4.19.8
Browse files Browse the repository at this point in the history
- allow multiple points and colors to be passed to `floodFill` (#21)

From: aoles <[email protected]>

git-svn-id: file:///home/git/hedgehog.fhcrc.org/bioconductor/trunk/madman/Rpacks/EBImage@131904 bc3139a8-67e5-0310-9ffc-ced21a209358
  • Loading branch information
aoles committed Aug 11, 2017
1 parent e57253b commit ea841b0
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 56 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Package: EBImage
Version: 4.19.7
Version: 4.19.8
Title: Image processing and analysis toolbox for R
Encoding: UTF-8
Author: Andrzej Oleś, Gregoire Pau, Mike Smith, Oleg Sklyar, Wolfgang Huber, with contributions from Joseph Barry and Philip A. Marais
Expand Down
11 changes: 7 additions & 4 deletions R/Image.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,11 @@ Image = function(data = array(0, dim=c(1,1)), dim, colormode) {
else
parseColorMode(colormode)

if (missing(dim)) {
if (missing(dim))
dim = setdim(data)

if ( colormode==Color )
dim = c(dim[1:2], 3L, dim[-(1:2)])
}
if ( colormode==Color )
dim = c(dim[1:2], 3L, dim[-(1:2)])

dimnames = dimnames(data)
data = col2rgb(data)/255
Expand Down Expand Up @@ -507,6 +506,10 @@ numberOfFrames = function(y, type = c('total', 'render')) {
.Call(C_numberOfFrames, y, type)
}

numberOfChannels = function(y, d = dim(y), cm = colorMode(y)) {
if ( cm==Grayscale || is.na(d[3L]) ) 1L else d[3L]
}

## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
showImage = function (object, short=FALSE) {
nd = dim(object)
Expand Down
5 changes: 1 addition & 4 deletions R/display.R
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,10 @@ displayWidget <- function(x, title, embed = !interactiveMode(), tempDir = tempfi
imageData(x) = abind(x, Image(0, fd), along = 3L)
}

nf = numberOfFrames(x, type='render')
colormode = colorMode(x)

x = clipImage(x) ## clip the image and change storage mode to double
x = transpose(x)

frames = seq_len(nf)
frames = seq_len(numberOfFrames(x, type='render'))
dependencies = NULL

if ( isTRUE(embed) ) {
Expand Down
47 changes: 38 additions & 9 deletions R/floodFill.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,44 @@
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
floodFill = function(x, pt, col, tolerance=0) {
validImage(x)

n = numberOfFrames(x, 'total')
if ( is.list(pt) ) pt = unlist(pt, use.names=FALSE)
pt = as.integer(pt)
if ( is.character(col) ) col = as.numeric(col2rgb(col)/255)
col = if ( typeof(x)=="double" ) as.double(col) else as.integer(col)
if ( any( pt<1L || pt>dim(x)[1:2] ) ) stop("coordinates 'pt' of the starting point(s) must be inside the image boundaries")

return( .Call(C_floodFill, x, matrix(pt, nrow=n, ncol=2L, byrow=TRUE), matrix(col, nrow=n, ncol=1L), as.numeric(tolerance)))
nf = numberOfFrames(x, 'render')
nc = numberOfChannels(x)

## make sure that `pt` and `col` are lists of length matching the number of frames
if ( is.list(pt) ) {
if ( length(pt) != nf ) stop("length of 'pt' must match the number of 'render' frames")
} else {
pt = rep(list(pt), nf)
}
if ( is.list(col) ) {
if ( length(col) != nf ) stop("length of 'col' must match the number of 'render' frames")
} else {
col = rep(list(col), nf)
}

for (i in seq_len(nf) ) {
pti = pt[[i]]
if ( is.list(pti) )
pti = unlist(pti, use.names=FALSE)
storage.mode(pti) = "integer"
if ( any(pti<1L) || any(pti>dim(x)[1:2]) )
stop("coordinates of starting point(s) 'pt' must be inside image boundaries")
if ( !is.matrix(pti) || dim(pti)[2L]!=2L )
pti = matrix(pti, nrow=length(pti)/2, ncol=2L, byrow=TRUE)
np = dim(pti)[1L]
pt[[i]] = pti

cli = col[[i]]
cli = if ( is.character(cli) )
unlist(lapply(cli, function(col) rep_len(col2rgb(col, alpha=TRUE)/255, nc)))
else
rep(cli, each=nc)
storage.mode(cli) = storage.mode(x)
cli = matrix(rep_len(cli, np * nc), nrow=np, ncol=nc, byrow=TRUE)
col[[i]] = cli
}

return( .Call(C_floodFill, x, pt, col, as.numeric(tolerance)))
}

## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand Down
2 changes: 1 addition & 1 deletion man/bwlabel.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ bwlabel(x)
nbnuclei = apply(z, 3, max)
cat('Number of nuclei=', paste(nbnuclei, collapse=','),'\n')

## recolor nuclei in colors
## paint nuclei in color
cols = c('black', sample(rainbow(max(z))))
zrainbow = Image(cols[1+z], dim=dim(z))
display(zrainbow, title='Cell nuclei (recolored)')
Expand Down
34 changes: 26 additions & 8 deletions man/floodFill.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ floodFill(x, pt, col, tolerance=0)
\arguments{
\item{x}{An \code{Image} object or an array.}

\item{pt}{Coordinates of the start filling point.}
\item{pt}{Coordinates of the start filling points provided as a matrix where rows represent points and columns are the x and y cordinates. For image stacks different points for each frame can be specified by providing a list whose length matches the number of 'render' frames.}

\item{col}{Fill color. This argument should be a numeric for Grayscale images
and an R color for Color images.}
and an R color for Color images. Values are recycled such that their length matches the number of points in `pt`. Can be a list similarly as `pt`.}

\item{tolerance}{Color tolerance used during the fill.}
}
Expand All @@ -41,15 +41,33 @@ floodFill(x, pt, col, tolerance=0)

\examples{
x = readImage(system.file("images", "shapes.png", package="EBImage"))

## fill a shape with 50% shade of gray
y = floodFill(x, c(67, 146), 0.5)
display(y)

y = channel(y, 'rgb')
y = floodFill(y, c(48, 78), 'red')
y = floodFill(y, c(156, 52), 'orange')
## fill with color
y = toRGB(y)
y = floodFill(y, c(48, 78), 'orange')
display(y)


## fill multiple shapes with different colors
y = y[110:512,1:130,]
points = rbind(c(50, 50), c(100, 50), c(150, 50))
colors = c("red", "green", "blue")
y = floodFill(y, points, colors)
display(y)

## area fill
x = readImage(system.file("images", "sample.png", package="EBImage"))
y = floodFill(x, c(226, 121), 1, tolerance=0.1)
y = floodFill(x, rbind(c(200, 400), c(200, 325)), 1, tolerance=0.1)
display(y)

## application to image stacks
f = system.file("images", "nuclei.tif", package="EBImage")
x = readImage(f)[1:250,1:250,]
x = opening(thresh(x, 12, 12), makeBrush(5, shape='disc'))
xy = lapply(getFrames(bwlabel(x)), function(x) computeFeatures.moment(x)[,1:2])
y = floodFill(toRGB(x), xy, c("red", "green", "blue"))
display(y)
}
55 changes: 31 additions & 24 deletions src/floodFill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,46 @@ template <class T> void _bwlabel(T *, int *, XYPoint);

/* -------------------------------------------------------------------------- */
SEXP
floodFill(SEXP x, SEXP point, SEXP col, SEXP tol) {
int i, nz, *dim, *pts;
floodFill(SEXP x, SEXP _pts, SEXP _col, SEXP _tol) {
int i, p, c, nf, np, nc, *dim, *pts;
double tol = REAL(_tol)[0];
XYPoint pt;
SEXP res;

SEXP res, points, colors;
// check image validity
validImage(x,0);
nz = getNumberOfFrames(x, 0);
validImage(x, 0);
nf = getNumberOfFrames(x, 1);
nc = getNumberOfChannels(x, COLOR_MODE(x));
dim = INTEGER(GET_DIM(x));
XYPoint size(dim[0], dim[1]);
if (size.x <= 0 || size.y <= 0) error("image must have positive dimensions");
if (LENGTH(point) != 2*nz) error("point must have a size of two times the number of frames");
if (LENGTH(col) != nz) error("color must have the same size as the number of frames");
if (LENGTH(_pts) != nf) error("length of points list must match the number of 'render' frames");
if (LENGTH(_col) != nf) error("length of color list must match the number of 'render' frames");

// initialize result
PROTECT(res = Rf_duplicate(x));

pts = INTEGER(point);

// do the job over images
for (i=0; i<nz; i++) {
pt.x = pts[i]-1;
pt.y = pts[nz+i]-1;

switch (TYPEOF(res)) {
case LGLSXP:
case INTSXP:
_floodFill<int>(&(INTEGER(res)[i*size.x*size.y]), size, pt, INTEGER(col)[i], REAL(tol)[0]);
break;
case REALSXP:
_floodFill<double>(&(REAL(res)[i*size.x*size.y]), size, pt, REAL(col)[i], REAL(tol)[0]);
break;
// iterate over images
for (i=0; i<nf; i++) {
points = VECTOR_ELT(_pts, i);
colors = VECTOR_ELT(_col, i);
np = INTEGER(GET_DIM(points))[0];
pts = INTEGER(points);
// iterate over points
for (p=0; p<np; p++) {
pt.x = pts[p]-1;
pt.y = pts[np+p]-1;
// iterate over channels
for (c=0; c<nc; c++) {
switch (TYPEOF(res)) {
case LGLSXP:
case INTSXP:
_floodFill<int>(&(INTEGER(res)[(i*nc+c)*size.x*size.y]), size, pt, INTEGER(colors)[c*np+p], tol);
break;
case REALSXP:
_floodFill<double>(&(REAL(res)[(i*nc+c)*size.x*size.y]), size, pt, REAL(colors)[c*np+p], tol);
break;
}
}
}
}

Expand Down Expand Up @@ -386,4 +394,3 @@ _fillHullT(T *_m, const XYPoint &srcsize) {
delete[] canvas;
delete[] bbox;
}

2 changes: 1 addition & 1 deletion tests/test.Rout.save
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ checking 'blackTopHat' ........................ PASS (af7e8fad58a0f587) 202411.9
checking 'selfComplementaryTopHat' ............ PASS (729954d933215c8d) 2151192
checking 'distmap' ............................ PASS (8ba0b0fb6770e8a3) 3856630
checking 'watershed' .......................... PASS (0bd6a31bc6197067) 1443978
checking 'floodFill' .......................... PASS (45775cc98ccde7d0) 1055560
checking 'floodFill' .......................... PASS (7a8a06c4be73e522) 2057384
checking 'fillHull' ........................... PASS (d3904950b8acdb73) 1096352
checking 'propagate' .......................... PASS (6c610fe4376714f6) 1613920
checking 'toRGB' .............................. PASS (57b9a77f5a12af4f) 8499672
Expand Down
8 changes: 4 additions & 4 deletions vignettes/EBImage-introduction.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -646,12 +646,12 @@ display(filled_logo)

```{r floodFill-logo, fig.width=dim(logo)[1L]/.dpi, fig.height=dim(logo)[2L]/.dpi, dpi=.dpi}
rgblogo = toRGB(logo)
rgblogo = floodFill(rgblogo, c(50, 50), "red")
rgblogo = floodFill(rgblogo, c(100, 50), "green")
rgblogo = floodFill(rgblogo, c(150, 50), "blue")
points = rbind(c(50, 50), c(100, 50), c(150, 50))
colors = c("red", "green", "blue")
rgblogo = floodFill(rgblogo, points, colors)
display( rgblogo )
```{r floodFill-img, fig.width=dim(img)[1L]/.dpi, fig.height=dim(img)[2L]/.dpi, dpi=.dpi/2}
display( floodFill(img, c(444, 222), col=0.2, tolerance=0.2) )
display(floodFill(img, rbind(c(200, 300), c(444, 222)), col=0.2, tolerance=0.2))
```

## Highlighting objects
Expand Down

0 comments on commit ea841b0

Please sign in to comment.