保存在 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 中时,从自定义相机获取图像会被拉伸的主要内容,如果未能解决你的问题,请参考以下文章