App Engine Cropping to a Specific Width and Height - google-app-engine

I need to resize and crop an image to a specific width and height. I was able to construct a method that will create a square thumbnail, but I'm unsure on how to apply this, when the desired thumbnail is not square.
def rescale(data, width, height):
"""Rescale the given image, optionally cropping it to make sure the result image has the specified width and height."""
from google.appengine.api import images
new_width = width
new_height = height
img = images.Image(data)
org_width, org_height = img.width, img.height
# We must determine if the image is portrait or landscape
# Landscape
if org_width > org_height:
# With the Landscape image we want the crop to be centered. We must find the
# height to width ratio of the image and Convert the denominater to a float
# so that ratio will be a decemal point. The ratio is the percentage of the image
# that will remain.
ratio = org_height / float(org_width)
# To find the percentage of the image that will be removed we subtract the ratio
# from 1 By dividing this number by 2 we find the percentage that should be
# removed from each side this is also our left_x coordinate
left_x = (1- ratio) / 2
# By subtract the left_x from 1 we find the right_x coordinate
right_x = 1 - left_x
# crop(image_data, left_x, top_y, right_x, bottom_y), output_encoding=images.PNG)
img.crop(left_x, 0.0, right_x, 1.0)
# resize(image_data, width=0, height=0, output_encoding=images.PNG)
img.resize(height=height)
# Portrait
elif org_width < org_height:
ratio = org_width / float(org_height)
# crop(image_data, left_x, top_y, right_x, bottom_y), output_encoding=images.PNG)
img.crop(0.0, 0.0, 1.0, ratio)
# resize(image_data, width=0, height=0, output_encoding=images.PNG)
img.resize(width=witdh)
thumbnail = img.execute_transforms()
return thumbnail
If there is a better way to do this please let me know. Any help would be greatly appreciated.
Here's a diagram explaining the desired process.
Thanks,
Kyle

I had a similar problem (your screenshot was very useful). This is my solution:
def rescale(img_data, width, height, halign='middle', valign='middle'):
"""Resize then optionally crop a given image.
Attributes:
img_data: The image data
width: The desired width
height: The desired height
halign: Acts like photoshop's 'Canvas Size' function, horizontally
aligning the crop to left, middle or right
valign: Verticallly aligns the crop to top, middle or bottom
"""
image = images.Image(img_data)
desired_wh_ratio = float(width) / float(height)
wh_ratio = float(image.width) / float(image.height)
if desired_wh_ratio > wh_ratio:
# resize to width, then crop to height
image.resize(width=width)
image.execute_transforms()
trim_y = (float(image.height - height) / 2) / image.height
if valign == 'top':
image.crop(0.0, 0.0, 1.0, 1 - (2 * trim_y))
elif valign == 'bottom':
image.crop(0.0, (2 * trim_y), 1.0, 1.0)
else:
image.crop(0.0, trim_y, 1.0, 1 - trim_y)
else:
# resize to height, then crop to width
image.resize(height=height)
image.execute_transforms()
trim_x = (float(image.width - width) / 2) / image.width
if halign == 'left':
image.crop(0.0, 0.0, 1 - (2 * trim_x), 1.0)
elif halign == 'right':
image.crop((2 * trim_x), 0.0, 1.0, 1.0)
else:
image.crop(trim_x, 0.0, 1 - trim_x, 1.0)
return image.execute_transforms()

You can specify both height and width parameters to resize -- it will not change the aspect ratio (you cannot do that with GAE's images module), but it will ensure that each of the two dimensions is <= the corresponding value you specify (in fact, one will be exactly equal to the value you specify, the other one will be <=).
I'm not sure why you're cropping first and resizing later -- it seems like you should do things the other way around... resize so that as much of the original image "fits" as is feasible, then crop to ensure exact resulting dimension. (So you wouldn't use the original provided values of height and width for the resize -- you'd scale them up so that none of the resulting image is "wasted" aka "blank", if I understand your requirements correctly). So maybe I'm not understanding exactly what you require -- could you provide an example (URLs to an image as it looks before the processing, to how it should look after the processing, and details of the parameters you'd be passing)?

Related

How to scale textures stored in/called from a Godot array

I have 100 .png images loaded into a Godot 3.5.1 array of objects (named splash_planets) as StreamTextures. I need to know how to reference them to allow scaling. I am able to draw them using the following:
for splash_planet in splash_planets:
center = Vector2(splash_planet._x, splash_planet._y)
draw_texture(splash_planet._texture, center)
Now I want to do something like:
draw_texture(splash_planet._texture, center, (scale_x, scale_y))
Thanks in advance for any advice.
First of all, in this line:
draw_texture(splash_planet._texture, center)
You would be drawing the texture with the upper left corner on the coordinates of center. If you want to draw the texture with the center on center, you do this:
var texture := splash_planet._texture
var size := texture.get_size()
draw_texture(texture, center - size * 0.5)
Since you are using draw_texture and you want to scale, you can use draw_set_transform:
draw_set_transform(Vector2.ZERO, 0.0, Vector2(scale_x, scale_y))
As you can see the third parameter is the scale. The first two parameters are translation and rotation. Once you have set the transform you can do the draw call as usual:
draw_set_transform(Vector2.ZERO, 0.0, Vector2(scale_x, scale_y))
var texture := splash_planet._texture
var size := texture.get_size()
draw_texture(texture, center - size * 0.5)
Calling draw_set_transform again (or calling draw_set_transform_matrix) will overwrite the previos transform.
Alternatively you can use draw_texture_rect:
var texture := splash_planet._texture
var size := texture.get_size() * Vector2(scale_x, scale_y)
var rect := Rect2(center - size * 0.5, size)
draw_texture_rect(texture, rect, false)
In this case we compute the rectangle where to draw the texture. We also specify we don't want to tile it (the false as third argument), so Godot will stretch it.
You might also be interested in draw_texture_rect_region, which allows you to draw a rectangular region of the source texture.

SceneKit: understanding the pivot property of SCNNode

The goal is to increase the length of a SCNBox such that it only grows in the positive-z direction.
This answer suggests playing with the pivot property.
However, the documentation for the pivot property is sparse on the SCNNode page, and there is nothing on the SCNBox page.
Can someone explain how the pivot property works?
Changing a node's pivot is conceptually the same as inserting an intermediate node between this node and its parent. This can be useful in different cases. One example is when the center of the node's geometry isn't where you expect it to be.
For instance if you have an SCNBox, it's bounding box is
min: (-0.5 * width, -0.5 * height, -0.5 * length)
max: (+0.5 * width, +0.5 * height, +0.5 * length)
center: (0.0, 0.0, 0.0)
If you want the length of the SCNBox to only increase in the positive Z axis, then what you want is
min: (-0.5 * width, -0.5 * height, 0.0)
max: (+0.5 * width, +0.5 * height, length)
center: (0.0, 0.0, +0.5 * length)
A geometry's bounding box will never change, but there are ways to arrange nodes and change their bounding boxes.
Solution 1: Intermediate node
One common solution when dealing with transforms is to use intermediate nodes to get a better understanding of how the transforms are applied.
In your case you will want to change the node hierarchy from
- parentNode
| - node
| * geometry
| * transform = SCNMatrix4MakeScale(...)
to
- parentNode
| - intermediateNode
| * transform = SCNMatrix4MakeScale(...)
| | - node
| | * geometry
| | * transform = SCNMatrix4MakeTranslation(0, 0, +0.5 * length)
With this new hierarchy, the center of the bounding box of node is still (0.0, 0.0, 0.0), but the center of the bounding box of intermediateNode is (0.0, 0.0, +0.5 * length).
By scaling intermediateNode instead of node you'll obtain the wanted result.
Solution 2: Pivot
It turns out that's exactly what the pivot property does:
node.pivot = SCNMatrix4MakeTranslation(0, 0, -0.5 * length);
Once you have mentally figured out the transform of the intermediate node, simply set its inverse to the pivot property.
You can find more information about the pivot property here: https://developer.apple.com/reference/scenekit/scnnode/1408044-pivot
This is very similar to Core Animation's anchorPoint property on CALayer, except that in Core Animation the anchor point is specified as relative to the layer's bounding box (goes from 0 to 1 as a percentage of the layer's width and height), while in SceneKit it's absolute.
Say you have a box created like this:
SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
The pivot point will be in the center of that box, you now want to move it to one of the edges. This can be done by translating the pivot node by 0.5. (This is half the width of the box or the distance between the center and the edge.)
boxNode.pivot = SCNMatrix4MakeTranslation(0, 0, -0.5)
The pivot point will now be located at center X, center Y, and zero Z of the object. If you now scale the box it will only grow in the positive Z direction.
Sounds like you want to increase the length of the SCNBox(a geometry). So you can simply increase the length property. The answer you mentioned is about the pivot property. As you can see from the doc:
The pivot point for the node’s position, rotation, and scale.
For example, by setting the pivot to a translation transform you can position a node containing a sphere geometry relative to where the sphere would rest on a floor instead of relative to its center.

Map scale factor when converting from Mercator to Equirectangular

Lets say I have a map in Mercator projection, and I know top and bottom latitudes:
topLatitude = 80; bottomLatitude = -55;
I also know width and height of a map:
width = 800; height = 500;
I want to rescale the map to Equirectangular projection, keeping the same width.
How can I calculate new height of a map?
I came up with a solution.
This is formula to calculate x and y of Mercator projected map:
Here is function for y:
function degreesToRadians(degrees){
return degrees / 180 * Math.PI;
}
function mercatorLatitudeToY(latitude){
return Math.log(Math.tan(Math.PI / 4 + degreesToRadians(latitude) / 2));
}
When R=1 the result you get is radians. So I calculate y for top and bottom latitudes.
Next, I calculate the same radians if the projection is Equirectangular - I simply need to convert top and bottom latitude to radians. And last thing - I just need to find aspect ratio of differences:
var scale = (mercatorLatitudeToY(topLatitude) - mercatorLatitudeToY(bottomLatitude))/ (degreesToRadians(topLatitude) - degreesToRadians(bottomLatitude));
For map without Antarctica, aspect ratio is ~1.5

How to interpolate a HSV color?

I am creating an application in win32 api, which will use a progress bar. This progress bar, should change its color. From red (left end) to green(right), and in the middle some yellow.
I searched a little, and found out, that I should use HSV to achieve this. I just don't know how? I found in this link, two functions, to convert the color, from RGB to HSV and back.
But what should I do if the color has been converted to HSV?
Like RGB coordinates, HSV coordinates define a point in a three dimensional space.
You may find a trajectory form one point (x0, one color) to the second (x1) with a formula like:
x = x0 + alpha * (x1-x0)
with alpha varying form 0.0 to 1.0
You can do this for all three components simultaneaously.
With a trajectory from green to red in HSV space you will mainly modify the H (Hue) value. If you want to see some yellow in the middle of your path (and not violett) you need to define a second or even third color and walk
green -> yellow -> red
Edit: Example
int hue0 = 0; // red
int hue2 = 120; // green
// find 100 colors between red and green
for(double alpha = 0; alpha <= 1.0; alpha += 0.01)
{
hueX = hue0 + alpha * (hue1 - hue0);
// same for value, saturation:
// valX = val0 + alpha * (val1 - val0)
// ...
// plot this color
}

Align text to bottom of a frame using Core Text

I have an attributed string that I want to draw bottom-aligned into a rectangular path, using Core Text. Is there a way to get CTFrameSetter / CTFrame to do this, or do I need to do it manually? The manual way being:
Figure out the height of the frame using CTFramesetterSuggestFrameSizeWithConstraints
Adjust the height of the path.
You'll have to do it manually.
CGRect boundingBox = CTFontGetBoundingBox(font);
//Get the position on the y axis
float midHeight = self.frame.size.height / 2;
midHeight -= boundingBox.size.height / 2;
CGPathAddRect(path, NULL, CGRectMake(0, midHeight, self.frame.size.width, boundingBox.size.height));
Reference: Vertical align with Core Text?

Resources