1# Copyright 2013 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 math 16import os.path 17import its.caps 18import its.device 19import its.image 20import its.objects 21import its.target 22import matplotlib 23from matplotlib import pylab 24import numpy 25 26NAME = os.path.basename(__file__).split('.')[0] 27RESIDUAL_THRESHOLD = 0.0003 # approximately each sample is off by 2/255 28# The HAL3.2 spec requires that curves up to 64 control points in length 29# must be supported. 30L = 64 31LM1 = float(L-1) 32 33 34def main(): 35 """Test that device processing can be inverted to linear pixels. 36 37 Captures a sequence of shots with the device pointed at a uniform 38 target. Attempts to invert all the ISP processing to get back to 39 linear R,G,B pixel data. 40 """ 41 gamma_lut = numpy.array( 42 sum([[i/LM1, math.pow(i/LM1, 1/2.2)] for i in xrange(L)], [])) 43 inv_gamma_lut = numpy.array( 44 sum([[i/LM1, math.pow(i/LM1, 2.2)] for i in xrange(L)], [])) 45 46 with its.device.ItsSession() as cam: 47 props = cam.get_camera_properties() 48 props = cam.override_with_hidden_physical_camera_props(props) 49 its.caps.skip_unless(its.caps.compute_target_exposure(props)) 50 sync_latency = its.caps.sync_latency(props) 51 52 debug = its.caps.debug_mode() 53 largest_yuv = its.objects.get_largest_yuv_format(props) 54 if debug: 55 fmt = largest_yuv 56 else: 57 match_ar = (largest_yuv['width'], largest_yuv['height']) 58 fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar) 59 60 e, s = its.target.get_target_exposure_combos(cam)['midSensitivity'] 61 s /= 2 62 sens_range = props['android.sensor.info.sensitivityRange'] 63 sensitivities = [s*1.0/3.0, s*2.0/3.0, s, s*4.0/3.0, s*5.0/3.0] 64 sensitivities = [s for s in sensitivities 65 if s > sens_range[0] and s < sens_range[1]] 66 67 req = its.objects.manual_capture_request(0, e) 68 req['android.blackLevel.lock'] = True 69 req['android.tonemap.mode'] = 0 70 req['android.tonemap.curve'] = { 71 'red': gamma_lut.tolist(), 72 'green': gamma_lut.tolist(), 73 'blue': gamma_lut.tolist()} 74 75 r_means = [] 76 g_means = [] 77 b_means = [] 78 79 for sens in sensitivities: 80 req['android.sensor.sensitivity'] = sens 81 cap = its.device.do_capture_with_latency( 82 cam, req, sync_latency, fmt) 83 img = its.image.convert_capture_to_rgb_image(cap) 84 its.image.write_image( 85 img, '%s_sens=%04d.jpg' % (NAME, sens)) 86 img = its.image.apply_lut_to_image(img, inv_gamma_lut[1::2] * LM1) 87 tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1) 88 rgb_means = its.image.compute_image_means(tile) 89 r_means.append(rgb_means[0]) 90 g_means.append(rgb_means[1]) 91 b_means.append(rgb_means[2]) 92 93 pylab.title(NAME) 94 pylab.plot(sensitivities, r_means, '-ro') 95 pylab.plot(sensitivities, g_means, '-go') 96 pylab.plot(sensitivities, b_means, '-bo') 97 pylab.xlim([sens_range[0], sens_range[1]/2]) 98 pylab.ylim([0, 1]) 99 pylab.xlabel('sensitivity(ISO)') 100 pylab.ylabel('RGB avg [0, 1]') 101 matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME)) 102 103 # Check that each plot is actually linear. 104 for means in [r_means, g_means, b_means]: 105 line, residuals, _, _, _ = numpy.polyfit(range(len(sensitivities)), 106 means, 1, full=True) 107 print 'Line: m=%f, b=%f, resid=%f'%(line[0], line[1], residuals[0]) 108 msg = 'residual: %.5f, THRESH: %.4f' % ( 109 residuals[0], RESIDUAL_THRESHOLD) 110 assert residuals[0] < RESIDUAL_THRESHOLD, msg 111 112if __name__ == '__main__': 113 main() 114 115