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 trying to plot some sine waves (code example in plain js here).
When freq is "low" (freq = 10 hz in that case), the plot is quite nice:
The problem is when I increase the freq (try to set var freq = 50 for example):
lots of ripples, it becomes distorted and not so good as plot. If I increment it more, even worse (var freq = 8030 for example is terrible).
When I see those kind of graph on pro systems, they are displayed just fine.
How would you improve it? FFT, splines, whatever? Which is the right approch?
I don't really need accurancy (i.e. for waveform analysis or whatever), just plot it nicely (as in Desmos https://www.desmos.com/calculator/eodkjlywjh, for example).
Like Matt said, the problem here is the same as in plotting a discrete-time signal shows amplitude modulation: "At higher frequencies, when the peak falls between two samples, the sampled points can be a lot lower than the peak." This causes the peaks of the waveform to vary, making a kind of ripple visual effect at the top and bottom of the plot.
Increasing the sampling resolution helps. If you can't increase sampling resolution by much, adjust the sampling resolution to the closest integer multiple of the sine's frequency and set the sampling phase so that it samples the waveform peaks. For instance for a 100 Hz wave cos(2 π (100*x + 0.3)), you could sample at 400 Hz at x[n] = n/400 - 0.003. That way there are four samples per period, with x[0], x[4], x[8], ... sampling exactly on the peaks.
Another thought: plotting with a thicker line can help to smooth over some defects. It looks like the desmos example that you linked is using something like a 3-pixel line width.
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 am trying to generate a plot which uses arrows as markers in Gnuplot. These arrows I want to turn in a specific angle which I know. So I have value triples of x1 ... xn, y1...yn, alpha1...alphan. Sorry, I wasn't able to include a pic from my hard drive to illustrate what I want to achieve.
Basically, for every (15th or so) x-y pair, the marker should be an arrow which uses a certain angle.
The measured data is tightly packed so I suppose I will have to define an increment between the markers. The length of the arrow can be the same all over.
I would appreciate your ideas.
Gnuplot has a plot mode with vectors that is what you want
Given that your file has the following format, x y angle and assuming that
your angle is in radians, you have to take into account that
with vectors requires 4 parameters, namely x y dx dy where dx
and dy are the projections of the lenght of the arrow.
this draws only the arrows, if you want a line you have to make
two passes on the data.
you want to draw an arrow for a data point over, say, 10 points.
That said, I'd proceed like this
dx(a) = 0.2*cos(a) # 0.2 is an arbitrary scaling factor
dy(a) = 0.2*sin(a)
# this draws the arrows
plot 'mydata.dat' every 10 using 1:2:(dx(a)):(dy(a)) with vectors
# this draws the line
plot 'mydata.dat'
You may want to use help plot to find the detailed explanation of all the parameters that you can apply to a with vectors plot.
Credits: An article on the gnuplotting site
There is this interesting game, that has numbers in a grid, where each number has progressively smaller font. The player's task is to click on the numbers in succession.
I'm interested in the algorithm that creates the boxes for the numbers, I cannot think of a way it works.
Apparently the grid has N numbers (apart from the 1.88 in the picture), number 1 has the biggest font and succesively the font size decreases. Then the numbers are somehow placed on the grid and boxes grow around them. But it doesn't seem totally random, as there are some horizotal lines that go across the whole grid.
Do you have an idea on how this might work?
It looks to me as though the boxes have been generated by successive division. That is, starting with the full rectangle, a dividing line (horizontal or vertical) was placed, and then the two resulting rectangles were subdivided in turn, until there were enough rectangles for the game.
Here's a sketch of the first algorithm I would try. N is the number of rectangles I want to divide the original rectangle into, and A is a critical aspect ratio used to stop the small rectangles getting too narrow. (Perhaps A = 1.5 would be a good start.)
Create an empty priority queue and add the full rectangle to it.
If the length of the priority queue is greater than or equal to N, stop.
Remove the largest rectangle, R, from the priority queue.
Choose whether to divide it horizontally or vertically: if its aspect ratio (width/height) is greater than A, divide it vertically; if less than 1/A, divide it horizontally, otherwise choose at random.
Decide where to put the dividing line. (Perhaps randomly between 40% and 60% along the chosen dimension.)
This divides R into two smaller rectangles. Add both of them to the priority queue. Go to step 2.
When this algorithm completes, there are N rectangles in the queue. Put the number 1 in the largest of them, the number 2 in the second largest, and so on.
It turns out that putting the numbers into the boxes is not quite as straightforward as I assumed in my first attempt. The area metric works well for subdividing the rectangles nicely, but it doesn't work for putting the numbers into the boxes, because for fitting text into a box, the height and width both have to be taken into account (the area is not so useful).
Instead of explaining the algorithm for putting numbers into the boxes, I will just give you some sample code in Python and let you reverse-engineer it!
import heapq, itertools, random
import Image, ImageDraw, ImageFont
# ALGORITHM PARAMETERS
aspect_max = 1.5 # Above this ratio, always divide vertically
aspect_min = 1.0 # Below this ratio, always divide horizontally
div_min = 0.4 # Minimum position for dividing line
div_max = 0.6 # Maximum position for dividing line
digit_ratio = 0.7 # Aspect ratio of widest digit in font
label_margin = 2 # Margin around label (pixels)
class Rectangle(object):
def __init__(self, x, y, w, h):
self.x = x
self.y = y
self.w = w
self.h = h
self.area = self.w * self.h
self.aspect = float(self.w) / self.h
def __le__(self, other):
# The sense of this comparison is inverted so we can put
# Rectangles into a min-heap and be able to pop the largest.
return other.area <= self.area
def __repr__(self):
return 'Rectangle({0.x}, {0.y}, {0.w}, {0.h})'.format(self)
def divide(self, n):
"""
Divide this rectangle into `n` smaller rectangles and yield
them in order by area (largest first).
"""
def division(l):
return random.randrange(int(l * div_min), int(l * div_max))
queue = [self]
while len(queue) < n:
r = heapq.heappop(queue)
if (r.aspect > aspect_max
or r.aspect > aspect_min
and random.random() < 0.5):
# Vertical division
w = division(r.w)
heapq.heappush(queue, Rectangle(r.x, r.y, w, r.h))
heapq.heappush(queue, Rectangle(r.x + w, r.y, r.w - w, r.h))
else:
# Horizontal division
h = division(r.h)
heapq.heappush(queue, Rectangle(r.x, r.y, r.w, h))
heapq.heappush(queue, Rectangle(r.x, r.y + h, r.w, r.h - h))
while queue:
yield heapq.heappop(queue)
def font_height(self, n):
"""
Return the largest font height such that we can draw `n`
digits in this rectangle.
"""
return min(int((self.w - label_margin * 2) / (digit_ratio * n)),
self.h - label_margin * 2)
def draw_rectangles(rectangles, fontfile):
"""
Create and return an Image containing `rectangles`. Label each
rectangle with a number using the TrueType font in `fontfile`.
"""
rectangles = list(rectangles)
im = Image.new('RGBA', (1 + max(r.x + r.w for r in rectangles),
1 + max(r.y + r.h for r in rectangles)))
draw = ImageDraw.Draw(im)
for digits in itertools.count(1):
rectangles = sorted(rectangles,
key = lambda r: r.font_height(digits),
reverse = True)
i_min = 10 ** (digits - 1)
i_max = 10 ** digits
i_range = i_max - i_min
for i in xrange(i_range):
if i >= len(rectangles): return im
r = rectangles[i]
draw.line((r.x, r.y, r.x + r.w, r.y, r.x + r.w, r.y + r.h,
r.x, r.y + r.h, r.x, r.y),
fill = 'black', width = 1)
label = str(i + i_min)
font = ImageFont.truetype(fontfile, r.font_height(digits))
lw, lh = font.getsize(label)
draw.text((r.x + (r.w - lw) // 2, r.y + (r.h - lh) // 2),
label, fill = 'black', font = font)
rectangles = rectangles[i_range:]
Here's a sample run:
>>> R = Rectangle(0, 0, 400, 400)
>>> draw_rectangles(R.divide(30), '/Library/Fonts/Verdana.ttf').save('q10009528.png')
The pattern of the cuts looks recursive. That is, the process of dividing the region into rectangles consists of cutting a rectangle in two, over and over. There are two cuts that divide the whole rectangular region (the horizontal cuts above and below 1), so we can't tell which cut came first, but we see the cuts as a kind of tree: the cut that separates 1 from 10 produced a large rectangle below it (20, 21, 4, 10, etc.), which was then divided by the vertical cut between 21 and 4, the rectangle containing 4 was later divided by the cut that separates 4 and 14, and so on. There are N cuts that produce N regions plus one leftover ("1.88") which is not necessary but which might give us a clue.
Now we just have to figure out the order of the cuts, the choice of proportion and the choice of orientation.
Consecutive numbers are rarely neighbors, so it looks as if numbers are not assigned as the cutting progresses. Instead, the region is chopped into rectangles, the rectangles are sorted by size and then numbers are assigned (notice that 20 and 21 are neighbors, but they were formed by other cuts after the one that divides them).
A plausible hypothesis for the order of the cuts is that the algorithm always cuts the largest rectangle. If that were not true, we might see, e.g., 14 bigger than 15 and 18 combined, and I see no example of that.
Proportion... With careful measurement we could see the actual distribution of proportions, but I don't feel like doing that much work. We see no very long, thin rectangles and no 50/50 cuts, so at a guess I'd say the algorithm chooses randomly, in some range like [0.6, 0.8]. Maybe it tries to avoid making a rectangle very close to the size of a rectangle that already exists. After all the cuts, the rectangle chosen to be left over ("1.88") is neither the biggest nor the smallest; maybe it's random, maybe it's the second-biggest, maybe it's something else-- more examples would be useful.
The orientation seems to be strongly biased towards cutting rectangles across their narrow width, rather than "lengthwise". This has the effect of producing rectangles more like squares and less like books on a shelf. The only possible exception I can see is the 1-9 cut, which might divide the block whose lower-right number is 1 lengthwise. But that depends on the order of cuts above and below 1, so it leads to a hypothesis: the algorithm always cuts a rectangle along its shorter dimension, and the 1-9 cut was actually the first.
That's about as far as I can go, short of breaking out a ruler and calculator.