Draw a square with grid in ggplot2 - r

I am trying to create my own geom. On screen, the geom should always be a square, irrespective of scaling. Given the values w and h (for width and height), transformed by ggplot from user coordinates to grid coordinates, the idea is to shrink one of them such that on screen, the aspect of the resulting rectangle is exactly w/h. If w==h, we get a square.
It is doable in basic R by consulting par("pin"):
## w and h are box width and height in user coordinates
## if w == h, we will should get a square
aspect <- 1 # change to get a rectangle with a given aspect ratio
pp <- par("pin")
pu <- par("usr")
pu <- c(pu[2]-pu[1], pu[4]-pu[3])
ppu <- pp/pu
wi <- w * ppu[1] # box width in inches
hi <- h * ppu[2] # box height in inches
if(wi > hi * aspect) {
wi <- hi * aspect
} else {
hi <- wi / aspect
}
w <- wi / ppu[1]
h <- hi / ppu[2]
However, I have hard time figuring out how to do the same with grid. Is it possible?
What I want to do: I want to make a little plot-in-plot which, in some cases, needs to be exactly a square otherwise it looks ugly.

Related

Draw a circle in a bitmap image and crop pixels outside circle in R

I'm loading bitmap images into R with dimensions that are roughly 17,000 X 17,000 pixels. I'd like to find a way to draw a circle with a radius (in pixels) of my choosing around the center of the picture and convert all pixels outside of the circle into NA's.
For example, if the radius desired was 500 pixels, all pixels within that distance (500) from the centroid would be kept as is. Any pixel farther than that distance (>= 501) from the centroid would be converted to an NA.
The bitmap images are made up entirely of 1's and 0's so here's a smaller example of what these images look like.
img=matrix(sample(c(1,0),1000000,replace=TRUE),ncol=1000,nrow=1000)
image(0:1000,0:1000,img)
This is a slight variation of the solution by eipi10. It does not use the "melt" function of the reshape package and rather uses subsetting the matrix directly:
# Number of rows and columns in image
nr = 200
nc = 100
# Create image values
set.seed(78)
img <- matrix(sample(c(1,0), nr*nc, prob=c(0.8, 1-0.8), replace=TRUE), ncol=nc, nrow=nr)
center <- c(median(1:nr), median(1:nc)) # center of image
r <- 40 # radius
# setting the matrix element inside the circle to value -1
img[(row(img) - center[1])^2 + (col(img) - center[2])^2 < r^2] <- -1
# plot image
par(mar = c(0, 0, 0, 0))
image(img, useRaster=TRUE, axes=FALSE)
I've created a fake image that's smaller than yours so that the code will run more quickly:
library(plotrix) # To draw a circle
library(reshape2) # For "melt" function
Create a fake image:
# Number of rows and columns in image
nr = 200
nc = 100
# Create image values
set.seed(78)
img = matrix(sample(c(1,0), nr*nc, prob=c(0.8, 1-0.8), replace=TRUE), ncol=nc, nrow=nr)
Now that we have our image, remove points outside the desired circle:
# melt matrix into "long" format
img = melt(id.var=1:nrow(img), img)
names(img) = c("rows","cols","z")
# Find center of image
center=c(median(1:nr), median(1:nc))
# Set desired radial distance from center
r=40
# Set values outside radius to -1 (or some value that can't otherwise appear in
# the matrix). You can set the value to NA, but then you won't be able to
# control the color of the excluded region (it will just be white).
img$z[sqrt((img$rows - center[1])^2 + (img$cols - center[2])^2) > r] = -1
# Plot image. Colors ordered from lowest (-1) to highest (1) value
image(1:nr, 1:nc, matrix(img$z, nrow=nr, byrow=FALSE), col=c("gray80", "green","red"))
# Draw a circle around the selected points
draw.circle(center[1], center[2], r, lwd=2)

Create bubble chart with biggest bubble at the center

I'm trying to create a bubble chart using a set of data as follows:
X --> 10
Y --> 20
Z --> 5
Q --> 10
I simply need to have the biggest bubble (based on its number) to be at the centre (give or take) and the rest of the bubbles be around it without overlapping.
All of the R examples I have seen require a two dimensional dataset, and since the data I have are only one dimensional, I like to know if it's at all possible to create such graphs in R.
It would be great if someone could suggest me some useful hints or so. By the way for this task I need to use a SA tools so something like d3js is out of options. However, I am open to using a tool other than R.
I wasn't quite sure if this question should be asked in On Stack Overflow or Cross Validated, so if moderators believe it doesn't belong here, I'll remove it.
This should do, the main idea being that you sort by the value of the radius, so the first is the biggest, then shift the values around it (odd on one side, even on the other) so that the values are decreasing both ways.
Further explanations in the code.
library(plotrix)
library(RColorBrewer)
# Set the random seed, to get reproducible results
set.seed(54321)
# Generate some random values for the radius
num.circles <- 11
rd <- runif(num.circles, 1, 20)
df <- data.frame(labels=paste("Lbl", 1:num.circles), radius=rd)
# Sort by descending radius. The biggest circle is always row 1
df <- df[rev(order(df$radius)),]
# Now we want to put the biggest circle in the middle and the others on either side
# To do so we reorder the data frame taking the even values first reversed, then the odd values.
# This ensure the biggest circle is in the middle
df <- df[c(rev(seq(2, num.circles, 2)), seq(1, num.circles, 2)),]
# Space between the circles. 0.2 * average radius seems OK
space.between <- 0.2 * mean(df$radius)
# Creat an empty plot
plot(0, 0, "n", axes=FALSE, bty="n", xlab="", ylab="",
xlim=c(0, sum(df$radius)*2+space.between*num.circles),
ylim=c(0, 2.5 * max(df$radius)))
# Draw the circle at half the height of the biggest circle (plus some padding)
xx <- 0
mid.y <- max(df$radius) * 1.25
# Some nice degrading tones of blue
colors <- colorRampPalette(brewer.pal(8,"Blues"))(num.circles/2)
for (i in 1:nrow(df))
{
row <- df[i,]
x <- xx + row$radius + i*space.between
y <- mid.y
# Draw the circle
draw.circle(x, y, row$radius,
col=colors[abs(num.circles/2-i)])
# Add the label
text(x, y, row$labels, cex=0.6)
# Update current x position
xx <- xx + row$radius * 2
}
The result:
Live version on RFiddle.

Estimate visible bounds of webcam using diagonal fov

I'm using a Logitech C920 webcam (specs here) and I need to estimate the visible bounds of it before installing it at the user place.
I see that it has a Diagonal FOV of 78°. So, following the math described here we have:
Where H is the vertical Fov, W is the horizontal Fov, D is the diagonal Fov and the aspect ratio is r.
Considering an aspect ratio of 16/9, that gives me approx. W = 67.9829 and H = 38.2403
So I create a frustum using W and H.
The problem is: a slice of this frustum isn't 16:9. Is it due because of the numeric approximations or I'm doing something else wrong?
Does the camera crop a bigger image?
How can I compute effectively what will be the visible frustum?
Thank you very much!
The formulae you have are for distances, not for angles. You would need to calculate the distance using tangens:
D = 2 * tan(diagonalFov / 2)
Then you can go ahead with your formula. H and W will again be distance values. If you need the according angles, you can use arc tan:
verticalFov = 2 * arc tan (H / 2)
horizontalFov = 2 * arc tan (W / 2)
For your values, you'll get
verticalFov = 43.3067°
horizontalFov = 70.428°

Kinect intrinsic parameters from field of view

Microsoft state that the field of view angles for the Kinect are 43 degrees vertical and 57 horizontal (stated here) . Given these, can we calculate the intrinsic parameters i.e. focal point and centre of projection? I assume centre of projection can be given as (0,0,0)?
Thanks
EDIT: some more information on what I'm trying to do
I have a dataset of images recorded with a Kinect, I am trying to convert pixel positions (x_screen,y_screen and z_world (in mm)) to real world coordinates.
If I know the camera is placed at point (x',y',z') in the real world coordinate system, is it sufficient to find the real world coordinates by doing the following:
x_world = (x_screen - c_x) * z_world / f_x
y_world = (y_screen - c_y) * z_world / f_y
where c_x = x' and c_y = y' and f_x, f_y is the focal length? And also how can I find the focal length given just knowledge of the field of view?
Thanks
If you equate the world origin (0,0,0) with the camera focus (center of projection as you call it) and you assume the camera is pointing along the positive z-axis, then the situation looks like this in the plane x=0:
Here the axes are z (horizontal) and y (vertical). The subscript v is for "viewport" or screen, and w is for world.
If I get your meaning correctly, you know h, the screen height in pixels. Also, zw, yv and xv. You want to know yw and xw. Note this calculation has (0,0) in the center of the viewport. Adjust appropriately for the usual screen coordinate system with (0,0) in the upper left corner. Apply a little trig:
tan(43/2) = (h/2) / f = h / (2f), so f = h / ( 2 tan(43/2) )
and similar triangles
yw / zw = yv / f also xw / zw = xv / f
Solve:
yw = zw * yv / f and xw = zw * xv / f
Note this assumes the "focal length" of the camera is equal in the x-direction. It doesn't have to be. For best accuracy in xw, you should recalculate with f = w / 2 tan(57/2) where w is the screen width. This is because f isn't a true focal length. It's just a constant of conversion. If the pixels of the camera are square and optics have no aberrations, these two f calculations will give the same result.
NB: In a deleted (improper) article the OP seemed to say that it isn't zw that's known but the length D of the hypotenuse: origin to (xw,yw,zw). In this case just note zw = D * f / sqrt(xv² + yv² + f²) (assuming camera pixels are square; some scaling is necessary if not). They you can proceed as above.
i cannot add comment since i have a too low reputation here.
But I remind that the camera angle of the kinect isn't general the same
like in a normal photo camera, due to the video stream format and its sensor chip. Therefore the SDK mentioning 57 degrees and 43 degrees, might refer to different degree resolution for hight and width.
it sends a bitmap of 320x240 pixels and those pixels relate to
Horizontal FOV: 58,5° (as distributed over 320 pixels horizontal)
Vertical FOV: 45,6° (as distributed over 240 pixels vertical).
Z is known your angle is known, so i supose law of sines can get you proper locations then https://en.wikipedia.org/wiki/Law_of_sines

How to generate random shapes given a specified area.(R language).?

My question is this.. I am working on some clustering algorithms.. For this first i am experimenting with 2d shapes..
Given a particular area say 500sq units .. I need to generate random shapes for a particular area
say a Rect, Square, Triangle of 500 sq units.. etc .. Any suggestions on how i should go about this problem.. I am using R language..
It's fairly straightforward to do this for regular polygon.
The area of an n-sided regular polygon, with a circumscribed circle of radius R is
A = 1/2 nR^2 * sin((2pi)/n)
Therefore, knowing n and A you can easily find R
R = sqrt((2*A)/(n*sin((2pi)/n))
So, you can pick the center, go at distance R and generate n points at 2pi/n angle increments.
In R:
regular.poly <- function(nSides, area)
{
# Find the radius of the circumscribed circle
radius <- sqrt((2*area)/(nSides*sin((2*pi)/nSides)))
# I assume the center is at (0;0) and the first point lies at (0; radius)
points <- list(x=NULL, y=NULL)
angles <- (2*pi)/nSides * 1:nSides
points$x <- cos(angles) * radius
points$y <- sin(angles) * radius
return (points);
}
# Some examples
par(mfrow=c(3,3))
for (i in 3:11)
{
p <- regular.poly(i, 100)
plot(0, 0, "n", xlim=c(-10, 10), ylim=c(-10, 10), xlab="", ylab="", main=paste("n=", i))
polygon(p)
}
We can extrapolate to a generic convex polygon.
The area of a convex polygon can be found as:
A = 1/2 * [(x1*y2 + x2*y3 + ... + xn*y1) - (y1*x2 + y2*x3 + ... + yn*x1)]
We generate the polygon as above, but deviate angles and radii from those of the regular polygon.
We then scale the points to get the desired area.
convex.poly <- function(nSides, area)
{
# Find the radius of the circumscribed circle, and the angle of each point if this was a regular polygon
radius <- sqrt((2*area)/(nSides*sin((2*pi)/nSides)))
angle <- (2*pi)/nSides
# Randomize the radii/angles
radii <- rnorm(nSides, radius, radius/10)
angles <- rnorm(nSides, angle, angle/10) * 1:nSides
angles <- sort(angles)
points <- list(x=NULL, y=NULL)
points$x <- cos(angles) * radii
points$y <- sin(angles) * radii
# Find the area of the polygon
m <- matrix(unlist(points), ncol=2)
m <- rbind(m, m[1,])
current.area <- 0.5 * (sum(m[1:nSides,1]*m[2:(nSides+1),2]) - sum(m[1:nSides,2]*m[2:(nSides+1),1]))
points$x <- points$x * sqrt(area/current.area)
points$y <- points$y * sqrt(area/current.area)
return (points)
}
A random square of area 500m^2 is easy - its a square of side sqrt(500)m. Do you care about rotations? Then rotate it by runif(x,0,2*pi). Do you care about its location? Add an (x,y) offset computed from runif or whatever.
Rectangle? Given the length of any one pair of sides you only have the freedom to choose the length of the other two. How do you choose the length of the first pair of sides? Well, you might want to use runif() between some 'sensible' limits for your application. You could use rnorm() but that might give you negative lengths, so maybe rnorm-squared. Then once you've got that side, the other side length is 500/L. Rotate, translate, and add salt and pepper to taste.
For triangles, the area formula is half-base-times-height. So generate a base length - again, runif, rnorm etc etc - then choose another point giving the required height. Rotate, etc.
Summarily, a shape has a number of "degrees of freedom", and constraining the area to be fixed will limit at least one of those freedoms[1], so if you start building a shape with random numbers you'll come to a point where you have to put in a computed value.
[1] exactly one? I'm not sure - these aren't degrees of freedom in the statistical sense...
I would suggest coding a random walk of adjacent tiny squares, so that the aggregation of the tiny squares could be of arbitrary shape with known area.
http://en.wikipedia.org/wiki/File:Random_walk_in2D.png
It would be very tough to make a generic method.
But you could code up example for 3, 4, 5 sided objects.
Here is an example of a random triangle.(in C#)
class Triangle
{
double Angle1;
double Angle2;
//double angle3; 180 - angle1 - angle2;
double Base;
}
Triangle randomTriangle(double area){
//A = (base*hieght)/2.0;
double angle1 = *random number < 180*;
double angle2 = *random number < (180 - angle1)*;
*use trig to get height in terms of angles and base*
double base = (area*2.0)/height;
return new Triangle(){Angle1 = angle1, Angle2 = angle2, Base = base};
}

Resources