Skip to content

Instantly share code, notes, and snippets.

@tractatus
Created September 7, 2017 14:17
Show Gist options
  • Save tractatus/9baea36c3c53b937dcc7f6aa06a1f082 to your computer and use it in GitHub Desktop.
Save tractatus/9baea36c3c53b937dcc7f6aa06a1f082 to your computer and use it in GitHub Desktop.
readroi ImageJ to R
## readroi.R - Read ImageJ files in to R
## Copyright (C) 2011 David C. Sterratt <david.c.sterratt@ed.ac.uk>
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
## ImageJ/NIH Image 64 byte ROI outline header
## 2 byte numbers are big-endian signed shorts
## 0-3 "Iout"
## 4-5 version (>=217)
## 6-7 roi type
## 8-9 top
## 10-11 left
## 12-13 bottom
## 14-15 right
## 16-17 NCoordinates
## 18-33 x1,y1,x2,y2 (straight line)
## 34-35 stroke width (v1.43i or later)
## 36-39 ShapeRoi size (type must be 1 if this value>0)
## 40-43 stroke color (v1.43i or later)
## 44-47 fill color (v1.43i or later)
## 48-49 subtype (v1.43k or later)
## 50-51 options (v1.43k or later)
## 52-52 arrow style or aspect ratio (v1.43p or later)
## 53-53 arrow head size (v1.43p or later)
## 54-55 rounded rect arc size (v1.43p or later)
## 56-59 position
## 60-63 reserved (zeros)
## 64- x-coordinates (short), followed by y-coordinates
##
##' Read an ImageJ ROI file. This returns a structure containing the
##' ImageJ data.
##'
##' @title Read an ImageJ ROI file
##' @param file Name of ImageJ ROI file to read
##' @param verbose Whether to report information
##' @return A structure of class IJROI containing the ROI information
##' @author David Sterratt
##' @export
##' @examples
##' path <- file.path(system.file(package = "retistruct"), "extdata", "ijroi")
##' im <- as.raster(readPNG(file.path(path, "imagej-logo.png")))
##' plot(NA, NA, xlim=c(0, ncol(im)), ylim=c(nrow(im), 0), asp=1)
##' rasterImage(im, 0, nrow(im), ncol(im), 0, interpolate=FALSE)
##' r <- read.ijroi(file.path(path, "rect.roi"))
##' plot(r, TRUE)
##' r <- read.ijroi(file.path(path, "polygon.roi"))
##' plot(r, TRUE)
##' r <- read.ijroi(file.path(path, "oval.roi"))
##' plot(r, TRUE)
##'
read.ijroi <- function(file, verbose=FALSE) {
## Define internal helper functions
getByte <- function(con) {
pos <- seek(con)
n <- readBin(con, raw(0), 1, size=1)
if (verbose)
message(paste("Pos ", pos , ": Byte ", n, sep=""))
return(as.integer(n))
}
getShort <- function(con) {
pos <- seek(con)
n <- readBin(con, integer(0), 1, size=2, signed=TRUE, endian="big")
if (n < -5000) {
seek(con, -2, origin="current")
n <- readBin(con, integer(0), 1, size=2, signed=FALSE, endian="big")
}
if (verbose)
message(paste("Pos ", pos , ": Short ", n, sep=""))
return(n)
}
getInt <- function(con) {
pos <- seek(con)
n <- readBin(con, integer(0), 1, size=4, signed=TRUE, endian="little")
if (verbose)
message(paste("Pos ", pos , ": Integer ", n, sep=""))
return (n);
}
getFloat <- function(con) {
pos <- seek(con)
n <- readBin(con, double(0), 1, size=4, signed=TRUE, endian="big")
if (verbose)
message(paste("Pos ", pos , ": Float ", n, sep=""))
return (n);
}
## subtypes
subtypes <- list(TEXT = 1,
ARROW = 2,
ELLIPSE = 3)
## options
opts <- list(SPLINE_FIT = as.raw(1),
DOUBLE_HEADED = as.raw(2),
OUTLINE = as.raw(4))
## types
types <- list(polygon = 0,
rect = 1,
oval = 2,
line = 3,
freeline = 4,
polyline = 5,
noRoi = 6,
freehand = 7,
traced = 8,
angle = 9,
point = 10)
## Main code
name <- NULL
if (!is.null(file)) {
size <- file.info(file)$size
if (!grepl(".roi$", file) && size>5242880)
stop("This is not an ROI or file size>5MB)")
name <- basename(file)
}
## Open the connection
con <- file(file, "rb")
## Test that it's the right kind of file
if (getByte(con) != 73 || getByte(con) != 111) { ## "Iout"
stop("This is not an ImageJ ROI");
}
if (verbose)
message("Reading format data")
## Create place to store data
r <- list()
## Get the data. This all has to be in the order corresponding to the
## positions mentioned at the top of the file
getShort(con) # Unused
r$version <- getShort(con)
r$type <- getByte(con)
getByte(con) # Unused
r$top <- getShort(con) # TOP
r$left <- getShort(con) # LEFT
r$bottom <- getShort(con) # Bottom
r$right <- getShort(con) # RIGHT
r$width <- with(r, right-left)
r$height <- with(r, bottom-top)
r$n <- getShort(con) # N_COORDINATES
r$x1 <- getFloat(con)
r$y1 <- getFloat(con)
r$x2 <- getFloat(con)
r$y2 <- getFloat(con)
r$strokeWidth <- getShort(con) # STROKE_WIDTH
r$shapeRoiSize <- getInt(con) # SHAPE_ROI_SIZE
r$strokeColor <- getInt(con)
r$fillColor <- getInt(con)
r$subtype <- getShort(con) # SUBTYPE
r$options < as.raw(getShort(con)) # OPTIONS
if (r$type == "oval") {
r$aspectRatio <- getFloat(con) # ELLIPSE_ASPECT_RATIO
} else {
r$style <- getByte(con) # ARROW_STYLE
r$headSize <- getByte(con) # ARROW_HEAD_SIZE
r$arcSize <- getShort(con) # ROUNDED_RECT_ARC_SIZE
}
r$position <- getInt(con) # POSITION
getShort(con) # Unused
getShort(con) # Unused
if (verbose)
message("Reading coordinate data")
if (!is.null(name) && (grepl(".roi$", name)))
r$name <- substring(name, 1, nchar(name) - 4)
isComposite <- (r$shapeRoiSize >0);
if (isComposite) {
stop("Composite ROIs not supported")
## roi = getShapeRoi();
## if (version>=218) getStrokeWidthAndColor(roi);
## roi.setPosition(position);
## return roi;
}
## if (r$type %in% types[c("rect")]) {
## if (r$type %in% types[c("oval")]) {
if (r$type %in% types["line"]) {
if (r$subtype %in% types["ARROW"]) {
r$doubleHeaded <- (r$options & opts$DOUBLE_HEADED) !=0
r$outline <- (r$options & opts$OUTLINE) !=0
## if (style>=Arrow.FILLED && style<=Arrow.OPEN)
## ((Arrow)roi).setStyle(style);
## if (headSize>=0 && style<=30)
## ((Arrow)roi).setHeadSize(headSize);
## } else
## roi = new Line(x1, y1, x2, y2);
}
}
## Read in coordinates
if (r$type %in% types[c("polygon", "freehand", "traced", "polyline", "freeline", "angle", "point")]) {
r$coords <- matrix(NA, r$n, 2)
if (r$n > 0) {
for (i in 1:r$n) {
r$coords[i, 1] <- getShort(con)
}
for (i in 1:r$n) {
r$coords[i, 2] <- getShort(con)
}
r$coords[r$coords<0] <- 0
r$coords[,1] <- r$coords[,1] + r$left
r$coords[,2] <- r$coords[,2] + r$top
}
}
r$strType <- names(types)[which(types==r$type)]
r$types <- types
close(con)
class(r) <- "IJROI"
return(r)
}
##' @title Plot IJROI object
##' @param x The IJROI object
##' @param add Whether to add to an existing plot
##' @param ... Additional parameters
##' @method plot IJROI
##' @export
##' @author David Sterratt
plot.IJROI <- function(x, add=FALSE, ...) {
with(x, {
if (!add)
plot(NA, NA, xlim=range(coords[,1]), ylim=range(coords[,2]))
if (type == types["rect"]) {
rect(left, bottom, right, top, ...)
}
if (type == types["oval"]) {
theta <- seq(0, 2*pi, len=360)
polygon(left + width/2*(1 + sin(theta)),
top + height/2*(1 + cos(theta)), ...)
}
if (type == types["line"]) {
warning("Plotting line not yet supported")
}
if (type %in% types[c("polygon", "freehand", "traced")]) {
coords <- rbind(coords, coords[1,])
lines(coords, ...)
}
if (type %in% types[c("polyline", "freeline", "angle")]) {
lines(coords, ...)
}
if (type %in% types[c("point")]) {
points(coords, ...)
}
})
}
## Demo
## demo.roi <- function() {
## im <- as.raster(readPNG("~/image.png"))
## plot(NA, NA, xlim=c(0, ncol(im)), ylim=c(nrow(im), 0), asp=1)
## rasterImage(im, 0, nrow(im), ncol(im), 0)
## r <- read.roi("~/image.roi")
## plot(r, TRUE, col="cyan")
## return(r)
## }
## ## if (subtype==ELLIPSE) {
## ## double ex1 = getFloat(X1);
## ## double ey1 = getFloat(Y1);
## ## double ex2 = getFloat(X2);
## ## double ey2 = getFloat(Y2);
## ## roi = new EllipseRoi(ex1,ey1,ex2,ey2,aspectRatio);
## ## break;
## ## }
## ## default:
## ## throw new IOException("Unrecognized ROI type: "+type);
## ## }
## ## if (name!=null) roi.setName(name);
## ## // read stroke width, stroke color and fill color (1.43i or later)
## ## if (version>=218) {
## ## getStrokeWidthAndColor(roi);
## ## boolean splineFit = (options&SPLINE_FIT)!=0;
## ## if (splineFit && roi instanceof PolygonRoi)
## ## ((PolygonRoi)roi).fitSpline();
## ## }
## ## if (version>=218 && subtype==TEXT)
## ## roi = getTextRoi(roi);
## ## roi.setPosition(position);
## ## return roi;
## ## }
## ## }
## ## void getStrokeWidthAndColor(Roi roi) {
## ## if (strokeWidth>0)
## ## roi.setStrokeWidth(strokeWidth);
## ## if (strokeColor!=0) {
## ## int alpha = (strokeColor>>24)&0xff;
## ## roi.setStrokeColor(new Color(strokeColor, alpha!=255));
## ## }
## ## if (fillColor!=0) {
## ## int alpha = (fillColor>>24)&0xff;
## ## roi.setFillColor(new Color(fillColor, alpha!=255));
## ## }
## ## }
## ## Roi getTextRoi(Roi roi) {
## ## Rectangle r = roi.getBounds();
## ## int hdrSize = RoiEncoder.HEADER_SIZE;
## ## int size = getInt(hdrSize);
## ## int style = getInt(hdrSize+4);
## ## int nameLength = getInt(hdrSize+8);
## ## int textLength = getInt(hdrSize+12);
## ## char[] name = new char[nameLength];
## ## char[] text = new char[textLength];
## ## for (int i=0; i<nameLength; i++)
## ## name[i] = (char)getShort(hdrSize+16+i*2);
## ## for (int i=0; i<textLength; i++)
## ## text[i] = (char)getShort(hdrSize+16+nameLength*2+i*2);
## ## Font font = new Font(new String(name), style, size);
## ## Roi roi2 = new TextRoi(r.x, r.y, new String(text), font);
## ## roi2.setStrokeColor(roi.getStrokeColor());
## ## roi2.setFillColor(roi.getFillColor());
## ## return roi2;
## ## }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment