首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >相机尺寸与4:3宽径比不匹配

相机尺寸与4:3宽径比不匹配
EN

Stack Overflow用户
提问于 2016-04-01 06:33:39
回答 1查看 1.7K关注 0票数 2

我使用相机的表面视图显示相机和拍照,我需要相机预览是4:3,instagram是一个正方形,我的是一个矩形。

如果你看instagram应用程序,相机预览不是拉伸或压缩,而是在我的压缩。

这是我的相机预览班:

代码语言:javascript
复制
class CustomCam extends SurfaceView implements SurfaceHolder.Callback {

    private final String TAG = "PIC-FRAME";
    private static final double ASPECT_RATIO = 4.0 / 3.0;
    private static final int PICTURE_SIZE_MAX_WIDTH = 1280;
    private static final int PREVIEW_SIZE_MAX_WIDTH = 640;

    private SurfaceHolder mHolder;
    private Camera mCamera;
    private Display display;
    public List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;

    public CustomCam(Activity context, Camera camera) {
        super(context);
        mCamera = camera;
        display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        for (Camera.Size str : mSupportedPreviewSizes)
            Log.e(TAG, str.width + "/" + str.height);
        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        setKeepScreenOn(true);
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
        this.getHolder().removeCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

    }

    private Camera.Size getBestPreviewSize(int width, int height) {
        Camera.Size result = null;
        Camera.Parameters p = mCamera.getParameters();
        for (Camera.Size size : p.getSupportedPreviewSizes()) {
            if (size.width <= width && size.height <= height) {
                if (result == null) {
                    result = size;
                } else {
                    int resultArea = result.width * result.height;
                    int newArea = size.width * size.height;

                    if (newArea > resultArea) {
                        result = size;
                    }
                }
            }
        }
        return result;

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //This line helped me set the preview Display Orientation to Portrait
        //Only works API Level 8 and higher unfortunately.

        try {
            Camera.Parameters parameters = mCamera.getParameters();
//        Camera.Size size = getBestPreviewSize(width, height);
//            Camera.Size size = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
//            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
//            initialCameraPictureSize(parameters);
//            parameters.setPreviewSize(size.width, size.height);

            Camera.Size bestPreviewSize = determineBestPreviewSize(parameters);
            Camera.Size bestPictureSize = determineBestPictureSize(parameters);

            parameters.setPreviewSize(bestPreviewSize.width, bestPreviewSize.height);
            parameters.setPictureSize(bestPictureSize.width, bestPictureSize.height);

            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            mCamera.setDisplayOrientation(90);
            mCamera.getParameters().setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            mCamera.setPreviewDisplay(mHolder);
            mCamera.setParameters(parameters);
            mCamera.startPreview();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void initialCameraPictureSize(Camera.Parameters parameters) {

        List list = parameters.getSupportedPictureSizes();
        if (list != null) {
            Camera.Size size = null;
            Iterator iterator = list.iterator();
            do {
                if (!iterator.hasNext())
                    break;
                Camera.Size size1 = (Camera.Size) iterator.next();
                if (Math.abs(3F * ((float) size1.width / 4F) - (float) size1.height) < 0.1F * (float) size1.width && (size == null || size1.height > size.height && size1.width < 3000))
                    size = size1;
            } while (true);
            if (size != null)
                parameters.setPictureSize(size.width, size.height);
            else
                Log.e("CameraSettings", "No supported picture size found");
        }
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) h / w;

        if (sizes == null) return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    /**
     * Measure the view and its content to determine the measured width and the
     * measured height.
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);

        if (width > height * ASPECT_RATIO) {
            width = (int) (height * ASPECT_RATIO + 0.5);
        } else {
            height = (int) (width / ASPECT_RATIO + 0.5);
        }

        setMeasuredDimension(width, height);
    }

    protected Camera.Size determineBestSize(List<Camera.Size> sizes, int widthThreshold) {
        Camera.Size bestSize = null;

        for (Camera.Size currentSize : sizes) {
            boolean isDesiredRatio = (currentSize.width / 4) == (currentSize.height / 3);
            boolean isBetterSize = (bestSize == null || currentSize.width > bestSize.width);
            boolean isInBounds = currentSize.width <= PICTURE_SIZE_MAX_WIDTH;

            if (isDesiredRatio && isInBounds && isBetterSize) {
                bestSize = currentSize;
            }
        }

        if (bestSize == null) {
            return sizes.get(0);
        }

        return bestSize;
    }

    private Camera.Size determineBestPreviewSize(Camera.Parameters parameters) {
        List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();

        return determineBestSize(sizes, PREVIEW_SIZE_MAX_WIDTH);
    }

    private Camera.Size determineBestPictureSize(Camera.Parameters parameters) {
        List<Camera.Size> sizes = parameters.getSupportedPictureSizes();

        return determineBestSize(sizes, PICTURE_SIZE_MAX_WIDTH);
    }
}

我的定制框架布局:

代码语言:javascript
复制
CustomFrameLayout extends FrameLayout {

    private static final float RATIO = 4f / 3f;

    public CustomFrameLayout(Context context) {
        super(context);
    }

    public CustomFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
                            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
        int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom();

        int maxWidth = (int) (heigthWithoutPadding * RATIO);
        int maxHeight = (int) (widthWithoutPadding / RATIO);

        if (widthWithoutPadding > maxWidth) {
            width = maxWidth + getPaddingLeft() + getPaddingRight();
        } else {
            height = maxHeight + getPaddingTop() + getPaddingBottom();
        }

        setMeasuredDimension(width, height);
    }

但是凸轮预览是压缩在框架布局内,我如何解决这个问题?

更新

好的,经过一些研究,我们知道这是因为onMeasure ASPECT_RATIO = 4:3。

代码语言:javascript
复制
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);

        if (width > height * ASPECT_RATIO) {
            width = (int) (height * ASPECT_RATIO + 0.5);
        } else {
            height = (int) (width / ASPECT_RATIO + 0.5);
        }

        setMeasuredDimension(width, height);
    }

溶液

所以我在想一个解决方案,可能是(就像Instagram一样)让你的相机完全尺寸,然后隐藏一些区域的布局,只是为了让它看起来像它的4:3 ratio.Then,使用一些作物机制,必须削减图像,使图像看起来像4:3。

假设我总是以4:3的比例显示来自顶部的预览,下面部分的其余部分是隐藏的,所以现在我一拍摄照片,我就想将图像从顶部裁剪到4:3,并保存它。

我怎样才能做到这一点,这是否一个可行的解决办法?

EN

回答 1

Stack Overflow用户

发布于 2016-04-06 12:44:26

据我所知,您当前的问题是如何裁剪接收到的图像并显示出来。下面是一个小例子:

代码语言:javascript
复制
@OnClick(R.id.btn_record_start)
    public void takePhoto() {
        if (null != actions) {
            EasyCamera.PictureCallback callback = new EasyCamera.PictureCallback() {
                public void onPictureTaken(byte[] data, EasyCamera.CameraActions actions) {
                    // store picture
                    Bitmap bitmap = ImageUtils.getExifOrientedBitmap(data);
                    if ((portrait && bitmap.getHeight() < bitmap.getWidth()) ||
                        (!portrait && bitmap.getHeight() > bitmap.getWidth())) {
                        Matrix matrix = new Matrix();
                        matrix.postRotate(90);
                        bitmap =
                            Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                                matrix, true);
                    }

                    Camera.CameraInfo info = new Camera.CameraInfo();
                    Camera.getCameraInfo(cameraId, info);
                    if (Camera.CameraInfo.CAMERA_FACING_FRONT == info.facing) {
                        Matrix matrix = new Matrix();
                        matrix.postRotate(180);
                        bitmap =
                            Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                                matrix, true);
                    }
                    showPhoto(bitmap);
                }
            };
            actions.takePicture(EasyCamera.Callbacks.create()
                .withJpegCallback(callback));
        }
    }

这是我使用的一种方法,在照片拍摄后处理图像方向。它可以很容易地修改,以处理种植。为此,您必须指定图像的目标宽度和高度(目前我正在发送整个位图的大小)。一个可能的解决方案是获取图像的高度并删除过多的宽度--因此您发送给createBitmap方法的参数将是bitmap.getHeight() * 4.0 / 3.0bitmap.getHeight()。下面是修改后的示例:

代码语言:javascript
复制
@OnClick(R.id.btn_record_start)
public void takePhoto() {
    if (null != actions) {
        EasyCamera.PictureCallback callback = new EasyCamera.PictureCallback() {
            public void onPictureTaken(byte[] data, EasyCamera.CameraActions actions) {
                // store picture
                Bitmap bitmap = ImageUtils.getExifOrientedBitmap(data);
                if ((portrait && bitmap.getHeight() < bitmap.getWidth()) ||
                    (!portrait && bitmap.getHeight() > bitmap.getWidth())) {
                    Matrix matrix = new Matrix();
                    matrix.postRotate(90);
                    bitmap =
                        Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                            matrix, true);
                }

                Camera.CameraInfo info = new Camera.CameraInfo();
                Camera.getCameraInfo(cameraId, info);
                if (Camera.CameraInfo.CAMERA_FACING_FRONT == info.facing) {
                    Matrix matrix = new Matrix();
                    matrix.postRotate(180);
                    bitmap =
                        Bitmap.createBitmap(bitmap, 0, 0, (int) (bitmap.getHeight() * 4.0 / 3.0), bitmap.getHeight(),
                            matrix, true);
                }
                showPhoto(bitmap);
            }
        };
        actions.takePicture(EasyCamera.Callbacks.create()
            .withJpegCallback(callback));
    }
}

有几件事要注意:

  • 您可以用4.0 / 3.0变量替换ASPECT_RATIO部件。
  • 我的例子是做图像旋转,所以它看起来像在预览期间,在您的情况下,所需的UI可能是不同的。
  • 我正在使用EasyCamera库来简化相机管理

下面是我使用的其他ImageUtils方法:

getExifOrientedBitmap

代码语言:javascript
复制
public static Bitmap getExifOrientedBitmap(byte[] data) {
    File newPhotoFile = writeToFile(data);
    if (newPhotoFile == null) {
        return null;
    }

    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
    bitmap = fixOrientationIfNeeded(newPhotoFile, bitmap);
    newPhotoFile.delete();
    return bitmap;
}

writeToFile

代码语言:javascript
复制
@Nullable
public static File writeToFile(byte[] data) {
    File dir = PhotoMessageComposer.getPhotoDir();
    if (!dir.exists()) {
        dir.mkdir();
    }
    File newPhotoFile = new File(dir, ImageUtils.getRandomFilename());
    FileOutputStream fos = null;
    try
    {
        fos = new FileOutputStream(newPhotoFile);
        fos.write(data);
        fos.close();
    } catch (Exception error) {
        return null;
    } finally {
        try {
            if (fos != null) {
                fos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return newPhotoFile;
}

getPhotoDir

代码语言:javascript
复制
@NonNull
public static File getPhotoDir() {
    return new File(
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) +
            PICTURES_DIR);
}

getRandomFileName

代码语言:javascript
复制
public static String getRandomFilename() {
    return UUID.randomUUID().toString() + IMAGE_EXTENSION;
}

fixOrientationIfNeeded

代码语言:javascript
复制
public static Bitmap fixOrientationIfNeeded(File sourceFile, Bitmap source) {
    ExifInterface exif;
    try {
        exif = new ExifInterface(sourceFile.getAbsolutePath());
        int exifOrientation = exif.getAttributeInt(
                ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);

        if (exifOrientation != ExifInterface.ORIENTATION_NORMAL) {
            Matrix matrix = new Matrix();
            int angle = findRotationAngle(exifOrientation);
            matrix.postRotate(angle);
            source = Bitmap.createBitmap(source, 0, 0, source.getWidth(),
                    source.getHeight(), matrix, true);
            return source;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return source;
}

findRotationAngle

代码语言:javascript
复制
protected static int findRotationAngle(int exifOrientation) {
    switch (exifOrientation) {
    case ExifInterface.ORIENTATION_ROTATE_270:
        return 270;
    case ExifInterface.ORIENTATION_ROTATE_180:
        return 180;
    case ExifInterface.ORIENTATION_ROTATE_90:
        return 90;
    default:
        return 0;
    }
}

这个ImageUtils类实现已经有几年了,所以可能有更好的方法来处理其中的一些操作。不过,作为一个起点,它们应该足够好。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36349530

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档