保存在 ImageView 中时,从自定义相机获取图像会被拉伸

Posted

技术标签:

【中文标题】保存在 ImageView 中时,从自定义相机获取图像会被拉伸【英文标题】:Taking image from custom camera is stretched when save in ImageView 【发布时间】:2016-06-12 04:00:22 【问题描述】:

我正在使用此代码将图片保存在 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);
    


【问题讨论】:

【参考方案1】:

使用以下类创建缩放位图

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);【参考方案2】:

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

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;
    

【讨论】:

【参考方案3】:

UIImageView 设置

<ImageView
    android:id="@id/img"
    android:layout_
    android:layout_
    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");
    

这就是我在我的测试应用程序中使用它的方式,它可以正常工作,我没有任何方向问题。我在纵向模式下使用纵向和横向图片进行测试,在横向模式下使用纵向和横向图片进行测试。在获得图像之前,我在这两个地方都改变了方向。

如果您使用的是自定义相机,并且上面的代码方向错误,只需添加此

需要做两件事:

    相机预览需要与您的旋转相同。由

    设置

    camera.setDisplayOrientation(result);

    将拍摄的图片保存为您的相机预览。通过 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);
    

希望对您有所帮助。

【讨论】:

它将图像方向转换为横向模式 @NeerajSharma 更新了我的代码,它在我的测试应用程序中运行良好。 你的代码只能在横向模式下正常工作,而不是在纵向模式下兄弟。 @NeerajSharma 你使用了我的整个代码吗? takeImageIntent 和 initImage 在我的 TestApplication 中工作正常。我应该添加屏幕截图吗? @NeerajSharma 因为我从其他问题中了解到您正在使用自定义相机,请参阅最后一节。如果您使用的是自定义相机并且方向错误。只需将其添加到代码中。但是您必须使用我的 initImage 函数来保留比率。【参考方案4】:

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

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

【讨论】:

【参考方案5】:

使用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)。

【讨论】:

【参考方案6】:

正如您在文档 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);

【讨论】:

【参考方案7】:

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

【讨论】:

以上是关于保存在 ImageView 中时,从自定义相机获取图像会被拉伸的主要内容,如果未能解决你的问题,请参考以下文章

从相机拍照后图像未在 ImageView 中设置

如何使用相机设置捕获的图像以适合固定高度和宽度的 Imageview

底部带有相机按钮的 ImageView

从自定义数据源中获取选定的行?

在相机活动中将图像从drawable保存到sdcard

如何使用UTF-8编码保存source()。R文件?