• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import multiprocessing
16import os.path
17import subprocess
18import sys
19import time
20import its.caps
21import its.device
22import its.image
23import its.objects
24
25BRIGHT_CHANGE_TOL = 0.2
26CONTINUOUS_PICTURE_MODE = 4
27CONVERGED_3A = [2, 2, 2]  # [AE, AF, AWB]
28# AE_STATES: {0: INACTIVE, 1: SEARCHING, 2: CONVERGED, 3: LOCKED,
29#             4: FLASH_REQ, 5: PRECAPTURE}
30# AF_STATES: {0: INACTIVE, 1: PASSIVE_SCAN, 2: PASSIVE_FOCUSED,
31#             3: ACTIVE_SCAN, 4: FOCUS_LOCKED, 5: NOT_FOCUSED_LOCKED,
32#             6: PASSIVE_UNFOCUSED}
33# AWB_STATES: {0: INACTIVE, 1: SEARCHING, 2: CONVERGED, 3: LOCKED}
34DELAY_CAPTURE = 1.5  # delay in first capture to sync events (sec)
35DELAY_DISPLAY = 3.0  # time when display turns OFF (sec)
36FPS = 30
37FRAME_SHIFT = 5.0  # number of frames to shift to try and find scene change
38NAME = os.path.basename(__file__).split('.')[0]
39NUM_BURSTS = 6
40NUM_FRAMES = 50
41W, H = 640, 480
42
43
44def get_cmd_line_args():
45    chart_host_id = None
46    for s in list(sys.argv[1:]):
47        if s[:6] == 'chart=' and len(s) > 6:
48            chart_host_id = s[6:]
49    return chart_host_id
50
51
52def mask_3a_settling_frames(cap_data):
53    converged_frame = -1
54    for i, cap in enumerate(cap_data):
55        if cap['3a_state'] == CONVERGED_3A:
56            converged_frame = i
57            break
58    print 'Frames index where 3A converges: %d' % converged_frame
59    return converged_frame
60
61
62def determine_if_scene_changed(cap_data, converged_frame):
63    scene_changed = False
64    bright_changed = False
65    start_frame_brightness = cap_data[0]['avg']
66    for i in range(converged_frame, len(cap_data)):
67        if cap_data[i]['avg'] <= (
68                start_frame_brightness * (1.0 - BRIGHT_CHANGE_TOL)):
69            bright_changed = True
70        if cap_data[i]['flag'] == 1:
71            scene_changed = True
72    return scene_changed, bright_changed
73
74
75def toggle_screen(chart_host_id, state, delay):
76    t0 = time.time()
77    screen_id_arg = ('screen=%s' % chart_host_id)
78    state_id_arg = 'state=%s' % state
79    delay_arg = 'delay=%.3f' % delay
80    cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
81                                  'toggle_screen.py'), screen_id_arg,
82           state_id_arg, delay_arg]
83    screen_cmd_code = subprocess.call(cmd)
84    assert screen_cmd_code == 0
85    t = time.time() - t0
86    print 'tablet event %s: %.3f' % (state, t)
87
88
89def capture_frames(cam, delay, burst):
90    """Capture frames."""
91    cap_data_list = []
92    req = its.objects.auto_capture_request()
93    req['android.control.afMode'] = CONTINUOUS_PICTURE_MODE
94    fmt = {'format': 'yuv', 'width': W, 'height': H}
95    t0 = time.time()
96    time.sleep(delay)
97    print 'cap event start:', time.time() - t0
98    caps = cam.do_capture([req]*NUM_FRAMES, fmt)
99    print 'cap event stop:', time.time() - t0
100    # extract frame metadata and frame
101    for i, cap in enumerate(caps):
102        cap_data = {}
103        md = cap['metadata']
104        exp = md['android.sensor.exposureTime']
105        iso = md['android.sensor.sensitivity']
106        fd = md['android.lens.focalLength']
107        ae_state = md['android.control.aeState']
108        af_state = md['android.control.afState']
109        awb_state = md['android.control.awbState']
110        fd_str = 'infinity'
111        if fd != 0.0:
112            fd_str = str(round(1.0E2/fd, 2)) + 'cm'
113        scene_change_flag = md['android.control.afSceneChange']
114        assert scene_change_flag in [0, 1], 'afSceneChange not in [0,1]'
115        img = its.image.convert_capture_to_rgb_image(cap)
116        its.image.write_image(img, '%s_%d_%d.jpg' % (NAME, burst, i))
117        tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
118        g = its.image.compute_image_means(tile)[1]
119        print '%d, iso: %d, exp: %.2fms, fd: %s, avg: %.3f' % (
120                i, iso, exp*1E-6, fd_str, g),
121        print '[ae,af,awb]: [%d,%d,%d], change: %d' % (
122                ae_state, af_state, awb_state, scene_change_flag)
123        cap_data['exp'] = exp
124        cap_data['iso'] = iso
125        cap_data['fd'] = fd
126        cap_data['3a_state'] = [ae_state, af_state, awb_state]
127        cap_data['avg'] = g
128        cap_data['flag'] = scene_change_flag
129        cap_data_list.append(cap_data)
130    return cap_data_list
131
132
133def main():
134    """Test scene change.
135
136    Do auto capture with face scene. Power down tablet and recapture.
137    Confirm android.control.afSceneChangeDetected is True.
138    """
139    # check for skip conditions and do 3a up front
140    with its.device.ItsSession() as cam:
141        props = cam.get_camera_properties()
142        props = cam.override_with_hidden_physical_camera_props(props)
143        its.caps.skip_unless(its.caps.continuous_picture(props) and
144                             its.caps.af_scene_change(props) and
145                             its.caps.read_3a(props))
146        cam.do_3a()
147
148        # do captures with scene change
149        chart_host_id = get_cmd_line_args()
150        scene_delay = DELAY_DISPLAY
151        for burst in range(NUM_BURSTS):
152            print 'burst number: %d' % burst
153            # create scene change by turning off chart display & capture frames
154            if chart_host_id:
155                print '\nToggling tablet. Scene change at %.3fs.' % scene_delay
156                multiprocessing.Process(name='p1', target=toggle_screen,
157                                        args=(chart_host_id, 'OFF',
158                                              scene_delay,)).start()
159            else:
160                print '\nWave hand in front of camera to create scene change.'
161            cap_data = capture_frames(cam, DELAY_CAPTURE, burst)
162
163            # find frame where 3A converges
164            converged_frame = mask_3a_settling_frames(cap_data)
165
166            # turn tablet back on to return to baseline scene state
167            if chart_host_id:
168                toggle_screen(chart_host_id, 'ON', 0)
169
170            # determine if brightness changed and/or scene change flag asserted
171            scene_changed, bright_changed = determine_if_scene_changed(
172                    cap_data, converged_frame)
173
174            # handle different capture cases
175            if converged_frame > -1:  # 3A converges
176                if scene_changed:
177                    if bright_changed:
178                        print ' scene & brightness change on burst %d.' % burst
179                        sys.exit(0)
180                    else:
181                        msg = ' scene change, but no brightness change.'
182                        assert False, msg
183                else:  # shift scene change timing if no scene change
184                    scene_shift = FRAME_SHIFT / FPS
185                    if bright_changed:
186                        print ' No scene change, but brightness change.'
187                        print 'Shift %.3fs earlier' % scene_shift
188                        scene_delay -= scene_shift  # tablet-off earlier
189                    else:
190                        scene_shift = FRAME_SHIFT / FPS * NUM_BURSTS
191                        print ' No scene change, no brightness change.'
192                        if cap_data[NUM_FRAMES-1]['avg'] < 0.2:
193                            print ' Scene dark entire capture.',
194                            print 'Shift %.3fs later.' % scene_shift
195                            scene_delay += scene_shift  # tablet-off later
196                        else:
197                            print ' Scene light entire capture.',
198                            print 'Shift %.3fs earlier.' % scene_shift
199                            scene_delay -= scene_shift  # tablet-off earlier
200
201            else:  # 3A does not converge
202                if bright_changed:
203                    scene_shift = FRAME_SHIFT / FPS
204                    print ' 3A does not converge, but brightness change.',
205                    print 'Shift %.3fs later' % scene_shift
206                    scene_delay += scene_shift  # tablet-off earlier
207                else:
208                    msg = ' 3A does not converge with no brightness change.'
209                    assert False, msg
210
211        # fail out if too many tries
212        msg = 'No scene change in %dx tries' % NUM_BURSTS
213        assert False, msg
214
215
216if __name__ == '__main__':
217    main()
218