Preserving aspect ratio when using torch's image.display - image

I have the following very simple script written in lua. I am running it with qlua.
require "image"
input_image = image.load(arg[1])
image.display{image = input_image}
If the image is large the qt window simply takes the whole screen, which also stretches the image to fit the screen.
I can't figure out a way to keep this from happening.
Thanks!

If the image is large, resize it down to what you can configure as "Max height/Max width", while preserving the aspect ratio.
Sample code:
maxSize = 480
-- find the smaller dimension, and resize it to maxSize (while keeping aspect ratio)
local iW = input:size(3)
local iH = input:size(2)
if iW < iH then
input = image.scale(input, maxSize, maxSize * iH / iW)
else
input = image.scale(input, maxSize * iW / iH, maxSize)
end

Related

How to get the correct `RGB` value of a `PNG` image?

Mapbox provides Global elevation data with height data encoded in PNG image. Height is decoded by height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1). Details are in https://www.mapbox.com/blog/terrain-rgb/.
I want to import the height data to generate terrains in Unity3D.
Texture2D dem = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/dem/12/12_3417_1536.png", typeof(Texture2D));
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
{
Color c = dem.GetPixel(i, j);
float R = c.r*255;
float G = c.g*255;
float B = c.b*255;
array[i, j] = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1f);
}
Here I set a break point and the rgba value of the first pixel is RGBA(0.000, 0.592, 0.718, 1.000). c.r is 0. The height is incorrect as this point represent the height of somewhere on a mountain.
Then I open the image in Photoshop and get RGB of the first pixel: R=1,G=152,B=179.
I write a test program in C#.
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap("12_3417_1536.png");
Color a = bitmap.GetPixel(0, 0);
It shows Color a is (R,G,B,A)=(1,147,249,255)
Here is the image I test:
https://api.mapbox.com/v4/mapbox.terrain-rgb/12/3417/1536.pngraw?access_token=pk.eyJ1Ijoib2xlb3RpZ2VyIiwiYSI6ImZ2cllZQ3cifQ.2yDE9wUcfO_BLiinccfOKg
Why I got different RGBA value with different method? Which one is correct?
According to the comments below, different read order and compressed data in unity may result in different value of the rgba of pixel at (0,0).
Now I want to focus on----How to convert the rgba(0~1) to RGBA(0~255)?
r_ps=r_unity*255? But how can I explain r=0 in unity and r=1 in PS of pixel at (0,0)
?
Try disabling compression from the texture's import settings in Unity (No compression). Alternatively, if you fetch the data at runtime, you can use Texture.LoadBytes() to avoid compression artifacts.
I will assume you are using the same picture and that there aren't two 12_3417_1536.png files in separate folders.
Each of these functions has a different concept of which pixel is at (0,0). Not sure what you mean by "first" pixel when you tested with photoshop, but Texture coordinates in unity start at lower left corner.
When I tested the lower left corner pixel using paint, I got the same value as you did with photoshop. However, if you test the upper left corner, you get (1,147,249,255) which is the result bitmap.GetPixel returns.
The unity values that you're getting seem to be way off. Try calling dem.GetPixel(0,0) so that you're sure you're analyzing the simplest case.

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)

Resizing images maintaining aspect ratio

I want to resize the images while maintaining the aspect ratio for displaying images on the web page. The maximum image size can be 640x480. What equation can be used to resize the images? I don't care about the newly resized image size. The resolution should be near to 640x480 pixels
I explain using C pseudo-code. First calculate the aspect ratio of the image you want to resize ("testImage"):
double rat = (double)testImage.Width / (double)testImage.Height;
Then we compare it with the aspect ratio of a 640x480 picture. If testImage's ratio ("rat") is bigger than the ratio of a 640x480 picture, then we know if we resize the picture so that its Width becomes 640, its height will not be more than 480. If testImage's aspect ratio is smaller, then we can resize the picture so that height becomes 480 without the width exceeding 640 pixels.
const double rat640x480 = 640.0 / 480.0;
if (rat > rat640x480)
testImage.Resize(Width := 640, Height := 640 / rat);
else
testImage.Resize(Width := 480 * rat, Height := 480);
Code in JAVA becomes
double ratio640x480 = 640.0 / 480.0;
double sourceRatio = (double) bitmap.getWidth() / (double) bitmap.getHeight();
if (sourceRatio > ratio640x480)
bitmap = Bitmap.createScaledBitmap(bitmap, 640, (int) (640 / sourceRatio), true);
else
bitmap = Bitmap.createScaledBitmap(bitmap, (int) (480 * sourceRatio), 480, true);

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

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

Resources