| # Copyright 2015 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import os |
| |
| import its.caps |
| import its.device |
| import its.image |
| import its.objects |
| |
| import matplotlib |
| from matplotlib import pylab |
| import numpy |
| |
| NAME = os.path.basename(__file__).split('.')[0] |
| NUM_FRAMES = 4 # number of frames for temporal info to settle |
| NUM_SHADING_MODE_SWITCH_LOOPS = 3 |
| SHADING_MODES = ['OFF', 'FAST', 'HQ'] |
| THRESHOLD_DIFF_RATIO = 0.15 |
| |
| |
| def main(): |
| """Test that the android.shading.mode param is applied. |
| |
| Switching shading modes and checks that the lens shading maps are |
| modified as expected. |
| """ |
| |
| with its.device.ItsSession() as cam: |
| props = cam.get_camera_properties() |
| |
| its.caps.skip_unless(its.caps.per_frame_control(props) and |
| its.caps.lsc_map(props) and |
| its.caps.lsc_off(props)) |
| |
| mono_camera = its.caps.mono_camera(props) |
| |
| # lsc_off devices should always support OFF(0), FAST(1), and HQ(2) |
| assert(props.has_key('android.shading.availableModes') and |
| set(props['android.shading.availableModes']) == set([0, 1, 2])) |
| |
| # Test 1: Switching shading modes several times and verify: |
| # 1. Lens shading maps with mode OFF are all 1.0 |
| # 2. Lens shading maps with mode FAST are similar after switching |
| # shading modes. |
| # 3. Lens shading maps with mode HIGH_QUALITY are similar after |
| # switching shading modes. |
| cam.do_3a(mono_camera=mono_camera) |
| |
| # Use smallest yuv size matching the aspect ratio of largest yuv size to |
| # reduce some USB bandwidth overhead since we are only looking at output |
| # metadata in this test. |
| largest_yuv_fmt = its.objects.get_largest_yuv_format(props) |
| largest_yuv_size = (largest_yuv_fmt['width'], largest_yuv_fmt['height']) |
| cap_fmt = its.objects.get_smallest_yuv_format(props, largest_yuv_size) |
| |
| # Get the reference lens shading maps for OFF, FAST, and HIGH_QUALITY |
| # in different sessions. |
| # reference_maps[mode] |
| num_shading_modes = len(SHADING_MODES) |
| reference_maps = [[] for mode in range(num_shading_modes)] |
| num_map_gains = 0 |
| for mode in range(1, num_shading_modes): |
| req = its.objects.auto_capture_request() |
| req['android.statistics.lensShadingMapMode'] = 1 |
| req['android.shading.mode'] = mode |
| cap_res = cam.do_capture([req]*NUM_FRAMES, cap_fmt)[NUM_FRAMES-1]['metadata'] |
| lsc_map = cap_res['android.statistics.lensShadingCorrectionMap'] |
| assert(lsc_map.has_key('width') and |
| lsc_map.has_key('height') and |
| lsc_map['width'] is not None and |
| lsc_map['height'] is not None) |
| if mode == 1: |
| num_map_gains = lsc_map['width'] * lsc_map['height'] * 4 |
| reference_maps[0] = [1.0] * num_map_gains |
| reference_maps[mode] = lsc_map['map'] |
| |
| # Get the lens shading maps while switching modes in one session. |
| reqs = [] |
| for i in range(NUM_SHADING_MODE_SWITCH_LOOPS): |
| for mode in range(num_shading_modes): |
| for _ in range(NUM_FRAMES): |
| req = its.objects.auto_capture_request() |
| req['android.statistics.lensShadingMapMode'] = 1 |
| req['android.shading.mode'] = mode |
| reqs.append(req) |
| |
| caps = cam.do_capture(reqs, cap_fmt) |
| |
| # shading_maps[mode][loop] |
| shading_maps = [[[] for loop in range(NUM_SHADING_MODE_SWITCH_LOOPS)] |
| for mode in range(num_shading_modes)] |
| |
| # Get the shading maps out of capture results |
| for i in range(len(caps)/NUM_FRAMES): |
| shading_maps[i%num_shading_modes][i/NUM_SHADING_MODE_SWITCH_LOOPS] = \ |
| caps[(i+1)*NUM_FRAMES-1]['metadata']['android.statistics.lensShadingCorrectionMap']['map'] |
| |
| # Draw the maps |
| for mode in range(num_shading_modes): |
| for i in range(NUM_SHADING_MODE_SWITCH_LOOPS): |
| pylab.clf() |
| pylab.figure(figsize=(5, 5)) |
| pylab.subplot(2, 1, 1) |
| pylab.plot(range(num_map_gains), shading_maps[mode][i], '-r.', |
| label='shading', alpha=0.7) |
| pylab.plot(range(num_map_gains), reference_maps[mode], '-g.', |
| label='ref', alpha=0.7) |
| pylab.xlim([0, num_map_gains]) |
| pylab.ylim([0.9, 4.0]) |
| name = '%s_ls_maps_mode_%d_loop_%d' % (NAME, mode, i) |
| pylab.title(name) |
| pylab.xlabel('Map gains') |
| pylab.ylabel('Lens shading maps') |
| pylab.legend(loc='upper center', numpoints=1, fancybox=True) |
| |
| pylab.subplot(2, 1, 2) |
| shading_ref_ratio = numpy.divide( |
| shading_maps[mode][i], reference_maps[mode]) |
| pylab.plot(range(num_map_gains), shading_ref_ratio, '-b.', |
| clip_on=False) |
| pylab.xlim([0, num_map_gains]) |
| pylab.ylim([1.0-THRESHOLD_DIFF_RATIO, 1.0+THRESHOLD_DIFF_RATIO]) |
| pylab.title('Shading/reference Maps Ratio vs Gain') |
| pylab.xlabel('Map gains') |
| pylab.ylabel('Shading/ref maps ratio') |
| |
| pylab.tight_layout() |
| matplotlib.pyplot.savefig('%s.png' % name) |
| |
| for mode in range(num_shading_modes): |
| if mode == 0: |
| print 'Verifying lens shading maps with mode %s are all 1.0' % ( |
| SHADING_MODES[mode]) |
| else: |
| print 'Verifying lens shading maps with mode %s are similar' % ( |
| SHADING_MODES[mode]) |
| for i in range(NUM_SHADING_MODE_SWITCH_LOOPS): |
| e_msg = 'FAIL mode: %s, loop: %d, THRESH: %.2f' % ( |
| SHADING_MODES[mode], i, THRESHOLD_DIFF_RATIO) |
| assert (numpy.allclose(shading_maps[mode][i], |
| reference_maps[mode], |
| rtol=THRESHOLD_DIFF_RATIO)), e_msg |
| |
| if __name__ == '__main__': |
| main() |