2

I would like to use a custom camera to take pictures of a lesser size than the preview,I have heard that maintaining the aspect ratio is very important.I have used the PreviewFrameLayout in AOSP ICS.I have found that the SurfaceView only displays at the center of the activity in a box and does not fill the entire screen.

public class PreviewFrameLayout extends RelativeLayout{

static final String TAG="PreviewFrameLayout";
public interface OnSizeChangedListener
{
    public void onSizeChanged(int w,int h);
}

OnSizeChangedListener mListener;

public void setOnSizeChangedListener(OnSizeChangedListener listener)
{
    mListener=listener;
}

private double mAspectRatio=4.0/3.0;

public PreviewFrameLayout(Context c,AttributeSet attrs)
{
    super(c,attrs);
}

public void setAspectRatio(double ratio)
{
    if(ratio<=0.0)
        throw new IllegalArgumentException();
    if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT)
        ratio=1/ratio;
    if(mAspectRatio!=ratio)
    {
        mAspectRatio=ratio;
        requestLayout();
    }
}

@Override
protected void onMeasure(int widthSpec,int heightSpec)
{
    int previewWidth=MeasureSpec.getSize(widthSpec);
    int previewHeight=MeasureSpec.getSize(heightSpec);

    int hPadding=getPaddingLeft()+getPaddingRight();
    int vPadding=getPaddingTop()+getPaddingBottom();

    previewWidth-=hPadding;
    previewHeight-=vPadding;

    if(previewWidth>previewHeight*mAspectRatio)
        previewWidth=(int) (previewHeight*mAspectRatio+.5);
    else
        previewHeight=(int)(previewWidth/mAspectRatio+.5);
    previewWidth+=hPadding;
    previewHeight+=vPadding;
    Log.d(TAG,"Aspect ratio "+mAspectRatio);
    Log.d(TAG, "Width: "+previewWidth+" Height: "+previewHeight);
    super.onMeasure(MeasureSpec.makeMeasureSpec(previewWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(previewHeight, MeasureSpec.EXACTLY));
}

protected void onSizeChanged(int w,int h,int oldw,int oldh)
{
    if(mListener!=null)
        mListener.onSizeChanged(w, h);
}
}

and have set the preview and picture size like this:

  private Size getOptimalPreviewSize(List<Size> supportedSizeList,double targetRatio)
{
    final double ASPECT_TOLERANCE=0.05;
    double minDiff=Double.MAX_VALUE;
    Size optimalSize=null;
    Display display=getWindowManager().getDefaultDisplay();
    @SuppressWarnings("deprecation")
    int targetHeight=Math.min(display.getWidth(), display.getHeight());
    if(targetHeight<=0)
    {   
        WindowManager windowManager=(WindowManager)getSystemService(Context.WINDOW_SERVICE);
        targetHeight=windowManager.getDefaultDisplay().getHeight();
    }
    for(Size size:supportedSizeList)
    {
        double ratio=(double)size.width/size.height;
        if(Math.abs(targetRatio-ratio)>ASPECT_TOLERANCE)
            continue;
        if(Math.abs(size.height-targetHeight)<minDiff)
        {
            optimalSize=size;
            minDiff=Math.abs(size.height-targetHeight);
        }
    }

    for(Size size:supportedSizeList)
    {
        if((Math.abs(size.height-targetHeight))<minDiff)
        {   
            optimalSize=size;
            minDiff=Math.abs(size.height-targetHeight);
        }
    }
    return optimalSize;
}

private Size getDesiredPictureSize(List<Size> supportedSizeList)
{
    //Resolution is widthxheight

    Size result=null;
    final int minArea=500*500;  
    final int maxArea=1000*1000;
    for(Size size:supportedSizeList)
    {
        if(size.width*size.height>minArea && size.width*size.height<maxArea)
        {
            if(result==null)
                result=size;
            else
            {
                int resultArea=result.width*result.height;
                int sizeArea=size.width*size.height;
                if(resultArea<sizeArea)
                {   
                    result=size;
                }   
            }
        }
    }
    return result;
 }

And I used the code here to set the aspect ratio to the picture size:

    mParameters=mCamera.getParameters();
    List<Size> supportedPictureSizes=mParameters.getSupportedPictureSizes();
    List<Size> supportedPreviewSizes=mParameters.getSupportedPreviewSizes();
    mPictureSize=getDesiredPictureSize(supportedPictureSizes);
    double targetRatio=(double)mPictureSize.width/mPictureSize.height;
    mPreviewPanel=findViewById(R.id.frame_layout);
    mPreviewFrameLayout=(PreviewFrameLayout)findViewById(R.id.frame);
    mPreviewFrameLayout.setAspectRatio(targetRatio);

The parent of this layout is a RelativeLayout:

<com.example.newcameraproject.PreviewFrameLayout android:id="@+id/frame"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <SurfaceView android:id="@+id/camera_preview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
      </com.example.newcameraproject.PreviewFrameLayout>

This is included in the main camera layout like this:

  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
>

 <include layout="@layout/preview_frame"/>

 </LinearLayout>

EDIT:

Portrait Orientation:

CameraActivity(6502): Resolution Width: 720 Resolution Height: 1184
CameraActivity(6502): The Picture Width: 1152 The Picture Height: 864
CameraActivity(6502): The Preview Width:960 Preview Height: 720
CameraActivity(6502): Picture Ratio: 1.3333333333333333
CameraActivity(6502): Preview Ratio: 1.3333333333333333
PreviewFrameLayout(6502): Left: 0 Right: 0 Top: 0 Bottom: 0
PreviewFrameLayout(6502): Aspect ratio 0.75
PreviewFrameLayout(6502): Width: 720 Height: 960

The aspect ratio is inverted because it is in the portrait orientation.

if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT)
        ratio=1/ratio;

The Camera displays as a box just smaller than the activity.

Landscape Orientation:

  CameraActivity(6502): Resolution Width: 1196 Resolution Height: 720
  CameraActivity(6502): The Picture Width: 1152 The Picture Height: 864
  CameraActivity(6502): The Preview Width:960 Preview Height: 720
  CameraActivity(6502): Picture Ratio: 1.3333333333333333
  CameraActivity(6502): Preview Ratio: 1.3333333333333333
  PreviewFrameLayout(6502): Left: 0 Right: 0 Top: 0 Bottom: 0
  PreviewFrameLayout(6502): Aspect ratio 1.3333333333333333
  PreviewFrameLayout(6502): Width: 787 Height: 590

 mPreviewSize=getOptimalPreviewSize(this,supportedPreviewSizes, targetRatio);
    Log.d(TAG, "The Preview Width:"+mPreviewSize.width+" Preview Height: "+mPreviewSize.height);
    double ratio=(double)mPreviewSize.width/mPreviewSize.height;
    Log.d(TAG,"Picture Ratio: "+targetRatio);
    Log.d(TAG, "Preview Ratio: "+ratio);
    int new_width=0, new_height=0;    
 if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT)
    {
        if((double)previewFrame.getWidth()/previewFrame.getHeight()<ratio)
        {
            new_width=(int)(Math.round(previewFrame.getHeight()*ratio));
            new_height=getWindowManager().getDefaultDisplay().getHeight();
        }
        else
        {
            new_width=getWindowManager().getDefaultDisplay().getHeight();
            new_height=(int)Math.round((double)new_width/ratio);
        }
    }
    if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE)
    {
        if((double)previewFrame.getWidth()/previewFrame.getHeight()<ratio)
        {
            new_width=(int)(Math.round(previewFrame.getHeight()*ratio));
            new_height=getWindowManager().getDefaultDisplay().getHeight();
        }
        else
        {
            new_width=getWindowManager().getDefaultDisplay().getWidth();
            new_height=(int)Math.round((double)new_width/ratio);
        }
    }

EDIT:

1.Ensured that getOptimalPreviewSize and other methods works,changed code outside of this post to do so.
2.Even though these methods now work,the preview was not filling the screen,so changed to FrameLayout.The final version(see above) now works for both landscape and portrait.
3.I have noticed that the image in the preview looks stretched when the Camera Activity is rotated to landscape.

Added a question to address this at Camera Preview stretched when using FrameLayout

EDIT2:

The Camera PreviewFrameLayout works almost perfectly(it is almost full screen)...just above the ShutterButton when I added this code to the constructor:

 public PreviewFrameLayout(Context c,AttributeSet attrs)
{
    super(c,attrs);
    setAspectRatio(4.0/3.0);
}

There still are problems such as taking a picture in landscape where the screen has a display orientation of 90 and previewing it...

I find that the image obtained is rotated 90 degrees clockwise in both landscape and portrait modes despite using the ExifInterface and Matrix to fix this rotation code in ImagePreviewActivity

The Exif orientation is always 0,I fixed this the last time(without the PreviewFrameLayout using Parameters.setRotation code when setting orientation...but it does not seem to be working here

Community
  • 1
  • 1
vamsiampolu
  • 6,328
  • 19
  • 82
  • 183
  • What is the natural resolution of your screen, and what does your code write to log about `PreviewFrameLayout` **width** and **height**? – Alex Cohn Jan 20 '14 at 09:07
  • Width:720 Height:540.The natural resolution of my Galaxy Nexus screen is WXGA (1280 x 800).Although the `getSupportedPreviewSize()` seems to support much larger resolutions as well – vamsiampolu Jan 20 '14 at 09:37
  • Can you check what `getPaddingLeft()` and its siblings report? – Alex Cohn Jan 20 '14 at 10:38
  • @AlexCohn PreviewFrameLayout: Left: 0 Right: 0 Top: 0 Bottom: 0 – vamsiampolu Jan 20 '14 at 10:45
  • @AlexCohn I have tried using the largest picture size as well [like this](http://pastebin.com/hs1GGCn8).The preview still appears in a box – vamsiampolu Jan 20 '14 at 11:21
  • __ You can open new questions for the same price; multiple edits render the question impossible to follow for anybody outside the immediate discussion, and does not fit the SO site phylosophy. __ – Alex Cohn Jan 21 '14 at 10:08

1 Answers1

1

I cannot see where getOptimalPreviewSize() is called; also, there is no real reason to set preview size to match the screen; usually, the default camera preview is chosen by the manufacturer to look good on the screen (which involves scaling).

With all that, the code you published could be significantly simplified. If I understand correctly, you have fixed screenOrientation in your activity, and want to fill all the screen with camera preview, preserving aspect ratio to match the desired picture size.

This simple example, shows how to set width and height for the preview surface view. If this is not enough for you, feel free to ask for more details.

Community
  • 1
  • 1
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • just had a facepalm moment I was not changing `mUpdateSet` to the correct value before calling the `updateCameraParameters(int updateSet)` method.The preview is bigger but still not filling the screen entirely.Can we use the fix you describe above on the parent of the PreviewFrameLayout which is a RelativeLayout??? – vamsiampolu Jan 20 '14 at 12:29
  • the orientation has not been fixed either – vamsiampolu Jan 20 '14 at 13:16
  • Yes, same solution works for RelativeLayout, if you really need it. But in your case, FrameLayout without even LinearLayout on top of it is more than enough. – Alex Cohn Jan 20 '14 at 18:22
  • Do we need to invert width and height in case it is landscape? – vamsiampolu Jan 21 '14 at 04:45
  • edited with different code for portrait and landscape,will this work for all cases? – vamsiampolu Jan 21 '14 at 05:08
  • the preview image seems stretched in landscape...is there a solution where i could both get the preview running full screen while keeping the image from getting stretched – vamsiampolu Jan 21 '14 at 07:11
  • It is very hared to follow your questions; what you changed, what you didn't change, what your code prints out and why. In the nutshell, you can set width and height of the preview surface view, and you should preserve the aspect ratio of the preview frame of the camera. This may eventually leave margins along the longer axis. – Alex Cohn Jan 21 '14 at 10:28
  • added a list of changes – vamsiampolu Jan 21 '14 at 10:39