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 15 import its.image 16 import its.caps 17 import its.device 18 import its.objects 19 import os.path 20 import numpy 21 import matplotlib.pyplot 22 23 # Required for 3d plot to work 24 import mpl_toolkits.mplot3d 25 26 def main(): 27 """Test that valid data comes back in CaptureResult objects. 28 """ 29 global NAME, auto_req, manual_req, w_map, h_map 30 global manual_tonemap, manual_transform, manual_gains, manual_region 31 global manual_exp_time, manual_sensitivity, manual_gains_ok 32 33 NAME = os.path.basename(__file__).split(".")[0] 34 35 with its.device.ItsSession() as cam: 36 props = cam.get_camera_properties() 37 its.caps.skip_unless(its.caps.manual_sensor(props) and 38 its.caps.manual_post_proc(props) and 39 its.caps.per_frame_control(props)) 40 41 manual_tonemap = [0,0, 1,1] # Linear 42 manual_transform = its.objects.int_to_rational([1,2,3, 4,5,6, 7,8,9]) 43 manual_gains = [1,2,3,4] 44 manual_region = [{"x":8,"y":8,"width":128,"height":128,"weight":1}] 45 manual_exp_time = min(props['android.sensor.info.exposureTimeRange']) 46 manual_sensitivity = min(props['android.sensor.info.sensitivityRange']) 47 48 # The camera HAL may not support different gains for two G channels. 49 manual_gains_ok = [[1,2,3,4],[1,2,2,4],[1,3,3,4]] 50 51 auto_req = its.objects.auto_capture_request() 52 auto_req["android.statistics.lensShadingMapMode"] = 1 53 54 manual_req = { 55 "android.control.mode": 0, 56 "android.control.aeMode": 0, 57 "android.control.awbMode": 0, 58 "android.control.afMode": 0, 59 "android.sensor.frameDuration": 0, 60 "android.sensor.sensitivity": manual_sensitivity, 61 "android.sensor.exposureTime": manual_exp_time, 62 "android.colorCorrection.mode": 0, 63 "android.colorCorrection.transform": manual_transform, 64 "android.colorCorrection.gains": manual_gains, 65 "android.tonemap.mode": 0, 66 "android.tonemap.curveRed": manual_tonemap, 67 "android.tonemap.curveGreen": manual_tonemap, 68 "android.tonemap.curveBlue": manual_tonemap, 69 "android.control.aeRegions": manual_region, 70 "android.control.afRegions": manual_region, 71 "android.control.awbRegions": manual_region, 72 "android.statistics.lensShadingMapMode":1 73 } 74 75 w_map = props["android.lens.info.shadingMapSize"]["width"] 76 h_map = props["android.lens.info.shadingMapSize"]["height"] 77 78 print "Testing auto capture results" 79 lsc_map_auto = test_auto(cam, w_map, h_map) 80 print "Testing manual capture results" 81 test_manual(cam, w_map, h_map, lsc_map_auto) 82 print "Testing auto capture results again" 83 test_auto(cam, w_map, h_map) 84 85 # A very loose definition for two floats being close to each other; 86 # there may be different interpolation and rounding used to get the 87 # two values, and all this test is looking at is whether there is 88 # something obviously broken; it's not looking for a perfect match. 89 def is_close_float(n1, n2): 90 return abs(n1 - n2) < 0.05 91 92 def is_close_rational(n1, n2): 93 return is_close_float(its.objects.rational_to_float(n1), 94 its.objects.rational_to_float(n2)) 95 96 def draw_lsc_plot(w_map, h_map, lsc_map, name): 97 for ch in range(4): 98 fig = matplotlib.pyplot.figure() 99 ax = fig.gca(projection='3d') 100 xs = numpy.array([range(w_map)] * h_map).reshape(h_map, w_map) 101 ys = numpy.array([[i]*w_map for i in range(h_map)]).reshape( 102 h_map, w_map) 103 zs = numpy.array(lsc_map[ch::4]).reshape(h_map, w_map) 104 ax.plot_wireframe(xs, ys, zs) 105 matplotlib.pyplot.savefig("%s_plot_lsc_%s_ch%d.png"%(NAME,name,ch)) 106 107 def test_auto(cam, w_map, h_map): 108 # Get 3A lock first, so the auto values in the capture result are 109 # populated properly. 110 rect = [[0,0,1,1,1]] 111 cam.do_3a(rect, rect, rect, do_af=False) 112 113 cap = cam.do_capture(auto_req) 114 cap_res = cap["metadata"] 115 116 gains = cap_res["android.colorCorrection.gains"] 117 transform = cap_res["android.colorCorrection.transform"] 118 exp_time = cap_res['android.sensor.exposureTime'] 119 lsc_map = cap_res["android.statistics.lensShadingMap"] 120 ctrl_mode = cap_res["android.control.mode"] 121 122 print "Control mode:", ctrl_mode 123 print "Gains:", gains 124 print "Transform:", [its.objects.rational_to_float(t) 125 for t in transform] 126 print "AE region:", cap_res['android.control.aeRegions'] 127 print "AF region:", cap_res['android.control.afRegions'] 128 print "AWB region:", cap_res['android.control.awbRegions'] 129 print "LSC map:", w_map, h_map, lsc_map[:8] 130 131 assert(ctrl_mode == 1) 132 133 # Color correction gain and transform must be valid. 134 assert(len(gains) == 4) 135 assert(len(transform) == 9) 136 assert(all([g > 0 for g in gains])) 137 assert(all([t["denominator"] != 0 for t in transform])) 138 139 # Color correction should not match the manual settings. 140 assert(any([not is_close_float(gains[i], manual_gains[i]) 141 for i in xrange(4)])) 142 assert(any([not is_close_rational(transform[i], manual_transform[i]) 143 for i in xrange(9)])) 144 145 # Exposure time must be valid. 146 assert(exp_time > 0) 147 148 # Lens shading map must be valid. 149 assert(w_map > 0 and h_map > 0 and w_map * h_map * 4 == len(lsc_map)) 150 assert(all([m >= 1 for m in lsc_map])) 151 152 draw_lsc_plot(w_map, h_map, lsc_map, "auto") 153 154 return lsc_map 155 156 def test_manual(cam, w_map, h_map, lsc_map_auto): 157 cap = cam.do_capture(manual_req) 158 cap_res = cap["metadata"] 159 160 gains = cap_res["android.colorCorrection.gains"] 161 transform = cap_res["android.colorCorrection.transform"] 162 curves = [cap_res["android.tonemap.curveRed"], 163 cap_res["android.tonemap.curveGreen"], 164 cap_res["android.tonemap.curveBlue"]] 165 exp_time = cap_res['android.sensor.exposureTime'] 166 lsc_map = cap_res["android.statistics.lensShadingMap"] 167 ctrl_mode = cap_res["android.control.mode"] 168 169 print "Control mode:", ctrl_mode 170 print "Gains:", gains 171 print "Transform:", [its.objects.rational_to_float(t) 172 for t in transform] 173 print "Tonemap:", curves[0][1::16] 174 print "AE region:", cap_res['android.control.aeRegions'] 175 print "AF region:", cap_res['android.control.afRegions'] 176 print "AWB region:", cap_res['android.control.awbRegions'] 177 print "LSC map:", w_map, h_map, lsc_map[:8] 178 179 assert(ctrl_mode == 0) 180 181 # Color correction gain and transform must be valid. 182 # Color correction gains and transform should be the same size and 183 # values as the manually set values. 184 assert(len(gains) == 4) 185 assert(len(transform) == 9) 186 assert( all([is_close_float(gains[i], manual_gains_ok[0][i]) 187 for i in xrange(4)]) or 188 all([is_close_float(gains[i], manual_gains_ok[1][i]) 189 for i in xrange(4)]) or 190 all([is_close_float(gains[i], manual_gains_ok[2][i]) 191 for i in xrange(4)])) 192 assert(all([is_close_rational(transform[i], manual_transform[i]) 193 for i in xrange(9)])) 194 195 # Tonemap must be valid. 196 # The returned tonemap must be linear. 197 for c in curves: 198 assert(len(c) > 0) 199 assert(all([is_close_float(c[i], c[i+1]) 200 for i in xrange(0,len(c),2)])) 201 202 # Exposure time must be close to the requested exposure time. 203 assert(is_close_float(exp_time/1000000.0, manual_exp_time/1000000.0)) 204 205 # Lens shading map must be valid. 206 assert(w_map > 0 and h_map > 0 and w_map * h_map * 4 == len(lsc_map)) 207 assert(all([m >= 1 for m in lsc_map])) 208 209 draw_lsc_plot(w_map, h_map, lsc_map, "manual") 210 211 if __name__ == '__main__': 212 main() 213 214