Hough transform R PET package detecting a line - r

I have image as below and i want to detect the line in the image using PET package's hough tranform. I need help to understand how to get the line from that image.
library("PET", lib.loc="~/R/win-library/3.1")
library("raster", lib.loc="~/R/win-library/3.1")
p=matrix(diag(100), 100)
library(raster)
r <- raster(p)
plot(r)
abc=hough(p)
viewData(list(p, abc$hData), list("Phantom", "Hough transformed phantom"))
I applied hough transformation as above. The original image and image that i get after running the last line are as below
any inputs on how to get line's coordinates (from original image)? I understand that white point from the second image right side pane represents the line. That line is plotted using Polar Cordinate system. But i dont know how to use the second image to get coordinate of the original line
I looked at PET package's documentation but found it hard to understand :( I ran their sample code but i didn't understand it
==============================================================================
I followed advice given in comments by user NicE and updated my code as below
library("PET", lib.loc="~/R/win-library/3.1")
library("raster", lib.loc="~/R/win-library/3.1")
#p=matrix(diag(1000), 1000)
p=matrix(rep(0,10000), 100, 100)
# for (i in 1:100)
# {p[i,100-i+1]=1
# }
for (i in 1:100)
{p[i,50]=1
}
# library(raster)
# r <- raster(p)
# plot(r)
abc=hough(p)
maxPoint<-which(abc$hData==max(abc$hData),arr.ind=T)
library(pracma)
a<-cot(maxPoint[1,"row"]*pi/180)
b<-maxPoint[1,"col"]/sin(maxPoint[1,"row"]*pi/180)
a
b
par(pty="s")
par(mfrow=c(1,2))
#image(r, main="org")
image(p,main="original")
image(abc$hData, main="Houghmatrix")
Are new values of a and b correct? I feel that b should be 50 (perpendicular distance of the original line from the (0,0)). What am I doing wrong?
I would also like to know why does abc$hData has 181 rows and 143 columns. I can imagine 181 rows has something to do with PI radians is 180 degrees. But I dont have any clue about 143 columns...
=======================================================================
update 2
If i update my original matrix as I feel that i get weird answers. I get a=-57.6 and b=1786.12.
p=matrix(rep(0,10000), 100, 100)
for (i in 1:100)
{p[80,i]=1
}

Once you have the Hough transformation of the data, find the indices of the max of the matrix (assuming you have only one line):
maxPoint<-which(abc$hData==max(abc$hData),arr.ind=T)
You can also take the average of all the maxPoitns if you know there is only one line. In your case, you get this:
row col
[1,] 137 72
You need the default parameters of the function as well
houghParam<-unlist(abc$Header)
These give you RhoMin, ThethaMin, and DeltaMin and DeltaRho, the increments of both variables. With these you can get rho and theta from the matrix coordinates.
theta=(maxPoint[1,"row"]-1)*houghParam["DeltaXY1"]+houghParam["XYmin1"]
rho=(maxPoint[1,"col"]-1)*houghParam["DeltaXY2"]+houghParam["XYmin2"]
If the equation of the line is y=ax+b, you can get a and b using:
library(pracma)
a<--cot(theta)
b<-(rho)/sin(theta)
Also, in the man page of hough they state that they take the center of the image as (0,0) points.
For the math explanation, look at the Wikipedia page of Hough Transform ...
Edit: changed a formula and removed some wrong info
The total code would be:
library(PET)
library(pracma)
a=matrix(rep(0,10000), 100, 100)
for (i in 1:100)
{a[i,60]=1
}
d=matrix(rep(0,10000), 100, 100)
for (i in 1:100)
{d[60,i]=1
}
e=matrix(diag(100), 100)
getLineHough<-function(p){
abc=hough(p)
#get the brightest point in the hough tranform
maxPoint<-which(abc$hData==max(abc$hData),arr.ind=T)
#if there is only one line, can average the results in case there are several brightest points
maxPoint<-apply(maxPoint,2,mean)
houghParam<-unlist(abc$Header)
theta=(maxPoint[1]-1)*houghParam["DeltaXY1"]+houghParam["XYmin1"]
rho=(maxPoint[2]-1)*houghParam["DeltaXY2"]+houghParam["XYmin2"]
a<--cot(theta)
b<-rho/sin(theta)
par(mfrow=c(1,2))
image(p,main="original")
#add the predicted lines, also have to change the slope and intercept because
#the origin of the plot function is not the center of the image the bottom left corner
if(theta==0){
abline(v=(rho+50)/100)
} else{
abline((b+50-a*50)/100,a)
}
image(abc$hData, main="Houghmatrix")
}
getLineHough(a)
getLineHough(d)
getLineHough(e)
Edit2: the documentation doesn't really say what the value of the first row of the matrix is. Since there are 181 rows it should start at 0 and not 1*houghParam["DeltaXY1"]. Changed the code accordingly
Edit3: made the code into a function and added the predicted line to the plot

Related

How can I measure the distance of two features within an image in R?

I'm currently struggling with some image analysis. I have images of zebrafish embryo vasculature, and I want to measure the distance between certain features (the highest point to the lowest etc).
I have processed the images to be more visible (higher contrast) using EBImage
.
I would appreciate any guidance.
Since you are using R and EBImage, I would presume that there is more analysis intended than just extracting measurements from an image. If that is all you intend, other software such as Fiji or the more streamline precursor, ImageJ, may be more user-friendly.
To answer the question, don't use display() for the image as you show here. Rather, use the plot() method that uses the option method = raster as the default. With the image plotted in a graphic window, you can use all the tools of R to interact with the plot. The resolution you have is determined by the size of your image and display. All values are returned in pixels and obviously need to be scaled appropriately.
This example uses locator() in a small helper function to measure diagonal distances between vascular junctions (?) in the image.
This simple helper function marks two points and measures the distance between the points. End the call to locator() with a right-click control-click or the escape key. In RStudio, you may have to explicitly press another button in the window and the points/lines may not be drawn until all calls to locator() are terminated.
p2p <- function(n = 512) # end with ctrl-click or Esc
{
ans <- numeric()
while (n > 0) {
# this call to locator places 2 points as crosses
# and connects them with a line
p <- locator(2, type = "o", pch = 3, col = "magenta")
if (is.null(p)) break
ans <- c(ans, sqrt(sum(sapply(p, diff)^2)))
n <- n - 1
}
return(ans) # return the vector of point-to-point distances
}
Now replot the image in the question (without the elements from the browser display) and then interact with the image.
plot(img) # not 'display(img)'
d <- p2p() # interact with the image, collecting distances
Here's the image after selecting six pairs of points with the distances measured between each pair of points.
round(d, 1)
> [1] 113.4 99.2 109.4 110.8 120.6 122.7
mean(d)
> 112.6736
Have fun!
Not dumb at all. Yes, it is in pixels—EBImage and R gives you fractional pixels.

R - draw line between two specific values in plot in R

I need to draw a line between two specific values from a plot in R. That's what I want. If it is possible to draw a line between those two consecutive values which the difference between values is higher than 3. Else, draw it knowing the values from the dataset. Also, I would like to add a number under or above the line. Thanks.
Here the link where you can find the image "ImageR.png"
https://www.dropbox.com/sh/blnr3jvius8f3eh/AACOhqyzZGiDHAOPmyE__873a?dl=0
Something like this should do it. You may have to play with pos and offset in text to get it to look good on your data.
x <- rnorm(20, sd=3)
d <- diff(x)
plot(x)
for (i in which(d>3)) {
lines(c(i,i+1), x[i:(i+1)])
text(i+.5, mean(x[i:(i+1)]), round(d[i],1), pos=2)
}

How to count line segment occurrences by pixel in R?

I am trying to convey the concentration of lines in 2D space by showing the number of crossings through each pixel in a grid. I am picturing something similar to a density plot, but with more intuitive units. I was drawn to the spatstat package and its line segment class (psp) as it allows you to define line segments by their end points and incorporate the entire line in calculations. However, I'm struggling to find the right combination of functions to tally these counts and would appreciate any suggestions.
As shown in the example below with 50 lines, the density function produces values in (0,140), the pixellate function tallies the total length through each pixel and takes values in (0, 0.04), and as.mask produces a binary indictor of whether a line went through each pixel. I'm hoping to see something where the scale takes integer values, say 0..10.
require(spatstat)
set.seed(1234)
numLines = 50
# define line segments
L = psp(runif(numLines),runif(numLines),runif(numLines),runif(numLines), window=owin())
# image with 2-dimensional kernel density estimate
D = density.psp(L, sigma=0.03)
# image with total length of lines through each pixel
P = pixellate.psp(L)
# binary mask giving whether a line went through a pixel
B = as.mask.psp(L)
par(mfrow=c(2,2), mar=c(2,2,2,2))
plot(L, main="L")
plot(D, main="density.psp(L)")
plot(P, main="pixellate.psp(L)")
plot(B, main="as.mask.psp(L)")
The pixellate.psp function allows you to optionally specify weights to use in the calculation. I considered trying to manipulate this to normalize the pixels to take a count of one for each crossing, but the weight is applied uniquely to each line (and not specific to the line/pixel pair). I also considered calculating a binary mask for each line and adding the results, but it seems like there should be an easier way. I know that you can sample points along a line, and then do a count of the points by pixel. However, I am concerned about getting the sampling right so that there is one and only one point per line crossing of a pixel.
Is there is a straight-forward way to do this in R? Otherwise would this be an appropriate suggestion for a future package enhancement? Is this more easily accomplished in another language such as python or matlab?
The example above and my testing has been with spatstat 1.40-0, R 3.1.2, on x86_64-w64-mingw32.
You are absolutely right that this is something to put in as a future enhancement. It will be done in one of the next versions of spatstat. It will probably be an option in pixellate.psp to count the number of crossing lines rather than measure the total length.
For now you have to do something a bit convoluted as e.g:
require(spatstat)
set.seed(1234)
numLines = 50
# define line segments
L <- psp(runif(numLines),runif(numLines),runif(numLines),runif(numLines), window=owin())
# split into individual lines and use as.mask.psp on each
masklist <- lapply(1:nsegments(L), function(i) as.mask.psp(L[i]))
# convert to 0-1 image for easy addition
imlist <- lapply(masklist, as.im.owin, na.replace = 0)
rslt <- Reduce("+", imlist)
# plot
plot(rslt, main = "")

Finding and labelling candidates/ouliers outside a curve in R plot

I am stuck in simple problem. I have a scatter plot.
I am plotted confidence lines around it using my a custom formula. Now, i just want only the names outside the cutoff lines to be displayed nothing inside. But, I can't figure out how to subset my data on the based of the line co-ordinates.
The line is plotted using the lines function which is a vector of 128 x and y values. Now, how do I subset my data (x,y points) based on these 2 values. I can apply a static limit of a single number of sub-setting data like 1,2 or 3 but how to use a vector to subset data, got me stuck.
For an reproducible example, consider :
df=data.frame(x=seq(2,16,by=2),y=seq(2,16,by=2),lab=paste("label",seq(2,16,by=2),sep=''))
plot(df[,1],df[,2])
# adding lines
lines(seq(1,15),seq(15,1),lwd=1, lty=2)
# adding labels
text(df[,1],df[,2],labels=df[,3],pos=3,col="red",cex=0.75)
Now, I need just the labels, which are outside or intersecting the line.
What I was trying to subset my dataframe with the values used for the lines, but I cant make it right.
Now, static sub-setting can be done for single values like
df[which(df[,1]>8 & df[,2]>8),] but how to do it for whole list.
I also tried sapply, to cycle over all the values of x and y used for lines on the df iteratively, but most values become +ve for a limit but false for other values.
Thanks
I will speak about your initial volcano-type-graph problem and not the made up one because they are totally different.
So I really thought this a lot and I believe I reached a solid conclusion. There are two options:
1. You know the equations of the lines, which would be really easy to work with.
2. You do not know the equation of the lines which means we need to work with an approximation.
Some geometry:
The function shows the equation of a line. For a given pair of coordinates (x, y), if y > the right hand side of the equation when you pass x in, then the point is above the line else below the line. The same concept stands if you have a curve (as in your case).
If you have the equations then it is easy to do the above in my code below and you are set. If not you need to make an approximation to the curve. To do that you will need the following code:
df=data.frame(x=seq(2,16,by=2),y=seq(2,16,by=2),lab=paste("label",seq(2,16,by=2),sep=''))
make_vector <- function(df) {
lab <- vector()
for (i in 1:nrow(df)) {
this_row <- df[i,] #this will contain the three elements per row
if ( (this_row[1] < max(line1x) & this_row[2] > max(line1y) & this_row[2] < a + b*this_row[1])
|
(this_row[1] > min(line2x) & this_row[2] > max(line2y) & this_row[2] > a + b*this_row[1]) ) {
lab[i] <- this_row[3]
} else {
lab[i] <- <NA>
}
}
return(lab)
}
#this_row[1] = your x
#this_row[2] = your y
#this_row[3] = your label
df$labels <- make_vector(df)
plot(df[,1],df[,2])
# adding lines
lines(seq(1,15),seq(15,1),lwd=1, lty=2)
# adding labels
text(df[,1],df[,2],labels=df[,4],pos=3,col="red",cex=0.75)
The important bit is the function. Imagine that you have df as you created it with x,y and labs. You also will have a vector with the x,y coordinates for line1 and x,y coordinates for line2.
Let's see the condition of line1 only (the same exists for line 2 which is implemented on the code above):
this_row[1] < max(line1x) & this_row[2] > max(line1y) & this_row[2] < a + b*this_row[1]
#translates to:
#this_row[1] < max(line1x) = your x needs to be less than the max x (vertical line in graph below
#this_row[2] > max(line1y) = your y needs to be greater than the max y (horizontal line in graph below
#this_row[2] < a + b*this_row[1] = your y needs to be less than the right hand side of the equation (to have a point above i.e. left of the line)
#check below what the line is
This will make something like the below graph (this is a bit horrible and also magnified but it is just a reference. Visualize it approximating your lines):
The above code would pick all the points in the area above the triangle and within the y=1 and x=1 lines.
Finally the equation:
Having 2 points' coordinates you can figure out a line's equation solving a system of two equations and 2 parameters a and b. (y = a +bx by replacing y,x for each point)
The 2 points to pick are the two points closest to the tangent of the first line (line1). Chose those arbitrarily according to your data. The closest to the tangent the better. Just plot the spots and eyeball.
Having done all the above you have your points with your labels (approximately at least).
And that is the only thing you can do!
Long talk but hope it helps.
P.S. I haven't tested the code because I have no data.

dimensions of kde object from ks package, R

I am using the ks package from R to estimate 2d space utilization using distance and depth information. What I would like to do is to use the 95% contour output to get the maximum vertical and horizontal distance. So essentially, I want to be able to get the dimensions or measurements of the resulting 95% contour.
Here is a piece of code with as an example,
require(ks)
dist<-c(1650,1300,3713,3718)
depth<-c(22,19.5,20.5,8.60)
dd<-data.frame(cbind(dist,depth))
## auto bandwidth selection
H.pi2<-Hpi(dd,binned=TRUE)*1
ddhat<-kde(dd,H=H.pi2)
plot(ddhat,cont=c(95),lwd=1.5,display="filled.contour2",col=c(NA,"palegreen"),
xlab="",ylab="",las=1,ann=F,bty="l",xaxs="i",yaxs="i",
xlim=c(0,max(dd[,1]+dd[,1]*0.4)),ylim=c(60,-3))
Any information about how to do this will be very helpful. Thanks in advance,
To create a 95% contour polygon from your 'kde' object:
library(raster)
im.kde <- image2Grid (list(x = ddhat$eval.points[[1]], y = ddhat$eval.points[[2]], z = ddhat$estimate))
kr <- raster(im.kde)
It is likely that one will want to resample this raster to a higher resolution before constructing polygons, and include the following two lines, before creation of the polygon object:
new.rast <- raster(extent(im.kde),res = c(50,50))
kr <- resample(kr, new.rast)
bin.kr <- kr
bin.kr[bin.kr < contourLevels(k, prob = 0.05)]<-NA
bin.kr[bin.kr > 0]<-1
k.poly<-rasterToPolygons(bin.kr,dissolve=T)
Note that the results are similar, but not identical, to Hawthorne Beier's GME function 'kde'. He does use the kde function from ks, but must do something slightly different for the output polygon.
At the moment I'm going for the "any information" prize rather than attempting a final answer. The ks:::plot.kde function dispatches to ks:::plotkde.2d in this case. It works its magic through side effects and I cannot get these functions to return values that can be inspected in code. You would need to hack the plotkde.2d function to return the values used to plot the contour lines. You can visualize what is in ddhat$estimate with:
persp(ddhat$estimate)
It appears that contourLevels examines the estimate-matrix and finds the value at which greater than the specified % of the total density will reside.
> contourLevels(ddhat, 0.95)
95%
1.891981e-05
And then draws the contout based on which values exceed that level. (I just haven't found the code that does that yet.)

Resources