保存在 ImageView 中时,从自定义相机拍摄的图像会被拉伸

Taking image from custom camera is stretched when save in ImageView

我正在使用此代码在 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);
    }

}

使用它可以防止图像拉伸并保持图像的纵横比

android:scaleType="fitXY"  // or desired scale type
android:adjustViewBounds="true"

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); //set rotation to save the picture camera.setDisplayOrientation(result); //set the rotation for preview camera 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);
    }
}

希望对您有所帮助。

在您的相机意图调用中使用此 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);

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

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;
    }
}

使用以下 class 创建缩放位图

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);
        }
    }


}

并在您的函数中使用此 class,如下所示

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);
    }

}

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

一个选项是手动计算目标高度,这样就不会发生裁剪:

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

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

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

如您在文档中所见enter link description here; 你必须在渲染之前调整位图的大小和比例,这样你就可以使用下面的代码为你的 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);