Convert pixels into relative cm from a clickable image? - image

Having a bit of a brain freeze on how to do this. I have an image of an area of a soccer field which the user clicks on to indicate where something occured.
I want to store the coordinates in real world units in my database so that I could change the image at a later time. However, I can't figure out the formula to do this (ignoring the fact that a soccer field has a variable length).
So say my image is 400 px by 300px representing a real-world field 12000cm x 9000cm, what would the point 300px,100px be in centimeters? How would I then convert this cm value back into px on an image that was 800x600 px? Doing it in yards/inches would also be acceptable.
Please show me the working rather than just the single values. Thanks!

just like any other unit conversion: 300 * 12000/400, 100 * 9000/300 is the point where the user click in the soccer field.
More generally, assuming the two scales have the same zeros (i.e. 0 source == 0 target) and you know a point where the scales equal x source == y target, then valueInTarget = valueInSource * (y / x).
Even more generally, assuming you know two points where two scales are equal, i.e. x1 source == y1 target and x2 source == y2 target, then valueInTarget == (valueInSource - x1) * (y2 - y1)/(x2 - x1) + y1 == (valueInSource - x2) * (y1 - y2)/(x1 - x2) + y2.

Use ratios. 400px represent 12000cm, so your width ratio is 30 centimeters per pixel. Applying that ratio to 300px gives us 9000cm.
In your second example, your width ratio is 15 centimeters per pixel (12000/800). So 9000cm gives 600px on the second image.
Same goes for the height.

Related

Is there a Octave function or general solution for locating dead space in a scatter plot? [duplicate]

This question already has answers here:
Fitting largest circle in free area in image with distributed particle
(5 answers)
Closed 1 year ago.
I deal with plots of data on the order of half a million points in Octave. I am trying to find the center of empty spaces that are in the data (on purpose).
I know how many points to look for and I was thinking of feeding in starter locations and then try to expand a circle in one direction until you hit valid data point locations and keep doing that in a few directions until you have a circle that is filled with no data but touches valid data points. The center of that circle would be the center of the void space. I'm not entirely sure how to write that since I'm very green in coding.
Obviously a graphical solution probably isn't the best method, but I don't know how to find big x and y gaps in a huge matrix of x y locations.
A section of the data I deal with. Trying to write a program to automatically find the center of that hole.
A sample of the data I'm working with. Each data point is an x and y location with a z height that isn't really valuable to what I'm trying to solve here. The values do not line up in consistent intervals
Here is a large sample of what I'm working with
I know you said your data does not line-up in x or y, but it still seems suspiciously grid-like.
In this case, you can probably express each gridpoint as a 'pixel' in an image; this gives you access to excellent functions you can use from the image package, such as the imregionalmin function. This will give you connected components of 'holes', in your case. For each component you can find their centres of mass easily by finding the 'average coordinate' over the pixels within that component. You can then perform a distance transform (e.g. using bwdist) to find the exact radius for the circle you describe, as the distance from that centre of mass to the nearest pixel. Alternatively, you can start with bwdist and then use immaximas to detect the centres of mass directly. If you have multiple such regions, you can use bwconncomp to find connected components first (or over the output of imregionalmin).
If your data is not specifically grid-like, then you could probably interpolate your data to make them fit such a grid.
Example:
pkg load image
t = 0 : 0.1 : 2 * pi; % for use when plotting circles later
[X0, Y0] = ndgrid( 1:100, 1:100 ); % Create 'index' grid
X = X0 - 0.25 * Y0; Y = 0.25 * X0 + Y0; % Create transformed grid
Z = 0.5 * (X0 - 50) .^ 2 + (Y0 - 50) .^ 2 > 250; % Assign a logical value to each 'index' point on grid
M = imregionalmin ( Z ); % Find 'hole' as mask
C = { round(mean(X0(M))), round(mean(Y0(M))) }; % Find centre of mass (as index)
R = bwdist( ~M )(C{:}); % Find distance from centre of mass to nearest pixel
R = min( abs( X(C{1}+R, C{2}) - X(C{:}) ), abs( Y(C{1}, C{2}+R) - Y(C{:}) ) ); % Adjust for transformed grid
figure(1); hold on
plot( X(Z), Y(Z), '.', 'markerfacecolor', 'b' ) % Draw original transformed grid data
plot( X(C{:}), Y(C{:}), 'o', 'markerfacecolor', 'r' ); % Draw centre of mass in transformed grid
plot( X(C{:}) + R * cos(t), Y(C{:}) + R * sin(t), 'r-' ) % Draw optimal circle on top
axis equal; hold off

Best fitting rectangle with a variable number of small rectangles keeping aspect ratio

I need to find the optimal placement of a given N child rectangles keeping the aspect ratio of the father rectangle.
Use case is the following:
- the father rectangle is a big picture, let's say 4000x3000 pixels (this one can be rescaled).
- child rectangles are 296x128 pixels (e-ink displays of users)
The objective is to show the big picture across all the current number of displays (this number can change from 1 to 100)
This is an example:
Can happen that number of small rectangles will not fit the big rectangle aspect ratio, like if number of small rectangles is odd, in this case I can think to have like a small number (max 5) of spare rectangles to add in order to complete the big rectangle.
this seems to be a valid approach (python + opencv)
import cv2
import imutils
def split_image(image, boards_no=25, boards_shape=(128, 296), additional=5):
# find image aspect ratio
aspect_ratio = image.shape[1]/image.shape[0]
print("\nIMAGE INFO:", image.shape, aspect_ratio)
# find all valid combination of a,b that maximize your available badges
valid_props = [(a, b) for a in range(boards_no+additional+1) for b in range(boards_no+additional+1) if a*b in [q for q in range(boards_no, boards_no+additional)]]
print("\nVALID COMBINATIONS", valid_props)
# find all aspect ratio from previous combination
aspect_ratio_all = [
{
'board_x': a,
'board_y': b,
'aspect_ratio': (a*boards_shape[1])/(b*boards_shape[0]),
'shape': (b*boards_shape[0], a*boards_shape[1]),
'type': 'h'
} for (a, b) in valid_props]
aspect_ratio_all += [
{
'board_x': a,
'board_y': b,
'aspect_ratio': (a*boards_shape[0])/(b*boards_shape[1]),
'shape': (b*boards_shape[1], a*boards_shape[0]),
'type': 'v'
} for (a, b) in valid_props]
min_ratio_diff = min([abs(aspect_ratio-x['aspect_ratio']) for x in aspect_ratio_all])
best_ratio = [x for x in aspect_ratio_all if abs(aspect_ratio-x['aspect_ratio']) == min_ratio_diff][0]
print("\MOST SIMILAR ASPECT RATIO:", best_ratio)
# resize image maximining height or width
resized_img = imutils.resize(image, height=best_ratio['shape'][0])
border_width = int((best_ratio['shape'][1] - resized_img.shape[1]) / 2)
border_height = 0
if resized_img.shape[1] > best_ratio['shape'][1]:
resized_img = imutils.resize(image, width=best_ratio['shape'][1])
border_height = int((best_ratio['shape'][0] - resized_img.shape[0]) / 2)
border_width = 0
print("RESIZED SHAPE:", resized_img.shape, "BORDERS (H, W):", (border_height, border_width))
# fill the border with black
resized_img = cv2.copyMakeBorder(
resized_img,
top=border_height,
bottom=border_height,
left=border_width,
right=border_width,
borderType=cv2.BORDER_CONSTANT,
value=[0, 0, 0]
)
# split in tiles
M = resized_img.shape[0] // best_ratio['board_y']
N = resized_img.shape[1] // best_ratio['board_x']
return [resized_img[x:x+M,y:y+N] for x in range(0,resized_img.shape[0],M) for y in range(0,resized_img.shape[1],N)]
image = cv2.imread('image.jpeg')
tiles = split_image(image)
Our solutions will always be rectangles into which we have fit the biggest picture that we can keeping the aspect ratio correct. The question is how we grow them.
In your example a single display is 296 x 128. (Which I assume is length and height.) Our scaled image to 1 display is 170.6 x 128. (You can take out fractional pixels in your scaling.)
The rule is that at all points, whatever direction is filled gets filled in with more displays so we can expand the picture. In the single display solution we therefore go from a 1x1 rectangle to a 1x2 one and we now have 296 x 256. Our scaled image is now 296 x 222.
Our next solution will be a 2x2 display. This gives us 594 x 256 and our scaled image is 321.3 x 256.
Next we get a 2x3 display. This gives us 594 x 384 and our scaled display is now 512 x 384.
Since we are still maxing on the second dimension we next go to 2x4. This gives us 594 x 512 and our scaled display is 594 x 445.5. And so on.
For your problem it will not take long to run through all of the sizes up to however many displays you have, and you just take the biggest rectangle that you can make from the list.
Important special case. If the display rectangle and image have the same aspect ratio, you have to add to both dimensions. Which in the case that the image and the displays have the same aspect ratio gives you 1 x 1, 2 x 2, 3 x 3 and so on through the squares.

How to generate a bounding box that overlaps with existing bounding box by at least a given ratio?

Given a bounding box bbox1, I want to randomly generate a new bounding box bbox2, which overlaps bbox1 by at least 0.5.
The overlap ratio is defined as the area of intersection between bbox1 and bbox2, divided by the area of the union of the two.
The naive way I can think of is to randomly generate bounding boxes until I find one that satisfies the condition. But obviously, it will waste some time in generating and evaluating unsatisfied candidates.
If the bounding box is encoded by the upper left corner and the width and height bbox1 = (x1, y1, w1, h1), the pseudocode below shows how I generate the new bounding box.
do
x2 = random(x1 - w1, x1 + w1/2)
y2 = random(y1 - h1, y1 + h1/2)
w2 = random(0, 2 * w1)
h2 = random(0, 2 * w1 * h1 / w2)
bbox2 = (x2, y2, w2, h2)
while bboxOverlapRatio(bbox1, bbox2) < 0.5
Any better solutions? For example, can I further narrow down the random range?
One way to further narrow down the random range is to only generate new bounding boxes bbox2 with the center inside bbox1.
If the center of bbox2 is outside of bbox1 then it is impossible to have an overlap of at least 0.5 (note that this is a separate, interesting mathematical problem in itself).
This additional constraint can be expressed as x1 < x2 + w2/2 < x1 + w1 (with a similar relation for the vertical axis), and can be used to narrow the range for w2 and h2:
do
x2 = random(x1 - w1, x1 + w1/2)
y2 = random(y1 - h1, y1 + h1/2)
w2 = random(max(0, 2 * (x1 - x2)), min(2 * w1, 2 * (x1 + w1 - x2)))
h2 = random(max(0, 2 * (y1 - y2)), min(2 * w1 * h1 / w2, 2 * (y1 + h1 - y2)))
bbox2 = (x2, y2, w2, h2)
while bboxOverlapRatio(bbox1, bbox2) < 0.5
Lets take the case of equal size boxes:
There are four cases:
one where you start from the left at x1-w1/2, y1
one from the top x1, y1-h1/2
on from upper left to move in diagonal x1-c, y1-c
one from the lower left same diagonal up x1-c, y1+h1+c
where c is a number you can find that gives at least 1/2 overlap in the diagonal position (for a square (w-c)*(w-c)>=w^2/2 you solve this quadratic equation and find c).
The search space is limited within these areas:
so you do:
choice=random from 1 to 4
if choice==1: xnew=x1-w1/2+random from 0 to 2*w1; ynew=y1
if choice==2: xnew=x1; ynew=y1-h1/2+random from 0 to 2*h1
if choice==3: xnew=x1-c+random from 0 to w1+2*c; ynew=y1-c+random from 0 to h1+2*c
if choice==4: xnew=x1-c+random from 0 to w1+2*c; ynew=y1+h1+c+random from 0 to -(h1+2*c)
Its not a completely random selection since you dont have the seach space up front and then choose some point in it but effectively it covers the whole space randomly.
This cover the four main routes; but leaves the corner cases/spaces uncovered. You effectively have a circle search space: if you move your starting point up you have to move it right also to guarantee 1/2 overlap. That is a circle of radius w1 (if w1=h1) centered on the center of the initial rectangle. If you start on any point on the circle you are guaranteed to have 1/2 coverage. You can pick your point anywhere within the circle.
If the rectangles are not squares but general rectangles you have an ellipse as the search space.
--
Then lets go the the different size scenario:
within your loop you pick the size at random and perform the above search.
The calculations where you start from and how far you will go change but can be done.
[Correction: the starting points cannot be on the entire circle but certain part of the circle on the left side - symmetrical on the right side]

Math/programming: How to make an object go through a path made from a line

Now I am doing this in VB6 but I don't think it matters what I do it in, does it? I believe it has to do with math.
Here is the problem, have a look at this picture
As you can see in this image, there is a black line and a grey circle. I want the circle to move from the bottom left to the bottom right, but I also want it to stay along the path of the line so it reaches our second picture like this:
Now how can I accomplish this? Again, using VB6.
There are various ways of accomplishing this I think, but here's the first that comes to my mind. It makes some assumptions... like that your line goes in a positive direction and it starts at 0,0. If either of these things aren't true then you've got more code to write to adjust for that.
=================================================
Psuedocode:
'To track current coordinates of the center of the circle
dim x as float, y as float
x = 0: y = 0
'Coordinates for the line
dim x1 as float, y1 as float, x2 as float, y2 as float
x1=0: y1=0: x2=50: y2=75
'How much we're going to move the circle at a time
dim xStep as float, yStep as float, stepSize as float
stepSize = 100
xStep = x2 / stepSize
yStep = y2 / stepSize
Do
'Draw circle here with x, y for coordinates
x = x + xStep
y = y + yStep
Loop Until xStep > x2
Ok, I don't know VBA6 but, since you said:
I don't think it matters what I do it in
I will give a generic solution that involves you having the center of the circles coordinates, and the lines endpoints.
This line can be treated as a vector:
(line.x2-line.x1, line.y2-line.y1)
You don't need to write this in your program or anything just saying it is a vector.
What you do need to is get the magnitude of the vector and assign it to a variable:
unitSize = sqrt((line.x2-line.x1)^2 + (line.y2-line.y1)^2)
Now make it into unit vector components and get the separate components:
unitX = (line.x2-line.x1)/unitSize
unitY = (line.y2-line.y1)/unitSize
Now how ever you update the circle:
do {
circle.x = circle.x + unitX * incrementSize //incrementSize scales how big the movement is assign it to whatever you seem fit.
circle.y = circle.y + unitY * incrementSize
until (circle.x >= line.x2) //Or <= line.x2 depends which way you are going.
Hopefully this helps.

Why would an image (the Mandelbrot) be skewed and wrap around?

So I just wrote a little snippet to generate the Mandelbrot fractal and imagine my surprise when it came out all ugly and skewed (as you can see at the bottom). I'd appreciate a point in the direction of why this would even happen. It's a learning experience and I'm not looking for anyone to do it for me, but I'm kinda at a dead end debugging it. The offending generation code is:
module Mandelbrot where
import Complex
import Image
main = writeFile "mb.ppm" $ imageMB 1000
mandelbrotPixel x y = mb (x:+y) (0:+0) 0
mb c x iter | magnitude x > 2 = iter
| iter >= 255 = 255
| otherwise = mb c (c+q^2) (iter+1)
where q = x -- Mandelbrot
-- q = (abs.realPart $ x) :+ (abs.imagPart $ x) --Burning Ship
argandPlane x0 x1 y0 y1 width height = [ (x,y) |
y <- [y1, y1 - dy .. y0], --traverse from
x <- [x0, x0 + dx .. x1] ] --top-left to bottom-right
where dx = (x1 - x0) / width
dy = (y1 - y0) / height
drawPicture :: (a -> b -> c) -> (c -> Colour) -> [(a, b)] -> Image
drawPicture function colourFunction = map (colourFunction . uncurry function)
imageMB s = createPPM s s
$ drawPicture mandelbrotPixel (replicate 3)
$ argandPlane (-1.8) (-1.7) (0.02) 0.055 s' s'
where s' = fromIntegral s
And the image code (which I'm fairly confident in) is:
module Image where
type Colour = [Int]
type Image = [Colour]
createPPM :: Int -> Int -> Image -> String
createPPM w h i = concat ["P3 ", show w, " ", show h, " 255\n",
unlines.map (unwords.map show) $ i]
Well, the image is skewed because the dimensions are wrong, but that's obvious. You're specifying the image size and then spitting out a list of pixels, but with an incorrect number of pixels per line somewhere.
More specifically, note that the image wraps around almost exactly once: In other words, skew per line * height of the image = width of the image. Since the image is square, that means you're generating an extra pixel per line--a good old off-by-one error.
The obvious place for this to happen is when you're generating the coordinates to iterate on. Let's try a small set and see what it gives us:
> length $ argandPlane (-2.5) (-2) 1.5 2 10 10
121
> 10 ^ 2
100
> 11 ^ 2
121
And so. I suspect the error is because you're calculating the increment as real distance divided by pixel size, which generates the correct number of intervals, but an extra point. Consider the interval from 0.0 to 1.0. Using your calculation with a width of 4, we get:
> let x0 = 0.0
> let x1 = 1.0
> let width = 4.0
> let dx = (x1 - x0) / width
> dx
0.25
> let xs = [x0, x0 + dx .. x1]
> xs
[0.0, 0.25, 0.5, 0.75, 1.0]
> length xs
5
So, to get the correct number of points, just reduce the size by 1 when generating the the coordinates.
It's a learning experience and I'm not looking for anyone to do it for me, but I'm kinda at a dead end debugging it
I know camccann already solved your problem, but he kind of "gave you the fish" while "teaching you how to fish" could be more useful.
So I'll share what I believe could be a useful way of reaching the solution.
So your mandelbrot image is skewed. Some likely possible causes:
You have a bug in your mandelbrot formula
You have a bug in presenting/saving your picture
You could do an experiment to learn further if any of the above explanations are relevant or not. Such an experiment could be for example drawing trivial images of, say, horizontal and vertical lines.
After doing that experiement you will see that your vertical lines are not so vertical. Going back to the likely possible causes, it's clear that you have a bug in presenting/saving your image, and that explains everything. You may still have a bug in your mandelbrot formula but you probably don't, and that's not relevant to the issue at hand now.
Now you should ponder what kind of image saving bug will cause vertical lines to be diagonal. If no idea pops up you can make your simple example smaller and smaller until the PPM result becomes small enough that you could examine it by hand. Then you'll surely catch the bug.

Resources