title: Correct way to calculate best camera preview size maintaing aspect ratio
body: <p><strong>QUESTION</strong></p>
  <p>How do you programmatically <em>and accurately</em> determine the best preview size for an application that is displaying the camera's preview at the device's screen size? (Or inside any view of variable dimensions, really).</p>
  <p><strong>BEFORE YOU TRY TO MARK THIS AS A DUPLICATE OF ONE OF THE MILLIONS OF OTHER ASPECT RATIO RELATED QUESTIONS ON SO please understand that I am searching for a different solution than what is generally available</strong>.  I ask this question because I have read so many &quot;answers&quot; but they all point to a solution that I feel is incomplete (and potentially flawed as I will describe here). If it's not a flaw then please help me understand what I am doing wrong.</p>
  <p>I have read a lot of different implementations on how applications choose preview sizes and most of them take an approach that I am calling the &quot;close enough&quot; approach (where you decide which option is best based on subtracting the ratio of the size option from the ratio of the screen resolution and find the option with the lowest value).  This approach seems to not ensure that it is picking the <em>best option</em> but ensure that it won't pick the <em>worst option</em>.</p>
  <p>For example if I try to iterate through each available preview size on a device with screen resolution of <code>720x1184</code> and display the preview full screen (<code>720x1184</code>) here are the results of the camera preview (sorted by options that are closest to the screen resolution in the format <code>abs(ratio of size option - screen resolution ratio)</code> ).  All sizes come from <a href="http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPreviewSizes()" rel="noreferrer">.getSupportedPreviewSizes</a>).  (&quot;Results&quot; are from visual observations using a test case using a static circle that appears in the camera's viewfinder and are not programmatic)</p>
  <pre><code>720.0/1184.0 = 0.6081081081081081
  
  Res         Ratio         Delta in ratios  Result       
  ----------------------------------------------------   
  176/144   = 1.22222222222 (0.614114114114) (vertically stretched)
  352/288   = 1.22222222222 (0.614114114114) (vertically stretched)
  320/240   = 1.33333333333 (0.725225225225) (vertically stretched)
  640/480   = 1.33333333333 (0.725225225225) (vertically stretched)
  720/480   = 1.5           (0.891891891892) (vertically stretched)
  800/480   = 1.66666666667 (1.05855855856)  (looks good)
  640/360   = 1.77777777778 (1.16966966967)  (horizontally squashed)
  1280/720  = 1.77777777778 (1.16966966967)  (slight horizontal squash)
  1920/1080 = 1.77777777778 (1.16966966967)  (slight horizontal squash) 
  </code></pre>
  <p>It wouldn't be a proper Android code test without running it on another device.  Here are the results of displaying a camera preview on a device with screen resolution 800x1216 and displaying the preview at the same resolution (<code>800x1216</code>)</p>
  <pre><code>800/1216.0 = 0.657894736842
  
  Res         Ratio         Delta in ratios  Results
  ------------------------------------------------
  176/144   = 1.22222222222 (0.56432748538)  (looks vertically stretched)
  352/288   = 1.22222222222 (0.56432748538)  (looks vertically stretched)
  480/368   = 1.30434782609 (0.646453089245) (looks vertically stretched)
  320/240   = 1.33333333333 (0.675438596491) (looks vertically stretched)
  640/480   = 1.33333333333 (0.675438596491) (looks vertically stretched)
  800/600   = 1.33333333333 (0.675438596491) (looks vertically stretched)
  480/320   = 1.5           (0.842105263158) (looks good)
  720/480   = 1.5           (0.842105263158) (looks good)
  800/480   = 1.66666666667 (1.00877192982)  (looks horizontally squashed)
  960/540   = 1.77777777778 (1.11988304094)  (looks horizontally squashed)
  1280/720  = 1.77777777778 (1.11988304094)  (looks horizontally squashed)
  1920/1080 = 1.77777777778 (1.11988304094)  (looks horizontally squashed)
  864/480   = 1.8           (1.14210526316)  (looks horizontally squashed)
  </code></pre>
  <p>The &quot;close enough&quot; approach (assuming that any delta in ratio is equal to or less than <code>1.4d</code> is acceptable) would return <code>1920x1080</code> on both devices if iterating through lowest to highest values.  If iterating through highest to lowest values it would pick <code>176x144</code> for DeviceA and <code>176x144</code> for DeviceB.  Both of those options (although &quot;close enough&quot;) are not the best options.</p>
  <p><strong>QUESTION</strong></p>
  <p>In studying the results above how can I programatically derive the values that &quot;look good&quot;?  I can't get these values with the &quot;close enough&quot; approach so I am misunderstanding the relationship between screen size, view that I am displaying the preview in and the preview sizes themselves. What am I missing?</p>
  <pre><code>           Screen dimensions      = 720x1184
               View dimensions      = 720x1184
  Screen and View Aspect Ratio      = 0.6081081081081081
  Best Preview Size ratio (720x480) = 1.5  
  </code></pre>
  <p>Why are the best options not the values that have the lowest delta in ratios?
  The results are surprising since everyone else seems to think the best option is by calculating the smallest difference in ratios but what I'm seeing is that the best option seems to be in the middle of all of the options.  And that their width is closer to the width of the view that will display the preview.</p>
  <p>Based on the above observations (that the best option is not the value with the lowest delta in ratios) I have developed this algorithm that iterates though all of the possible preview sizes, checks if it meets my &quot;close enough&quot; criteria , stores the sizes that meet this criteria and finally find the value that is at least greater than or equal to provided width.</p>
  <pre><code>public static Size getBestAspectPreviewSize(int displayOrientation,
                                              int width,
                                              int height,
                                              Camera.Parameters parameters,
                                              double closeEnough) {
  
  
      double targetRatio=(double)width / height;
      Size bestSize = null;
  
      if (displayOrientation == 90 || displayOrientation == 270) {
          targetRatio=(double)height / width;
      }
  
      List&lt;Size&gt; sizes=parameters.getSupportedPreviewSizes();
      TreeMap&lt;Double, List&gt; diffs = new TreeMap&lt;Double, List&gt;();
  
  
      for (Size size : sizes) {
  
          double ratio=(double)size.width / size.height;
  
          double diff = Math.abs(ratio - targetRatio);
          if (diff &lt; closeEnough){
              if (diffs.keySet().contains(diff)){
                  //add the value to the list
                  diffs.get(diff).add(size);
              } else {
                  List newList = new ArrayList&lt;Camera.Size&gt;();
                  newList.add(size);
                  diffs.put(diff, newList);
              }
  
              Logging.format(&quot;usable: %sx%s %s&quot;, size.width, size.height, diff);
          }
      }
  
      //diffs now contains all of the usable sizes
      //now let's see which one has the least amount of
      for (Map.Entry entry: diffs.entrySet()){
          List&lt;Size&gt; entries = (List)entry.getValue();
          for (Camera.Size s: entries) {
  
              if (s.width &gt;= width &amp;&amp; s.height &gt;= width) {
                  bestSize = s;
              }
              Logging.format(&quot;results: %s %sx%s&quot;, entry.getKey(), s.width, s.height);
          }
      }
  
      //if we don't have bestSize then just use whatever the default was to begin with
      if (bestSize==null){
          if (parameters.getPreviewSize()!=null){
              bestSize = parameters.getPreviewSize();
              return bestSize;
          }
  
          //pick the smallest difference in ratio?  or pick the largest resolution?
          //right now we are just picking the lowest ratio difference
          for (Map.Entry entry: diffs.entrySet()){
              List&lt;Size&gt; entries = (List)entry.getValue();
              for (Camera.Size s: entries) {
                  if (bestSize == null){
                      bestSize = s;
                  }
              }
          }
      }
  
      return bestSize;
  }
  </code></pre>
  <p>Obviously this algorithm doesn't know how to pick the best option just knows how to pick an option that is not the worst like every other implementation I have seen out there.  I need to understand the relationship between the sizes that actually look good and the view dimensions that the preview will be displayed in before I can improve my algorithm to actually pick the best option.</p>
  <p>I have looked at the implementation of how <a href="https://github.com/commonsguy/cwac-camera/blob/master/camera/src/com/commonsware/cwac/camera/CameraUtils.java#L28" rel="noreferrer">CommonWares' camera-cwac</a> project deals with this and it appears to also use the &quot;close enough&quot; algorithm.  If I apply that same logic to my project then I get back values that are decent but not the &quot;perfect&quot; size.  I get <code>1920x1080</code> back for both devices.  Although that value is not the worst option it's also slightly squished.  I am going to run his code in my test app with test cases to determine if it also squishes the image slightly since I already know that it will return a size that isn't as optimal as it could be.</p>