从自定义相机拍摄的照片在保存到ImageView中时会被拉伸

10
我正在使用这段代码将图片保存在ImageView中,但是当在ImageView中保存图像时,图像会被拉伸。相机预览很完美,拍摄正确的图像,但当我将该图像设置为ImageView时,图像会被拉伸。
    public void onPicTaken(byte[] data) {

    if (data != null) {
        int screenWidth = getResources().getDisplayMetrics().widthPixels;
        int screenHeight = getResources().getDisplayMetrics().heightPixels;
        Bitmap bm = BitmapFactory.decodeByteArray(data, 0, (data != null) ? data.length : 0);

        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            // Notice that width and height are reversed
            Bitmap scaled = Bitmap.createScaledBitmap(bm, screenHeight, screenWidth, true);
            int w = scaled.getWidth();
            int h = scaled.getHeight();
            // Setting post rotate to 90
            Matrix mtx = new Matrix();
            mtx.postRotate(90);
            // Rotating Bitmap
            bm = Bitmap.createBitmap(scaled, 0, 0, w, h, mtx, true);
        }else{// LANDSCAPE MODE
            //No need to reverse width and height
            Bitmap scaled = Bitmap.createScaledBitmap(bm, screenWidth,screenHeight , true);
            bm=scaled;
        }
        ivCaptureImagePreview.setImageBitmap(bm);
        ivCaptureImagePreview.setVisibility(View.VISIBLE);
    }

}
7个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
6
使用以下类来创建缩放位图:
public class ScalingUtilities 
{
     /**
     * Utility function for decoding an image resource. The decoded bitmap will
     * be optimized for further scaling to the requested destination dimensions
     * and scaling logic.
     *
     * @param res The resources object containing the image data
     * @param resId The resource id of the image data
     * @param dstWidth Width of destination area
     * @param dstHeight Height of destination area
     * @param scalingLogic Logic to use to avoid image stretching
     * @return Decoded bitmap
     */
    public static Bitmap decodeResource(Resources res, int resId, int dstWidth, int dstHeight,
            ScalingLogic scalingLogic) {
        Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        options.inJustDecodeBounds = false;
        options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,
                dstHeight, scalingLogic);
        Bitmap unscaledBitmap = BitmapFactory.decodeResource(res, resId, options);

        return unscaledBitmap;
    }

    /**
     * Utility function for creating a scaled version of an existing bitmap
     *
     * @param unscaledBitmap Bitmap to scale
     * @param dstWidth Wanted width of destination bitmap
     * @param dstHeight Wanted height of destination bitmap
     * @param scalingLogic Logic to use to avoid image stretching
     * @return New scaled bitmap object
     */
    public static Bitmap createScaledBitmap(Bitmap unscaledBitmap, int dstWidth, int dstHeight,
            ScalingLogic scalingLogic) {
        Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(),
                dstWidth, dstHeight, scalingLogic);
        Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(),
                dstWidth, dstHeight, scalingLogic);
        Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(),
                Config.ARGB_8888);
        Canvas canvas = new Canvas(scaledBitmap);
        canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG));

        return scaledBitmap;
    }

    /**
     * ScalingLogic defines how scaling should be carried out if source and
     * destination image has different aspect ratio.
     *
     * CROP: Scales the image the minimum amount while making sure that at least
     * one of the two dimensions fit inside the requested destination area.
     * Parts of the source image will be cropped to realize this.
     *
     * FIT: Scales the image the minimum amount while making sure both
     * dimensions fit inside the requested destination area. The resulting
     * destination dimensions might be adjusted to a smaller size than
     * requested.
     */
    public static enum ScalingLogic {
        CROP, FIT
    }

    /**
     * Calculate optimal down-sampling factor given the dimensions of a source
     * image, the dimensions of a destination area and a scaling logic.
     *
     * @param srcWidth Width of source image
     * @param srcHeight Height of source image
     * @param dstWidth Width of destination area
     * @param dstHeight Height of destination area
     * @param scalingLogic Logic to use to avoid image stretching
     * @return Optimal down scaling sample size for decoding
     */
    public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
            ScalingLogic scalingLogic) {
        if (scalingLogic == ScalingLogic.FIT) {
            final float srcAspect = (float)srcWidth / (float)srcHeight;
            final float dstAspect = (float)dstWidth / (float)dstHeight;

            if (srcAspect > dstAspect) {
                return srcWidth / dstWidth;
            } else {
                return srcHeight / dstHeight;
            }
        } else {
            final float srcAspect = (float)srcWidth / (float)srcHeight;
            final float dstAspect = (float)dstWidth / (float)dstHeight;

            if (srcAspect > dstAspect) {
                return srcHeight / dstHeight;
            } else {
                return srcWidth / dstWidth;
            }
        }
    }

    /**
     * Calculates source rectangle for scaling bitmap
     *
     * @param srcWidth Width of source image
     * @param srcHeight Height of source image
     * @param dstWidth Width of destination area
     * @param dstHeight Height of destination area
     * @param scalingLogic Logic to use to avoid image stretching
     * @return Optimal source rectangle
     */
    public static Rect calculateSrcRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
            ScalingLogic scalingLogic) {
        if (scalingLogic == ScalingLogic.CROP) {
            final float srcAspect = (float)srcWidth / (float)srcHeight;
            final float dstAspect = (float)dstWidth / (float)dstHeight;

            if (srcAspect > dstAspect) {
                final int srcRectWidth = (int)(srcHeight * dstAspect);
                final int srcRectLeft = (srcWidth - srcRectWidth) / 2;
                return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight);
            } else {
                final int srcRectHeight = (int)(srcWidth / dstAspect);
                final int scrRectTop = (int)(srcHeight - srcRectHeight) / 2;
                return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight);
            }
        } else {
            return new Rect(0, 0, srcWidth, srcHeight);
        }
    }

    /**
     * Calculates destination rectangle for scaling bitmap
     *
     * @param srcWidth Width of source image
     * @param srcHeight Height of source image
     * @param dstWidth Width of destination area
     * @param dstHeight Height of destination area
     * @param scalingLogic Logic to use to avoid image stretching
     * @return Optimal destination rectangle
     */
    public static Rect calculateDstRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
            ScalingLogic scalingLogic) {
        if (scalingLogic == ScalingLogic.FIT) {
            final float srcAspect = (float)srcWidth / (float)srcHeight;
            final float dstAspect = (float)dstWidth / (float)dstHeight;

            if (srcAspect > dstAspect) {
                return new Rect(0, 0, dstWidth, (int)(dstWidth / srcAspect));
            } else {
                return new Rect(0, 0, (int)(dstHeight * srcAspect), dstHeight);
            }
        } else {
            return new Rect(0, 0, dstWidth, dstHeight);
        }
    }


}
并在你的函数中按如下方式使用此类
public void onPicTaken(byte[] data) {

    if (data != null) {
        int screenWidth = getResources().getDisplayMetrics().widthPixels;
        int screenHeight = getResources().getDisplayMetrics().heightPixels;
        Bitmap bm = BitmapFactory.decodeByteArray(data, 0, (data != null) ? data.length : 0);

        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            // Notice that width and height are reversed
            Bitmap scaled = ScalingUtilities.createScaledBitmap(bm, screenHeight, screenWidth, ScalingLogic.FIT);
            int w = scaled.getWidth();
            int h = scaled.getHeight();
            // Setting post rotate to 90
            Matrix mtx = new Matrix();
            mtx.postRotate(90);
            // Rotating Bitmap
            bm = Bitmap.createBitmap(scaled, 0, 0, w, h, mtx, true);
        }else{// LANDSCAPE MODE
            //No need to reverse width and height
            Bitmap scaled = ScalingUtilities.createScaledBitmap(bm, screenHeight, screenWidth, ScalingLogic.FIT);
            bm=scaled;
        }
        ivCaptureImagePreview.setImageBitmap(bm);
        ivCaptureImagePreview.setVisibility(View.VISIBLE);
    }

}

@SrujanBarai 这是矩阵的方法,可以在矩阵对象上调用。Matrix mtx = new Matrix(); mtx.postRotate(90); - Ajinkya

1

UIImageView设置

<ImageView
    android:id="@id/img"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter" />

我的测试应用程序针对这个问题

为了从相机获取图片,我使用以下方法:

private void pickImageIntent()
{
    Intent pickPhoto = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    getCurrentActivity().startActivityForResult(pickPhoto, REQUEST_PICK_IMAGE);
}

private void takeImageIntent()
{
    try
    {
        File outputDir = getCurrentActivity().getExternalCacheDir();
        File outputFile = File.createTempFile("prefix", "extension", outputDir);
        selectedImageUri = Uri.fromFile(outputFile);

        Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        takePicture.putExtra(MediaStore.EXTRA_OUTPUT, selectedImageUri);
        getCurrentActivity().startActivityForResult(takePicture, REQUEST_TAKE_IMAGE);
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
}

获取相机或图像选择器的结果,并将其显示在imageView中。这里使用this.myInterfaceImage

public void onActivityResult(int requestCode, int resultCode, Intent data)
{
    switch(requestCode)
    {
        case REQUEST_PICK_IMAGE:
            if(resultCode == Activity.RESULT_OK)
            {
                selectedImageUri = data.getData();
                initImage(selectedImageUri);
            }
            break;
        case REQUEST_TAKE_IMAGE:
            if(resultCode == Activity.RESULT_OK)
            {
                initImage(selectedImageUri);
            }
            break;
    }
}


protected void initImage(Uri selectedImageUri_) {
    try {
        ParcelFileDescriptor parcelFileDescriptor = getContext().getContentResolver().openFileDescriptor(selectedImageUri_, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap source = BitmapFactory.decodeFileDescriptor(fileDescriptor);
        float ratio = (float)source.getWidth() /  (float)source.getHeight();
        // here perhaps limit the size of the image
        int height =   Math.min(getContext().getResources().getDisplayMetrics().heightPixels, source.getHeight());
        int width = (int)(ratio*height);
        Bitmap result = Bitmap.createScaledBitmap(source, width, height, false);
        this.interfaceImage.setImageBitmap(result);
        if (result != source) {
            source.recycle();
        }
    } catch (Exception ex) {
        System.out.println("File not found");
    }
}

这是我在我的测试应用程序中使用它的方式,它有效,我没有任何方向问题。我在纵向模式下测试了纵向和横向图片,在横向模式下测试了纵向和横向图片,并在获取图像之前更改了方向。

如果您正在使用自定义相机,并且使用上述代码时方向不正确,请添加以下内容

需要两件事:

  1. 相机预览需要与您的旋转相同。通过以下方式设置:

    camera.setDisplayOrientation(result);

  2. 将捕获的图片保存为您的相机预览。通过Camera.Parameters实现。

    int mRotation = getCameraDisplayOrientation(); Camera.Parameters parameters = camera.getParameters(); parameters.setRotation(mRotation); //将旋转设置为保存图片 camera.setDisplayOrientation(result); //设置预览相机的旋转 camera.setParameters(parameters);

然后,您可以在没有方向问题的情况下使用您的函数

public void onPicTaken(byte[] data) {
    if (data != null) {
        Bitmap bm = BitmapFactory.decodeByteArray(data, 0, (data != null) ? data.length : 0);
        int screenWidth = getResources().getDisplayMetrics().widthPixels;
        float ratio = (float)bm.getWidth() /  (float)bm.getHeight();
        int screenHeight = (int)(ratio*screenWidth)
        Bitmap scaled = Bitmap.createScaledBitmap(bm, screenHeight, screenWidth, true);
        if (scaled != bm) {
            source.recycle();
        }        
        ivCaptureImagePreview.setImageBitmap(scaled);
        ivCaptureImagePreview.setVisibility(View.VISIBLE);
    }
}
希望有所帮助。

它将图像方向转换为横向模式。 - Neeraj Sharma
@NeerajSharma 更新了我的代码,在我的测试应用程序中它运行良好。 - Kordi
你的代码只在横屏模式下运行正常,竖屏模式下不行。兄弟。 - Neeraj Sharma
@NeerajSharma 你使用了我的整个代码吗?takeImageIntent和initImage在我的TestApplication中运行良好。我需要添加截图吗? - Kordi
@NeerajSharma,我从其他问题中了解到您正在使用自定义相机,请查看最后一节。如果您正在使用自定义相机且方向不正确,请将此代码添加到其中。但是,您必须使用我的initImage函数来保留比例。 - Kordi
@NeerajSharma 看看完整的第二部分,也许我可以删除我的答案前半部分,因为你正在使用自定义相机,是吗? - Kordi

1

我在我的一个项目中使用了这个函数,请检查它是否对你有用。

public static Bitmap Preview(String path) {
    //SCALE IMAGE
    int SCALE = 4;
    try {
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = SCALE;
        Bitmap bitmap = BitmapFactory.decodeFile(path, o2);
        OutputStream os = new FileOutputStream(path);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
        os.flush();
        os.close();
        File file = new File(path);

        while (file.length() > 800000) {
            SCALE += 2;
            o2.inSampleSize = SCALE;
            bitmap = BitmapFactory.decodeFile(path, o2);
            os = new FileOutputStream(path);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
            os.flush();
            os.close();
            file = new File(path);
        }

        bitmap = BitmapFactory.decodeFile(path, o2);
        return bitmap;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

0
使用此代码可以防止图像拉伸并保持图像的纵横比。
android:scaleType="fitXY"  // or desired scale type
android:adjustViewBounds="true"

0

使用 Bitmap.createScaledBitmap(bm, screenHeight, screenWidth, true); 缩放位图不会保持纵横比,因为这会拉伸位图的宽度和高度以匹配目标。

一种选择是手动计算目标高度,以避免裁剪:

double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
int targetHeight = (int) (screenWidth * aspectRatio);

然后在createScaledBitmap()中使用targetHeight,而不是screenHeight作为高度参数。

此外,请确保您加载位图的imageview具有适当的缩放类型(例如FIT_CENTER或CENTER_CROP)。


0

正如您在文档输入链接说明中所看到的; 在渲染之前,您必须调整和重新缩放位图,因此您可以使用以下代码来处理您的BitmapFactory.Options

mBitmapOptions.inScaled = true;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity =  dstWidth;

// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeResources(getResources(), 
      mImageIDs, mBitmapOptions);

-1

在您的intent调用中使用此putExtra来调用相机

intent.putExtra("outputX", 80);
    intent.putExtra("outputY", 80);
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1);
    intent.putExtra("scale", true);
    intent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, 20);

    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(intent, Utility.REQUEST_FOR_CAMERA);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,