10

Given a rectangular shape S, with aspect ratio sx/sy, and two other rectangular shapes A (with aspect ratio ax/ay) and B (with aspect ratio bx/by) how can I find out which of shape A or B has the closest aspect ratio to S? The shapes' sizes are unimportant.

Is it just whichever of (sx/sy)/(ax/ay) and (sx/sy)/(bx/by) is closest to 1?

What I am actually trying to do is find out which shape on a PPTX slide would best fit an image that will be resized and then cropped to fit that shape. I guess another approach would be to work out which shape results in the fewest pixels being lost, although in my code it will be easier if I can do it by comparing aspect ratios.

In the end I went with the algorithm below, implemented as follows (thanks to Matt Ball for his feedback):

ShapeInPPTXLocation closest;
double imageAR = a_imageDim.getWidth()/a_imageDim.getHeight();
double aspectRatioCandidateA = a_candidateA.getWidth()/a_candidateA.getHeight();
double aspectRatioCandidateB = a_candidateB.getWidth()/a_candidateB.getHeight();
double closenessScoreA=1-(imageAR/aspectRatioCandidateA);
double closenessScoreB=1-(imageAR/aspectRatioCandidateB);

if (Math.abs(closenessScoreA) <= Math.abs(closenessScoreB))
{
    closest=a_candidateA;
}
else
{
    closest=a_candidateB;
}
Martin Wilson
  • 3,386
  • 1
  • 24
  • 29

2 Answers2

3

Is it just whichever of (sx/sy)/(ax/ay) and (sx/sy)/(bx/by) is closest to 1?

That sounds reasonable. You could also just minimize the difference:

let target_ratio = sx/sy
let a_ratio = ax/ay
let b_ration = bx/by

if |target_ratio - a_ratio| < |target_ratio - b_ratio|
    a_ratio is closer to target
else
    b_ratio is closer to target

Update: the algorithm in this answer does not quite work, as explained in the comments below. The OP updated his question to include the algorithm that he used, which works seems to work fine.

Martin Wilson
  • 3,386
  • 1
  • 24
  • 29
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • I thought about this initially but I think it only works if we assume all the shapes have X>Y (or vice verse). For example, for portrait images the aspect ratio is always a fraction; for landscape it is an integer. Let me know if I'm wrong about this though - thanks – Martin Wilson May 02 '12 at 15:09
  • Assume that `target_ratio` is `1`, `a_ratio` is `1.1`, and `b_ratio` is `0.5` (so the correct answer is `a_ratio`). Then you have `|-0.1| < |0.5|` or `0.1 < 0.5` so the `if` case is `true`, so we get `a_ratio` from the pseudocode algorithm. So this works just fine when using a mix of landscape and portrait. – Matt Ball May 02 '12 at 15:14
  • 1
    Just implemented this in my code and it works perfectly - thanks :-) – Martin Wilson May 02 '12 at 16:02
  • Having said that... there's an edge case for which this algorithm doesn't work, which is related to my comment above. – Martin Wilson May 02 '12 at 18:42
  • Which is: consider the case where the image S and shape A are both landscape, B is portrait, and a_ratio is >2*target_ratio. In this case _any_ b_ratio is going to look like a better fit as |target_ratio - b_ratio| must be less than target_ratio (as b_ratio is a fraction) but |target_ratio-a_ratio| must be greater than target_ratio. As an example, try with target_ratio = 2, a_ratio=5, b_ratio=0.0000001 (i.e. b_ratio is extremely tall and thin - not a good match) – Martin Wilson May 02 '12 at 18:50
  • Hm, then perhaps it would be better to compare the ratio of ratios (as you initially mentioned) than the differences - after all, `0.0000001` **is** closer to `2` than `5` is to `2`. – Matt Ball May 02 '12 at 18:59
  • Have now done that - works for all cases I have thrown at it. Thanks for your help – Martin Wilson May 02 '12 at 19:15
3

Looking at the suggestion above, I'm not convinced:

Think on the following example: A = 1:2 B = 2:1 and

targetRatio = 1:1

Clearly both A & B should be equally suitable, but with the comparison of

( 1 - GoalAR/CandiateAR) as suggested,

aspectRatioCandidateA = 0.5 [ 1 : 2 ]

aspectRatioCandidateB = 2 [ 2 : 1 ]

you'd get

closenessScoreA = 1

closenessScoreB = 0.5

The best way to compare aspect ratios is thinking of them as defining an angle:

tan(o) = h/w

o = atan( h/w )

You can then simply compare the difference of the angles now.

BmyGuest
  • 6,331
  • 1
  • 21
  • 35
  • The accepted answer as stated didn't work, as your example demonstrates. If you read the comment thread for that answer you'll see what I ended up doing- which was using the algorithm I put forward in the question. Anyway, thanks for your input :-) – Martin Wilson Apr 09 '14 at 07:37
  • Hi. Slightly confused now. As I understand the current thread, the code printed in the question (after your edit) is what you're using, isn't it? I was referring to that with my counter-example. Take the example of the two ratios A=3:4=0.75 and B=17:10=1.7. Which is closer to G=5:4=1.25? According to your code it would be A with a score of 0.66 while B has a score of 0.735. However, if you compare the angles, you end up with B being "closer" to the goal. B has a theta of 59.53, A has a theta of 36.869 and the goal is at 51.34. – BmyGuest Apr 09 '14 at 15:23
  • 1
    Your method may be more accurate (it seems reasonable). However, using 'my' algorithm, B also win:s A's (sx/sy)/(ax/ay) = 1.67; B's (sx/sy)/(ax/ay) = 0.74; A's score (closeness to 1) = 1-1.67 = 0.67; B's score = 1-0.74 = 0.26. Unless I've made a mistake ;-) – Martin Wilson Apr 10 '14 at 19:25