Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Measuring whitespace in a jpeg

I want to measure the amount of a jpeg that is white/yellow (within a tolerance that can be adjusted).

I am trying to develop a quality control tool that measures defects in almonds. The defects are scratches in the skin of brown almonds (see image below). Since these defects are white/yellow I would like a way to simply load the image into R and and have it measure the amount of the image that is white. I can then experimentally determine an acceptable level. All images would be the same size.

Almond Picture

like image 283
stm027 Avatar asked Feb 12 '23 02:02

stm027


2 Answers

Carl's post is 99% of the answer, here's a tiny bit more to get a measurement of the amount of the image that is white/near-white:

# Required package
library(jpeg)

# Load and plot data
jpg <- "C:\\my_image.jpg"
my_jpg <- readJPEG(jpg)

# or for stand-alone reproducibility: 
# my_jpg <- readJPEG(system.file("img", "Rlogo.jpg", package="jpeg"))

# have a look at the original image
plot(0:1,0:1,type="n",ann=FALSE,axes=FALSE)
rasterImage(my_jpg,0,0,1,1)
# prints the jpg, just to make sure it's gone in ok

enter image description here

# Following Carl's example, subset each channel to get
# the pixels with white values (ie. close to 1) and make
# non-white pixels black for convienence. As Carl says,
# you'll need to adjust the values from 0.99 for your
# use case 
white_red_channel <- ifelse(my_jpg[,,1] > 0.99, 1,0)
white_green_channel <- ifelse(my_jpg[,,2] > 0.99, 1,0)
white_blue_channel <- ifelse(my_jpg[,,3] > 0.99, 1,0)
# combine channels into array
white <- simplify2array(list(white_red_channel, 
                             white_green_channel, 
                             white_blue_channel))

# plot white/near-white pixels only
plot(0:1,0:1,type="n",ann=FALSE,axes=FALSE)
rasterImage(white, 0, 0, 1, 1)
# looks pretty good, whiter areas on original are highlighted here:

enter image description here

# find proportion of image that is not black
whites <- white_red_channel + white_green_channel + white_blue_channel # sum channels
not_black <- sum(whites > 0) # count pixels that are not black
total_pixels <- ncol(whites) * nrow(whites) # find total number of pixels
not_black / total_pixels # proportion of non-black pixels
[1] 0.01390833
like image 108
Ben Avatar answered Feb 15 '23 11:02

Ben


It would be easier to rasterize, as BondedDust suggested, but regardless, what you need is to define the color range (RGB or otherwise) which qualifies as a defect.
Consider:

library(jpeg)
foo <- readJPEG('foo.jpeg')  #returns a NxMx3 array

# find qualifiers in each layer for lmit values you chose
badred <-which(foo[,,1] > redlimit, arr.ind=TRUE)
badgreen <-which(foo[,,2] > greenlimit, arr.ind=TRUE)
badblue <-which(foo[,,3] > bluelimit, arr.ind=TRUE)

That gives you three matrices full of row,column index pairs. Find pairs which match up (there's some quick way to do that but naturally I forget how right now :-( ). Or you could plot all three matrices and observe the overlap regions:

plot(badred[,1],badred[,2],pch=19,cex=.5,col='red')
points(badgreen[,1],badgreen[,2],pch=19,cex=.5,col='green')
points(badblue[,1],badblue[,2],pch=19,cex=.5,col='blue')
like image 39
Carl Witthoft Avatar answered Feb 15 '23 09:02

Carl Witthoft