I have an image representing an intensity graph:
intensity graph
In order to multiplicate two intensitiy graphs I need to save the coordinates of this graph. Thus, I first want to find the (middle or one border of the) line and then get its coordinates. So far I tried a few things. Which came nearest to the solution was using the LineSegmentDetector package:
library(pixmap)
image <- read.pnm(file = "graph.pgm", cellres = 1)
x <- image#grey * 255
linesegments <- image_line_segment_detector(x)
linesegments
plot(image)
plot(linesegments, add = TRUE, col = "red")
This gives me a couple of line segments:
enter image description here
However, the aim is to get one line of 1 pixel width like this:
enter image description here
Subsequently, I would need the coordinates of this graph. I would need one y value for every pixel in x direction.
I hope my problem is clear and am thankful for any help!
I'm working on the following: I have a store layout, example see below (cannot add the real thing for GDPR reasons but the example should do the trick) on which I have xy coordinates from visitors (anonymous of course)
I already placed a grid on the picture so I can see which route they take in the store. That works fine. origin is bottom left and x & y are scaled from 0-100.
So far so good. Now next step is identifying the coordinates of the shelves, rectangles in the picture. Is there a way to do this without having to do this manually? The real store layout contains more than 900 shelves or am I pushing out the boat too far?
The output I'm looking for is a dataframe that contains a shelve ID and the coordinates for the corners. Idea is to create some heatmaps in the store to see that there are blind spots, hotspots, ...
The second analysis needs also the integer points. The idea is to create vectors of visitor points so we get a direction to which they are looking. By using the scope of what a human being can see I would give percentages of "seen" the products based on intersection with integer points.
thx!
JL
One approach is to perform clustering on the black pixels of the image. The clusters are then the shelves. If the shelves are axis parallel you can find the rectangles by just taking min/max in each direction. This works quite well:
Sample code (I converted the image to PNG as it is easier to read than gif):
library(png)
library(dbscan)
library(tidyverse)
library(RColorBrewer)
img <- readPNG("G18JU.png")
is_black <-
img %>%
apply(c(1, 2), sum) %>% #sum all color channels
{. < 2.5} %>% # we assume black if the sum is lower than 2.5 (max value is 3)
which(arr.ind=TRUE) # the indices of the black pixels
clust <- dbscan(is_black, 2) # identify clusters
rects <-
as.tibble(is_black) %>%
mutate(cluster = clust$cluster) %>% # add cluster information
group_by(cluster) %>%
## find corner points of rectangles normalized to [0, 1]
summarise(xleft = max(col) / dim(img)[2],
ybottom = 1 - min(row) / dim(img)[1],
xright = min(col) / dim(img)[2],
ytop = 1 - max(row) / dim(img)[1])
## plot the image and the rectangles
plot(c(0, 1), c(0, 1), type="n")
rasterImage(img, 0, 0, 1, 1)
for (i in seq_len(nrow(rects))) {
rect(rects$xleft[i], rects$ybottom[i], rects$xright[i], rects$ytop[i],
border = brewer.pal(nrow(rects), "Paired")[i], lwd = 2)
}
Of course this approach also detects other black lines as "rectangles" (e.g. the black border). But I guess you can easily create a "clean" image.
Edit: extend method to find shelves that share a black line
To extend the method such that it can separate shelves that share a black line:
First, identify the rectangles in the way outlined above.
Then, extract each rectangle from the image and compute the row means. This gives you a 1d image (= line) for each rectangle. In this line apply thresholding and clustering as before. The clusters are now the black line segments, and the mean of each cluster corresponds to a vertical line shared by two shelves.
To find horizontal shared lines, the same procedure can be applied, but with column means instead of row means.
I have the following dataframe:
df<-data.frame(consumed= c("level1_plt1", "level1_plt2", "level1_plt3", "level1_plt3","level1_plt2","level1_plt4","level1_plt5","level1_plt5","level1_plt6","level1_plt7","level1_plt8","level1_plt9","level1_plt10","level1_plt10","level1_plt1","level1_plt1","level1_plt6","level1_plt6","level1_plt9","level1_plt9","level1_plt11","level1_plt11","level1_plt11","level2_lep1","level2_lep4","level2_lep3"),consumer=c("level2_lep1","level2_lep2","level2_lep3","level2_lep2","level2_lep4", "level2_lep4","level2_lep5","level2_lep5","level2_lep6","level2_lep7","level2_lep8","level2_lep9","level2_lep10","level2_lep10","level2_lep8","level2_lep8","level2_lep1","level2_lep1","level2_lep3","level2_lep11","level2_lep12","level2_lep13","level2_lep13", "level3_pst1","level3_pst3","level3_pst4"))
And have preformed the following steps to get an igraph tripartite output:
links<-
df%>%
group_by(consumed, consumer) %>%
summarize(freq=n())
g<- graph_from_data_frame(d=links,directed=FALSE)
layer <- rep(2, length(V(g)$name))
layer[grepl("level1_",V(g)$name)]=1
layer[grepl("level3_",V(g)$name)]=3
names<- V(g)$name
names<-sub("level2_","", names)
names<-sub("level3_","", names)
names<-sub("level1_","", names)
V(g)$name = names
layout = layout_with_sugiyama(g, layers=layer)
E(g)$width <- E(g)$freq
V(g)$vertex_degree <- degree(g)*7
plot(g,
layout=cbind(layer,layout$layout[,1]),edge.curved=0,
vertex.shape=c("square","circle","square")[layer],
vertex.frame.color = c("darkolivegreen","darkgoldenrod","orange3")
[layer],
vertex.color=c("olivedrab","goldenrod1","orange1")[layer],
vertex.label.color="white",
vertex.label.font=2,
vertex.size=V(g)$vertex_degree,
vertex.label.dist=c(0,0,0)[layer],
vertex.label.degree=0, vertex.label.cex=0.5)
And I would like to do two things to adjust the picture, if possible:
Order the layers from the largest shape (highest degree) to smallest shape (smallest degree). For example, in the green layer the order could be as follows: plt9, plt3,plt2,plt11,plt6,plt1,plt7,plt5,plt4,plt10,plt8.
Create space between the shapes so that there is no overlap (e.g. lep3 and lep4). I like the current sizes/proportions so I am opposed to making shapes smaller to create space between shapes.
Flip the graph and vertex font 90 degrees counter-clockwise so that from bottom to top it would be in the order green layer-->yellow layer-->orange layer. (I guess it is always an option to rotate vertex text and I can rotate the image in word or ppt.)
I know this question is old, but I hope that the answer will help someone.
Rather than using layout_with_sugiyama, It may be easiest to do this with
a custom layout. It is not very hard to do so. You already constructed the
horizontal position with your layer variable. To get the vertical positions,
we need to order the vertices by size (vertex_degree) and then allow shape proportional to the size, so we will set the height using cumsum on the vertex_degrees within each layer. After I make the layout the complex call to plot is the same as yours except
that I swap my custom layout for your call to sugiyama.
MyLO = matrix(0, nrow=vcount(g), ncol=2)
## Horizontal position is determined by layer
MyLO[,1] = layer
## Vertical position is determined by sum of sorted vertex_degree
for(i in 1:3) {
L = which(layer ==i)
OL = order(V(g)$vertex_degree[L], decreasing=TRUE)
MyLO[L[OL],2] = cumsum(V(g)$vertex_degree[L][OL])
}
plot(g,
layout=MyLO, edge.curved=0,
vertex.shape=c("square","circle","square")[layer],
vertex.frame.color = c("darkolivegreen","darkgoldenrod","orange3")[layer],
vertex.color=c("olivedrab","goldenrod1","orange1")[layer],
vertex.label.color="white",
vertex.label.font=2,
vertex.size=V(g)$vertex_degree,
vertex.label.dist=0,
vertex.label.degree=0, vertex.label.cex=0.5)
Hi R expert of the world,
Assume I have a point pattern that generate an intensity map and that this map is color coded in 3 region in an pixeled image.... how could I get the color-coded area?
here it is an example using spatstat:
library(spatstat)
japanesepines
Z<-density(japanesepines); plot(dens) # ---> I create a density map
b <- quantile(Z, probs = (0:3)/3) # ---> I "reduce it" to 3 color-ceded zones
Zcut <- cut(Z, breaks = b, labels = 1:3); plot(Zcut)
class(Zcut) # ---> and Zcut is my resultant image ("im")
Thank you in advance
Sacc
In your specific example it is very easy to calculate the area because you used quantile to cut the image: This effectively divides the image into areas of equal size, so there should be three areas of size 1/3 since the window is a unit square. In general to calculate areas from a factor valued image you could use as.tess and tile.areas (continuing your example):
Ztess <- as.tess(Zcut)
tile.areas(Ztess)
In this case the areas are 0.333313, which must be due to discretization.
I'm not exactly sure what you're after, but you can count up the number of pixels in each color using the table() function.
table(Zcut[[1]])
I want to draw circles with semitransparent colour on top of a map (showing prevalence), the colour at overlap areas is too heavy to show the background image, the code am using is too long to put here, but for circles am using:
symbols(data[,c(9, 10)],
circles = 5/(pi * a.rad * cos(atan(b.rad / a.rad * tan((data[,10])*pi/180)))/180000),
fg = NULL,
bg = rgb(0, 1, 0, 0.18),
inches = F,
add = T)
I want to joint circles to plot them with the same "transparent" colour, is there some way to do this using R? Or is there any other alternative using R*?
*Please note: The maps am using are develped using R, and I can't use other program to develop them.
There's no simple way, since you're plotting a series of circles. If you don't demand the fill color be consistent, you could set the alpha value to a very small level. To get a consistent fill color, you'll have to calculate the intersection areas of the circles and plot those areas as well as the nonintersection areas (instead of plotting circles). If you have multiple overlapping circles, you can see that will become a computational nightmare.
My personal recommendation is to plot with the smallest visible alpha value so that the worst-case overlap area doesn't obscure the map. This has the side-effect of clearly indicating the density of overlap areas.