ProgramingTip

미리보기없이 카메라에서 사진 찍기

bestdevel 2020. 12. 13. 10:25
반응형

미리보기없이 카메라에서 사진 찍기


부팅 시작되는 Android 1.5 애플리케이션을 작성 중입니다. 이것은 Service미리보기없이 사진을 찍어야합니다. 이 앱은 어떤 영역에서든 빛의 밀도를 기록합니다. 사진을 찍을 수 있었는데 사진이 검은 색이었다.

오랜 시간 연구 끝에 버그를 발견했습니다. 미리보기를 생성하지 않고 Android 카메라가 노출과 확장을 설정하기 위해 미리보기가 필요합니다. 내가 만든 SurfaceView청취는하지만, onSurfaceCreated()사건은 발생합니다.

그 이유는 표면이 표면이 생성되지 않기 때문이라고 생각합니다. 또한 MediaStore.CAPTURE_OR_SOMETHING사진을 찍고 두 줄의 코드로 원하는 폴더에 저장 하는 카메라를 정적으로 호출하는 몇 가지 예를 명명지만 사진도 찍지.

IPC를 사용 bindService()하고이 함수를 호출해야합니까? 아니면 어디에서 달성하는 다른 방법이 있습니까?


안드로이드 플랫폼의 카메라가 유효한 미리보기 화면을 제공 할 때까지 비디오를 스트리밍 할 수있는 것이 정말 이상합니다. 플랫폼 설계자들은 거의 비디오 스트리밍 애플리케이션에 대해 전혀 생각하지 않습니다. 증강 현실의 경우에는 실시간 카메라 스트림이 아닌 경우에도 사진은 대체물로 표시 될 수 있습니다.

어쨌든 미리보기 표면의 크기를 1x1 픽셀로 조정 하고 위젯 (시각적 요소)의 어딘가에 배치 할 수 있습니다 . 주의하십시오-카메라 프레임 크기가 아닌 미리보기 표면의 크기를 조정하십시오.

물론 트릭은 일부 시스템 리소스와 배터리를 소모하는 일상적인 데이터 스트리밍 (미리보기 용)을 제거하지 않습니다.


Android 카메라 문서 에서 이에 대한 답을 찾았습니다 .

참고 : MediaRecorder먼저 카메라를 미리보기를 생성하지 않고 사용 하고 프로세스의 처음 몇 단계를 건너 뛸 수 있습니다. 그러나 사용자는 일반적으로 녹음을 시작하기 전에 미리보기를 보는 것을 선호하는 여기에서는 프로세스에 대해 설명하지 않습니다.

위 링크에서 단계별 지침을 사용할 수 있습니다. 지침 나중에는 제공되는 견적이 표시됩니다.


SurfaceView는 미리보기를 가짜로 만들 수도 있습니다.

SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);

2011 년 9 월 21 일 업데이트 : 분명히 이것은 모든 Android 기기에서 작동하지 않습니다.


사진 찍기

미리보기를 먼저하기 전에 먼저 작업을 수행하십시오.

  • 미리보기를 설정 설정
    • 용도 SurfaceView(사전 안드로이드 4.0 사용) 또는 SurfaceTexture(안드로이드 4 +를 투명하게 할 수 있습니다)
    • 사진을 찍기 전에 설정 및 초기화
    • 보기를 설정 미리하고 초기화하기 전에 SurfaceViewSurfaceHolder(를 통해 getHolder())보고 surfaceCreated()하거나 TextureView보고 onSurfaceTextureAvailable때까지 기다 SurfaceTextureListener립니다.
  • 미리보기가 표시 여부를 확인합니다.
    • 그것을 추가하십시오 WindowManager
    • 레이아웃 크기가 1x1 픽셀 이상인지 확인합니다 ( 테스트를 위해 MATCH_PARENTx MATCH_PARENT로 설정 하여 시작하는 것이 좋습니다).
    • 가시성이 있는지 확인하십시오 View.VISIBLE.
    • 당신은 사용하여 확인 FLAG_HARDWARE_ACCELERATEDLayoutParams권한이 TextureView있습니다.
  • takePicture문서에 다른 모든 장치에서 지원되지 않습니다 .

문제 해결

  • 경우 surfaceCreated/가 onSurfaceTextureAvailable호출되지 않습니다는 SurfaceView/ TextureView아마 표시되지 않습니다.
  • 경우 takePicture에 실패 먼저 미리보기가 제대로 작동 확인합니다. takePicture통화 를 제거 하고 미리보기를 실행하여 화면에 표시 확인할 수 있습니다.
  • 사진이 예상보다 어두우면 takePicture미리보기가 시작된 후 카메라가 노출을 약 시간을 갖도록 전화하기 전에 1 초 동안 지연해야 할 수 있습니다 .

미리보기

  • View을 최소화하기 가시성 위해 미리보기를 1 × 1로 크기 만듭니다 ( 또는 안정성을 높이 려면 8 × 16을 사용 해보세요 ).

    new WindowManager.LayoutParams(1, 1, /*...*/)
    
  • 눈에 잘 만나도록 미리보기를 중앙 외곽 이동합니다.

    new WindowManager.LayoutParams(width, height,
        Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
    
  • 미리보기를 투명하게 만들기 (에서만 작동 TextureView)

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        width, height, /*...*/
        PixelFormat.TRANSPARENT);
    params.alpha = 0;
    

작업 예 (Sony Xperia M, Android 4.3에서 테스트 됨)

/** Takes a single photo on service start. */
public class PhotoTakingService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        takePhoto(this);
    }

    @SuppressWarnings("deprecation")
    private static void takePhoto(final Context context) {
        final SurfaceView preview = new SurfaceView(context);
        SurfaceHolder holder = preview.getHolder();
        // deprecated setting, but required on Android versions prior to 3.0
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        holder.addCallback(new Callback() {
            @Override
            //The preview must happen at or after this point or takePicture fails
            public void surfaceCreated(SurfaceHolder holder) {
                showMessage("Surface created");

                Camera camera = null;

                try {
                    camera = Camera.open();
                    showMessage("Opened camera");

                    try {
                        camera.setPreviewDisplay(holder);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }

                    camera.startPreview();
                    showMessage("Started preview");

                    camera.takePicture(null, null, new PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            showMessage("Took picture");
                            camera.release();
                        }
                    });
                } catch (Exception e) {
                    if (camera != null)
                        camera.release();
                    throw new RuntimeException(e);
                }
            }

            @Override public void surfaceDestroyed(SurfaceHolder holder) {}
            @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
        });

        WindowManager wm = (WindowManager)context
            .getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                1, 1, //Must be at least 1x1
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                0,
                //Don't know if this is a safe default
                PixelFormat.UNKNOWN);

        //Don't set the preview visibility to GONE or INVISIBLE
        wm.addView(preview, params);
    }

    private static void showMessage(String message) {
        Log.i("Camera", message);
    }

    @Override public IBinder onBind(Intent intent) { return null; }
}

Android 4.0 이상 (API 레벨> = 14)에서는 TextureView사용 하여하여 카메라 스트림을 미리보고 사용자에게 표시하지 않도록 숨길 수 있습니다 . 방법은 다음과 가변합니다.

먼저 미리보기 화면에 대한 만들기 / 먼저 업데이트를 화면에 SurfaceTextureListener를 구현하는 클래스를 만듭니다. 이 클래스는 또한 카메라 객체를 입력으로 사용하여 표면이 생성되는 즉시 카메라의 startPreview 함수를 호출 할 수 있습니다.

public class CamPreview extends TextureView implements SurfaceTextureListener {

  private Camera mCamera;

  public CamPreview(Context context, Camera camera) {
    super(context);
    mCamera = camera;
   }

  @Override
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
    setLayoutParams(new FrameLayout.LayoutParams(
        previewSize.width, previewSize.height, Gravity.CENTER));

    try{
      mCamera.setPreviewTexture(surface);
     } catch (IOException t) {}

    mCamera.startPreview();
    this.setVisibility(INVISIBLE); // Make the surface invisible as soon as it is created
  }

  @Override
  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
      // Put code here to handle texture size change if you want to
  }

  @Override
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    return true;
  }

  @Override
  public void onSurfaceTextureUpdated(SurfaceTexture surface) {
      // Update your view here!
  }
}

미리보기 데이터를 처리 비용 처리 클래스도 구현해야합니다.

public class CamCallback implements Camera.PreviewCallback{
  public void onPreviewFrame(byte[] data, Camera camera){
     // Process the camera data here
  }
}

위의 CamPreview 및 CamCallback 클래스를 사용하여 활동의 onCreate () 또는 시작 함수에서 카메라를 설정합니다.

// Setup the camera and the preview object
Camera mCamera = Camera.open(0);
CamPreview camPreview = new CamPreview(Context,mCamera);
camPreview.setSurfaceTextureListener(camPreview);

// Connect the preview object to a FrameLayout in your UI
// You'll have to create a FrameLayout object in your UI to place this preview in
FrameLayout preview = (FrameLayout) findViewById(R.id.cameraView); 
preview.addView(camPreview);

// Attach a callback for preview
CamCallback camCallback = new CamCallback();
mCamera.setPreviewCallback(camCallback);

다소 까다 롭습니다. 해야 할 일은 서비스에서 창 관리자에 외장을 연결하는 것입니다.

WindowManager wm = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT);        
wm.addView(surfaceview, params);

다음 설정

surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);

여기서 mHolder는 표면보기에서 얻은 전화기입니다.

이런 식으로 Surfaceview의 알파를 사용하여 완전히 투명하게 만들 수 있습니다 카메라는 여전히 프레임을 얻습니다.

그게 내가하는 방법이야. 도움이 되길 바랍니다 :)


3.0 이하 버전에서 더미 SurfaceView (실제 GUI에 추가되지 않음)를 사용하여이 문제를 해결했습니다 (또는 태블릿의 카메라 서비스가 실제로 의미가없는 4.0이라고 가정 해 보겠습니다). 4.0 이상 버전에서는 에뮬레이터에서만 작동했습니다. (SurfaceView (및 setSurfaceView ()) 대신 SurfaceTexture (및 setSurfaceTexture ()) 사용이 여기서 작동했습니다. 적어도 Nexus S에서 작동합니다.

이것이 정말 안드로이드 프레임 워크의 단점이라고 생각합니다.


"Sam의 작업 예"에서 (Thank you Sam ...)

istruction "wm.addView (preview, params);"

"창 android.view.ViewRoot를 추가 할 수 없음-이 창 유형에 대한 권한이 거부되었습니다."예외 획득

AndroidManifest에서이 권한을 사용하여 해결 :

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

이 작업 코드를 시도해 볼 수 있습니다.이 서비스는 전면 사진을 클릭하고, 카메라 사진을 다시 캡처하려면 코드에서 backCamera의 주석을 제거하고 frontCamera에 주석을 추가합니다.

참고 :-활동 또는 어디서나 앱 및 startService에 대한 카메라 및 저장소 권한을 허용하십시오.

public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        CapturePhoto();
    }

    private void CapturePhoto() {

        Log.d("kkkk","Preparing to take photo");
        Camera camera = null;

        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();

            int frontCamera = 1;
            //int backCamera=0;

            Camera.getCameraInfo(frontCamera, cameraInfo);

            try {
                camera = Camera.open(frontCamera);
            } catch (RuntimeException e) {
                Log.d("kkkk","Camera not available: " + 1);
                camera = null;
                //e.printStackTrace();
            }
            try {
                if (null == camera) {
                    Log.d("kkkk","Could not get camera instance");
                } else {
                    Log.d("kkkk","Got the camera, creating the dummy surface texture");
                     try {
                         camera.setPreviewTexture(new SurfaceTexture(0));
                        camera.startPreview();
                    } catch (Exception e) {
                        Log.d("kkkk","Could not set the surface preview texture");
                        e.printStackTrace();
                    }
                    camera.takePicture(null, null, new Camera.PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            File pictureFileDir=new File("/sdcard/CaptureByService");

                            if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
                                pictureFileDir.mkdirs();
                            }
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
                            String date = dateFormat.format(new Date());
                            String photoFile = "ServiceClickedPic_" + "_" + date + ".jpg";
                            String filename = pictureFileDir.getPath() + File.separator + photoFile;
                            File mainPicture = new File(filename);

                            try {
                                FileOutputStream fos = new FileOutputStream(mainPicture);
                                fos.write(data);
                                fos.close();
                                Log.d("kkkk","image saved");
                            } catch (Exception error) {
                                Log.d("kkkk","Image could not be saved");
                            }
                            camera.release();
                        }
                    });
                }
            } catch (Exception e) {
                camera.release();
            }
    }
}

참고 URL : https://stackoverflow.com/questions/2386025/taking-picture-from-camera-without-preview

반응형