How do I find a dimension of aspect ratio 4:3 which fits within a predetermined size? - image

The problem here is I have a display window of size x by y, and I need to display an image inside the window without any scrolling, and to maintain the aspect ratio of 4:3. I have the following snippet of code:
// Lock the current height, calculate new width of the canvas and scale the viewport.
// get width of the movie canvas
qreal width = canvas_->width();
// Find the height of the video
qreal height = (width/4.0) * 3;
// find original width and height for video to calculate scaling factor
qreal videoWidth = movieData_->GetWidth();
qreal videoHeight = movieData_->GetHeight();
// calculate scaling factor
qreal scaleFactorWidth = width/videoWidth;
qreal scaleFactorHeight = height/videoHeight;
Of course, by using either the height, or the width as the 'anchor', one way or other the new image will cause scrolling (assuming the original image is larger than the window in the first place). How do I find a dimension of aspect ratio 4:3 which fits within a predetermined size?
Edit
I would need to pass in a scale factor for both x and y to do the scaling
canvas_->scale(scaleFactorWidth, scaleFactorHeight);

Just take the minimum of the both calculated values:
scale = min(scaleFactorWidth, scaleFactorHeight)
or (if you want outer-fit)
scale = max(scaleFactorWidth, scaleFactorHeight)

struct dimensions resize_to_fit_in(struct dimensions a, struct dimensions b) {
double wf, hf, f;
struct dimensions out;
wf = (double) b.w / a.w;
hf = (double) b.h / a.h;
if (wf > hf)
f = hf;
else
f = wf;
out.w = a.w * f;
out.h = a.h * f;
return out;
}
An here is a C version where the returned dimension will be a dimension 'a' fitted in dimension 'b' without loosing aspect ratio.

Find the largest of the two values width, w and height h. Say your maximum width x height is 100 x 80. Note that 100/80 = 1.25.
Case 1: If w/h > 1.25, then divide w by 100 to get the ratio of your original size to the new size. Then multiply h by that ratio.
Case 2: Otherwise, then divide h by 80 to get the ratio of your original size to the new size. Then multiply w by that ratio.

Here's an ActionScript version of what you ask (resize while maintaining aspect ratio)... shouldn't be too hard to port to whatever:
private static function resizeTo(dispObj:DisplayObject, width:Number, height:Number) : void
{
var ar:Number = width / height;
var dispObjAr:Number = dispObj.width/dispObj.height;
if (ar < dispObjAr)
{
dispObj.width = width;
dispObj.height = width / dispObjAr;
}
else
{
dispObj.height = height;
dispObj.width = height * dispObjAr;
}
return;
}
EDIT: In order to maintain 4:3 the source images would need to be 4:3

Related

Processing: Efficiently create uniform grid

I'm trying to create a grid of an image (in the way one would tile a background with). Here's what I've been using:
PImage bgtile;
PGraphics bg;
int tilesize = 50;
void setup() {
int t = millis();
fullScreen(P2D);
background(0);
bgtile = loadImage("bgtile.png");
int bgw = ceil( ((float) width) / tilesize) + 1;
int bgh = ceil( ((float) height) / tilesize) + 1;
bg = createGraphics(bgw*tilesize,bgh*tilesize);
bg.beginDraw();
for(int i = 0; i < bgw; i++){
for(int j = 0; j < bgh; j++){
bg.image(bgtile, i*tilesize, j*tilesize, tilesize, tilesize);
}
}
bg.endDraw();
print(millis() - t);
}
The timing code says that this takes about a quarter of a second, but by my count there's a full second once the window opens before anything shows up on screen (which should happen as soon as draw is first run). Is there a faster way to get this same effect? (I want to avoid rendering bgtile hundreds of times in the draw loop for obvious reasons)
One way could be to make use of the GPU and let OpenGL repeat a texture for you.
Processing makes it fairly easy to repeat a texture via textureWrap(REPEAT)
Instead of drawing an image you'd make your own quad shape and instead of calling vertex(x, y) for example, you'd call vertex(x, y, u, v); passing texture coordinates (more low level info on the OpenGL link above). The simple idea is x,y would control the geometry on screen and u,v would control how the texture is applied to the geometry.
Another thing you can control is textureMode() which allows you control how you specify the texture coordinates (U, V):
IMAGE mode is the default: you use pixel coordinates (based on the dimensions of the texture)
NORMAL mode uses values between 0.0 and 1.0 (also known as normalised values) where 1.0 means the maximum the texture can go (e.g. image width for U or image height for V) and you don't need to worry about knowing the texture image dimensions
Here's a basic example based on the textureMode() example above:
PImage img;
void setup() {
fullScreen(P2D);
noStroke();
img = loadImage("https://processing.org/examples/moonwalk.jpg");
// texture mode can be IMAGE (pixel dimensions) or NORMAL (0.0 to 1.0)
// normal means 1.0 is full width (for U) or height (for V) without having to know the image resolution
textureMode(NORMAL);
// this is what will make handle tiling for you
textureWrap(REPEAT);
}
void draw() {
// drag mouse on X axis to change tiling
int tileRepeats = (int)map(constrain(mouseX,0,width), 0, width, 1, 100);
// draw a textured quad
beginShape(QUAD);
// set the texture
texture(img);
// x , y , U , V
vertex(0 , 0 , 0 , 0);
vertex(width, 0 , tileRepeats, 0);
vertex(width, height, tileRepeats, tileRepeats);
vertex(0 , height, 0 , tileRepeats);
endShape();
text((int)frameRate+"fps",15,15);
}
Drag the mouse on the Y axis to control the number of repetitions.
In this simple example both vertex coordinates and texture coordinates are going clockwise (top left, top right, bottom right, bottom left order).
There are probably other ways to achieve the same result: using a PShader comes to mind.
Your approach caching the tiles in setup is ok.
Even flattening your nested loop into a single loop at best may only shave a few milliseconds off, but nothing substantial.
If you tried to cache my snippet above it would make a minimal difference.
In this particular case, because of the back and forth between Java/OpenGL (via JOGL), as far as I can tell using VisualVM, it looks like there's not a lot of room for improvement since simply swapping buffers takes so long (e.g. bg.image()):
An easy way to do this would be to use processing's built in get(); which saves a PImage of the coordinates you pass, for example: PImage pic = get(0, 0, width, height); will capture a "screenshot" of your entire window. So, you can create the image like you already are, and then take a screenshot and display that screenshot.
PImage bgtile;
PGraphics bg;
PImage screenGrab;
int tilesize = 50;
void setup() {
fullScreen(P2D);
background(0);
bgtile = loadImage("bgtile.png");
int bgw = ceil(((float) width) / tilesize) + 1;
int bgh = ceil(((float) height) / tilesize) + 1;
bg = createGraphics(bgw * tilesize, bgh * tilesize);
bg.beginDraw();
for (int i = 0; i < bgw; i++) {
for (int j = 0; j < bgh; j++) {
bg.image(bgtile, i * tilesize, j * tilesize, tilesize, tilesize);
}
}
bg.endDraw();
screenGrab = get(0, 0, width, height);
}
void draw() {
image(screenGrab, 0, 0);
}
This will still take a little bit to generate the image, but once it does, there is no need to use the for loops again unless you change the tilesize.
#George Profenza's answer looks more efficient than my solution, but mine may take a little less modification to the code you already have.

Processing - creating circles from current pixels

I'm using processing, and I'm trying to create a circle from the pixels i have on my display.
I managed to pull the pixels on screen and create a growing circle from them.
However i'm looking for something much more sophisticated, I want to make it seem as if the pixels on the display are moving from their current location and forming a turning circle or something like this.
This is what i have for now:
int c = 0;
int radius = 30;
allPixels = removeBlackP();
void draw {
loadPixels();
for (int alpha = 0; alpha < 360; alpha++)
{
float xf = 350 + radius*cos(alpha);
float yf = 350 + radius*sin(alpha);
int x = (int) xf;
int y = (int) yf;
if (radius > 200) {radius =30;break;}
if (c> allPixels.length) {c= 0;}
pixels[y*700 +x] = allPixels[c];
updatePixels();
}
radius++;
c++;
}
the function removeBlackP return an array with all the pixels except for the black ones.
This code works for me. There is an issue that the circle only has the numbers as int so it seems like some pixels inside the circle won't fill, i can live with that. I'm looking for something a bit more complex like I explained.
Thanks!
Fill all pixels of scanlines belonging to the circle. Using this approach, you will paint all places inside the circle. For every line calculate start coordinate (end one is symmetric). Pseudocode:
for y = center_y - radius; y <= center_y + radius; y++
dx = Sqrt(radius * radius - y * y)
for x = center_x - dx; x <= center_x + dx; x++
fill a[y, x]
When you find places for all pixels, you can make correlation between initial pixels places and calculated ones and move them step-by-step.
For example, if initial coordinates relative to center point for k-th pixel are (x0, y0) and final coordinates are (x1,y1), and you want to make M steps, moving pixel by spiral, calculate intermediate coordinates:
calc values once:
r0 = Sqrt(x0*x0 + y0*y0) //Math.Hypot if available
r1 = Sqrt(x1*x1 + y1*y1)
fi0 = Math.Atan2(y0, x0)
fi1 = Math.Atan2(y1, x1)
if fi1 < fi0 then
fi1 = fi1 + 2 * Pi;
for i = 1; i <=M ; i++
x = (r0 + i / M * (r1 - r0)) * Cos(fi0 + i / M * (fi1 - fi0))
y = (r0 + i / M * (r1 - r0)) * Sin(fi0 + i / M * (fi1 - fi0))
shift by center coordinates
The way you go about drawing circles in Processing looks a little convoluted.
The simplest way is to use the ellipse() function, no pixels involved though:
If you do need to draw an ellipse and use pixels, you can make use of PGraphics which is similar to using a separate buffer/"layer" to draw into using Processing drawing commands but it also has pixels[] you can access.
Let's say you want to draw a low-res pixel circle circle, you can create a small PGraphics, disable smoothing, draw the circle, then render the circle at a higher resolution. The only catch is these drawing commands must be placed within beginDraw()/endDraw() calls:
PGraphics buffer;
void setup(){
//disable sketch's aliasing
noSmooth();
buffer = createGraphics(25,25);
buffer.beginDraw();
//disable buffer's aliasing
buffer.noSmooth();
buffer.noFill();
buffer.stroke(255);
buffer.endDraw();
}
void draw(){
background(255);
//draw small circle
float circleSize = map(sin(frameCount * .01),-1.0,1.0,0.0,20.0);
buffer.beginDraw();
buffer.background(0);
buffer.ellipse(buffer.width / 2,buffer.height / 2, circleSize,circleSize);
buffer.endDraw();
//render small circle at higher resolution (blocky - no aliasing)
image(buffer,0,0,width,height);
}
If you want to manually draw a circle using pixels[] you are on the right using the polar to cartesian conversion formula (x = cos(angle) * radius, y = sin(angle) * radius).Even though it's focusing on drawing a radial gradient, you can find an example of drawing a circle(a lot actually) using pixels in this answer

Scaling an image proportionally based on the image's dimensions and my restriction size

Looking for an expression that allows me to accomplish this:
I have an image of arbitrary width/height, whose dimensions I can grab before I draw it.
Because the image may be very large, I want to scale it down.
My canvas is going to have width w and height h.
For illustration purposes let's just say it's 320x240.
If the dimensions of the image are equal or smaller than the dimensions of the canvas, then the scale ratio is just 1.
If they are larger, I will scale it proportional to how much larger it is compared to the canvas size.
So for example if my image is 640x480, my scale ratio will be 0.5
If my image is 640x240, my scale ratio would still be 0.5
Similarly if it were 320x480
Can this be written in a single math expression? For ex:
def scale_ratio(canvas_width, canvas_height, image_width, image_height)
#math formula for calculating scale
return scale
function scale(canvas_width, canvas_height, image_width, image_height) {
return Math.min(Math.max(canvas_width / image_width, canvas_height / image_height), 1);
}
EDIT: You might want to do something like this to reduce rounding errors:
var scale_width = image_width;
var scale_height = image_height;
if (image_width > canvas_width || image_height > canvas_height) {
var image_ratio = image_height / image_width;
if (image_ratio * canvas_width > canvas_height) {
scale_width = canvas_height / image_ratio;
scale_height = canvas_height;
} else {
scale_width = canvas_width;
scale_height = image_ratio * canvas_width;
}
}

Resize image by pixel amount

I tried to find out, but I couldn't.
A image, for example, 241x76 has a total of 18,316 pixels (241 * 76).
The resize rule is, the amount of pixels cannot pass 10,000.
Then, how can I get the new size keeping the aspect ratio and getting less than 10,000 pixels?
Pseudocode:
pixels = width * height
if (pixels > 10000) then
ratio = width / height
scale = sqrt(pixels / 10000)
height2 = floor(height / scale)
width2 = floor(ratio * height / scale)
ASSERT width2 * height2 <= 10000
end if
Remember to use floating-point math for all calculations involving ratio and scale when implementing.
Python
import math
def capDimensions(width, height, maxPixels=10000):
pixels = width * height
if (pixels <= maxPixels):
return (width, height)
ratio = float(width) / height
scale = math.sqrt(float(pixels) / maxPixels)
height2 = int(float(height) / scale)
width2 = int(ratio * height / scale)
return (width2, height2)
An alternative function in C# which takes and returns an Image object:
using System.Drawing.Drawing2D;
public Image resizeMaxPixels(int maxPixelCount, Image originalImg)
{
Double pixelCount = originalImg.Width * originalImg.Height;
if (pixelCount < maxPixelCount) //no downsize needed
{
return originalImg;
}
else
{
//EDIT: not actually needed - scaleRatio takes care of this
//Double aspectRatio = originalImg.Width / originalImg.Height;
//scale varies as the square root of the ratio (width x height):
Double scaleRatio = Math.Sqrt(maxPixelCount / pixelCount);
Int32 newWidth = (Int32)(originalImg.Width * scaleRatio);
Int32 newHeight = (Int32)(originalImg.Height * scaleRatio);
Bitmap newImg = new Bitmap(newWidth, newHeight);
//this keeps the quality as good as possible when resizing
using (Graphics gr = Graphics.FromImage(newImg))
{
gr.SmoothingMode = SmoothingMode.AntiAlias;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(originalImg, new Rectangle(0, 0, newWidth, newHeight));
}
return newImg;
}
}
with graphics code from the answer to Resizing an Image without losing any quality
EDIT: Calculating the aspect ratio is actually irrelevant here as we're already scaling the width and height by the (square root) of the total pixel ratio. You could use it to calculate the newWidth based on the newHeight (or vice versa) but this isn't necessary.
Deestan's code works for square images, but in situations where the aspect ratio is different than 1, a square root won't do. You need to take scale to the power of aspect ratio divided by 2.
Observe (Python):
def capDimensions(width, height, maxPixels):
pixels = width * height
if (pixels <= maxPixels):
return (width, height)
ratio = float(width) / height
scale = (float(pixels) / maxPixels)**(width/(height*2))
height2 = round(float(height) / scale)
width2 = round(ratio * height2)
return (width2, height2)
Let's compare the results.
initial dimensions: 450x600
initial pixels: 270000
I'm trying to resize to get as close as possible to 119850 pixels.
with Deestan's algorithm:
capDimensions: 300x400
resized pixels: 67500
with the modified algorithm:
capDimensions. 332x442
resized pixels: 82668
width2 = int(ratio * height / scale)
would better be
width2 = int(ratio * height2)
because this would potentially preserve the aspect ratio better (as height2 has been truncated).
Without introducing another variable like 'sc', one can write
new_height = floor(sqrt(m / r))
and
new_width = floor(sqrt(m * r))
given m=max_pixels (here: 10.000), r=ratio=w/h (here: 241/76 = 3.171)
Both results are independent of each other! From each new_value, you can calculate the other dimension, with
(given: new_height) new_width = floor(new_height * r)
(given: new_width) new_height = floor(new_width / r)
Because of clipping the values (floor-function), both pairs of dimensions may differ in how close their ratio is to the original ratio; you'd choose the better pair.
Scaling images down to max number of pixels, while maintaining aspect ratio
This is what I came up with this afternoon, while trying to solve the math problem on my own, for fun. My code seems to work fine, I tested with a few different shapes and sizes of images. Make sure to use floating point variables or the math will break.
Pseudocode
orig_width=1920
orig_height=1080
orig_pixels=(orig_width * orig_height)
max_pixels=180000
if (orig_pixels <= max_pixels) {
# use original image
}
else {
# scale image down
ratio=sqrt(orig_pixels / max_pixels)
new_width=floor(orig_width / ratio)
new_height=floor(orig_height / ratio)
}
Example results
1920x1080 (1.77778 ratio) becomes 565x318 (1.77673 ratio, 179,670 pixels)
1000x1000 (1.00000 ratio) becomes 424x424 (1.00000 ratio, 179,776 pixels)
200x1200 (0.16667 ratio) becomes 173x1039 (0.16651 ratio, 179,747 pixels)

Given aspect ratio of a rectangle, find maximum scale and angle to fit it inside another rectangle

I've read a few dozen questions on this topic, but none seem to be exactly what I'm looking for, so I'm hoping this isn't a duplicate.
I have an image, whose aspect ratio I want to maintain, because it's an image.
I want to find the largest scale factor, and corresponding angle between 0 and 90 degrees inclusive, such that the image will fit wholly inside a given rectangle.
Example 1: If the image and rectangle are the same ratio, the angle will be 0, and the scale factor will be the ratio of the rectangle's width to the image's width. (Or height-to-height.)
Example 2: If the image and rectangle ratios are the inverse of each other, the scale factor will be the same as the first example, but the angle will be 90 degrees.
So, for the general case, given image.width, image.height, rect.width, rect.height, how do I find image.scale and image.angle?
OK, I figured it out on my own.
First, calculate the aspect ratio. If your image is 1:1, there's no point in this, because the angle is always zero, and the scale is always min(Width, Height). Degeneration.
Otherwise, you can use this:
// assuming below that Width and Height are the rectangle's
_imageAspect = _image.width / _image.height;
if (_imageAspect == 1) { // div by zero implied
trace( "square image...this does not lend itself to rotation ;)" );
return;
}
_imageAspectSq = Math.pow( _imageAspect, 2 );
var rotate:Float;
var newHeight:Float;
if (Width > Height && Width / Height > _imageAspect) { // wider aspect than the image
newHeight = Height;
rotate = 0;
} else if (Height > Width && Height / Width > _imageAspect) { // skinnier aspect than the image rotated 90 degrees
newHeight = Width;
rotate = Math.PI / 2;
} else {
var hPrime = (_imageAspect * Width - _imageAspectSq * Height) / ( 1 - _imageAspectSq );
var wPrime = _imageAspect * (Height - hPrime);
rotate = Math.atan2( hPrime, wPrime );
var sine = Math.sin(rotate);
if (sine == 0) {
newHeight = Height;
} else {
newHeight = (Width - wPrime) / sine;
}
}
The first two cases are also degenerate: the image's aspect ratio is less than the rectangle. This is similar to the square-within-a-rectangle case, except that in that case, the square is always degenerate.
The code assumes radians instead of degrees, but it's not hard to convert.
(Also I'm a bit shocked that my browser's dictionary didn't have 'radians'.)

Resources