Search code examples
rimageidentification

R language: Converting images with regular dots to data frame


I have many ultraviolet images (*.png) each with 96 "wells" that appear as circles. These wells are arranged in a 8 by 12 fashion. Please see example below.

In each image, some of the 96 circles are lighted up (reacting to UV), and some of them are not. I would like to give a number to each circle, then identify them as either "lighted up" or "not lighted up" (with a pre-defined cutoff).

What do you think is the easiest way to achieve this in R? I've been playing with the package imager without much success.

One more note: Not all of my images have the same magnification (ie, circles are not always the same sizes across files, but within a file, they are roughly the same in size).

Here is an example of one of the images (embedded)


Solution

  • source("https://bioconductor.org/biocLite.R")
    biocLite("EBImage")
    library(EBImage)
    
    fn = YOURFILE
    img <- readImage(fn)
    
    # remove outer frame
    border <- 5 # 5px
    dims <- dim(img)
    img <- img[border:(dims[1]-border), border:(dims[2]-border),1:dims[3]]
    
    # some despectling
    img <- medianFilter(img,size=10) # blur image 
    # display(img) # check if blurring is fine
    
    # get plate mask.
    highpass_rect <- .02 # if images are darker, lower this
    rect <- bwlabel(dilate(img>highpass_rect, makeBrush(size=3)))
    rect <- rect > 0 # remove background
    # display(rect) # check if plate is recognized correctly
    
    highpass_light <- .4 # again, darker images need lower values here for the light objects
    # get single light objects
    img <- bwlabel(img > highpass_light)
    # display(img) # # check if all lights are displayed
    
    # get dimensions of rectangle
    rect_mid <- round(dim(rect@.Data[,,1]) / 2)
    x_range <- c(min(which(rect@.Data[,rect_mid[2],1])), max(which(rect@.Data[,rect_mid[2],1])))
    y_range <- c(min(which(rect@.Data[rect_mid[1],,1])), max(which(rect@.Data[rect_mid[1],,1])))
    
    # now substract border of plate
    # measured from the image you provided. 'should' scale to other images as well
    # x: left 69px, right: 74px, ROI: 1085
    x_range[1] <- x_range[1] + round((diff(x_range)) * (74/(1085+74+69)))
    x_range[2] <- x_range[2] - round((diff(x_range)) * (69/(1085+74+69)))
    
    # y: top 17px, bottom: 12px, ROI: 722
    y_range[1] <- y_range[1] + round((diff(y_range)) * (17/(722+17+12)))
    y_range[2] <- y_range[2] - round((diff(y_range)) * (12/(722+17+12)))
    
    # get pixel ranges for the 12x8 cells
    # we will use this as indexes in df
    x_cuts <- c(rep(NA,x_range[1]), cut(x_range[1]:x_range[2],12,dig.lab = 0,include.lowest = T,labels=F))
    y_cuts <- c(rep(NA,y_range[1]), cut(y_range[1]:y_range[2],8,dig.lab = 0,include.lowest = T,labels=F))
    
    # create 12X8 matrix
    df <- matrix(rep(0,12*8),nrow=8,dimnames = list(levels(cut(y_range,8,dig.lab = 0)),
                                                    levels(cut(x_range,12,dig.lab = 0,include.lowest = T))))
    # now go through lighted objects
    for (i in 1:(dim(table(img))-1)) { 
      # img == i is light nr i
      # get position of object
      pos <- which(img@.Data[,,1] == i, arr.ind = T)
    
      # add up enlighted pixels in df
      for (row in 1:nrow(pos)) {
        df[y_cuts[pos[row,2]], x_cuts[pos[row,1]]] <- df[y_cuts[pos[row,2]], x_cuts[pos[row,1]]] + 1
      }
    }
    
    print(df)
    
    >                  [143,235] (235,326] (326,417] (417,508] (508,599] (599,690] (690,781] (781,872] (872,963] (963,1.05e+03] (1.05e+03,1.14e+03] (1.14e+03,1.24e+03]
    > (90,1.8e+02]           1116         0         0         0      1974         0         0         0         0              0                   0                   0
    > (1.8e+02,2.7e+02]         0      2528         0         0         0      2528         0         0         0              0                1938                 845
    > (2.7e+02,3.6e+02]         0      2479      2518      2430         0         0         0         0         0           2477                2409                   0
    > (3.6e+02,4.5e+02]      1731      2339         0      2601         0      2775         0         0         0              0                   0                   0
    > (4.5e+02,5.4e+02]         0         0         0      2549         0      1033         0         0         0              0                   0                   0
    > (5.4e+02,6.3e+02]      2370      2449      2570         0         0         0         0      2555         0              0                   0                   0
    > (6.3e+02,7.3e+02]         0         0         0         0         0         0         0         0         0              0                1836                2348
    > (7.3e+02,8.2e+02]         0      1447      1760         0         0         0         0         0         0              0                2303                   0