第28讲 geekcamera2连拍实战 -凯发k8手机登录

本讲是android camera专题系列的第28讲,我们介绍android camera2 api专题的连拍实战。

更多资源:

资源 描述
在线课程
知识星球 星球名称:深入浅出android camera
星球id: 17296815
wechat 极客笔记圈

通过连拍实现三种连续拍图的需求

连拍获取多张图片

连拍获取多张不同曝光的图片

连拍获取多张不同对焦距离的图片

geekcamera2连拍功能

burst type

geekcamera2连拍功能

连拍实现方式

  • captureburst

  • 多次调用capture方法

点击拍照调用流程

mainactivity#clickedtakephoto
    mainactivity#takepicture
        mainactivity#takepicturepressed
            preview#takepicturepressed
                preview#takepicture
                    preview#takephoto
                        preview#takephotowhenfocused 决定burst type
                            cameracontroller2#setbursttype
                            cameracontroller2#takepicture
                                cameracontroller2#takepictureafterprecapture

连拍获取多张不同曝光或不同对焦距离的图片

private void takepictureburstbracketing() {
    if( mydebug.log )
        log.i(tag, "takepictureburstbracketing");
    if( burst_type != bursttype.bursttype_expo && burst_type != bursttype.bursttype_focus ) {
        log.e(tag, "takepictureburstbracketing called but unexpected burst_type: "   burst_type);
    }
    list requests = new arraylist<>();
    boolean ok = true;
    errorcallback push_take_picture_error_cb = null;
    synchronized( background_camera_lock ) {
        if( mcameradevice == null || mcameracapturesession == null ) {
            if( mydebug.log )
                log.i(tag, "no camera or capture session");
            return;
        }
        try {
            if( mydebug.log ) {
                log.i(tag, "imagereader: "   imagereader.tostring());
                log.i(tag, "imagereader surface: "   imagereader.getsurface().tostring());
            }
            capturerequest.builder stillbuilder = mcameradevice.createcapturerequest(previewisvideomode ? cameradevice.template_video_snapshot : cameradevice.template_still_capture);
            stillbuilder.set(capturerequest.control_capture_intent, capturerequest.control_capture_intent_still_capture);
            // n.b., don't set requesttagtype.capture here - we only do it for the last of the burst captures (see below)
            camera_settings.setupbuilder(stillbuilder, true);
            clearpending();
            // shouldn't add preview surface as a target - see note in takepictureafterprecapture()
            // but also, adding the preview surface causes the dark/light exposures to be visible, which we don't want
            stillbuilder.addtarget(imagereader.getsurface());
            if( raw_todo )
                stillbuilder.addtarget(imagereaderraw.getsurface());
            if( burst_type == bursttype.bursttype_expo ) {
                if( mydebug.log )
                    log.i(tag, "[cs]expo bracketing");
                /*stillbuilder.set(capturerequest.control_ae_mode, camerametadata.control_ae_mode_on);
                stillbuilder.set(capturerequest.flash_mode, camerametadata.flash_mode_off);
                stillbuilder.set(capturerequest.control_ae_exposure_compensation, -6);
                requests.add( stillbuilder.build() );
                stillbuilder.set(capturerequest.control_ae_exposure_compensation, 0);
                requests.add( stillbuilder.build() );
                stillbuilder.set(capturerequest.control_ae_exposure_compensation, 6);
                requests.add( stillbuilder.build() );*/
                stillbuilder.set(capturerequest.control_ae_mode, camerametadata.control_ae_mode_off);
                if( use_fake_precapture_mode && fake_precapture_torch_performed ) {
                    if( mydebug.log )
                        log.i(tag, "setting torch for capture");
                    stillbuilder.set(capturerequest.flash_mode, camerametadata.flash_mode_torch);
                    test_fake_flash_photo  ;
                }
                // else don't turn torch off, as user may be in torch on mode
                range iso_range = characteristics.get(cameracharacteristics.sensor_info_sensitivity_range); // may be null on some devices
                if( iso_range == null ) {
                    log.e(tag, "takepictureburstbracketing called but null iso_range");
                }
                else {
                    // set iso
                    int iso = 800;
                    // obtain current iso/etc settings from the capture result - but if we're in manual iso mode,
                    // might as well use the settings the user has actually requested (also useful for workaround for
                    // oneplus 3t bug where the reported iso and exposure_time are wrong in dark scenes)
                    if( camera_settings.has_iso )
                        iso = camera_settings.iso;
                    else if( capture_result_has_iso )
                        iso = capture_result_iso;
                    // see https://sourceforge.net/p/opencamera/tickets/321/ - some devices may have auto iso that's
                    // outside of the allowed manual iso range!
                    iso = math.max(iso, iso_range.getlower());
                    iso = math.min(iso, iso_range.getupper());
                    stillbuilder.set(capturerequest.sensor_sensitivity, iso );
                }
                if( capture_result_has_frame_duration  )
                    stillbuilder.set(capturerequest.sensor_frame_duration, capture_result_frame_duration);
                else
                    stillbuilder.set(capturerequest.sensor_frame_duration, 1000000000l/30);
                long base_exposure_time = 1000000000l/30;
                if( camera_settings.has_iso )
                    base_exposure_time = camera_settings.exposure_time;
                else if( capture_result_has_exposure_time )
                    base_exposure_time = capture_result_exposure_time;
                int n_half_images = expo_bracketing_n_images/2;
                long min_exposure_time = base_exposure_time;
                long max_exposure_time = base_exposure_time;
                final double scale = math.pow(2.0, expo_bracketing_stops/(double)n_half_images);
                range exposure_time_range = characteristics.get(cameracharacteristics.sensor_info_exposure_time_range); // may be null on some devices
                if( exposure_time_range != null ) {
                    min_exposure_time = exposure_time_range.getlower();
                    max_exposure_time = exposure_time_range.getupper();
                }
                if( mydebug.log ) {
                    log.i(tag, "taking expo bracketing with n_images: "   expo_bracketing_n_images);
                    log.i(tag, "iso: "   stillbuilder.get(capturerequest.sensor_sensitivity));
                    log.i(tag, "frame duration: "   stillbuilder.get(capturerequest.sensor_frame_duration));
                    log.i(tag, "base exposure time: "   base_exposure_time);
                    log.i(tag, "min exposure time: "   min_exposure_time);
                    log.i(tag, "max exposure time: "   max_exposure_time);
                }
                // darker images
                for(int i=0;i max_exposure_time )
                            exposure_time = max_exposure_time;
                        if( mydebug.log ) {
                            log.i(tag, "add burst request for "   i   "th light image:");
                            log.i(tag, "    this_scale: "   this_scale);
                            log.i(tag, "    exposure_time: "   exposure_time);
                        }
                        stillbuilder.set(capturerequest.sensor_exposure_time, exposure_time);
                        if( i == n_half_images - 1 ) {
                            // requesttagtype.capture should only be set for the last request, otherwise we'll may do things like turning
                            // off torch (for fake flash) before all images are received
                            // more generally, doesn't seem a good idea to be doing the post-capture commands (resetting ae state etc)
                            // multiple times, and before all captures are complete!
                            if( mydebug.log )
                                log.i(tag, "set requesttagtype.capture for last burst request");
                            stillbuilder.settag(new requesttagobject(requesttagtype.capture));
                        }
                        else {
                            stillbuilder.settag(new requesttagobject(requesttagtype.capture_burst_in_progress));
                        }
                        requests.add( stillbuilder.build() );
                    }
                }
                burst_single_request = true;
            } else { // bursttype_focus
                if( mydebug.log )
                    log.i(tag, "[cs] focus bracketing");
                if( use_fake_precapture_mode && fake_precapture_torch_performed ) {
                    if( mydebug.log )
                        log.i(tag, "setting torch for capture");
                    if( !camera_settings.has_iso )
                        stillbuilder.set(capturerequest.control_ae_mode, camerametadata.control_ae_mode_on);
                    stillbuilder.set(capturerequest.flash_mode, camerametadata.flash_mode_torch);
                    test_fake_flash_photo  ;
                }
                stillbuilder.set(capturerequest.control_af_mode, camerametadata.control_af_mode_off); // just in case
                if( math.abs(camera_settings.focus_distance - focus_bracketing_source_distance) < 1.0e-5 ) {
                    if( mydebug.log )
                        log.i(tag, "current focus matches source");
                }
                else if( math.abs(camera_settings.focus_distance - focus_bracketing_target_distance) < 1.0e-5 ) {
                    if( mydebug.log )
                        log.i(tag, "current focus matches target");
                }
                else {
                    log.i(tag, "current focus matches neither source nor target");
                }
                list focus_distances = setupfocusbracketingdistances(focus_bracketing_source_distance, focus_bracketing_target_distance, focus_bracketing_n_images);
                if( focus_bracketing_add_infinity ) {
                    focus_distances.add(0.0f);
                }
                for(int i=0;i

正常连拍

private void takepictureburst(boolean continuing_fast_burst) {
    if( mydebug.log )
        log.i(tag, "takepictureburst continuing_fast_burst:"   continuing_fast_burst);
    if( burst_type != bursttype.bursttype_normal && burst_type != bursttype.bursttype_continuous ) {
        log.e(tag, "takepictureburstbracketing called but unexpected burst_type: "   burst_type);
    }
    boolean is_new_burst = true;
    capturerequest request = null;
    capturerequest last_request = null;
    boolean ok = true;
    errorcallback push_take_picture_error_cb = null;
    synchronized( background_camera_lock ) {
        if( mcameradevice == null || mcameracapturesession == null ) {
            if( mydebug.log )
                log.i(tag, "no camera or capture session");
            return;
        }
        try {
            if( mydebug.log ) {
                log.i(tag, "imagereader: "   imagereader.tostring());
                log.i(tag, "imagereader surface: "   imagereader.getsurface().tostring());
            }
            capturerequest.builder stillbuilder = mcameradevice.createcapturerequest(previewisvideomode ? cameradevice.template_video_snapshot : cameradevice.template_still_capture);
            stillbuilder.set(capturerequest.control_capture_intent, capturerequest.control_capture_intent_still_capture);
            // n.b., don't set requesttagtype.capture here - we only do it for the last of the burst captures (see below)
            camera_settings.setupbuilder(stillbuilder, true);
            if( use_fake_precapture_mode && fake_precapture_torch_performed ) {
                if( mydebug.log )
                    log.i(tag, "setting torch for capture");
                if( !camera_settings.has_iso )
                    stillbuilder.set(capturerequest.control_ae_mode, camerametadata.control_ae_mode_on);
                stillbuilder.set(capturerequest.flash_mode, camerametadata.flash_mode_torch);
                test_fake_flash_photo  ;
            }
            if( burst_type == bursttype.bursttype_normal && burst_for_noise_reduction ) {
                // must be done after calling setupbuilder(), so we override the default edge_mode and noise_reduction_mode
                if( mydebug.log )
                    log.i(tag, "optimise settings for burst_for_noise_reduction");
                stillbuilder.set(capturerequest.noise_reduction_mode, capturerequest.noise_reduction_mode_off);
                stillbuilder.set(capturerequest.color_correction_aberration_mode, capturerequest.color_correction_aberration_mode_off);
                stillbuilder.set(capturerequest.edge_mode, capturerequest.edge_mode_off);
            }
            if( !continuing_fast_burst ) {
                clearpending();
            }
            // shouldn't add preview surface as a target - see note in takepictureafterprecapture()
            stillbuilder.addtarget(imagereader.getsurface());
            // raw target added below
            if( use_fake_precapture_mode && fake_precapture_torch_performed ) {
                stillbuilder.set(capturerequest.flash_mode, camerametadata.flash_mode_torch);
                test_fake_flash_photo  ;
            }
            // else don't turn torch off, as user may be in torch on mode
            if( burst_type == bursttype.bursttype_continuous ) {
                if( mydebug.log )
                    log.i(tag, "continuous burst mode");
                raw_todo = false; // raw works in continuous burst mode, but makes things very slow...
                if( continuing_fast_burst ) {
                    if( mydebug.log )
                        log.i(tag, "continuing fast burst");
                    n_burst  ;
                    is_new_burst = false;
                    /*if( !continuous_burst_in_progress ) // test bug where we call callback oncompleted() before all burst images are received
                        n_burst = 1;*/
                }
                else {
                    if( mydebug.log )
                        log.i(tag, "start continuous burst");
                    continuous_burst_in_progress = true;
                    n_burst = 1;
                    n_burst_taken = 0;
                }
                if( mydebug.log )
                    log.i(tag, "n_burst is now "   n_burst);
            }
            else if( burst_for_noise_reduction ) {
                if( mydebug.log )
                    log.i(tag, "choose n_burst for burst_for_noise_reduction");
                n_burst = 4;
                n_burst_taken = 0;
                if( capture_result_has_iso ) {
                    // for nexus 6, max reported iso is 1196, so the limit for dark scenes shouldn't be more than this
                    // nokia 8's max reported iso is 1551
                    // note that oneplus 3t has max reported iso of 800, but this is a device bug
                    if( capture_result_iso >= iso_for_dark ) {
                        if( mydebug.log )
                            log.i(tag, "optimise for dark scene");
                        n_burst = noise_reduction_low_light ? n_images_nr_dark_low_light : n_images_nr_dark;
                        boolean is_oneplus = build.manufacturer.tolowercase(locale.us).contains("oneplus");
                        // oneplus 3t at least has bug where manual iso can't be set to above 800, so dark images end up too dark -
                        // so no point enabling this code, which is meant to brighten the scene, not make it darker!
                        if( !camera_settings.has_iso && !is_oneplus ) {
                            long exposure_time = noise_reduction_low_light ? 1000000000l/3 : 1000000000l/10;
                            if( !capture_result_has_exposure_time || capture_result_exposure_time < exposure_time ) {
                                if( mydebug.log )
                                    log.i(tag, "also set long exposure time");
                                modified_from_camera_settings = true;
                                setmanualexposuretime(stillbuilder, exposure_time);
                            }
                            else {
                                if( mydebug.log )
                                    log.i(tag, "no need to extend exposure time for dark scene, already long enough: "   exposure_time);
                            }
                        }
                    }
                    else if( capture_result_has_exposure_time ) {
                        //final double full_exposure_time_scale = 0.5;
                        final double full_exposure_time_scale = math.pow(2.0, -0.5);
                        final long fixed_exposure_time = 1000000000l/60; // we only scale the exposure time at all if it's less than this value
                        final long scaled_exposure_time = 1000000000l/120; // we only scale the exposure time by the full_exposure_time_scale if the exposure time is less than this value
                        long exposure_time = capture_result_exposure_time;
                        if( exposure_time <= fixed_exposure_time ) {
                            if( mydebug.log )
                                log.i(tag, "optimise for bright scene");
                            //n_burst = 2;
                            n_burst = 3;
                            if( !camera_settings.has_iso ) {
                                double exposure_time_scale = getscaleforexposuretime(exposure_time, fixed_exposure_time, scaled_exposure_time, full_exposure_time_scale);
                                exposure_time *= exposure_time_scale;
                                if( mydebug.log ) {
                                    log.i(tag, "reduce exposure shutter speed further, was: "   exposure_time);
                                    log.i(tag, "exposure_time_scale: "   exposure_time_scale);
                                }
                                modified_from_camera_settings = true;
                                setmanualexposuretime(stillbuilder, exposure_time);
                            }
                        }
                    }
                }
            }
            else {
                if( mydebug.log )
                    log.i(tag, "user requested n_burst");
                n_burst = burst_requested_n_images;
                n_burst_taken = 0;
            }
            if( raw_todo )
                stillbuilder.addtarget(imagereaderraw.getsurface());
            n_burst_total = n_burst;
            n_burst_raw = raw_todo ? n_burst : 0;
            burst_single_request = false;
            if( mydebug.log )
                log.i(tag, "n_burst: "   n_burst);
            stillbuilder.settag(new requesttagobject(requesttagtype.capture_burst_in_progress));
            request = stillbuilder.build();
            stillbuilder.settag(new requesttagobject(requesttagtype.capture));
            last_request = stillbuilder.build();
            // n.b., don't stop the preview with stop.repeating when capturing a burst
        }
        catch(cameraaccessexception e) {
            if( mydebug.log ) {
                log.e(tag, "failed to take picture burst");
                log.e(tag, "reason: "   e.getreason());
                log.e(tag, "message: "   e.getmessage());
            }
            e.printstacktrace();
            ok = false;
            jpeg_todo = false;
            raw_todo = false;
            picture_cb = null;
            push_take_picture_error_cb = take_picture_error_cb;
        }
    }
    // need to call callbacks without a lock
    if( ok && picture_cb != null && is_new_burst ) {
        if( mydebug.log )
            log.i(tag, "call onstarted() in callback");
        picture_cb.onstarted();
    }
    if( ok ) {
        synchronized( background_camera_lock ) {
            if( mcameradevice == null || mcameracapturesession == null ) {
                if( mydebug.log )
                    log.i(tag, "no camera or capture session");
                return;
            }
            try {
                final boolean use_burst = true;
                //final boolean use_burst = false;
                if( burst_type == bursttype.bursttype_continuous ) {
                    if( mydebug.log ) {
                        log.i(tag, "continuous capture");
                        if( !continuous_burst_in_progress )
                            log.i(tag, "    last continuous capture");
                    }
                    continuous_burst_requested_last_capture = !continuous_burst_in_progress;
                    int sequenceid = mcameracapturesession.capture(continuous_burst_in_progress ? request : last_request, previewcapturecallback, mcamerabackgroundhandler);
                    log.i(tag, "[create_sequence] takepictureburst bursttype_continuous capture sequenceid:"   sequenceid);
                    if( continuous_burst_in_progress ) {
                        final int continuous_burst_rate_ms = 100;
                        // also take the next burst after a delay
                        mcamerabackgroundhandler.postdelayed(new runnable() {
                            @override
                            public void run() {
                                // note, even if continuous_burst_in_progress has become false by this point, still take one last
                                // photo, as need to ensure that we have a request with requesttagtype.capture, as well as ensuring
                                // we call the oncompleted() method of the callback
                                if( mydebug.log ) {
                                    log.i(tag, "take next continuous burst");
                                    log.i(tag, "continuous_burst_in_progress: "   continuous_burst_in_progress);
                                    log.i(tag, "n_burst: "   n_burst);
                                }
                                if( n_burst >= 10 || n_burst_raw >= 10 ) {
                                    // nokia 8 in std mode without post-processing options doesn't hit this limit (we only hit this
                                    // if it's set to "n_burst >= 5")
                                    if( mydebug.log ) {
                                        log.i(tag, "...but wait for continuous burst, as waiting for too many photos");
                                    }
                                    //throw new runtimeexception(); // test
                                    mcamerabackgroundhandler.postdelayed(this, continuous_burst_rate_ms);
                                }
                                else if( picture_cb.imagequeuewouldblock(n_burst_raw, n_burst 1) ) {
                                    if( mydebug.log ) {
                                        log.i(tag, "...but wait for continuous burst, as image queue would block");
                                    }
                                    //throw new runtimeexception(); // test
                                    mcamerabackgroundhandler.postdelayed(this, continuous_burst_rate_ms);
                                }
                                else {
                                    takepictureburst(true);
                                }
                            }
                        }, continuous_burst_rate_ms);
                    }
                }
                else if( use_burst ) {
                    list requests = new arraylist<>();
                    for(int i=0;i 0 ) {
                                        mcamerabackgroundhandler.postdelayed(this, burst_delay);
                                    }
                                }
                                catch(cameraaccessexception e) {
                                    if( mydebug.log ) {
                                        log.e(tag, "failed to take picture burst");
                                        log.e(tag, "reason: "   e.getreason());
                                        log.e(tag, "message: "   e.getmessage());
                                    }
                                    e.printstacktrace();
                                    jpeg_todo = false;
                                    raw_todo = false;
                                    picture_cb = null;
                                    push_take_picture_error_cb = take_picture_error_cb;
                                }
                                // need to call callbacks without a lock
                                if( push_take_picture_error_cb != null ) {
                                    push_take_picture_error_cb.onerror();
                                }
                            }
                        }
                    }.run();
                }
                if( !continuing_fast_burst ) {
                    playsound(mediaactionsound.shutter_click); // play shutter sound asap, otherwise user has the illusion of being slow to take photos
                }
            }
            catch(cameraaccessexception e) {
                if( mydebug.log ) {
                    log.e(tag, "failed to take picture burst");
                    log.e(tag, "reason: "   e.getreason());
                    log.e(tag, "message: "   e.getmessage());
                }
                e.printstacktrace();
                //noinspection unusedassignment
                ok = false;
                jpeg_todo = false;
                raw_todo = false;
                picture_cb = null;
                push_take_picture_error_cb = take_picture_error_cb;
            }
        }
    }
    // need to call callbacks without a lock
    if( push_take_picture_error_cb != null ) {
        push_take_picture_error_cb.onerror();
    }
}

camera课程

python教程

java教程

web教程

数据库教程

图形图像教程

办公软件教程

linux教程

计算机教程

大数据教程

开发工具教程

android camera2 api

网站地图