使用相机拍照并获取位图。

13

我正在开发一款 Android 应用程序,使用前置摄像头为用户模拟镜子。我需要应用程序每隔 5 秒自动且无需用户交互地拍摄一张位图图片,之后再与另一个位图图片合并。

对我来说比较困难的部分是:如何拍照并将其作为位图获取?

我尝试了几种解决方案,但还没有成功。

这个问题被标记为重复,但是该问题是手动执行而不是自动执行。


1
你看过这个吗?如果你看过了但仍然有问题,为什么不尝试发布一些你的代码呢? - magmike
请检查我的答案以获取完整代码。 - mremremre1
1
@tyczj,您提供的链接会打开相机,用户需要自己拍照。我尝试过类似的方法,但无法使用,因为我希望我的应用程序可以在应用内自动拍照! - moobi
被提名重新开放。这个要求没有用户交互的方法,而另一个需要用户交互。 - Ciro Santilli OurBigBook.com
一个可行的方法是使用BitmapFactory.decodeByteArray对从camera.takePicture获取的JPEG字节数组进行解码。可能是一种愚蠢的方法,因为它进行了两次编码/解码 :-) - Ciro Santilli OurBigBook.com
camera.takePicture可能没有其他格式可用:https://dev59.com/BmYq5IYBdhLWcg3wwDNA,因此也许`BitmapFactory.decodeByteArray`是正确的方法? - Ciro Santilli OurBigBook.com
3个回答

8
我使用以下代码在布局背景中显示实时相机图像,按钮会将图像保存为jpeg格式。您可以根据需要尝试修改它: 您可以在此处下载整个测试项目以便快速测试: http://www.4shared.com/rar/v-ZQPybcce/Test.html 与使用意图的其他代码不同之处在于它自动拍照而无需打开相机应用程序,这使应用程序看起来更好 :)
package com.mreprogramming.test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;


public class CameraActivity extends Activity implements SurfaceHolder.Callback{

    protected static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 0;
    private SurfaceView SurView;
    private SurfaceHolder camHolder;
    private boolean previewRunning;
    private Button button1;
    final Context context = this;
    public static Camera camera = null;
    private ImageView camera_image;
    private Bitmap bmp,bmp1;
    private ByteArrayOutputStream bos;
    private BitmapFactory.Options options,o,o2;
    private FileInputStream fis;
    ByteArrayInputStream fis2;
    private FileOutputStream fos;
    private File dir_image2,dir_image;
    private RelativeLayout CamView;

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.camera);



    CamView = (RelativeLayout) findViewById(R.id.camview);

    SurView = (SurfaceView)findViewById(R.id.sview);
    camHolder = SurView.getHolder();
    camHolder.addCallback(this);
    camHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    button1 = (Button)findViewById(R.id.button_1);


    camera_image = (ImageView) findViewById(R.id.camera_image);


    button1.setOnClickListener(new OnClickListener()
    {

    public void onClick(View v)
    {

        button1.setClickable(false);
        button1.setVisibility(View.INVISIBLE);  //<-----HIDE HERE 
        camera.takePicture(null, null, mPicture);

    }

    });


    }




    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {
    if(previewRunning){
        camera.stopPreview();
    }
    Camera.Parameters camParams = camera.getParameters();
    Camera.Size size = camParams.getSupportedPreviewSizes().get(0); 
    camParams.setPreviewSize(size.width, size.height);
    camera.setParameters(camParams);
    try{
        camera.setPreviewDisplay(holder);
        camera.startPreview();
        previewRunning=true;
    }catch(IOException e){
        e.printStackTrace();
    }
    }

    public void surfaceCreated(SurfaceHolder holder) {
    try{
        camera=Camera.open();
    }catch(Exception e){
        e.printStackTrace();
        Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
        finish();
    }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    camera.stopPreview();
    camera.release();
    camera=null;
    }



    public void TakeScreenshot(){

        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        int nu = preferences.getInt("image_num",0);
        nu++;
        SharedPreferences.Editor editor = preferences.edit();
        editor.putInt("image_num",nu);
        editor.commit();
        CamView.setDrawingCacheEnabled(true); 
        CamView.buildDrawingCache(true);
        bmp = Bitmap.createBitmap(CamView.getDrawingCache());
        CamView.setDrawingCacheEnabled(false);
                        bos = new ByteArrayOutputStream(); 
                        bmp.compress(CompressFormat.JPEG, 100, bos); 
                        byte[] bitmapdata = bos.toByteArray();
                        fis2 = new ByteArrayInputStream(bitmapdata);

                        String picId=String.valueOf(nu);
                        String myfile="MyImage"+picId+".jpeg";

                        dir_image = new  File(Environment.getExternalStorageDirectory()+
                                File.separator+"My Custom Folder");
                        dir_image.mkdirs();

                        try {
                            File tmpFile = new File(dir_image,myfile); 
                            fos = new FileOutputStream(tmpFile);

                             byte[] buf = new byte[1024];
                                int len;
                                while ((len = fis2.read(buf)) > 0) {
                                    fos.write(buf, 0, len);
                                }
                                    fis2.close();
                                    fos.close();

                                    Toast.makeText(getApplicationContext(),
                                            "The file is saved at :/My Custom Folder/"+"MyImage"+picId+".jpeg",Toast.LENGTH_LONG).show();

                                    bmp1 = null;
                                    camera_image.setImageBitmap(bmp1);
                                    camera.startPreview();
                                    button1.setClickable(true);
                                          button1.setVisibility(View.VISIBLE);//<----UNHIDE HER
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }


    }

    private PictureCallback mPicture = new PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            dir_image2 = new  File(Environment.getExternalStorageDirectory()+
                    File.separator+"My Custom Folder");
            dir_image2.mkdirs();


            File tmpFile = new File(dir_image2,"TempImage.jpg");
            try {
                fos = new FileOutputStream(tmpFile);
                fos.write(data);
                fos.close();
            } catch (FileNotFoundException e) {
                Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
            }
            options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;

                bmp1 = decodeFile(tmpFile);
                bmp=Bitmap.createScaledBitmap(bmp1,CamView.getWidth(), CamView.getHeight(),true);
                camera_image.setImageBitmap(bmp);
                tmpFile.delete();
                TakeScreenshot();

        }
    };


    public Bitmap decodeFile(File f) {
        Bitmap b = null;
        try {
            // Decode image size
            o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;

            fis = new FileInputStream(f);
            BitmapFactory.decodeStream(fis, null, o);
            fis.close();
            int IMAGE_MAX_SIZE = 1000;
            int scale = 1;
            if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
                scale = (int) Math.pow(
                        2,
                        (int) Math.round(Math.log(IMAGE_MAX_SIZE
                                / (double) Math.max(o.outHeight, o.outWidth))
                                / Math.log(0.5)));
            }

            // Decode with inSampleSize
            o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            fis = new FileInputStream(f);
            b = BitmapFactory.decodeStream(fis, null, o2);
            fis.close();

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

        return b;
    }


}

这是相机配置文件 camera.xml。
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" 
android:id="@+id/camview">

<SurfaceView
android:id="@+id/sview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />

<ImageView
    android:id="@+id/camera_image"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:contentDescription="@string/app_name" />

<Button
    android:id="@+id/button_1"
    android:layout_width="20dp"
    android:layout_height="20dp"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true" />

将以下内容添加到你的清单文件中:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

在清单文件中的“CameraActivity”活动标签下添加以下内容,以确保您的活动始终处于横向方向,否则,除非更改代码,否则将会反转图像的长宽比并严重扭曲。

<activity
        android:name="com.mreprogramming.test.CameraActivity"
        android:label="@string/app_name"
        android:screenOrientation="landscape" >   <-------ADD THIS ---!!!!!
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

将此保存为styles.xml以使您的布局全屏显示。
<resources>

<!--
    Base application theme, dependent on API level. This theme is replaced
    by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.NoTitleBar.Fullscreen">
    <!--
        Theme customizations available in newer API levels can go in
        res/values-vXX/styles.xml, while customizations related to
        backward-compatibility can go here.
    -->
</style>

<!-- Application theme. -->
<style name="AppTheme" parent="android:Theme.NoTitleBar.Fullscreen">
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowNoTitle">true</item>
</style>

在我的应用程序中,我希望图像捕获每个布局的视图,而不仅仅是相机拍摄的内容,类似于这样:enter image description here

我发布的测试代码隐藏了捕获按钮,以便它不会出现在您的照片中。如果您的应用程序中有更多视图,并且不希望它们显示在照片中,请在捕获时隐藏它们(请参阅代码以了解如何隐藏),或编辑代码。

------>总之,这段代码可以进行基本的捕获和保存jpeg,但如果您想要专业图片,您需要稍微修改一下。祝你好运 :)<------


1
自动拍照,无需打开相机应用程序。这很重要,我会在接下来的几天尝试一下 :) - moobi
@SimonIrsch 我添加了下载链接,这样你就可以获取测试项目并快速加载到你的Eclipse中。 - mremremre1
@mremremre1 感谢您分享您的代码。如果我想对由Android相机看到的每个图像进行分类而无需按按钮,您能否给出提示在哪里添加函数?换句话说:我必须将方法camera.takePicture(null, null, mPicture);移动到哪里,以便它在无限循环中被调用。此外,您的下载链接似乎已损坏。 - mcExchange
使用相机默认获取位图的最简单解决方案是从您的相机实例设置Camera.Parameters.setPictureFormat(ImageFormat.JPEG)...您仍然可以更改其旋转和JPEG质量。 - Ely Dantas

5

我不确定这个解决方案是否符合您的需求。然而,将图片作为位图进行拍摄的基本步骤应该如下:

 private static final int CAMERA_REQUEST = 1888; // field

 private void takePicture(){ //you can call this every 5 seconds using a timer or whenever you want
  Intent cameraIntent = new  Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); 
  startActivityForResult(cameraIntent, CAMERA_REQUEST); 
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 
    if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK) {  
        Bitmap picture = (Bitmap) data.getExtras().get("data");//this is your bitmap image and now you can do whatever you want with this 
        imageView.setImageBitmap(picture); //for example I put bmp in an ImageView
    }  
} 

不要忘记在你的清单文件中设置相机权限:
```

不要忘记在你的清单文件中设置相机权限:

```
<uses-feature android:name="android.hardware.camera" />

1
我尝试过类似这样的方法。它会在设备上打开标准相机应用程序,是吗?如果是的话,我不能使用它,因为我希望我的应用程序能自动拍照并在应用程序内部完成! - moobi
2
好的,所以您想在没有用户交互的情况下拍照(我认为Android不允许您在没有预览的情况下拍照),并使用前置摄像头。如果可以帮助您,请参阅以下内容: https://dev59.com/JWkw5IYBdhLWcg3woMFD - Augusto Picciani
@AugustoPicciani 那个线程似乎主要是询问如何将JPEG保存到磁盘,因为camera.takePicture返回JPEG更容易。但它确实教会了我如何使用BitmapFactory.decodeByteArray - Ciro Santilli OurBigBook.com
3
这只获取缩略图位图,而不是完整尺寸的图片! - Rasovica
2
它运行得很完美,但为什么上传的图像尺寸如此之小? - Amit Kumar Pawar

-1

你应该首先使用 IMAGE_CAPTURE Intent 拍下照片。

然后,你需要创建一个临时文件来存储照片,以防手机内存不足。

之后,使用 android.provider.MediaStore.Images.Media.getBitmap() 获取 Bitmap,并选择临时文件。

以下是关于如何完成此操作的完整代码示例:

Android Camera Intent: how to get full sized photo?


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