相机图片转换为位图导致图像混乱

14

这是我用来将图片保存到Bitmap的代码。这段代码基于CyanogenMod相机应用程序的代码,因此我认为它应该能正常工作,但事实并非如此。这个问题最重要的是,在Nexus 4上测试时,Bitmap在使用后置摄像头拍摄照片时被正确创建,但使用前置摄像头则会导致下面所示的结果。

我正在使用的创建Bitmap的代码:

private class XyzPictureCallback implements Camera.PictureCallback {

    @Override
    public void onPictureTaken (byte [] data, Camera camera) {
        Options options = new Options();
        options.inDither = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;

        Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length, options);
    }
}

我尝试使用不同的选项(甚至不使用选项),但没有效果。可能是由于两个不同相机返回的像素格式的问题,但当我运行getSupportedPictureFormats()时,它们都会返回ImageFormat.JPEG...

我已经没有任何想法了...

我应该还提到直接使用FileOutputStream保存data可以创建一个正确的JPEG图像。所以问题肯定在BitmapFactory和我创建Bitmap的方式上。

这是此代码生成的位图:

Broken image

编辑 (24.03.2013):

花费多个小时尝试修复后,我仍然没有真正的解决方案。 我发现问题只在将图片大小(使用Camera.Parameters.setPictureSize(int width, int height))设置为前置摄像头可用的最高分辨率1280x960时才会发生问题,这个分辨率是通过调用Camera.Parameters.getSupportedPictureSizes()获得的。

导致问题的分辨率为1280x960。就如我之前提到的,这是最高分辨率。第二高的是1280x720,当我使用这个分辨率时,输出图片很好。我确实检查了相机输出的格式,它一直是ImageFormat.JPEG,所以我不认为像素格式是问题所在...

编辑 (08.03.2013): 调用takePicture:

private class XyzAutoFocusCallback implements Camera.AutoFocusCallback {

            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                    if (takingPicture) {
                            camera.takePicture(null, null, myPictureCallback);
                    } else {
                    ...
            }

}

请展示你的 takePicture 调用。 - twaddington
这个是干什么用的 -> options.inDither = false; 这不是必要的 -> options.inPreferredConfig = Bitmap.Config.ARGB_8888; 这是默认值: 图像默认使用 ARGB_8888 配置加载。 - g00dy
@twaddington,我已经添加了我的takePicture调用。不过,这没有什么特别的。 - Paul
你有尝试过访问https://dev59.com/QWzXa4cB1Zd3GeqPZOwm吗? - f20k
哇,我搜索了多次但没有找到那个问题。我没有尝试过这个。我今天晚些时候会去做这个。我只指定了我使用相机和自动对焦。事实上,我不知道这种功能的存在。我希望它能够正常工作。 - Paul
显示剩余3条评论
7个回答

3

该相机不支持高度和宽度设置。这就是为什么看起来像这样,以及为什么1280x720设置有效的原因。这就好比你将星际争霸设定为在显示器不支持的分辨率下运行。


嗯...那个分辨率是由getSupportedPictureSizes()返回的。除非驱动程序报告错误信息(这是极不可能的),否则这是不正确的。 - Paul

0
这是我的代码,我用它来拍照并将其保存在数据库中,而不改变其质量。
首先,您需要这些变量:
 private static final int CAMERA_PIC_REQUEST = 2500;
private Rect Padding = new Rect(-1,-1,-1,-1);
private Matrix matrix = new Matrix();
private Bitmap rotatedBitmap = null;
private Bitmap bm = null;
private Bitmap scaledBitmap= null;
private ExifInterface exif ;
private int rotation=0;
private final BitmapFactory.Options options = new BitmapFactory.Options();

然后根据你的应用程序(我的使用按钮),设置请求,可以是从按钮、活动或片段中进行。

btn_camera.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {

              Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
              startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST);

          }
    }
    );

现在,由于我正在从一个Fragment请求CAMERA_PIC_REQUEST,因此我在OnActivityResult()中实现了以下代码:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == CAMERA_PIC_REQUEST)
        if (data != null) {
        imageuri = data.getData();
        //Bitmap image = (Bitmap) data.getExtras().get("data");

        try {
            options.inSampleSize = 2;
            exif = new ExifInterface(getRealPathFromURI(imageuri));
            rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            rotation = exifToDegrees(rotation);

            bm = BitmapFactory.decodeStream(
                    getActivity().getContentResolver().openInputStream(imageuri),Padding,options);

            scaledBitmap = Bitmap.createScaledBitmap(bm, 400,400, true);

            if (rotation != 0)
            {matrix.postRotate(rotation);}
            //matrix.postRotate(180);
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap , 0, 0, scaledBitmap .getWidth(), scaledBitmap .getHeight(), matrix, true);

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

        mIvFoto.setImageBitmap(rotatedBitmap);


     }
 }

旋转和矩阵是用于将拍摄的图片缩放到ImageView上,以便在保存之前预览照片。这不会影响图片的质量,仅用于预览位图。因此,在创建ScaledBitmap时,可以看到我将图片的尺寸设置为400,400,您可以将图片的尺寸设置为所需的尺寸,并确保旋转位图,以便即使在纵向模式下拍摄照片,也能正确获取图片。

最后,介绍从相机保存路径中旋转和获取图片的方法。

private static int exifToDegrees(int exifOrientation) {
    if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; }
    else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {  return 180; }
    else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {  return 270; }
    return 0;
}

private String getRealPathFromURI(Uri contentURI) {
    String result;
    Cursor cursor =getActivity().getContentResolver().query(contentURI, null, null, null, null);
    if (cursor == null) { // Source is Dropbox or other similar local file path
        result = contentURI.getPath();
    } else {
        cursor.moveToFirst();
        int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
        result = cursor.getString(idx);
        cursor.close();
    }
    return result;
}

希望这能对你有所帮助。


0

0
我应该也提一下,直接使用FileOutputStream保存数据可以创建一个正确的JPEG图像。所以问题肯定出在BitmapFactory和我创建Bitmap的方式上。
相机输出完全处理过的JPEG数据(希望如此),您可以将其写入磁盘。现在您应该尝试:
  1. 将文件转储到磁盘。
  2. 使用验证工具或PC上的某个图像编辑器检查该文件。
  3. 如果图像编辑器正常打开图像,请使用标准JPEG配置文件从该图像编辑器保存副本。
  4. BitmapFactory处理原始文件和由图像编辑器保存的文件,看看两者是否都会产生错误的位图。
如果图像是有效的JPEG格式且BitmapFactory仍然生成错误的位图,则问题出在BitmapFactory上(可能性较小)。如果相机输出的文件无效,则问题出在相机上(可能性更大)。

0

试试这个

Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
imv.setImageBitmap(bmp);

ByteArrayOutputStream bos = new ByteArrayOutputStream();
bmp.compress(CompressFormat.JPEG, 70, bos);

我不认为这会有帮助。基本上这是相同的代码,只是最后将其保存到文件系统中。 - Paul
我认为这些东西不需要。选项选项 = 新选项(); 选项.inDither = false; 选项.inPreferredConfig = Bitmap.Config.ARGB_8888; - Siva

0

你尝试在选项中放置 null 吗?试试这个:

@Override
public void onPictureTaken(final byte[] data, Camera camera) {
    Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length, null);
    try {
        FileOutputStream out = new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath()+"/image.jpg");
        bm.compress(Bitmap.CompressFormat.JPEG, 95, out);
        out.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

0
你是否将后置摄像头的参数设置为前置摄像头? 另一个需要测试的是前置摄像头,首先尝试不设置任何参数,只使用默认设置来查看是否可以工作。我在三星设备上遇到了类似的问题,但那是针对后置摄像头的。 此外,你可能需要旋转使用前置摄像头拍摄的图像(对于三星设备,后置摄像头也需要这样做)。
public static Bitmap rotate(Bitmap bitmap, int degree) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        Matrix mtx = new Matrix();
        mtx.postRotate(degree);

        return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
    }

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