Cropping
First you need to decide whether the image is too wide or too tall for the wanted ratio.
Then adjust the corresponding dimension.
I don't know which python image processing library you're using, so my answer only contains the arithmetic code to compute the new width and height, not the actual code to transform the image.
def cropped_dims(width, height, ratio):
if width > height*ratio:
width = int(height*ratio + 0.5)
else:
height = int(width/ratio + 0.5)
return (width, height)
print(cropped_dims(1833, 2133, 3/4))
# (1600, 2133)
Closest number pair
In the example you give, 1833 x 2133 gets resized to 1623 x 2164, not to 1600 x 2133. Instead of cropping down to the appropriate ratio, you mention looking for the "nearest number pair" with the appropriate ratio.
A number pair can be represented as a point in the plane, using Cartesian coordinates.
Under this representation, the number pairs with the appropriate ratio are exactly the points on the line whose slope is this ratio (with the exception of point (0,0) which doesn't have a ratio).
The original dimensions of the image are also a number pair, and this number pair is represented by a point in the plane; a priori, this point is not on the line.
The solution your are looking for is the closest point on the line. This is a very classical geometry problem, with a simple solution. For an explanation on how to solve this problem, see for instance this question: Point on a line closest to third point.
The line L containing the points of ratio ratio
consists in the points of the form (ratio * t, t)
for some real number t
.
The perpendicular D to that line L passing through (width, height)
consists in the points of the form (width+s, height - ratio * s)
for some real number s
.
The closest point to (width, height)
on line L is the point at the intersection of L and D. You can find its coordinates by solving for unknowns (s,t)
the system of equations (ratio * t, t) == (width+s, height - ratio * s)
.
The solution is t = (ratio * width + height) / (1 + ratio**2)
.
Hence the python code:
def resized_dims(width, height, ratio):
t = (height + ratio * width) / (ratio**2 + 1)
new_width = int(t*ratio + 0.5)
new_height = int(t + 0.5)
return new_width, new_height
print(resized_dims(1833, 2133, 3/4))
# (1684, 2245)
Comparing the different solutions:
import math
w, h = (1833, 2133)
cropped_w, cropped_h = cropped_dims(1833, 2133, 3/4) # (1600, 2133)
closest_w, closest_h = resized_dims(1833, 2133, 3/4) # (1684, 2245)
your_w, your_h = (1623, 2164)
print('Original ratio: ', w/h)
print('Wanted ratio: ', 3/4)
print('Ratio of cropped: ', cropped_w/cropped_h)
print('Ratio of closest: ', closest_w/closest_h)
print('Ratio of your solution: ', your_w/your_h)
print()
print('Distance of cropped: ', math.dist((w,h), (cropped_w,cropped_h)))
print('Distance of closest: ', math.dist((w,h), (closest_w,closest_h)))
print('Distance of your solution:', math.dist((w,h), (your_w,your_h)))
# Original ratio: 0.8594
# Wanted ratio: 0.75
# Ratio of cropped: 0.7501
# Ratio of closest: 0.7501
# Ratio of your solution: 0.75
#
# Distance of cropped: 233.0
# Distance of closest: 186.40
# Distance of your solution: 212.28